diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..9fe93ed --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +; EditorConfig file: https://EditorConfig.org +; Install the "EditorConfig" plugin into your editor to use + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +[package.json] +indent_size = 2 diff --git a/.eslintrc.json b/.eslintrc.json index 7c36f98..7bea33e 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,7 +1,19 @@ { "extends": "eslint", "env": { - "es6": true, - "node": true - } + "es2020": true + }, + "parserOptions": { + "sourceType": "module", + "ecmaVersion": 2020 + }, + "overrides": [ + { + "files": ["*.cjs"], + "parserOptions": { + "sourceType": "script" + } + } + ], + "ignorePatterns": ["/dist", "/coverage"] } diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e408474..7ed8d02 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - node: [14.x, 12.x, 10.x] + node: [16.x, 14.x, 12.x, "12.22.0"] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 diff --git a/.gitignore b/.gitignore index eb0f93d..aa803db 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ /node_modules /test.* .eslint-release-info.json +/dist diff --git a/README.md b/README.md index d7dbe65..71217e3 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,14 @@ $ npm install eslint-visitor-keys ## 📖 Usage +To use in an ESM file: + +```js +import * as evk from "eslint-visitor-keys" +``` + +To use in a CommonJS file: + ```js const evk = require("eslint-visitor-keys") ``` diff --git a/lib/index.js b/lib/index.js index cd8a326..44f73d6 100644 --- a/lib/index.js +++ b/lib/index.js @@ -2,18 +2,7 @@ * @author Toru Nagashima * See LICENSE file in root directory for full license. */ -"use strict"; - -const KEYS = require("./visitor-keys.json"); - -// Types. -const NODE_TYPES = Object.freeze(Object.keys(KEYS)); - -// Freeze the keys. -for (const type of NODE_TYPES) { - Object.freeze(KEYS[type]); -} -Object.freeze(KEYS); +import KEYS from "./visitor-keys.js"; // List to ignore keys. const KEY_BLACKLIST = new Set([ @@ -31,51 +20,40 @@ function filterKey(key) { return !KEY_BLACKLIST.has(key) && key[0] !== "_"; } -//------------------------------------------------------------------------------ -// Public interfaces -//------------------------------------------------------------------------------ - -module.exports = Object.freeze({ - - /** - * Visitor keys. - * @type {{ [type: string]: string[] | undefined }} - */ - KEYS, - - /** - * Get visitor keys of a given node. - * @param {Object} node The AST node to get keys. - * @returns {string[]} Visitor keys of the node. - */ - getKeys(node) { - return Object.keys(node).filter(filterKey); - }, - - // Disable valid-jsdoc rule because it reports syntax error on the type of @returns. - // eslint-disable-next-line valid-jsdoc - /** - * Make the union set with `KEYS` and given keys. - * @param {Object} additionalKeys The additional keys. - * @returns {{ [type: string]: string[] | undefined }} The union set. - */ - unionWith(additionalKeys) { - const retv = Object.assign({}, KEYS); +/** + * Get visitor keys of a given node. + * @param {Object} node The AST node to get keys. + * @returns {string[]} Visitor keys of the node. + */ +export function getKeys(node) { + return Object.keys(node).filter(filterKey); +} - for (const type of Object.keys(additionalKeys)) { - if (retv.hasOwnProperty(type)) { - const keys = new Set(additionalKeys[type]); +// Disable valid-jsdoc rule because it reports syntax error on the type of @returns. +// eslint-disable-next-line valid-jsdoc +/** + * Make the union set with `KEYS` and given keys. + * @param {Object} additionalKeys The additional keys. + * @returns {{ [type: string]: string[] | undefined }} The union set. + */ +export function unionWith(additionalKeys) { + const retv = Object.assign({}, KEYS); - for (const key of retv[type]) { - keys.add(key); - } + for (const type of Object.keys(additionalKeys)) { + if (Object.prototype.hasOwnProperty.call(retv, type)) { + const keys = new Set(additionalKeys[type]); - retv[type] = Object.freeze(Array.from(keys)); - } else { - retv[type] = Object.freeze(Array.from(additionalKeys[type])); + for (const key of retv[type]) { + keys.add(key); } - } - return Object.freeze(retv); + retv[type] = Object.freeze(Array.from(keys)); + } else { + retv[type] = Object.freeze(Array.from(additionalKeys[type])); + } } -}); + + return Object.freeze(retv); +} + +export { KEYS }; diff --git a/lib/visitor-keys.json b/lib/visitor-keys.js similarity index 56% rename from lib/visitor-keys.json rename to lib/visitor-keys.js index e648ee1..ade8fc3 100644 --- a/lib/visitor-keys.json +++ b/lib/visitor-keys.js @@ -1,289 +1,300 @@ -{ - "AssignmentExpression": [ +const KEYS = { + AssignmentExpression: [ "left", "right" ], - "AssignmentPattern": [ + AssignmentPattern: [ "left", "right" ], - "ArrayExpression": [ + ArrayExpression: [ "elements" ], - "ArrayPattern": [ + ArrayPattern: [ "elements" ], - "ArrowFunctionExpression": [ + ArrowFunctionExpression: [ "params", "body" ], - "AwaitExpression": [ + AwaitExpression: [ "argument" ], - "BlockStatement": [ + BlockStatement: [ "body" ], - "BinaryExpression": [ + BinaryExpression: [ "left", "right" ], - "BreakStatement": [ + BreakStatement: [ "label" ], - "CallExpression": [ + CallExpression: [ "callee", "arguments" ], - "CatchClause": [ + CatchClause: [ "param", "body" ], - "ChainExpression": [ + ChainExpression: [ "expression" ], - "ClassBody": [ + ClassBody: [ "body" ], - "ClassDeclaration": [ + ClassDeclaration: [ "id", "superClass", "body" ], - "ClassExpression": [ + ClassExpression: [ "id", "superClass", "body" ], - "ConditionalExpression": [ + ConditionalExpression: [ "test", "consequent", "alternate" ], - "ContinueStatement": [ + ContinueStatement: [ "label" ], - "DebuggerStatement": [], - "DoWhileStatement": [ + DebuggerStatement: [], + DoWhileStatement: [ "body", "test" ], - "EmptyStatement": [], - "ExportAllDeclaration": [ + EmptyStatement: [], + ExportAllDeclaration: [ "exported", "source" ], - "ExportDefaultDeclaration": [ + ExportDefaultDeclaration: [ "declaration" ], - "ExportNamedDeclaration": [ + ExportNamedDeclaration: [ "declaration", "specifiers", "source" ], - "ExportSpecifier": [ + ExportSpecifier: [ "exported", "local" ], - "ExpressionStatement": [ + ExpressionStatement: [ "expression" ], - "ExperimentalRestProperty": [ + ExperimentalRestProperty: [ "argument" ], - "ExperimentalSpreadProperty": [ + ExperimentalSpreadProperty: [ "argument" ], - "ForStatement": [ + ForStatement: [ "init", "test", "update", "body" ], - "ForInStatement": [ + ForInStatement: [ "left", "right", "body" ], - "ForOfStatement": [ + ForOfStatement: [ "left", "right", "body" ], - "FunctionDeclaration": [ + FunctionDeclaration: [ "id", "params", "body" ], - "FunctionExpression": [ + FunctionExpression: [ "id", "params", "body" ], - "Identifier": [], - "IfStatement": [ + Identifier: [], + IfStatement: [ "test", "consequent", "alternate" ], - "ImportDeclaration": [ + ImportDeclaration: [ "specifiers", "source" ], - "ImportDefaultSpecifier": [ + ImportDefaultSpecifier: [ "local" ], - "ImportExpression": [ + ImportExpression: [ "source" ], - "ImportNamespaceSpecifier": [ + ImportNamespaceSpecifier: [ "local" ], - "ImportSpecifier": [ + ImportSpecifier: [ "imported", "local" ], - "JSXAttribute": [ + JSXAttribute: [ "name", "value" ], - "JSXClosingElement": [ + JSXClosingElement: [ "name" ], - "JSXElement": [ + JSXElement: [ "openingElement", "children", "closingElement" ], - "JSXEmptyExpression": [], - "JSXExpressionContainer": [ + JSXEmptyExpression: [], + JSXExpressionContainer: [ "expression" ], - "JSXIdentifier": [], - "JSXMemberExpression": [ + JSXIdentifier: [], + JSXMemberExpression: [ "object", "property" ], - "JSXNamespacedName": [ + JSXNamespacedName: [ "namespace", "name" ], - "JSXOpeningElement": [ + JSXOpeningElement: [ "name", "attributes" ], - "JSXSpreadAttribute": [ + JSXSpreadAttribute: [ "argument" ], - "JSXText": [], - "JSXFragment": [ + JSXText: [], + JSXFragment: [ "openingFragment", "children", "closingFragment" ], - "Literal": [], - "LabeledStatement": [ + Literal: [], + LabeledStatement: [ "label", "body" ], - "LogicalExpression": [ + LogicalExpression: [ "left", "right" ], - "MemberExpression": [ + MemberExpression: [ "object", "property" ], - "MetaProperty": [ + MetaProperty: [ "meta", "property" ], - "MethodDefinition": [ + MethodDefinition: [ "key", "value" ], - "NewExpression": [ + NewExpression: [ "callee", "arguments" ], - "ObjectExpression": [ + ObjectExpression: [ "properties" ], - "ObjectPattern": [ + ObjectPattern: [ "properties" ], - "PrivateIdentifier": [], - "Program": [ + PrivateIdentifier: [], + Program: [ "body" ], - "Property": [ + Property: [ "key", "value" ], - "PropertyDefinition": [ + PropertyDefinition: [ "key", "value" ], - "RestElement": [ + RestElement: [ "argument" ], - "ReturnStatement": [ + ReturnStatement: [ "argument" ], - "SequenceExpression": [ + SequenceExpression: [ "expressions" ], - "SpreadElement": [ + SpreadElement: [ "argument" ], - "Super": [], - "SwitchStatement": [ + Super: [], + SwitchStatement: [ "discriminant", "cases" ], - "SwitchCase": [ + SwitchCase: [ "test", "consequent" ], - "TaggedTemplateExpression": [ + TaggedTemplateExpression: [ "tag", "quasi" ], - "TemplateElement": [], - "TemplateLiteral": [ + TemplateElement: [], + TemplateLiteral: [ "quasis", "expressions" ], - "ThisExpression": [], - "ThrowStatement": [ + ThisExpression: [], + ThrowStatement: [ "argument" ], - "TryStatement": [ + TryStatement: [ "block", "handler", "finalizer" ], - "UnaryExpression": [ + UnaryExpression: [ "argument" ], - "UpdateExpression": [ + UpdateExpression: [ "argument" ], - "VariableDeclaration": [ + VariableDeclaration: [ "declarations" ], - "VariableDeclarator": [ + VariableDeclarator: [ "id", "init" ], - "WhileStatement": [ + WhileStatement: [ "test", "body" ], - "WithStatement": [ + WithStatement: [ "object", "body" ], - "YieldExpression": [ + YieldExpression: [ "argument" ] +}; + +// Types. +const NODE_TYPES = Object.keys(KEYS); + +// Freeze the keys. +for (const type of NODE_TYPES) { + Object.freeze(KEYS[type]); } +Object.freeze(KEYS); + +export default KEYS; diff --git a/package.json b/package.json index a4a7d41..2531df5 100644 --- a/package.json +++ b/package.json @@ -2,25 +2,42 @@ "name": "eslint-visitor-keys", "version": "2.1.0", "description": "Constants and utilities about visitor keys to traverse AST.", - "main": "lib/index.js", + "type": "module", + "main": "dist/eslint-visitor-keys.cjs", + "exports": { + ".": [ + { + "import": "./lib/index.js", + "require": "./dist/eslint-visitor-keys.cjs" + }, + "./dist/eslint-visitor-keys.cjs" + ], + "./package.json": "./package.json" + }, "files": [ + "dist/eslint-visitor-keys.cjs", "lib" ], "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "devDependencies": { - "eslint": "^4.7.2", - "eslint-config-eslint": "^4.0.0", + "c8": "^7.7.3", + "eslint": "^7.29.0", + "eslint-config-eslint": "^7.0.0", + "eslint-plugin-jsdoc": "^35.4.0", + "eslint-plugin-node": "^11.1.0", "eslint-release": "^3.1.2", - "mocha": "^3.5.3", - "nyc": "^11.2.1", - "opener": "^1.4.3" + "mocha": "^9.0.1", + "opener": "^1.5.2", + "rollup": "^2.52.1" }, "scripts": { - "lint": "eslint lib tests/lib", - "test": "nyc mocha tests/lib", - "coverage": "nyc report --reporter lcov && opener coverage/lcov-report/index.html", + "prepare": "npm run build", + "build": "rollup -c", + "lint": "eslint .", + "test": "mocha tests/lib/**/*.cjs && c8 mocha tests/lib/**/*.js", + "coverage": "c8 report --reporter lcov && opener coverage/lcov-report/index.html", "generate-release": "eslint-generate-release", "generate-alpharelease": "eslint-generate-prerelease alpha", "generate-betarelease": "eslint-generate-prerelease beta", diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..d3fc0ff --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,9 @@ +export default { + input: "./lib/index.js", + treeshake: false, + output: { + format: "cjs", + file: "dist/eslint-visitor-keys.cjs", + sourcemap: true + } +}; diff --git a/tests/lib/commonjs.cjs b/tests/lib/commonjs.cjs new file mode 100644 index 0000000..47b2cf7 --- /dev/null +++ b/tests/lib/commonjs.cjs @@ -0,0 +1,56 @@ +/** + * @fileoverview Tests for checking that the commonjs entry points are still accessible + * @author Mike Reinstein + */ + +// eslint-disable-next-line strict +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const assert = require("assert"); +const eslintVisitorKeys = require("../../dist/eslint-visitor-keys.cjs"); + + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +describe("commonjs", () => { + it("is an object", () => { + assert.strictEqual(typeof eslintVisitorKeys, "object"); + }); + + it("has exported keys object", () => { + assert.strictEqual(typeof eslintVisitorKeys.KEYS, "object"); + }); + + it("has key array with AST type", () => { + assert.ok(Array.isArray(eslintVisitorKeys.KEYS.ArrayExpression)); + }); + + it("has getKeys function", () => { + assert.strictEqual(typeof eslintVisitorKeys.getKeys, "function"); + }); + + it("should have getKeys which returns keys", () => { + assert.deepStrictEqual(eslintVisitorKeys.getKeys({ a: 1, b: 2 }), ["a", "b"]); + }); + + it("has unionWith function", () => { + assert.strictEqual(typeof eslintVisitorKeys.unionWith, "function"); + }); + + it("should have unionWith which includes all additional keys", () => { + const additionalKeys = { Program: ["body", "a"], AssignmentExpression: ["b"], additional: ["c"], MethodDefinition: ["a", "key", "b"] }; + const unionKeys = eslintVisitorKeys.unionWith(additionalKeys); + + for (const type of Object.keys(additionalKeys)) { + for (const key of additionalKeys[type]) { + assert(unionKeys[type].indexOf(key) !== -1, `'${key}' should be included in '${type}'.`); + } + } + }); +}); diff --git a/tests/lib/index.js b/tests/lib/index.js index d13d8cc..c7497eb 100644 --- a/tests/lib/index.js +++ b/tests/lib/index.js @@ -2,17 +2,13 @@ * @author Toru Nagashima * See LICENSE file in root directory for full license. */ -"use strict"; - -const assert = require("assert"); -const fs = require("fs"); -const evk = require("../.."); - -const keys = JSON.parse(fs.readFileSync("lib/visitor-keys.json", "utf8")); +import assert from "assert"; +import * as evk from "../../lib/index.js"; +import keys from "../../lib/visitor-keys.js"; describe("eslint-visitor-keys", () => { describe("KEYS", () => { - it("should be same as lib/visitor-keys.json", () => { + it("should be same as lib/visitor-keys.js", () => { assert.deepStrictEqual(evk.KEYS, keys); }); }); @@ -43,7 +39,7 @@ describe("eslint-visitor-keys", () => { const additionalKeys = { Program: ["body", "a"], AssignmentExpression: ["b"], additional: ["c"], MethodDefinition: ["a", "key", "b"] }; const unionKeys = evk.unionWith(additionalKeys); - it("should include all keys of lib/visitor-keys.json", () => { + it("should include all keys of lib/visitor-keys.js", () => { for (const type of Object.keys(keys)) { for (const key of keys[type]) { assert(unionKeys[type].indexOf(key) !== -1, `'${key}' should be included in '${type}'.`);