From faf1ebc9f35324b22d251bc3a732097f3034ba17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=AF=E7=84=B6?= Date: Fri, 14 Feb 2020 03:46:48 +0800 Subject: [PATCH] Chore(*): restruct repo --- .editorconfig | 18 +- .../bug-report--eslint-plugin-autofix.md | 36 +- .../bug-report--eslint-plugin-no-autofix.md | 36 +- .gitignore | 20 +- .npmrc | 2 +- appveyor.yml | 56 +- package.json | 81 +- packages/autofix/.npmrc | 2 +- packages/autofix/LICENSE | 40 +- packages/autofix/lib/ast-utils.js | 99 +- packages/autofix/lib/configs/all.js | 48 +- packages/autofix/lib/configs/recommended.js | 48 +- packages/autofix/lib/configs/unsafe.js | 48 +- packages/autofix/lib/index.js | 38 +- packages/autofix/lib/rules.js | 120 +- packages/autofix/lib/rules/no-alert.js | 64 +- packages/autofix/lib/rules/no-caller.js | 78 +- packages/autofix/lib/rules/no-console.js | 96 +- packages/autofix/lib/rules/no-debugger.js | 52 +- packages/autofix/lib/rules/no-eq-null.js | 52 +- packages/autofix/lib/rules/no-new-symbol.js | 44 +- packages/autofix/lib/rules/no-plusplus.js | 54 +- packages/autofix/lib/rules/no-proto.js | 44 +- .../lib/rules/no-prototype-builtins.js | 54 +- .../autofix/lib/rules/no-throw-literal.js | 52 +- packages/autofix/lib/rules/no-unused-vars.js | 434 ++- .../autofix/lib/rules/no-useless-catch.js | 52 +- .../autofix/lib/rules/no-useless-concat.js | 44 +- packages/autofix/lib/rules/prefer-spread.js | 70 +- packages/autofix/lib/rules/radix.js | 74 +- packages/autofix/lib/rules/valid-typeof.js | 62 +- packages/autofix/lib/utils.js | 30 +- packages/autofix/package.json | 136 +- packages/autofix/readme.md | 330 +- packages/autofix/tests/lib/index.js | 50 +- packages/autofix/tests/lib/rules/no-alert.js | 150 +- packages/autofix/tests/lib/rules/no-caller.js | 116 +- .../autofix/tests/lib/rules/no-console.js | 154 +- .../autofix/tests/lib/rules/no-debugger.js | 72 +- .../autofix/tests/lib/rules/no-eq-null.js | 96 +- .../autofix/tests/lib/rules/no-new-symbol.js | 64 +- .../autofix/tests/lib/rules/no-plusplus.js | 132 +- packages/autofix/tests/lib/rules/no-proto.js | 74 +- .../tests/lib/rules/no-prototype-builtins.js | 94 +- .../tests/lib/rules/no-throw-literal.js | 86 +- .../autofix/tests/lib/rules/no-unused-vars.js | 646 ++-- .../tests/lib/rules/no-useless-catch.js | 94 +- .../tests/lib/rules/no-useless-concat.js | 66 +- .../autofix/tests/lib/rules/prefer-spread.js | 190 +- packages/autofix/tests/lib/rules/radix.js | 86 +- .../autofix/tests/lib/rules/valid-typeof.js | 126 +- .../tools/eslint-rules/rule-def-format.js | 162 +- .../autofix/tools/generate-readme-table.js | 90 +- packages/autofix/tools/new-rule.js | 190 +- packages/no-autofix/package.json | 39 +- packages/web/.npmrc | 2 +- packages/web/LICENSE | 38 +- packages/web/README.md | 116 +- packages/web/lib/index.js | 50 +- packages/web/lib/rules/no-alert.js | 254 +- packages/web/lib/rules/no-script-url.js | 86 +- packages/web/lib/utils.js | 2684 ++++++++--------- packages/web/package.json | 114 +- packages/web/tests/lib/rules/no-alert.js | 218 +- packages/web/tests/lib/rules/no-script-url.js | 82 +- readme.md | 16 +- shims.d.ts | 60 +- 67 files changed, 4468 insertions(+), 4543 deletions(-) diff --git a/.editorconfig b/.editorconfig index 3c44241..dab7106 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,9 +1,9 @@ -root = true - -[*] -indent_style = space -indent_size = 4 -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/.github/ISSUE_TEMPLATE/bug-report--eslint-plugin-autofix.md b/.github/ISSUE_TEMPLATE/bug-report--eslint-plugin-autofix.md index 22d7929..896f74c 100644 --- a/.github/ISSUE_TEMPLATE/bug-report--eslint-plugin-autofix.md +++ b/.github/ISSUE_TEMPLATE/bug-report--eslint-plugin-autofix.md @@ -1,18 +1,18 @@ ---- -name: 'bug report: eslint-plugin-autofix' -about: Create a report to help us improve -title: eslint-plugin-autofix bug -labels: bug -assignees: aladdin-add - ---- - -**Tell us about your environment** - -* **eslint version:** -* **eslint-plugin-autofix version:** -* **eslint config:** - -**Expected behavior** - -**Actual behavior** +--- +name: 'bug report: eslint-plugin-autofix' +about: Create a report to help us improve +title: eslint-plugin-autofix bug +labels: bug +assignees: aladdin-add + +--- + +**Tell us about your environment** + +* **eslint version:** +* **eslint-plugin-autofix version:** +* **eslint config:** + +**Expected behavior** + +**Actual behavior** diff --git a/.github/ISSUE_TEMPLATE/bug-report--eslint-plugin-no-autofix.md b/.github/ISSUE_TEMPLATE/bug-report--eslint-plugin-no-autofix.md index 5269c97..3301555 100644 --- a/.github/ISSUE_TEMPLATE/bug-report--eslint-plugin-no-autofix.md +++ b/.github/ISSUE_TEMPLATE/bug-report--eslint-plugin-no-autofix.md @@ -1,18 +1,18 @@ ---- -name: 'bug report: eslint-plugin-no-autofix' -about: eslint-plugin-no-autofix -title: 'bug report: eslint-plugin-no-autofix' -labels: bug -assignees: aladdin-add - ---- - -**Tell us about your environment** - -* **eslint version:** -* **eslint-plugin-no-autofix version:** -* **eslint config:** - -**Expected behavior** - -**Actual behavior** +--- +name: 'bug report: eslint-plugin-no-autofix' +about: eslint-plugin-no-autofix +title: 'bug report: eslint-plugin-no-autofix' +labels: bug +assignees: aladdin-add + +--- + +**Tell us about your environment** + +* **eslint version:** +* **eslint-plugin-no-autofix version:** +* **eslint config:** + +**Expected behavior** + +**Actual behavior** diff --git a/.gitignore b/.gitignore index f715013..de8028f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,10 @@ -node_modules -dist -coverage/ -npm-debug.log -.DS_Store -.idea -*.iml -.cache -/.vscode -.sublimelinterrc +node_modules +dist +coverage/ +npm-debug.log +.DS_Store +.idea +*.iml +.cache +/.vscode +.sublimelinterrc diff --git a/.npmrc b/.npmrc index 43c97e7..094e384 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1 @@ -package-lock=false +package-lock=false diff --git a/appveyor.yml b/appveyor.yml index 966263e..563602b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,28 +1,28 @@ -# AppVeyor file -# http://www.appveyor.com/docs/appveyor-yml - -# Build version format -version: "{build}" - -# What combinations to test -environment: - matrix: - - nodejs_version: 8 - -branches: - only: - - master - -install: - # Get the latest stable version of Node.js - - ps: Install-Product node $env:nodejs_version - # install modules - - npm install - -build: off - -test_script: - - npm test - -matrix: - fast_finish: true # set this flag to immediately finish build once one of the jobs fails. +# AppVeyor file +# http://www.appveyor.com/docs/appveyor-yml + +# Build version format +version: "{build}" + +# What combinations to test +environment: + matrix: + - nodejs_version: 8 + +branches: + only: + - master + +install: + # Get the latest stable version of Node.js + - ps: Install-Product node $env:nodejs_version + # install modules + - npm install + +build: off + +test_script: + - npm test + +matrix: + fast_finish: true # set this flag to immediately finish build once one of the jobs fails. diff --git a/package.json b/package.json index 795afd5..72cc28e 100644 --- a/package.json +++ b/package.json @@ -1,26 +1,55 @@ -{ - "name": "eslint-plugin", - "version": "0.0.0", - "private": true, - "author": "薛定谔的猫 ", - "description": "some eslint plugins", - "dependencies": {}, - "devDependencies": {}, - "scripts": { - "install": "npm run install:autofix && npm run install:no-autofix && npm run install:web", - "install:autofix": "cd packages/autofix && npm i", - "install:no-autofix": "cd packages/no-autofix && npm i", - "install:web": "cd packages/web && npm i", - "test": "npm run test:autofix && npm run test:no-autofix && npm run test:web", - "test:autofix": "cd packages/autofix && npm t", - "test:no-autofix": "cd packages/no-autofix && npm t", - "test:web": "cd packages/web && npm t" - }, - "license": "MIT", - "repository": "https://github.com/aladdin-add/eslint-plugin", - "homepage": "https://github.com/aladdin-add/eslint-plugin", - "bugs": "https://github.com/aladdin-add/eslint-plugin/issues/", - "engines": { - "node": ">=8" - } -} +{ + "name": "eslint-plugin", + "version": "0.0.0", + "private": true, + "author": "薛定谔的猫 ", + "description": "some eslint plugins", + "dependencies": {}, + "devDependencies": { + "eslint": "^7.0.0-alpha.0", + "eslint-config-eslint": "^6.0.0", + "eslint-plugin-eslint-plugin": "^2.2.1", + "eslint-plugin-jsdoc": "^21.0.0", + "eslint-plugin-node": "^11.0.0" + }, + "scripts": { + "postinstall": "npm run install:autofix && npm run install:no-autofix && npm run install:web", + "install:autofix": "cd packages/autofix && npm i", + "install:no-autofix": "cd packages/no-autofix && npm i", + "install:web": "cd packages/web && npm i", + "test": "npm run test:autofix && npm run test:no-autofix && npm run test:web", + "test:autofix": "cd packages/autofix && npm t", + "test:no-autofix": "cd packages/no-autofix && npm t", + "test:web": "cd packages/web && npm t", + "lint": "eslint packages/" + }, + "eslintConfig": { + "extends": [ + "eslint-config-eslint", + "plugin:eslint-plugin/recommended" + ], + "plugins": [ + "eslint-plugin" + ], + "overrides": [ + { + "files": [ + "packages/*/tests/**/*" + ], + "env": { + "mocha": true + } + } + ] + }, + "eslintIgnore": [ + "node_modules/" + ], + "license": "MIT", + "repository": "https://github.com/aladdin-add/eslint-plugin", + "homepage": "https://github.com/aladdin-add/eslint-plugin", + "bugs": "https://github.com/aladdin-add/eslint-plugin/issues/", + "engines": { + "node": ">=8" + } +} diff --git a/packages/autofix/.npmrc b/packages/autofix/.npmrc index 43c97e7..094e384 100644 --- a/packages/autofix/.npmrc +++ b/packages/autofix/.npmrc @@ -1 +1 @@ -package-lock=false +package-lock=false diff --git a/packages/autofix/LICENSE b/packages/autofix/LICENSE index a4e68e9..2f502d6 100644 --- a/packages/autofix/LICENSE +++ b/packages/autofix/LICENSE @@ -1,20 +1,20 @@ -The MIT License (MIT) - -Copyright (c) 2018 薛定谔的猫 - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +The MIT License (MIT) + +Copyright (c) 2018 薛定谔的猫 + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/autofix/lib/ast-utils.js b/packages/autofix/lib/ast-utils.js index 6b9050c..1058249 100644 --- a/packages/autofix/lib/ast-utils.js +++ b/packages/autofix/lib/ast-utils.js @@ -1,50 +1,49 @@ -/** - * @fileoverview Common utils for AST. (copied from eslint repo) - * @author Gyandeep Singh - */ - -"use strict"; - -const anyFunctionPattern = /^(?:Function(?:Declaration|Expression)|ArrowFunctionExpression)$/u; -const sideEffectFree = new Set(["Literal", "Identifier", "ThisExpression"]); - -/** - * Finds a function node from ancestors of a node. - * @param {ASTNode} node - A start node to find. - * @returns {Node|null} A found function node. - */ -function getUpperFunction(node) { - for (let currentNode = node; currentNode; currentNode = currentNode.parent) { - if (anyFunctionPattern.test(currentNode.type)) { - return currentNode; - } - } - return null; -} - -/** - * Check if an expression has side effect. - * - * @param {Node} node AST node - * @returns {boolean} result - */ -function hasSideEffect(node) { - if (sideEffectFree.has(node.type)) { - return false; - } - - if (node.type === "MemberExpression") { - return hasSideEffect(node.object) || hasSideEffect(node.property); - } - - if (node.type === "TemplateLiteral") { - return node.expressions.length !== 0; - } - - return true; -} - -module.exports = { - getUpperFunction, - hasSideEffect -}; +/** + * @fileoverview Common utils for AST. (copied from eslint repo) + * @author Gyandeep Singh + */ + +"use strict"; + +const anyFunctionPattern = /^(?:Function(?:Declaration|Expression)|ArrowFunctionExpression)$/u; +const sideEffectFree = new Set(["Literal", "Identifier", "ThisExpression"]); + +/** + * Finds a function node from ancestors of a node. + * @param {ASTNode} node A start node to find. + * @returns {Node|null} A found function node. + */ +function getUpperFunction(node) { + for (let currentNode = node; currentNode; currentNode = currentNode.parent) { + if (anyFunctionPattern.test(currentNode.type)) { + return currentNode; + } + } + return null; +} + +/** + * Check if an expression has side effect. + * @param {Node} node AST node + * @returns {boolean} result + */ +function hasSideEffect(node) { + if (sideEffectFree.has(node.type)) { + return false; + } + + if (node.type === "MemberExpression") { + return hasSideEffect(node.object) || hasSideEffect(node.property); + } + + if (node.type === "TemplateLiteral") { + return node.expressions.length !== 0; + } + + return true; +} + +module.exports = { + getUpperFunction, + hasSideEffect +}; diff --git a/packages/autofix/lib/configs/all.js b/packages/autofix/lib/configs/all.js index 85dfbed..a65d833 100644 --- a/packages/autofix/lib/configs/all.js +++ b/packages/autofix/lib/configs/all.js @@ -1,24 +1,24 @@ -/** - * @fileoverview configs:all for the plugin - * @author 唯然 - */ -"use strict"; - -const packageMetadata = require("../../package"); -const PLUGIN_NAME = packageMetadata.name.replace(/^eslint-plugin-/, ""); -const allRules = require("../rules"); - -const all = { - plugins: [PLUGIN_NAME], - rules: {} -}; - -// turn off core rules -Object.keys(allRules) - .reduce((rules, ruleName) => Object.assign(rules, { [ruleName]: "off" }), all.rules); - -// turn on plugin rules -Object.keys(allRules) - .reduce((rules, ruleName) => Object.assign(rules, { [`${PLUGIN_NAME}/${ruleName}`]: "error" }), all.rules); - -module.exports = all; +/** + * @fileoverview configs:all for the plugin + * @author 唯然 + */ +"use strict"; + +const packageMetadata = require("../../package"); +const PLUGIN_NAME = packageMetadata.name.replace(/^eslint-plugin-/u, ""); +const allRules = require("../rules"); + +const all = { + plugins: [PLUGIN_NAME], + rules: {} +}; + +// turn off core rules +Object.keys(allRules) + .reduce((rules, ruleName) => Object.assign(rules, { [ruleName]: "off" }), all.rules); + +// turn on plugin rules +Object.keys(allRules) + .reduce((rules, ruleName) => Object.assign(rules, { [`${PLUGIN_NAME}/${ruleName}`]: "error" }), all.rules); + +module.exports = all; diff --git a/packages/autofix/lib/configs/recommended.js b/packages/autofix/lib/configs/recommended.js index 366e4f1..14509a5 100644 --- a/packages/autofix/lib/configs/recommended.js +++ b/packages/autofix/lib/configs/recommended.js @@ -1,24 +1,24 @@ -/** - * @fileoverview configs:recommended for the plugin - * @author 唯然 - */ -"use strict"; - -const packageMetadata = require("../../package"); -const PLUGIN_NAME = packageMetadata.name.replace(/^eslint-plugin-/, ""); -const allRules = require("../rules"); - -const recommended = { - plugins: [PLUGIN_NAME], - rules: {} -}; - -// turn off core rules -Object.keys(allRules).filter(rule => allRules[rule].meta.recommended === true) - .reduce((rules, ruleName) => Object.assign(rules, { [ruleName]: "off" }), recommended.rules); - -// turn on plugin rules -Object.keys(allRules).filter(rule => allRules[rule].meta.recommended === true) - .reduce((rules, ruleName) => Object.assign(rules, { [`${PLUGIN_NAME}/${ruleName}`]: "error" }), recommended.rules); - -module.exports = recommended; +/** + * @fileoverview configs:recommended for the plugin + * @author 唯然 + */ +"use strict"; + +const packageMetadata = require("../../package"); +const PLUGIN_NAME = packageMetadata.name.replace(/^eslint-plugin-/u, ""); +const allRules = require("../rules"); + +const recommended = { + plugins: [PLUGIN_NAME], + rules: {} +}; + +// turn off core rules +Object.keys(allRules).filter(rule => allRules[rule].meta.recommended === true) + .reduce((rules, ruleName) => Object.assign(rules, { [ruleName]: "off" }), recommended.rules); + +// turn on plugin rules +Object.keys(allRules).filter(rule => allRules[rule].meta.recommended === true) + .reduce((rules, ruleName) => Object.assign(rules, { [`${PLUGIN_NAME}/${ruleName}`]: "error" }), recommended.rules); + +module.exports = recommended; diff --git a/packages/autofix/lib/configs/unsafe.js b/packages/autofix/lib/configs/unsafe.js index e4cbaa8..09adebb 100644 --- a/packages/autofix/lib/configs/unsafe.js +++ b/packages/autofix/lib/configs/unsafe.js @@ -1,24 +1,24 @@ -/** - * @fileoverview configs:unsafe for the plugin - * @author 唯然 - */ -"use strict"; - -const packageMetadata = require("../../package"); -const PLUGIN_NAME = packageMetadata.name.replace(/^eslint-plugin-/, ""); -const allRules = require("../rules"); - -const unsafe = { - plugins: [PLUGIN_NAME], - rules: {} -}; - -// turn off core rules -Object.keys(allRules).filter(rule => allRules[rule].meta.recommended === false) - .reduce((rules, ruleName) => Object.assign(rules, { [ruleName]: "off" }), unsafe.rules); - -// turn on plugin rules -Object.keys(allRules).filter(rule => allRules[rule].meta.recommended === false) - .reduce((rules, ruleName) => Object.assign(rules, { [`${PLUGIN_NAME}/${ruleName}`]: "error" }), unsafe.rules); - -module.exports = unsafe; +/** + * @fileoverview configs:unsafe for the plugin + * @author 唯然 + */ +"use strict"; + +const packageMetadata = require("../../package"); +const PLUGIN_NAME = packageMetadata.name.replace(/^eslint-plugin-/u, ""); +const allRules = require("../rules"); + +const unsafe = { + plugins: [PLUGIN_NAME], + rules: {} +}; + +// turn off core rules +Object.keys(allRules).filter(rule => allRules[rule].meta.recommended === false) + .reduce((rules, ruleName) => Object.assign(rules, { [ruleName]: "off" }), unsafe.rules); + +// turn on plugin rules +Object.keys(allRules).filter(rule => allRules[rule].meta.recommended === false) + .reduce((rules, ruleName) => Object.assign(rules, { [`${PLUGIN_NAME}/${ruleName}`]: "error" }), unsafe.rules); + +module.exports = unsafe; diff --git a/packages/autofix/lib/index.js b/packages/autofix/lib/index.js index e9b2718..0f4472e 100644 --- a/packages/autofix/lib/index.js +++ b/packages/autofix/lib/index.js @@ -1,19 +1,19 @@ -/** - * @fileoverview An ESLint plugin provides autofix for some core rules. - * @author 唯然 - */ -"use strict"; - -const all = require("./configs/all"); -const recommended = require("./configs/recommended"); -const unsafe = require("./configs/unsafe"); -const rules = require("./rules"); - -module.exports = { - configs: { - all, - recommended, - unsafe - }, - rules -}; +/** + * @fileoverview An ESLint plugin provides autofix for some core rules. + * @author 唯然 + */ +"use strict"; + +const all = require("./configs/all"); +const recommended = require("./configs/recommended"); +const unsafe = require("./configs/unsafe"); +const rules = require("./rules"); + +module.exports = { + configs: { + all, + recommended, + unsafe + }, + rules +}; diff --git a/packages/autofix/lib/rules.js b/packages/autofix/lib/rules.js index 073aa72..339afd0 100644 --- a/packages/autofix/lib/rules.js +++ b/packages/autofix/lib/rules.js @@ -1,60 +1,60 @@ -/** - * @fileoverview expored all rules in the plugin. - * @author 唯然 - */ - -"use strict"; - -// ------------------------------------------------------------------------------ -// Requirements -// ------------------------------------------------------------------------------ - -const fs = require("fs"); -const path = require("path"); -const eslintVersion = require("eslint/package.json").version; - - -/** - * Loads a given rule from the filesystem and generates its documentation URL - * @param {string} ruleName The name of the rule - * @returns {Rule} The ESLint rule to export - */ -function loadRule(ruleName) { - const rule = require(path.join(__dirname, "rules", ruleName)); - - rule.meta.docs.url = - `https://eslint.org/docs/rules/${ruleName}`; - - return rule; -} - -const allRules = {}; - -// eslint v6 restructed its codebase -// TODO: this might be unreliable -if (eslintVersion >= "6.0.0") { - const builtin = require("eslint/lib/rules"); - - for (const [ruleId, rule] of builtin) { - if (rule.meta.fixable) { - allRules[ruleId] = rule; - } - } -} else { - const builtin = require("eslint/lib/built-in-rules-index"); // eslint-disable-line node/no-missing-require - - Object.keys(builtin) - .filter(rule => builtin[rule].meta.fixable) - .reduce((acc, cur) => { - acc[cur] = builtin[cur]; - return acc; - }, allRules); -} - -// import all rules in lib/rules -fs.readdirSync(`${__dirname}/rules`) - .filter(fileName => fileName.endsWith(".js") && /^[^._]/.test(fileName)) - .map(fileName => fileName.replace(/\.js$/, "")) - .reduce((rules, ruleName) => Object.assign(rules, { [ruleName]: loadRule(ruleName) }), allRules); - -module.exports = allRules; +/** + * @fileoverview expored all rules in the plugin. + * @author 唯然 + */ + +"use strict"; + +// ------------------------------------------------------------------------------ +// Requirements +// ------------------------------------------------------------------------------ + +const fs = require("fs"); +const path = require("path"); +const eslintVersion = require("eslint/package.json").version; + + +/** + * Loads a given rule from the filesystem and generates its documentation URL + * @param {string} ruleName The name of the rule + * @returns {Rule} The ESLint rule to export + */ +function loadRule(ruleName) { + const rule = require(path.join(__dirname, "rules", ruleName)); + + rule.meta.docs.url = + `https://eslint.org/docs/rules/${ruleName}`; + + return rule; +} + +const allRules = {}; + +// eslint v6 restructed its codebase +// TODO: this might be unreliable +if (eslintVersion >= "6.0.0") { + const builtin = require("eslint/lib/rules"); + + for (const [ruleId, rule] of builtin) { + if (rule.meta.fixable) { + allRules[ruleId] = rule; + } + } +} else { + const builtin = require("eslint/lib/built-in-rules-index"); // eslint-disable-line node/no-missing-require + + Object.keys(builtin) + .filter(rule => builtin[rule].meta.fixable) + .reduce((acc, cur) => { + acc[cur] = builtin[cur]; + return acc; + }, allRules); +} + +// import all rules in lib/rules +fs.readdirSync(`${__dirname}/rules`) + .filter(fileName => fileName.endsWith(".js") && /^[^._]/u.test(fileName)) + .map(fileName => fileName.replace(/\.js$/u, "")) + .reduce((rules, ruleName) => Object.assign(rules, { [ruleName]: loadRule(ruleName) }), allRules); + +module.exports = allRules; diff --git a/packages/autofix/lib/rules/no-alert.js b/packages/autofix/lib/rules/no-alert.js index 33e7148..bb9dd40 100644 --- a/packages/autofix/lib/rules/no-alert.js +++ b/packages/autofix/lib/rules/no-alert.js @@ -1,32 +1,32 @@ -/** - * @fileoverview add fixer to rule no-alert. - * @author Pig Fang - */ -"use strict"; - -const ruleComposer = require("eslint-rule-composer"); -const utils = require("../utils"); - -const rule = utils.getFixableRule("no-alert", false); - -module.exports = ruleComposer.mapReports( - rule, - problem => { - problem.fix = fixer => { - const { node } = problem; - - // We only remove `alert` function, because `confirm` and `prompt` has return value. - if ( - (node.callee.type === "Identifier" && node.callee.name === "alert") || - (node.callee.type === "MemberExpression" && node.callee.property.name === "alert") - ) { - if (node.arguments.every(arg => arg.type === "Literal" || arg.type === "Identifier")) { - return fixer.remove(node); - } - } - - return null; - }; - return problem; - } -); +/** + * @fileoverview add fixer to rule no-alert. + * @author Pig Fang + */ +"use strict"; + +const ruleComposer = require("eslint-rule-composer"); +const utils = require("../utils"); + +const rule = utils.getFixableRule("no-alert", false); + +module.exports = ruleComposer.mapReports( + rule, + problem => { + problem.fix = fixer => { + const { node } = problem; + + // We only remove `alert` function, because `confirm` and `prompt` has return value. + if ( + (node.callee.type === "Identifier" && node.callee.name === "alert") || + (node.callee.type === "MemberExpression" && node.callee.property.name === "alert") + ) { + if (node.arguments.every(arg => arg.type === "Literal" || arg.type === "Identifier")) { + return fixer.remove(node); + } + } + + return null; + }; + return problem; + } +); diff --git a/packages/autofix/lib/rules/no-caller.js b/packages/autofix/lib/rules/no-caller.js index 7ba094b..7441a69 100644 --- a/packages/autofix/lib/rules/no-caller.js +++ b/packages/autofix/lib/rules/no-caller.js @@ -1,39 +1,39 @@ -/** - * @fileoverview Add fixer to rule no-caller. - * @author Pig Fang - */ -"use strict"; - -const ruleComposer = require("eslint-rule-composer"); -const astUtils = require("../ast-utils"); -const utils = require("../utils"); - -const rule = utils.getFixableRule("no-caller", false); - -module.exports = ruleComposer.mapReports( - rule, - problem => { - problem.fix = fixer => { - const { node } = problem; - - if (node.property.name !== "callee") { - return null; - } - - const nearestFunction = astUtils.getUpperFunction(node); - - if (!nearestFunction) { - return null; - } - - const name = nearestFunction.id && nearestFunction.id.name; - - if (!name) { - return null; - } - - return fixer.replaceText(node, name); - }; - return problem; - } -); +/** + * @fileoverview Add fixer to rule no-caller. + * @author Pig Fang + */ +"use strict"; + +const ruleComposer = require("eslint-rule-composer"); +const astUtils = require("../ast-utils"); +const utils = require("../utils"); + +const rule = utils.getFixableRule("no-caller", false); + +module.exports = ruleComposer.mapReports( + rule, + problem => { + problem.fix = fixer => { + const { node } = problem; + + if (node.property.name !== "callee") { + return null; + } + + const nearestFunction = astUtils.getUpperFunction(node); + + if (!nearestFunction) { + return null; + } + + const name = nearestFunction.id && nearestFunction.id.name; + + if (!name) { + return null; + } + + return fixer.replaceText(node, name); + }; + return problem; + } +); diff --git a/packages/autofix/lib/rules/no-console.js b/packages/autofix/lib/rules/no-console.js index e3590da..5d76f5f 100644 --- a/packages/autofix/lib/rules/no-console.js +++ b/packages/autofix/lib/rules/no-console.js @@ -1,48 +1,48 @@ -/** - * @fileoverview Add fixer to rule "no-console" - * @author Pig Fang - */ -"use strict"; - -const ruleComposer = require("eslint-rule-composer"); -const utils = require("../utils"); - -const rule = utils.getFixableRule("no-console", true); - -module.exports = ruleComposer.mapReports( - rule, - problem => { - problem.fix = fixer => { - const { node } = problem; - const parentType = node.parent.type; - const isMethodCall = parentType === "CallExpression"; - - if (parentType === "VariableDeclarator") { - return null; - } - - if (isMethodCall && !node.parent.arguments.every(arg => arg.type === "Literal" || arg.type === "Identifier")) { - return null; - } - - if (node.parent.parent) { - const grandType = node.parent.parent.type; - const maybeFlowType = isMethodCall && node.parent.parent.parent - ? node.parent.parent.parent.type - : grandType; - - if ( - (maybeFlowType === "IfStatement" || maybeFlowType === "WhileStatement" || maybeFlowType === "ForStatement") && - (isMethodCall ? grandType === "ExpressionStatement" : parentType === "ExpressionStatement") - ) { - return isMethodCall ? fixer.replaceText(node.parent, "{}") : fixer.replaceText(node, "{}"); - } - } - - return isMethodCall ? fixer.remove(node.parent) : fixer.remove(problem.node); - - }; - - return problem; - } -); +/** + * @fileoverview Add fixer to rule "no-console" + * @author Pig Fang + */ +"use strict"; + +const ruleComposer = require("eslint-rule-composer"); +const utils = require("../utils"); + +const rule = utils.getFixableRule("no-console", true); + +module.exports = ruleComposer.mapReports( + rule, + problem => { + problem.fix = fixer => { + const { node } = problem; + const parentType = node.parent.type; + const isMethodCall = parentType === "CallExpression"; + + if (parentType === "VariableDeclarator") { + return null; + } + + if (isMethodCall && !node.parent.arguments.every(arg => arg.type === "Literal" || arg.type === "Identifier")) { + return null; + } + + if (node.parent.parent) { + const grandType = node.parent.parent.type; + const maybeFlowType = isMethodCall && node.parent.parent.parent + ? node.parent.parent.parent.type + : grandType; + + if ( + (maybeFlowType === "IfStatement" || maybeFlowType === "WhileStatement" || maybeFlowType === "ForStatement") && + (isMethodCall ? grandType === "ExpressionStatement" : parentType === "ExpressionStatement") + ) { + return isMethodCall ? fixer.replaceText(node.parent, "{}") : fixer.replaceText(node, "{}"); + } + } + + return isMethodCall ? fixer.remove(node.parent) : fixer.remove(problem.node); + + }; + + return problem; + } +); diff --git a/packages/autofix/lib/rules/no-debugger.js b/packages/autofix/lib/rules/no-debugger.js index ec4e33b..d21335d 100644 --- a/packages/autofix/lib/rules/no-debugger.js +++ b/packages/autofix/lib/rules/no-debugger.js @@ -1,26 +1,26 @@ -/** - * @fileoverview add fixer to rule no-debugger - * @author 唯然 - */ -"use strict"; - -const ruleComposer = require("eslint-rule-composer"); -const utils = require("../utils"); - -const rule = utils.getFixableRule("no-debugger", true); - -// A set of node types that can contain a list of statements -const STATEMENT_LIST_PARENTS = new Set(["Program", "BlockStatement", "SwitchCase"]); - -module.exports = ruleComposer.mapReports( - rule, - problem => { - problem.fix = function(fixer) { - if (STATEMENT_LIST_PARENTS.has(problem.node.parent.type)) { - return fixer.remove(problem.node); - } - return null; - }; - return problem; - } -); +/** + * @fileoverview add fixer to rule no-debugger + * @author 唯然 + */ +"use strict"; + +const ruleComposer = require("eslint-rule-composer"); +const utils = require("../utils"); + +const rule = utils.getFixableRule("no-debugger", true); + +// A set of node types that can contain a list of statements +const STATEMENT_LIST_PARENTS = new Set(["Program", "BlockStatement", "SwitchCase"]); + +module.exports = ruleComposer.mapReports( + rule, + problem => { + problem.fix = function(fixer) { + if (STATEMENT_LIST_PARENTS.has(problem.node.parent.type)) { + return fixer.remove(problem.node); + } + return null; + }; + return problem; + } +); diff --git a/packages/autofix/lib/rules/no-eq-null.js b/packages/autofix/lib/rules/no-eq-null.js index bea57a6..2c20c93 100644 --- a/packages/autofix/lib/rules/no-eq-null.js +++ b/packages/autofix/lib/rules/no-eq-null.js @@ -1,26 +1,26 @@ -/** - * @fileoverview Add fixer to rule no-eq-null. - * @author Pig Fang - */ -"use strict"; - -const ruleComposer = require("eslint-rule-composer"); -const utils = require("../utils"); - -const rule = utils.getFixableRule("no-eq-null", false); - -module.exports = ruleComposer.mapReports( - rule, - (problem, { sourceCode }) => { - problem.fix = fixer => { - const { node } = problem; - - const token = node.right.type === "Literal" && node.right.raw === "null" - ? sourceCode.getTokenBefore(node.right) - : sourceCode.getTokenAfter(node.left); - - return fixer.insertTextAfter(token, "="); - }; - return problem; - } -); +/** + * @fileoverview Add fixer to rule no-eq-null. + * @author Pig Fang + */ +"use strict"; + +const ruleComposer = require("eslint-rule-composer"); +const utils = require("../utils"); + +const rule = utils.getFixableRule("no-eq-null", false); + +module.exports = ruleComposer.mapReports( + rule, + (problem, { sourceCode }) => { + problem.fix = fixer => { + const { node } = problem; + + const token = node.right.type === "Literal" && node.right.raw === "null" + ? sourceCode.getTokenBefore(node.right) + : sourceCode.getTokenAfter(node.left); + + return fixer.insertTextAfter(token, "="); + }; + return problem; + } +); diff --git a/packages/autofix/lib/rules/no-new-symbol.js b/packages/autofix/lib/rules/no-new-symbol.js index 92f9542..4abbd24 100644 --- a/packages/autofix/lib/rules/no-new-symbol.js +++ b/packages/autofix/lib/rules/no-new-symbol.js @@ -1,22 +1,22 @@ -/** - * @fileoverview add fixer to rule no-new-symbol. - * @author Pig Fang - */ -"use strict"; - -const ruleComposer = require("eslint-rule-composer"); -const utils = require("../utils"); - -const rule = utils.getFixableRule("no-new-symbol"); - -module.exports = ruleComposer.mapReports( - rule, - problem => { - problem.fix = fixer => { - const parent = problem.node.parent; - - return fixer.removeRange([parent.range[0], parent.range[0] + 4]); - }; - return problem; - } -); +/** + * @fileoverview add fixer to rule no-new-symbol. + * @author Pig Fang + */ +"use strict"; + +const ruleComposer = require("eslint-rule-composer"); +const utils = require("../utils"); + +const rule = utils.getFixableRule("no-new-symbol"); + +module.exports = ruleComposer.mapReports( + rule, + problem => { + problem.fix = fixer => { + const parent = problem.node.parent; + + return fixer.removeRange([parent.range[0], parent.range[0] + 4]); + }; + return problem; + } +); diff --git a/packages/autofix/lib/rules/no-plusplus.js b/packages/autofix/lib/rules/no-plusplus.js index 1d4aa1d..5c0653e 100644 --- a/packages/autofix/lib/rules/no-plusplus.js +++ b/packages/autofix/lib/rules/no-plusplus.js @@ -1,27 +1,27 @@ -/** - * @fileoverview add fixer to rule no-plusplus - * @author 唯然 - */ -"use strict"; - -const ruleComposer = require("eslint-rule-composer"); -const utils = require("../utils"); - -const rule = utils.getFixableRule("no-plusplus", true); -const fixableTypes = new Set(["ExpressionStatement", "ForStatement"]); - -module.exports = ruleComposer.mapReports( - rule, - problem => { - const node = problem.node; - const op = node.operator === "++" ? "+=1" : "-=1"; - - problem.fix = function(fixer) { - return fixableTypes.has(node.parent.type) - ? fixer.replaceText(node, `${node.argument.name}${op}`) - : null; - }; - - return problem; - } -); +/** + * @fileoverview add fixer to rule no-plusplus + * @author 唯然 + */ +"use strict"; + +const ruleComposer = require("eslint-rule-composer"); +const utils = require("../utils"); + +const rule = utils.getFixableRule("no-plusplus", true); +const fixableTypes = new Set(["ExpressionStatement", "ForStatement"]); + +module.exports = ruleComposer.mapReports( + rule, + problem => { + const node = problem.node; + const op = node.operator === "++" ? "+=1" : "-=1"; + + problem.fix = function(fixer) { + return fixableTypes.has(node.parent.type) + ? fixer.replaceText(node, `${node.argument.name}${op}`) + : null; + }; + + return problem; + } +); diff --git a/packages/autofix/lib/rules/no-proto.js b/packages/autofix/lib/rules/no-proto.js index c4d4a87..5bf2b91 100644 --- a/packages/autofix/lib/rules/no-proto.js +++ b/packages/autofix/lib/rules/no-proto.js @@ -1,22 +1,22 @@ -/** - * @fileoverview Add fixer to rule no-proto. - * @author Pig Fang - */ -"use strict"; - -const ruleComposer = require("eslint-rule-composer"); -const utils = require("../utils"); - -const rule = utils.getFixableRule("no-proto", false); - -module.exports = ruleComposer.mapReports( - rule, - (problem, { sourceCode }) => { - problem.fix = fixer => { - const { node } = problem; - - return fixer.replaceText(node, `Object.getPrototypeOf(${sourceCode.getText(node.object)})`); - }; - return problem; - } -); +/** + * @fileoverview Add fixer to rule no-proto. + * @author Pig Fang + */ +"use strict"; + +const ruleComposer = require("eslint-rule-composer"); +const utils = require("../utils"); + +const rule = utils.getFixableRule("no-proto", false); + +module.exports = ruleComposer.mapReports( + rule, + (problem, { sourceCode }) => { + problem.fix = fixer => { + const { node } = problem; + + return fixer.replaceText(node, `Object.getPrototypeOf(${sourceCode.getText(node.object)})`); + }; + return problem; + } +); diff --git a/packages/autofix/lib/rules/no-prototype-builtins.js b/packages/autofix/lib/rules/no-prototype-builtins.js index ff04a70..dfcb966 100644 --- a/packages/autofix/lib/rules/no-prototype-builtins.js +++ b/packages/autofix/lib/rules/no-prototype-builtins.js @@ -1,27 +1,27 @@ -/** - * @fileoverview add fixer to rule no-prototype-builtins. - * @author Pig Fang - */ -"use strict"; - -const ruleComposer = require("eslint-rule-composer"); -const utils = require("../utils"); - -const rule = utils.getFixableRule("no-prototype-builtins"); - -module.exports = ruleComposer.mapReports( - rule, - (problem, { sourceCode }) => { - problem.fix = fixer => { - const { node } = problem; - const prop = node.callee.property.name; - const args = node.arguments.map(arg => sourceCode.getText(arg)).join(", "); - - return fixer.replaceText( - node, - `Object.prototype.${prop}.call(${sourceCode.getText(node.callee.object)}, ${args})` - ); - }; - return problem; - } -); +/** + * @fileoverview add fixer to rule no-prototype-builtins. + * @author Pig Fang + */ +"use strict"; + +const ruleComposer = require("eslint-rule-composer"); +const utils = require("../utils"); + +const rule = utils.getFixableRule("no-prototype-builtins"); + +module.exports = ruleComposer.mapReports( + rule, + (problem, { sourceCode }) => { + problem.fix = fixer => { + const { node } = problem; + const prop = node.callee.property.name; + const args = node.arguments.map(arg => sourceCode.getText(arg)).join(", "); + + return fixer.replaceText( + node, + `Object.prototype.${prop}.call(${sourceCode.getText(node.callee.object)}, ${args})` + ); + }; + return problem; + } +); diff --git a/packages/autofix/lib/rules/no-throw-literal.js b/packages/autofix/lib/rules/no-throw-literal.js index 880efef..aad101b 100644 --- a/packages/autofix/lib/rules/no-throw-literal.js +++ b/packages/autofix/lib/rules/no-throw-literal.js @@ -1,26 +1,26 @@ -/** - * @fileoverview Add fixer to rule no-throw-literal. - * @author Pig Fang - */ -"use strict"; - -const ruleComposer = require("eslint-rule-composer"); -const utils = require("../utils"); - -const rule = utils.getFixableRule("no-throw-literal", false); - -module.exports = ruleComposer.mapReports( - rule, - (problem, { sourceCode }) => { - problem.fix = fixer => { - const { node } = problem; - const { argument } = node; - - if (argument.type === "Identifier") { - return null; - } - return fixer.replaceText(node, `throw new Error(${sourceCode.getText(argument)});`); - }; - return problem; - } -); +/** + * @fileoverview Add fixer to rule no-throw-literal. + * @author Pig Fang + */ +"use strict"; + +const ruleComposer = require("eslint-rule-composer"); +const utils = require("../utils"); + +const rule = utils.getFixableRule("no-throw-literal", false); + +module.exports = ruleComposer.mapReports( + rule, + (problem, { sourceCode }) => { + problem.fix = fixer => { + const { node } = problem; + const { argument } = node; + + if (argument.type === "Identifier") { + return null; + } + return fixer.replaceText(node, `throw new Error(${sourceCode.getText(argument)});`); + }; + return problem; + } +); diff --git a/packages/autofix/lib/rules/no-unused-vars.js b/packages/autofix/lib/rules/no-unused-vars.js index cd4af86..301fdf3 100644 --- a/packages/autofix/lib/rules/no-unused-vars.js +++ b/packages/autofix/lib/rules/no-unused-vars.js @@ -1,218 +1,216 @@ -/** - * @fileoverview Add fixer to rule no-unused-vars. - * @author Pig Fang - */ -"use strict"; - -const ruleComposer = require("eslint-rule-composer"); -const utils = require("../utils"); -const { hasSideEffect } = require("../ast-utils"); - -const rule = utils.getFixableRule("no-unused-vars", false); - -const commaFilter = { filter: token => token.value === "," }; - -/** - * Process the raw options into a config object. - * - * This function is directly copied from eslint source code. - * - * @param {Object} options Options from context - * @returns {Object} config The rule config - */ -function getConfig(options) { - const firstOption = options[0]; - const config = { - vars: "all", - args: "after-used", - ignoreRestSiblings: false, - caughtErrors: "none" - }; - - if (firstOption) { - if (typeof firstOption === "string") { - config.vars = firstOption; - } else { - config.vars = firstOption.vars || config.vars; - config.args = firstOption.args || config.args; - config.ignoreRestSiblings = firstOption.ignoreRestSiblings || config.ignoreRestSiblings; - config.caughtErrors = firstOption.caughtErrors || config.caughtErrors; - - if (firstOption.varsIgnorePattern) { - config.varsIgnorePattern = new RegExp(firstOption.varsIgnorePattern, "u"); - } - - if (firstOption.argsIgnorePattern) { - config.argsIgnorePattern = new RegExp(firstOption.argsIgnorePattern, "u"); - } - - if (firstOption.caughtErrorsIgnorePattern) { - config.caughtErrorsIgnorePattern = new RegExp(firstOption.caughtErrorsIgnorePattern, "u"); - } - } - } - - return config; -} - -/** - * getIgnoredArgPrefix extracts the prefix from the regex. - * - * Commonly argsIgnorePattern will be a regex that matches a prefix to a - * variable name. The prefix is a way to indicate the arguments is explicitly - * unused. - * - * @param {Object} config rule config - * @returns {string | null} the prefix, if it exists - */ -function getIgnoredArgPrefix(config) { - const { argsIgnorePattern } = config; - - if (!argsIgnorePattern) { - return null; - } - - const m = argsIgnorePattern.toString().match(/^\/\^(\w+)\/\w?$/); - - if (!m) { - return null; - } - - return m[1]; -} - -module.exports = ruleComposer.mapReports( - rule, - (problem, context) => { - const { sourceCode } = context; - - const config = getConfig(context.options); - - problem.fix = fixer => { - const { node } = problem; - const { parent } = node; - - if (!parent) { - return null; - } - const grand = parent.parent; - - switch (parent.type) { - case "FunctionExpression": - case "FunctionDeclaration": - case "ArrowFunctionExpression": - - // Don't autofix unused functions, it's likely a mistake - if (parent.id === node) { - return null; - } - - switch (config.args) { - case "all": { - const prefix = getIgnoredArgPrefix(config); - - if (prefix === null) { - return null; - } - - return fixer.insertTextBefore(node, prefix); - } - case "after-used": { - const comma = sourceCode.getTokenBefore(node, commaFilter); - - return [fixer.remove(comma), fixer.remove(node)]; - } - default: - throw new Error("Invalid rule config value for 'args'"); - } - - case "ImportSpecifier": - case "ImportDefaultSpecifier": - case "ImportNamespaceSpecifier": - if (!grand) { - return null; - } - - if (grand.specifiers.length === 1) { - return fixer.remove(grand); - } - - if (parent !== grand.specifiers[grand.specifiers.length - 1]) { - const comma = sourceCode.getTokenAfter(parent, commaFilter); - - return [fixer.remove(parent), fixer.remove(comma)]; - } - - if (grand.specifiers.filter(specifier => specifier.type === "ImportSpecifier").length === 1) { - const start = sourceCode.getTokenBefore(parent, commaFilter), - end = sourceCode.getTokenAfter(parent, { filter: token => token.value === "}" }); - - return fixer.removeRange([start.range[0], end.range[1]]); - } - - return fixer.removeRange([ - sourceCode.getTokenBefore(parent, commaFilter).range[0], - parent.range[1] - ]); - case "VariableDeclarator": - if (!grand) { - return null; - } - - if (parent.init && hasSideEffect(parent.init)) { - return null; - } - - if (grand.declarations.length === 1) { - return fixer.remove(grand); - } - - if (parent !== grand.declarations[grand.declarations.length - 1]) { - const comma = sourceCode.getTokenAfter(parent, commaFilter); - - return [fixer.remove(parent), fixer.remove(comma)]; - } - - return [ - fixer.remove(sourceCode.getTokenBefore(parent, commaFilter)), - fixer.remove(parent) - ]; - case "AssignmentPattern": - if (hasSideEffect(parent.right)) { - return null; - } - return fixer.remove(parent); - case "RestElement": - case "Property": - - // https://github.com/aladdin-add/eslint-plugin-autofix/issues/48 - if (!(grand && grand.type === "ObjectPattern")) { - return null; - } - - if (grand.properties.length === 1) { - const identifierRemoval = fixer.remove(parent); - const comma = sourceCode.getLastToken(grand, commaFilter); - - return comma ? [identifierRemoval, fixer.remove(comma)] : identifierRemoval; - } - - if (parent === grand.properties[grand.properties.length - 1]) { - const comma = sourceCode.getTokenBefore(parent, commaFilter); - - return [fixer.remove(parent), fixer.remove(comma)]; - } - - return [ - fixer.remove(parent), - fixer.remove(sourceCode.getTokenAfter(parent, commaFilter)) - ]; - case "ArrayPattern": - return fixer.remove(node); - default: - return null; - } - }; - return problem; - } -); +/** + * @fileoverview Add fixer to rule no-unused-vars. + * @author Pig Fang + */ +"use strict"; + +const ruleComposer = require("eslint-rule-composer"); +const utils = require("../utils"); +const { hasSideEffect } = require("../ast-utils"); + +const rule = utils.getFixableRule("no-unused-vars", false); + +const commaFilter = { filter: token => token.value === "," }; + +/** + * Process the raw options into a config object. + * + * This function is directly copied from eslint source code. + * @param {Object} options Options from context + * @returns {Object} config The rule config + */ +function getConfig(options) { + const firstOption = options[0]; + const config = { + vars: "all", + args: "after-used", + ignoreRestSiblings: false, + caughtErrors: "none" + }; + + if (firstOption) { + if (typeof firstOption === "string") { + config.vars = firstOption; + } else { + config.vars = firstOption.vars || config.vars; + config.args = firstOption.args || config.args; + config.ignoreRestSiblings = firstOption.ignoreRestSiblings || config.ignoreRestSiblings; + config.caughtErrors = firstOption.caughtErrors || config.caughtErrors; + + if (firstOption.varsIgnorePattern) { + config.varsIgnorePattern = new RegExp(firstOption.varsIgnorePattern, "u"); + } + + if (firstOption.argsIgnorePattern) { + config.argsIgnorePattern = new RegExp(firstOption.argsIgnorePattern, "u"); + } + + if (firstOption.caughtErrorsIgnorePattern) { + config.caughtErrorsIgnorePattern = new RegExp(firstOption.caughtErrorsIgnorePattern, "u"); + } + } + } + + return config; +} + +/** + * getIgnoredArgPrefix extracts the prefix from the regex. + * + * Commonly argsIgnorePattern will be a regex that matches a prefix to a + * variable name. The prefix is a way to indicate the arguments is explicitly + * unused. + * @param {Object} config rule config + * @returns {string | null} the prefix, if it exists + */ +function getIgnoredArgPrefix(config) { + const { argsIgnorePattern } = config; + + if (!argsIgnorePattern) { + return null; + } + + const m = argsIgnorePattern.toString().match(/^\/\^(\w+)\/\w?$/u); + + if (!m) { + return null; + } + + return m[1]; +} + +module.exports = ruleComposer.mapReports( + rule, + (problem, context) => { + const { sourceCode } = context; + + const config = getConfig(context.options); + + problem.fix = fixer => { + const { node } = problem; + const { parent } = node; + + if (!parent) { + return null; + } + const grand = parent.parent; + + switch (parent.type) { + case "FunctionExpression": + case "FunctionDeclaration": + case "ArrowFunctionExpression": + + // Don't autofix unused functions, it's likely a mistake + if (parent.id === node) { + return null; + } + + switch (config.args) { + case "all": { + const prefix = getIgnoredArgPrefix(config); + + if (prefix === null) { + return null; + } + + return fixer.insertTextBefore(node, prefix); + } + case "after-used": { + const comma = sourceCode.getTokenBefore(node, commaFilter); + + return [fixer.remove(comma), fixer.remove(node)]; + } + default: + throw new Error("Invalid rule config value for 'args'"); + } + + case "ImportSpecifier": + case "ImportDefaultSpecifier": + case "ImportNamespaceSpecifier": + if (!grand) { + return null; + } + + if (grand.specifiers.length === 1) { + return fixer.remove(grand); + } + + if (parent !== grand.specifiers[grand.specifiers.length - 1]) { + const comma = sourceCode.getTokenAfter(parent, commaFilter); + + return [fixer.remove(parent), fixer.remove(comma)]; + } + + if (grand.specifiers.filter(specifier => specifier.type === "ImportSpecifier").length === 1) { + const start = sourceCode.getTokenBefore(parent, commaFilter), + end = sourceCode.getTokenAfter(parent, { filter: token => token.value === "}" }); + + return fixer.removeRange([start.range[0], end.range[1]]); + } + + return fixer.removeRange([ + sourceCode.getTokenBefore(parent, commaFilter).range[0], + parent.range[1] + ]); + case "VariableDeclarator": + if (!grand) { + return null; + } + + if (parent.init && hasSideEffect(parent.init)) { + return null; + } + + if (grand.declarations.length === 1) { + return fixer.remove(grand); + } + + if (parent !== grand.declarations[grand.declarations.length - 1]) { + const comma = sourceCode.getTokenAfter(parent, commaFilter); + + return [fixer.remove(parent), fixer.remove(comma)]; + } + + return [ + fixer.remove(sourceCode.getTokenBefore(parent, commaFilter)), + fixer.remove(parent) + ]; + case "AssignmentPattern": + if (hasSideEffect(parent.right)) { + return null; + } + return fixer.remove(parent); + case "RestElement": + case "Property": + + // https://github.com/aladdin-add/eslint-plugin-autofix/issues/48 + if (!(grand && grand.type === "ObjectPattern")) { + return null; + } + + if (grand.properties.length === 1) { + const identifierRemoval = fixer.remove(parent); + const comma = sourceCode.getLastToken(grand, commaFilter); + + return comma ? [identifierRemoval, fixer.remove(comma)] : identifierRemoval; + } + + if (parent === grand.properties[grand.properties.length - 1]) { + const comma = sourceCode.getTokenBefore(parent, commaFilter); + + return [fixer.remove(parent), fixer.remove(comma)]; + } + + return [ + fixer.remove(parent), + fixer.remove(sourceCode.getTokenAfter(parent, commaFilter)) + ]; + case "ArrayPattern": + return fixer.remove(node); + default: + return null; + } + }; + return problem; + } +); diff --git a/packages/autofix/lib/rules/no-useless-catch.js b/packages/autofix/lib/rules/no-useless-catch.js index 1d71f1f..4385410 100644 --- a/packages/autofix/lib/rules/no-useless-catch.js +++ b/packages/autofix/lib/rules/no-useless-catch.js @@ -1,26 +1,26 @@ -/** - * @fileoverview Add fixer to rule no-useless-catch. - * @author Pig Fang - */ -"use strict"; - -const ruleComposer = require("eslint-rule-composer"); -const utils = require("../utils"); - -const rule = utils.getFixableRule("no-useless-catch", false); - -module.exports = ruleComposer.mapReports( - rule, - (problem, { sourceCode }) => { - problem.fix = fixer => { - const { node } = problem; - - if (node.parent.finalizer) { - return fixer.remove(node); - } - - return fixer.replaceText(node, sourceCode.getText(node.block)); - }; - return problem; - } -); +/** + * @fileoverview Add fixer to rule no-useless-catch. + * @author Pig Fang + */ +"use strict"; + +const ruleComposer = require("eslint-rule-composer"); +const utils = require("../utils"); + +const rule = utils.getFixableRule("no-useless-catch", false); + +module.exports = ruleComposer.mapReports( + rule, + (problem, { sourceCode }) => { + problem.fix = fixer => { + const { node } = problem; + + if (node.parent.finalizer) { + return fixer.remove(node); + } + + return fixer.replaceText(node, sourceCode.getText(node.block)); + }; + return problem; + } +); diff --git a/packages/autofix/lib/rules/no-useless-concat.js b/packages/autofix/lib/rules/no-useless-concat.js index 3bb0ab3..40d7e8c 100644 --- a/packages/autofix/lib/rules/no-useless-concat.js +++ b/packages/autofix/lib/rules/no-useless-concat.js @@ -1,22 +1,22 @@ -/** - * @fileoverview add fixer to rule no-useless-concat. - * @author Pig Fang - */ -"use strict"; - -const ruleComposer = require("eslint-rule-composer"); -const utils = require("../utils"); - -const rule = utils.getFixableRule("no-useless-concat"); - -module.exports = ruleComposer.mapReports( - rule, - problem => { - problem.fix = fixer => { - const { node } = problem; - - return fixer.replaceText(node, `"${node.left.value}${node.right.value}"`); - }; - return problem; - } -); +/** + * @fileoverview add fixer to rule no-useless-concat. + * @author Pig Fang + */ +"use strict"; + +const ruleComposer = require("eslint-rule-composer"); +const utils = require("../utils"); + +const rule = utils.getFixableRule("no-useless-concat"); + +module.exports = ruleComposer.mapReports( + rule, + problem => { + problem.fix = fixer => { + const { node } = problem; + + return fixer.replaceText(node, `"${node.left.value}${node.right.value}"`); + }; + return problem; + } +); diff --git a/packages/autofix/lib/rules/prefer-spread.js b/packages/autofix/lib/rules/prefer-spread.js index e95eb27..0cd42bf 100644 --- a/packages/autofix/lib/rules/prefer-spread.js +++ b/packages/autofix/lib/rules/prefer-spread.js @@ -1,35 +1,35 @@ -/** - * @fileoverview add fixer to rule prefer-spread - * @author Toru Nagashima - * @author 唯然 - */ -"use strict"; - -const ruleComposer = require("eslint-rule-composer"); -const utils = require("../utils"); - -const rule = utils.getFixableRule("prefer-spread", false); - -module.exports = ruleComposer.mapReports( - rule, - (problem, metadata) => { - problem.fix = function(fixer) { - const node = problem.node; - const sourceCode = metadata.sourceCode; - const applied = node.callee.object; - const expectedThis = (applied.type === "MemberExpression") ? applied.object : null; - - if (expectedThis && expectedThis.type !== "Identifier") { - - // Don't fix cases where the `this` value could be a computed expression. - return null; - } - - const propertyDot = metadata.sourceCode.getFirstTokenBetween(applied, node.callee.property, token => token.value === "."); - - return fixer.replaceTextRange([propertyDot.range[0], node.range[1]], `(...${sourceCode.getText(node.arguments[1])})`); - - }; - return problem; - } -); +/** + * @fileoverview add fixer to rule prefer-spread + * @author Toru Nagashima + * @author 唯然 + */ +"use strict"; + +const ruleComposer = require("eslint-rule-composer"); +const utils = require("../utils"); + +const rule = utils.getFixableRule("prefer-spread", false); + +module.exports = ruleComposer.mapReports( + rule, + (problem, metadata) => { + problem.fix = function(fixer) { + const node = problem.node; + const sourceCode = metadata.sourceCode; + const applied = node.callee.object; + const expectedThis = (applied.type === "MemberExpression") ? applied.object : null; + + if (expectedThis && expectedThis.type !== "Identifier") { + + // Don't fix cases where the `this` value could be a computed expression. + return null; + } + + const propertyDot = metadata.sourceCode.getFirstTokenBetween(applied, node.callee.property, token => token.value === "."); + + return fixer.replaceTextRange([propertyDot.range[0], node.range[1]], `(...${sourceCode.getText(node.arguments[1])})`); + + }; + return problem; + } +); diff --git a/packages/autofix/lib/rules/radix.js b/packages/autofix/lib/rules/radix.js index de78ed5..0f2ab04 100644 --- a/packages/autofix/lib/rules/radix.js +++ b/packages/autofix/lib/rules/radix.js @@ -1,37 +1,37 @@ -/** - * @fileoverview add fixer to rule radix. - * @author Pig Fang - */ -"use strict"; - -const ruleComposer = require("eslint-rule-composer"); -const utils = require("../utils"); - -const rule = utils.getFixableRule("radix"); - -module.exports = ruleComposer.mapReports( - rule, - (problem, { options, sourceCode }) => { - problem.fix = fixer => { - const [option = "always"] = options; - const { node } = problem; - - if (node.arguments.length === 0) { - return null; - } - - if (option === "always" && node.arguments.length === 1) { - return fixer.insertTextAfter(node.arguments[0], ", 10"); - } - if (option === "as-needed" && node.arguments.length > 1) { - const radix = node.arguments[1]; - - if (radix.type === "Literal" && radix.value === 10) { - return fixer.removeRange([sourceCode.getTokenBefore(radix).range[0], radix.range[1]]); - } - } - return null; - }; - return problem; - } -); +/** + * @fileoverview add fixer to rule radix. + * @author Pig Fang + */ +"use strict"; + +const ruleComposer = require("eslint-rule-composer"); +const utils = require("../utils"); + +const rule = utils.getFixableRule("radix"); + +module.exports = ruleComposer.mapReports( + rule, + (problem, { options, sourceCode }) => { + problem.fix = fixer => { + const [option = "always"] = options; + const { node } = problem; + + if (node.arguments.length === 0) { + return null; + } + + if (option === "always" && node.arguments.length === 1) { + return fixer.insertTextAfter(node.arguments[0], ", 10"); + } + if (option === "as-needed" && node.arguments.length > 1) { + const radix = node.arguments[1]; + + if (radix.type === "Literal" && radix.value === 10) { + return fixer.removeRange([sourceCode.getTokenBefore(radix).range[0], radix.range[1]]); + } + } + return null; + }; + return problem; + } +); diff --git a/packages/autofix/lib/rules/valid-typeof.js b/packages/autofix/lib/rules/valid-typeof.js index ad74009..dc429ca 100644 --- a/packages/autofix/lib/rules/valid-typeof.js +++ b/packages/autofix/lib/rules/valid-typeof.js @@ -1,31 +1,31 @@ -/** - * @fileoverview add fixer to rule valid-typeof. - * @author Pig Fang - */ -"use strict"; - -const ruleComposer = require("eslint-rule-composer"); -const utils = require("../utils"); -const stringSimilarity = require("string-similarity"); - -const rule = utils.getFixableRule("valid-typeof", false); -const VALID_TYPES = ["symbol", "undefined", "object", "boolean", "number", "string", "function"]; - -module.exports = ruleComposer.mapReports( - rule, - problem => { - problem.fix = fixer => { - const { node } = problem, - { parent } = node; - const sibling = parent.left.type.endsWith("Literal") ? parent.left : parent.right; - const value = sibling.type === "Literal" ? sibling.value : sibling.quasis[0].value.cooked; - const best = stringSimilarity.findBestMatch(value, VALID_TYPES).bestMatch; - - if (best.rating > 0.3) { - return fixer.replaceText(sibling, `"${best.target}"`); - } - return null; - }; - return problem; - } -); +/** + * @fileoverview add fixer to rule valid-typeof. + * @author Pig Fang + */ +"use strict"; + +const ruleComposer = require("eslint-rule-composer"); +const utils = require("../utils"); +const stringSimilarity = require("string-similarity"); + +const rule = utils.getFixableRule("valid-typeof", false); +const VALID_TYPES = ["symbol", "undefined", "object", "boolean", "number", "string", "function"]; + +module.exports = ruleComposer.mapReports( + rule, + problem => { + problem.fix = fixer => { + const { node } = problem, + { parent } = node; + const sibling = parent.left.type.endsWith("Literal") ? parent.left : parent.right; + const value = sibling.type === "Literal" ? sibling.value : sibling.quasis[0].value.cooked; + const best = stringSimilarity.findBestMatch(value, VALID_TYPES).bestMatch; + + if (best.rating > 0.3) { + return fixer.replaceText(sibling, `"${best.target}"`); + } + return null; + }; + return problem; + } +); diff --git a/packages/autofix/lib/utils.js b/packages/autofix/lib/utils.js index e9d5f55..628e223 100644 --- a/packages/autofix/lib/utils.js +++ b/packages/autofix/lib/utils.js @@ -1,15 +1,15 @@ -/** - * @fileoverview utils used by the rule fixers. - * @author 唯然 - */ -"use strict"; -const eslint = require("eslint"); -const linter = new eslint.Linter(); - -exports.getFixableRule = function(ruleName, recommended = false, fixable = "code") { - const rule = linter.getRules().get(ruleName); - - rule.meta.recommended = recommended; - rule.meta.fixable = fixable; - return rule; -}; +/** + * @fileoverview utils used by the rule fixers. + * @author 唯然 + */ +"use strict"; +const eslint = require("eslint"); +const linter = new eslint.Linter(); + +exports.getFixableRule = function(ruleName, recommended = false, fixable = "code") { + const rule = linter.getRules().get(ruleName); + + rule.meta.recommended = recommended; + rule.meta.fixable = fixable; + return rule; +}; diff --git a/packages/autofix/package.json b/packages/autofix/package.json index 4c0b523..abd8967 100644 --- a/packages/autofix/package.json +++ b/packages/autofix/package.json @@ -1,87 +1,49 @@ -{ - "name": "eslint-plugin-autofix", - "version": "0.0.9", - "author": "薛定谔的猫 ", - "description": "autofix some errors reported by eslint rules.", - "main": "./lib/index.js", - "scripts": { - "lint": "eslint .", - "test": "mocha \"tests/**/*.js\"", - "pretest": "npm run lint", - "generate-readme-table": "node tools/generate-readme-table.js", - "generate-release": "node-release-script", - "new": "node tools/new-rule.js" - }, - "files": [ - "LICENSE", - "readme.md", - "lib" - ], - "dependencies": { - "eslint-rule-composer": "^0.3.0", - "espree": "^6.0.0", - "esutils": "^2.0.2", - "lodash": "^4.17.15", - "string-similarity": "^3.0.0" - }, - "devDependencies": { - "@not-an-aardvark/node-release-script": "^0.1.0", - "@types/eslint": "^4.16.8", - "eslint": "^6.1.0", - "eslint-config-eslint": "^5.0.1", - "eslint-plugin-eslint-plugin": "^2.0.1", - "eslint-plugin-node": "^9.1.0", - "execa": "^2.0.4", - "git-config": "0.0.7", - "husky": "^3.0.3", - "minimist": "^1.2.0", - "mocha": "^6.2.0", - "standard-version": "^7.0.0" - }, - "peerDependencies": { - "eslint": ">= 5.12.1" - }, - "keywords": [ - "eslint plugin", - "autofix" - ], - "eslintConfig": { - "extends": [ - "eslint", - "plugin:eslint-plugin/recommended" - ], - "plugins": [ - "eslint-plugin" - ], - "overrides": [ - { - "files": [ - "lib/rules/*.js" - ], - "rules": { - "project/rule-def-format": 0 - } - }, - { - "files": [ - "tests/**/*.js" - ], - "env": { - "mocha": true - } - } - ] - }, - "husky": { - "hooks": { - "pre-commit": "npm run lint -- --fix && git add ." - } - }, - "license": "MIT", - "repository": "https://github.com/aladdin-add/eslint-plugin/tree/master/packages/autofix", - "homepage": "https://github.com/aladdin-add/eslint-plugin/tree/master/packages/autofix", - "bugs": "https://github.com/aladdin-add/eslint-plugin/issues/new?assignees=aladdin-add&labels=bug&template=bug-report--eslint-plugin-autofix.md&title=eslint-plugin-autofix+bug", - "engines": { - "node": ">=8" - } -} +{ + "name": "eslint-plugin-autofix", + "version": "0.0.9", + "author": "薛定谔的猫 ", + "description": "autofix some errors reported by eslint rules.", + "main": "./lib/index.js", + "scripts": { + "test": "mocha \"tests/**/*.js\"", + "generate-readme-table": "node tools/generate-readme-table.js", + "generate-release": "node-release-script", + "new": "node tools/new-rule.js" + }, + "files": [ + "LICENSE", + "readme.md", + "lib" + ], + "dependencies": { + "eslint-rule-composer": "^0.3.0", + "espree": "^6.1.2", + "esutils": "^2.0.2", + "lodash": "^4.17.15", + "string-similarity": "^4.0.1" + }, + "devDependencies": { + "@not-an-aardvark/node-release-script": "^0.1.0", + "@types/eslint": "^6.1.8", + "execa": "^4.0.0", + "git-config": "0.0.7", + "husky": "^4.2.3", + "minimist": "^1.2.0", + "mocha": "^7.0.1", + "standard-version": "^7.1.0" + }, + "peerDependencies": { + "eslint": ">= 5.12.1" + }, + "keywords": [ + "eslint plugin", + "autofix" + ], + "license": "MIT", + "repository": "https://github.com/aladdin-add/eslint-plugin/tree/master/packages/autofix", + "homepage": "https://github.com/aladdin-add/eslint-plugin/tree/master/packages/autofix", + "bugs": "https://github.com/aladdin-add/eslint-plugin/issues/new?assignees=aladdin-add&labels=bug&template=bug-report--eslint-plugin-autofix.md&title=eslint-plugin-autofix+bug", + "engines": { + "node": ">=8" + } +} diff --git a/packages/autofix/readme.md b/packages/autofix/readme.md index d0f4a54..a972096 100644 --- a/packages/autofix/readme.md +++ b/packages/autofix/readme.md @@ -1,166 +1,166 @@ -

- - build status - - - dependency status - -

- -# eslint-plugin-autofix - -## Install & usage - -```bash -$ npm i eslint-plugin-autofix -D -``` - -add prefix "autofix" to the rulename in eslintrc: -```js -{ - "plugins": ["autofix"], - "rules": { - "autofix/no-debugger": "error" - } -} -``` - -## Supported rules - -✔️ indicates that a rule is recommended for all users. -🛠 indicates that a rule is fixable. - - -Name | ✔️ | 🛠 | Description ------ | ----- | ----- | ----- -[array-bracket-newline](https://eslint.org/docs/rules/array-bracket-newline) | | 🛠 | enforce linebreaks after opening and before closing array brackets -[array-bracket-spacing](https://eslint.org/docs/rules/array-bracket-spacing) | | 🛠 | enforce consistent spacing inside array brackets -[array-element-newline](https://eslint.org/docs/rules/array-element-newline) | | 🛠 | enforce line breaks after each array element -[arrow-body-style](https://eslint.org/docs/rules/arrow-body-style) | | 🛠 | require braces around arrow function bodies -[arrow-parens](https://eslint.org/docs/rules/arrow-parens) | | 🛠 | require parentheses around arrow function arguments -[arrow-spacing](https://eslint.org/docs/rules/arrow-spacing) | | 🛠 | enforce consistent spacing before and after the arrow in arrow functions -[block-spacing](https://eslint.org/docs/rules/block-spacing) | | 🛠 | disallow or enforce spaces inside of blocks after opening block and before closing block -[brace-style](https://eslint.org/docs/rules/brace-style) | | 🛠 | enforce consistent brace style for blocks -[capitalized-comments](https://eslint.org/docs/rules/capitalized-comments) | | 🛠 | enforce or disallow capitalization of the first letter of a comment -[comma-dangle](https://eslint.org/docs/rules/comma-dangle) | | 🛠 | require or disallow trailing commas -[comma-spacing](https://eslint.org/docs/rules/comma-spacing) | | 🛠 | enforce consistent spacing before and after commas -[comma-style](https://eslint.org/docs/rules/comma-style) | | 🛠 | enforce consistent comma style -[computed-property-spacing](https://eslint.org/docs/rules/computed-property-spacing) | | 🛠 | enforce consistent spacing inside computed property brackets -[curly](https://eslint.org/docs/rules/curly) | | 🛠 | enforce consistent brace style for all control statements -[dot-location](https://eslint.org/docs/rules/dot-location) | | 🛠 | enforce consistent newlines before and after dots -[dot-notation](https://eslint.org/docs/rules/dot-notation) | | 🛠 | enforce dot notation whenever possible -[eol-last](https://eslint.org/docs/rules/eol-last) | | 🛠 | require or disallow newline at the end of files -[eqeqeq](https://eslint.org/docs/rules/eqeqeq) | | 🛠 | require the use of `===` and `!==` -[func-call-spacing](https://eslint.org/docs/rules/func-call-spacing) | | 🛠 | require or disallow spacing between function identifiers and their invocations -[function-paren-newline](https://eslint.org/docs/rules/function-paren-newline) | | 🛠 | enforce consistent line breaks inside function parentheses -[generator-star-spacing](https://eslint.org/docs/rules/generator-star-spacing) | | 🛠 | enforce consistent spacing around `*` operators in generator functions -[implicit-arrow-linebreak](https://eslint.org/docs/rules/implicit-arrow-linebreak) | | 🛠 | enforce the location of arrow function bodies -[indent](https://eslint.org/docs/rules/indent) | | 🛠 | enforce consistent indentation -[indent-legacy](https://eslint.org/docs/rules/indent-legacy) | | 🛠 | enforce consistent indentation -[jsx-quotes](https://eslint.org/docs/rules/jsx-quotes) | | 🛠 | enforce the consistent use of either double or single quotes in JSX attributes -[key-spacing](https://eslint.org/docs/rules/key-spacing) | | 🛠 | enforce consistent spacing between keys and values in object literal properties -[keyword-spacing](https://eslint.org/docs/rules/keyword-spacing) | | 🛠 | enforce consistent spacing before and after keywords -[linebreak-style](https://eslint.org/docs/rules/linebreak-style) | | 🛠 | enforce consistent linebreak style -[lines-around-comment](https://eslint.org/docs/rules/lines-around-comment) | | 🛠 | require empty lines around comments -[lines-around-directive](https://eslint.org/docs/rules/lines-around-directive) | | 🛠 | require or disallow newlines around directives -[lines-between-class-members](https://eslint.org/docs/rules/lines-between-class-members) | | 🛠 | require or disallow an empty line between class members -[multiline-comment-style](https://eslint.org/docs/rules/multiline-comment-style) | | 🛠 | enforce a particular style for multiline comments -[new-parens](https://eslint.org/docs/rules/new-parens) | | 🛠 | require parentheses when invoking a constructor with no arguments -[newline-after-var](https://eslint.org/docs/rules/newline-after-var) | | 🛠 | require or disallow an empty line after variable declarations -[newline-before-return](https://eslint.org/docs/rules/newline-before-return) | | 🛠 | require an empty line before `return` statements -[newline-per-chained-call](https://eslint.org/docs/rules/newline-per-chained-call) | | 🛠 | require a newline after each call in a method chain -[no-alert](https://eslint.org/docs/rules/no-alert) | | 🛠 | disallow the use of `alert`, `confirm`, and `prompt` -[no-caller](https://eslint.org/docs/rules/no-caller) | | 🛠 | disallow the use of `arguments.caller` or `arguments.callee` -[no-confusing-arrow](https://eslint.org/docs/rules/no-confusing-arrow) | | 🛠 | disallow arrow functions where they could be confused with comparisons -[no-console](https://eslint.org/docs/rules/no-console) | ✔️ | 🛠 | disallow the use of `console` -[no-debugger](https://eslint.org/docs/rules/no-debugger) | ✔️ | 🛠 | disallow the use of `debugger` -[no-else-return](https://eslint.org/docs/rules/no-else-return) | | 🛠 | disallow `else` blocks after `return` statements in `if` statements -[no-eq-null](https://eslint.org/docs/rules/no-eq-null) | | 🛠 | disallow `null` comparisons without type-checking operators -[no-extra-bind](https://eslint.org/docs/rules/no-extra-bind) | | 🛠 | disallow unnecessary calls to `.bind()` -[no-extra-boolean-cast](https://eslint.org/docs/rules/no-extra-boolean-cast) | | 🛠 | disallow unnecessary boolean casts -[no-extra-label](https://eslint.org/docs/rules/no-extra-label) | | 🛠 | disallow unnecessary labels -[no-extra-parens](https://eslint.org/docs/rules/no-extra-parens) | | 🛠 | disallow unnecessary parentheses -[no-extra-semi](https://eslint.org/docs/rules/no-extra-semi) | | 🛠 | disallow unnecessary semicolons -[no-floating-decimal](https://eslint.org/docs/rules/no-floating-decimal) | | 🛠 | disallow leading or trailing decimal points in numeric literals -[no-implicit-coercion](https://eslint.org/docs/rules/no-implicit-coercion) | | 🛠 | disallow shorthand type conversions -[no-lonely-if](https://eslint.org/docs/rules/no-lonely-if) | | 🛠 | disallow `if` statements as the only statement in `else` blocks -[no-multi-spaces](https://eslint.org/docs/rules/no-multi-spaces) | | 🛠 | disallow multiple spaces -[no-multiple-empty-lines](https://eslint.org/docs/rules/no-multiple-empty-lines) | | 🛠 | disallow multiple empty lines -[no-new-symbol](https://eslint.org/docs/rules/no-new-symbol) | | 🛠 | disallow `new` operators with the `Symbol` object -[no-plusplus](https://eslint.org/docs/rules/no-plusplus) | ✔️ | 🛠 | disallow the unary operators `++` and `--` -[no-proto](https://eslint.org/docs/rules/no-proto) | | 🛠 | disallow the use of the `__proto__` property -[no-prototype-builtins](https://eslint.org/docs/rules/no-prototype-builtins) | | 🛠 | disallow calling some `Object.prototype` methods directly on objects -[no-regex-spaces](https://eslint.org/docs/rules/no-regex-spaces) | | 🛠 | disallow multiple spaces in regular expressions -[no-spaced-func](https://eslint.org/docs/rules/no-spaced-func) | | 🛠 | disallow spacing between function identifiers and their applications (deprecated) -[no-throw-literal](https://eslint.org/docs/rules/no-throw-literal) | | 🛠 | disallow throwing literals as exceptions -[no-trailing-spaces](https://eslint.org/docs/rules/no-trailing-spaces) | | 🛠 | disallow trailing whitespace at the end of lines -[no-undef-init](https://eslint.org/docs/rules/no-undef-init) | | 🛠 | disallow initializing variables to `undefined` -[no-unneeded-ternary](https://eslint.org/docs/rules/no-unneeded-ternary) | | 🛠 | disallow ternary operators when simpler alternatives exist -[no-unsafe-negation](https://eslint.org/docs/rules/no-unsafe-negation) | | 🛠 | disallow negating the left operand of relational operators -[no-unused-labels](https://eslint.org/docs/rules/no-unused-labels) | | 🛠 | disallow unused labels -[no-unused-vars](https://eslint.org/docs/rules/no-unused-vars) | | 🛠 | disallow unused variables -[no-useless-catch](https://eslint.org/docs/rules/no-useless-catch) | | 🛠 | disallow unnecessary `catch` clauses -[no-useless-computed-key](https://eslint.org/docs/rules/no-useless-computed-key) | | 🛠 | disallow unnecessary computed property keys in object literals -[no-useless-concat](https://eslint.org/docs/rules/no-useless-concat) | | 🛠 | disallow unnecessary concatenation of literals or template literals -[no-useless-rename](https://eslint.org/docs/rules/no-useless-rename) | | 🛠 | disallow renaming import, export, and destructured assignments to the same name -[no-useless-return](https://eslint.org/docs/rules/no-useless-return) | | 🛠 | disallow redundant return statements -[no-var](https://eslint.org/docs/rules/no-var) | | 🛠 | require `let` or `const` instead of `var` -[no-whitespace-before-property](https://eslint.org/docs/rules/no-whitespace-before-property) | | 🛠 | disallow whitespace before properties -[nonblock-statement-body-position](https://eslint.org/docs/rules/nonblock-statement-body-position) | | 🛠 | enforce the location of single-line statements -[object-curly-newline](https://eslint.org/docs/rules/object-curly-newline) | | 🛠 | enforce consistent line breaks inside braces -[object-curly-spacing](https://eslint.org/docs/rules/object-curly-spacing) | | 🛠 | enforce consistent spacing inside braces -[object-property-newline](https://eslint.org/docs/rules/object-property-newline) | | 🛠 | enforce placing object properties on separate lines -[object-shorthand](https://eslint.org/docs/rules/object-shorthand) | | 🛠 | require or disallow method and property shorthand syntax for object literals -[one-var](https://eslint.org/docs/rules/one-var) | | 🛠 | enforce variables to be declared either together or separately in functions -[one-var-declaration-per-line](https://eslint.org/docs/rules/one-var-declaration-per-line) | | 🛠 | require or disallow newlines around variable declarations -[operator-assignment](https://eslint.org/docs/rules/operator-assignment) | | 🛠 | require or disallow assignment operator shorthand where possible -[operator-linebreak](https://eslint.org/docs/rules/operator-linebreak) | | 🛠 | enforce consistent linebreak style for operators -[padded-blocks](https://eslint.org/docs/rules/padded-blocks) | | 🛠 | require or disallow padding within blocks -[padding-line-between-statements](https://eslint.org/docs/rules/padding-line-between-statements) | | 🛠 | require or disallow padding lines between statements -[prefer-arrow-callback](https://eslint.org/docs/rules/prefer-arrow-callback) | | 🛠 | require using arrow functions for callbacks -[prefer-const](https://eslint.org/docs/rules/prefer-const) | | 🛠 | require `const` declarations for variables that are never reassigned after declared -[prefer-destructuring](https://eslint.org/docs/rules/prefer-destructuring) | | 🛠 | require destructuring from arrays and/or objects -[prefer-numeric-literals](https://eslint.org/docs/rules/prefer-numeric-literals) | | 🛠 | disallow `parseInt()` and `Number.parseInt()` in favor of binary, octal, and hexadecimal literals -[prefer-object-spread](https://eslint.org/docs/rules/prefer-object-spread) | | 🛠 | disallow using Object.assign with an object literal as the first argument and prefer the use of object spread instead. -[prefer-spread](https://eslint.org/docs/rules/prefer-spread) | | 🛠 | require spread operators instead of `.apply()` -[prefer-template](https://eslint.org/docs/rules/prefer-template) | | 🛠 | require template literals instead of string concatenation -[quote-props](https://eslint.org/docs/rules/quote-props) | | 🛠 | require quotes around object literal property names -[quotes](https://eslint.org/docs/rules/quotes) | | 🛠 | enforce the consistent use of either backticks, double, or single quotes -[radix](https://eslint.org/docs/rules/radix) | | 🛠 | enforce the consistent use of the radix argument when using `parseInt()` -[rest-spread-spacing](https://eslint.org/docs/rules/rest-spread-spacing) | | 🛠 | enforce spacing between rest and spread operators and their expressions -[semi](https://eslint.org/docs/rules/semi) | | 🛠 | require or disallow semicolons instead of ASI -[semi-spacing](https://eslint.org/docs/rules/semi-spacing) | | 🛠 | enforce consistent spacing before and after semicolons -[semi-style](https://eslint.org/docs/rules/semi-style) | | 🛠 | enforce location of semicolons -[sort-imports](https://eslint.org/docs/rules/sort-imports) | | 🛠 | enforce sorted import declarations within modules -[sort-vars](https://eslint.org/docs/rules/sort-vars) | | 🛠 | require variables within the same declaration block to be sorted -[space-before-blocks](https://eslint.org/docs/rules/space-before-blocks) | | 🛠 | enforce consistent spacing before blocks -[space-before-function-paren](https://eslint.org/docs/rules/space-before-function-paren) | | 🛠 | enforce consistent spacing before `function` definition opening parenthesis -[space-in-parens](https://eslint.org/docs/rules/space-in-parens) | | 🛠 | enforce consistent spacing inside parentheses -[space-infix-ops](https://eslint.org/docs/rules/space-infix-ops) | | 🛠 | require spacing around infix operators -[space-unary-ops](https://eslint.org/docs/rules/space-unary-ops) | | 🛠 | enforce consistent spacing before or after unary operators -[spaced-comment](https://eslint.org/docs/rules/spaced-comment) | | 🛠 | enforce consistent spacing after the `//` or `/*` in a comment -[strict](https://eslint.org/docs/rules/strict) | | 🛠 | require or disallow strict mode directives -[switch-colon-spacing](https://eslint.org/docs/rules/switch-colon-spacing) | | 🛠 | enforce spacing around colons of switch statements -[template-curly-spacing](https://eslint.org/docs/rules/template-curly-spacing) | | 🛠 | require or disallow spacing around embedded expressions of template strings -[template-tag-spacing](https://eslint.org/docs/rules/template-tag-spacing) | | 🛠 | require or disallow spacing between template tags and their literals -[unicode-bom](https://eslint.org/docs/rules/unicode-bom) | | 🛠 | require or disallow Unicode byte order mark (BOM) -[valid-jsdoc](https://eslint.org/docs/rules/valid-jsdoc) | | 🛠 | enforce valid JSDoc comments -[valid-typeof](https://eslint.org/docs/rules/valid-typeof) | | 🛠 | enforce comparing `typeof` expressions against valid strings -[wrap-iife](https://eslint.org/docs/rules/wrap-iife) | | 🛠 | require parentheses around immediate `function` invocations -[wrap-regex](https://eslint.org/docs/rules/wrap-regex) | | 🛠 | require parenthesis around regex literals -[yield-star-spacing](https://eslint.org/docs/rules/yield-star-spacing) | | 🛠 | require or disallow spacing around the `*` in `yield*` expressions -[yoda](https://eslint.org/docs/rules/yoda) | | 🛠 | require or disallow "Yoda" conditions - - -## Contributing - -+ to add a new rule: -```bash -npm run new ${ruleName} -``` - -## Acknowledgement -+ [ESLint](https://eslint.org) +

+ + build status + + + dependency status + +

+ +# eslint-plugin-autofix + +## Install & usage + +```bash +$ npm i eslint-plugin-autofix -D +``` + +add prefix "autofix" to the rulename in eslintrc: +```js +{ + "plugins": ["autofix"], + "rules": { + "autofix/no-debugger": "error" + } +} +``` + +## Supported rules + +✔️ indicates that a rule is recommended for all users. +🛠 indicates that a rule is fixable. + + +Name | ✔️ | 🛠 | Description +----- | ----- | ----- | ----- +[array-bracket-newline](https://eslint.org/docs/rules/array-bracket-newline) | | 🛠 | enforce linebreaks after opening and before closing array brackets +[array-bracket-spacing](https://eslint.org/docs/rules/array-bracket-spacing) | | 🛠 | enforce consistent spacing inside array brackets +[array-element-newline](https://eslint.org/docs/rules/array-element-newline) | | 🛠 | enforce line breaks after each array element +[arrow-body-style](https://eslint.org/docs/rules/arrow-body-style) | | 🛠 | require braces around arrow function bodies +[arrow-parens](https://eslint.org/docs/rules/arrow-parens) | | 🛠 | require parentheses around arrow function arguments +[arrow-spacing](https://eslint.org/docs/rules/arrow-spacing) | | 🛠 | enforce consistent spacing before and after the arrow in arrow functions +[block-spacing](https://eslint.org/docs/rules/block-spacing) | | 🛠 | disallow or enforce spaces inside of blocks after opening block and before closing block +[brace-style](https://eslint.org/docs/rules/brace-style) | | 🛠 | enforce consistent brace style for blocks +[capitalized-comments](https://eslint.org/docs/rules/capitalized-comments) | | 🛠 | enforce or disallow capitalization of the first letter of a comment +[comma-dangle](https://eslint.org/docs/rules/comma-dangle) | | 🛠 | require or disallow trailing commas +[comma-spacing](https://eslint.org/docs/rules/comma-spacing) | | 🛠 | enforce consistent spacing before and after commas +[comma-style](https://eslint.org/docs/rules/comma-style) | | 🛠 | enforce consistent comma style +[computed-property-spacing](https://eslint.org/docs/rules/computed-property-spacing) | | 🛠 | enforce consistent spacing inside computed property brackets +[curly](https://eslint.org/docs/rules/curly) | | 🛠 | enforce consistent brace style for all control statements +[dot-location](https://eslint.org/docs/rules/dot-location) | | 🛠 | enforce consistent newlines before and after dots +[dot-notation](https://eslint.org/docs/rules/dot-notation) | | 🛠 | enforce dot notation whenever possible +[eol-last](https://eslint.org/docs/rules/eol-last) | | 🛠 | require or disallow newline at the end of files +[eqeqeq](https://eslint.org/docs/rules/eqeqeq) | | 🛠 | require the use of `===` and `!==` +[func-call-spacing](https://eslint.org/docs/rules/func-call-spacing) | | 🛠 | require or disallow spacing between function identifiers and their invocations +[function-paren-newline](https://eslint.org/docs/rules/function-paren-newline) | | 🛠 | enforce consistent line breaks inside function parentheses +[generator-star-spacing](https://eslint.org/docs/rules/generator-star-spacing) | | 🛠 | enforce consistent spacing around `*` operators in generator functions +[implicit-arrow-linebreak](https://eslint.org/docs/rules/implicit-arrow-linebreak) | | 🛠 | enforce the location of arrow function bodies +[indent](https://eslint.org/docs/rules/indent) | | 🛠 | enforce consistent indentation +[indent-legacy](https://eslint.org/docs/rules/indent-legacy) | | 🛠 | enforce consistent indentation +[jsx-quotes](https://eslint.org/docs/rules/jsx-quotes) | | 🛠 | enforce the consistent use of either double or single quotes in JSX attributes +[key-spacing](https://eslint.org/docs/rules/key-spacing) | | 🛠 | enforce consistent spacing between keys and values in object literal properties +[keyword-spacing](https://eslint.org/docs/rules/keyword-spacing) | | 🛠 | enforce consistent spacing before and after keywords +[linebreak-style](https://eslint.org/docs/rules/linebreak-style) | | 🛠 | enforce consistent linebreak style +[lines-around-comment](https://eslint.org/docs/rules/lines-around-comment) | | 🛠 | require empty lines around comments +[lines-around-directive](https://eslint.org/docs/rules/lines-around-directive) | | 🛠 | require or disallow newlines around directives +[lines-between-class-members](https://eslint.org/docs/rules/lines-between-class-members) | | 🛠 | require or disallow an empty line between class members +[multiline-comment-style](https://eslint.org/docs/rules/multiline-comment-style) | | 🛠 | enforce a particular style for multiline comments +[new-parens](https://eslint.org/docs/rules/new-parens) | | 🛠 | require parentheses when invoking a constructor with no arguments +[newline-after-var](https://eslint.org/docs/rules/newline-after-var) | | 🛠 | require or disallow an empty line after variable declarations +[newline-before-return](https://eslint.org/docs/rules/newline-before-return) | | 🛠 | require an empty line before `return` statements +[newline-per-chained-call](https://eslint.org/docs/rules/newline-per-chained-call) | | 🛠 | require a newline after each call in a method chain +[no-alert](https://eslint.org/docs/rules/no-alert) | | 🛠 | disallow the use of `alert`, `confirm`, and `prompt` +[no-caller](https://eslint.org/docs/rules/no-caller) | | 🛠 | disallow the use of `arguments.caller` or `arguments.callee` +[no-confusing-arrow](https://eslint.org/docs/rules/no-confusing-arrow) | | 🛠 | disallow arrow functions where they could be confused with comparisons +[no-console](https://eslint.org/docs/rules/no-console) | ✔️ | 🛠 | disallow the use of `console` +[no-debugger](https://eslint.org/docs/rules/no-debugger) | ✔️ | 🛠 | disallow the use of `debugger` +[no-else-return](https://eslint.org/docs/rules/no-else-return) | | 🛠 | disallow `else` blocks after `return` statements in `if` statements +[no-eq-null](https://eslint.org/docs/rules/no-eq-null) | | 🛠 | disallow `null` comparisons without type-checking operators +[no-extra-bind](https://eslint.org/docs/rules/no-extra-bind) | | 🛠 | disallow unnecessary calls to `.bind()` +[no-extra-boolean-cast](https://eslint.org/docs/rules/no-extra-boolean-cast) | | 🛠 | disallow unnecessary boolean casts +[no-extra-label](https://eslint.org/docs/rules/no-extra-label) | | 🛠 | disallow unnecessary labels +[no-extra-parens](https://eslint.org/docs/rules/no-extra-parens) | | 🛠 | disallow unnecessary parentheses +[no-extra-semi](https://eslint.org/docs/rules/no-extra-semi) | | 🛠 | disallow unnecessary semicolons +[no-floating-decimal](https://eslint.org/docs/rules/no-floating-decimal) | | 🛠 | disallow leading or trailing decimal points in numeric literals +[no-implicit-coercion](https://eslint.org/docs/rules/no-implicit-coercion) | | 🛠 | disallow shorthand type conversions +[no-lonely-if](https://eslint.org/docs/rules/no-lonely-if) | | 🛠 | disallow `if` statements as the only statement in `else` blocks +[no-multi-spaces](https://eslint.org/docs/rules/no-multi-spaces) | | 🛠 | disallow multiple spaces +[no-multiple-empty-lines](https://eslint.org/docs/rules/no-multiple-empty-lines) | | 🛠 | disallow multiple empty lines +[no-new-symbol](https://eslint.org/docs/rules/no-new-symbol) | | 🛠 | disallow `new` operators with the `Symbol` object +[no-plusplus](https://eslint.org/docs/rules/no-plusplus) | ✔️ | 🛠 | disallow the unary operators `++` and `--` +[no-proto](https://eslint.org/docs/rules/no-proto) | | 🛠 | disallow the use of the `__proto__` property +[no-prototype-builtins](https://eslint.org/docs/rules/no-prototype-builtins) | | 🛠 | disallow calling some `Object.prototype` methods directly on objects +[no-regex-spaces](https://eslint.org/docs/rules/no-regex-spaces) | | 🛠 | disallow multiple spaces in regular expressions +[no-spaced-func](https://eslint.org/docs/rules/no-spaced-func) | | 🛠 | disallow spacing between function identifiers and their applications (deprecated) +[no-throw-literal](https://eslint.org/docs/rules/no-throw-literal) | | 🛠 | disallow throwing literals as exceptions +[no-trailing-spaces](https://eslint.org/docs/rules/no-trailing-spaces) | | 🛠 | disallow trailing whitespace at the end of lines +[no-undef-init](https://eslint.org/docs/rules/no-undef-init) | | 🛠 | disallow initializing variables to `undefined` +[no-unneeded-ternary](https://eslint.org/docs/rules/no-unneeded-ternary) | | 🛠 | disallow ternary operators when simpler alternatives exist +[no-unsafe-negation](https://eslint.org/docs/rules/no-unsafe-negation) | | 🛠 | disallow negating the left operand of relational operators +[no-unused-labels](https://eslint.org/docs/rules/no-unused-labels) | | 🛠 | disallow unused labels +[no-unused-vars](https://eslint.org/docs/rules/no-unused-vars) | | 🛠 | disallow unused variables +[no-useless-catch](https://eslint.org/docs/rules/no-useless-catch) | | 🛠 | disallow unnecessary `catch` clauses +[no-useless-computed-key](https://eslint.org/docs/rules/no-useless-computed-key) | | 🛠 | disallow unnecessary computed property keys in object literals +[no-useless-concat](https://eslint.org/docs/rules/no-useless-concat) | | 🛠 | disallow unnecessary concatenation of literals or template literals +[no-useless-rename](https://eslint.org/docs/rules/no-useless-rename) | | 🛠 | disallow renaming import, export, and destructured assignments to the same name +[no-useless-return](https://eslint.org/docs/rules/no-useless-return) | | 🛠 | disallow redundant return statements +[no-var](https://eslint.org/docs/rules/no-var) | | 🛠 | require `let` or `const` instead of `var` +[no-whitespace-before-property](https://eslint.org/docs/rules/no-whitespace-before-property) | | 🛠 | disallow whitespace before properties +[nonblock-statement-body-position](https://eslint.org/docs/rules/nonblock-statement-body-position) | | 🛠 | enforce the location of single-line statements +[object-curly-newline](https://eslint.org/docs/rules/object-curly-newline) | | 🛠 | enforce consistent line breaks inside braces +[object-curly-spacing](https://eslint.org/docs/rules/object-curly-spacing) | | 🛠 | enforce consistent spacing inside braces +[object-property-newline](https://eslint.org/docs/rules/object-property-newline) | | 🛠 | enforce placing object properties on separate lines +[object-shorthand](https://eslint.org/docs/rules/object-shorthand) | | 🛠 | require or disallow method and property shorthand syntax for object literals +[one-var](https://eslint.org/docs/rules/one-var) | | 🛠 | enforce variables to be declared either together or separately in functions +[one-var-declaration-per-line](https://eslint.org/docs/rules/one-var-declaration-per-line) | | 🛠 | require or disallow newlines around variable declarations +[operator-assignment](https://eslint.org/docs/rules/operator-assignment) | | 🛠 | require or disallow assignment operator shorthand where possible +[operator-linebreak](https://eslint.org/docs/rules/operator-linebreak) | | 🛠 | enforce consistent linebreak style for operators +[padded-blocks](https://eslint.org/docs/rules/padded-blocks) | | 🛠 | require or disallow padding within blocks +[padding-line-between-statements](https://eslint.org/docs/rules/padding-line-between-statements) | | 🛠 | require or disallow padding lines between statements +[prefer-arrow-callback](https://eslint.org/docs/rules/prefer-arrow-callback) | | 🛠 | require using arrow functions for callbacks +[prefer-const](https://eslint.org/docs/rules/prefer-const) | | 🛠 | require `const` declarations for variables that are never reassigned after declared +[prefer-destructuring](https://eslint.org/docs/rules/prefer-destructuring) | | 🛠 | require destructuring from arrays and/or objects +[prefer-numeric-literals](https://eslint.org/docs/rules/prefer-numeric-literals) | | 🛠 | disallow `parseInt()` and `Number.parseInt()` in favor of binary, octal, and hexadecimal literals +[prefer-object-spread](https://eslint.org/docs/rules/prefer-object-spread) | | 🛠 | disallow using Object.assign with an object literal as the first argument and prefer the use of object spread instead. +[prefer-spread](https://eslint.org/docs/rules/prefer-spread) | | 🛠 | require spread operators instead of `.apply()` +[prefer-template](https://eslint.org/docs/rules/prefer-template) | | 🛠 | require template literals instead of string concatenation +[quote-props](https://eslint.org/docs/rules/quote-props) | | 🛠 | require quotes around object literal property names +[quotes](https://eslint.org/docs/rules/quotes) | | 🛠 | enforce the consistent use of either backticks, double, or single quotes +[radix](https://eslint.org/docs/rules/radix) | | 🛠 | enforce the consistent use of the radix argument when using `parseInt()` +[rest-spread-spacing](https://eslint.org/docs/rules/rest-spread-spacing) | | 🛠 | enforce spacing between rest and spread operators and their expressions +[semi](https://eslint.org/docs/rules/semi) | | 🛠 | require or disallow semicolons instead of ASI +[semi-spacing](https://eslint.org/docs/rules/semi-spacing) | | 🛠 | enforce consistent spacing before and after semicolons +[semi-style](https://eslint.org/docs/rules/semi-style) | | 🛠 | enforce location of semicolons +[sort-imports](https://eslint.org/docs/rules/sort-imports) | | 🛠 | enforce sorted import declarations within modules +[sort-vars](https://eslint.org/docs/rules/sort-vars) | | 🛠 | require variables within the same declaration block to be sorted +[space-before-blocks](https://eslint.org/docs/rules/space-before-blocks) | | 🛠 | enforce consistent spacing before blocks +[space-before-function-paren](https://eslint.org/docs/rules/space-before-function-paren) | | 🛠 | enforce consistent spacing before `function` definition opening parenthesis +[space-in-parens](https://eslint.org/docs/rules/space-in-parens) | | 🛠 | enforce consistent spacing inside parentheses +[space-infix-ops](https://eslint.org/docs/rules/space-infix-ops) | | 🛠 | require spacing around infix operators +[space-unary-ops](https://eslint.org/docs/rules/space-unary-ops) | | 🛠 | enforce consistent spacing before or after unary operators +[spaced-comment](https://eslint.org/docs/rules/spaced-comment) | | 🛠 | enforce consistent spacing after the `//` or `/*` in a comment +[strict](https://eslint.org/docs/rules/strict) | | 🛠 | require or disallow strict mode directives +[switch-colon-spacing](https://eslint.org/docs/rules/switch-colon-spacing) | | 🛠 | enforce spacing around colons of switch statements +[template-curly-spacing](https://eslint.org/docs/rules/template-curly-spacing) | | 🛠 | require or disallow spacing around embedded expressions of template strings +[template-tag-spacing](https://eslint.org/docs/rules/template-tag-spacing) | | 🛠 | require or disallow spacing between template tags and their literals +[unicode-bom](https://eslint.org/docs/rules/unicode-bom) | | 🛠 | require or disallow Unicode byte order mark (BOM) +[valid-jsdoc](https://eslint.org/docs/rules/valid-jsdoc) | | 🛠 | enforce valid JSDoc comments +[valid-typeof](https://eslint.org/docs/rules/valid-typeof) | | 🛠 | enforce comparing `typeof` expressions against valid strings +[wrap-iife](https://eslint.org/docs/rules/wrap-iife) | | 🛠 | require parentheses around immediate `function` invocations +[wrap-regex](https://eslint.org/docs/rules/wrap-regex) | | 🛠 | require parenthesis around regex literals +[yield-star-spacing](https://eslint.org/docs/rules/yield-star-spacing) | | 🛠 | require or disallow spacing around the `*` in `yield*` expressions +[yoda](https://eslint.org/docs/rules/yoda) | | 🛠 | require or disallow "Yoda" conditions + + +## Contributing + ++ to add a new rule: +```bash +npm run new ${ruleName} +``` + +## Acknowledgement ++ [ESLint](https://eslint.org) + [eslint-rule-composer](https://github.com/not-an-aardvark/eslint-rule-composer) \ No newline at end of file diff --git a/packages/autofix/tests/lib/index.js b/packages/autofix/tests/lib/index.js index 64f2528..10b6541 100644 --- a/packages/autofix/tests/lib/index.js +++ b/packages/autofix/tests/lib/index.js @@ -1,25 +1,25 @@ -/** - * @fileoverview Tests for index.js - * @author 唯然 - */ - -"use strict"; - -const entry = require("../../lib/index"); -const assert = require("assert"); -let rule; - -describe("should have the corrent entry", () => { - it("should export config: all", () => { - assert(entry && entry.configs && entry.configs.all); - assert(entry.configs.all.rules["no-debugger"], "off"); - assert(entry.configs.all.rules["autofix/no-debugger"], "error"); - }); - it("should export all rules", () => { - rule = entry.rules["no-debugger"]; - assert(rule && rule.meta && rule.create); - - rule = entry.rules.semi; - assert(rule && rule.meta && rule.create); - }); -}); +/** + * @fileoverview Tests for index.js + * @author 唯然 + */ + +"use strict"; + +const entry = require("../../lib/index"); +const assert = require("assert"); +let rule; + +describe("should have the corrent entry", () => { + it("should export config: all", () => { + assert(entry && entry.configs && entry.configs.all); + assert(entry.configs.all.rules["no-debugger"], "off"); + assert(entry.configs.all.rules["autofix/no-debugger"], "error"); + }); + it("should export all rules", () => { + rule = entry.rules["no-debugger"]; + assert(rule && rule.meta && rule.create); + + rule = entry.rules.semi; + assert(rule && rule.meta && rule.create); + }); +}); diff --git a/packages/autofix/tests/lib/rules/no-alert.js b/packages/autofix/tests/lib/rules/no-alert.js index 324cb07..c731392 100644 --- a/packages/autofix/tests/lib/rules/no-alert.js +++ b/packages/autofix/tests/lib/rules/no-alert.js @@ -1,75 +1,75 @@ -/** - * @fileoverview Tests for rule no-alert - * @author Pig Fang - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require("../../../lib/rules/no-alert"); -const RuleTester = require("eslint").RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new RuleTester(); - -const errors = [{ messageId: "unexpected", type: "CallExpression" }]; - -ruleTester.run("no-alert", rule, { - valid: [ - "alert", - "prompt", - "confirm" - ], - invalid: [ - { - code: "alert('test')", - output: "", - errors - }, - { - code: "alert(test)", - output: "", - errors - }, - { - code: "prompt('test')", - output: null, - errors - }, - { - code: "prompt(test)", - output: null, - errors - }, - { - code: "confirm('test')", - output: null, - errors - }, - { - code: "confirm(test)", - output: null, - errors - }, - { - code: "window.alert('test')", - output: "", - errors - }, - { - code: "window.confirm('test')", - output: null, - errors - }, - { - code: "window.prompt('test')", - output: null, - errors - } - ] -}); +/** + * @fileoverview Tests for rule no-alert + * @author Pig Fang + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require("../../../lib/rules/no-alert"); +const RuleTester = require("eslint").RuleTester; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const ruleTester = new RuleTester(); + +const errors = [{ messageId: "unexpected", type: "CallExpression" }]; + +ruleTester.run("no-alert", rule, { + valid: [ + "alert", + "prompt", + "confirm" + ], + invalid: [ + { + code: "alert('test')", + output: "", + errors + }, + { + code: "alert(test)", + output: "", + errors + }, + { + code: "prompt('test')", + output: null, + errors + }, + { + code: "prompt(test)", + output: null, + errors + }, + { + code: "confirm('test')", + output: null, + errors + }, + { + code: "confirm(test)", + output: null, + errors + }, + { + code: "window.alert('test')", + output: "", + errors + }, + { + code: "window.confirm('test')", + output: null, + errors + }, + { + code: "window.prompt('test')", + output: null, + errors + } + ] +}); diff --git a/packages/autofix/tests/lib/rules/no-caller.js b/packages/autofix/tests/lib/rules/no-caller.js index 53d243e..4c66037 100644 --- a/packages/autofix/tests/lib/rules/no-caller.js +++ b/packages/autofix/tests/lib/rules/no-caller.js @@ -1,58 +1,58 @@ -/** - * @fileoverview Tests for rule no-caller. - * @author Pig Fang - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require("../../../lib/rules/no-caller"); -const RuleTester = require("eslint").RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new RuleTester(); -const errors = [{ type: "MemberExpression" }]; - -ruleTester.run("no-caller", rule, { - valid: [ - "function foo() { foo(); }" - ], - invalid: [ - { - code: "function foo() { arguments.caller }", - output: null, - errors - }, - { - code: "arguments.callee", - output: null, - errors - }, - { - code: "var foo = function () { arguments.callee }", - output: null, - errors - }, - { - code: "var foo = () => { arguments.callee }", - parserOptions: { ecmaVersion: 6 }, - output: null, - errors - }, - { - code: "function foo() { arguments.callee }", - output: "function foo() { foo }", - errors - }, - { - code: "var foo = function foo() { arguments.callee }", - output: "var foo = function foo() { foo }", - errors - } - ] -}); +/** + * @fileoverview Tests for rule no-caller. + * @author Pig Fang + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require("../../../lib/rules/no-caller"); +const RuleTester = require("eslint").RuleTester; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const ruleTester = new RuleTester(); +const errors = [{ type: "MemberExpression" }]; + +ruleTester.run("no-caller", rule, { + valid: [ + "function foo() { foo(); }" + ], + invalid: [ + { + code: "function foo() { arguments.caller }", + output: null, + errors + }, + { + code: "arguments.callee", + output: null, + errors + }, + { + code: "var foo = function () { arguments.callee }", + output: null, + errors + }, + { + code: "var foo = () => { arguments.callee }", + parserOptions: { ecmaVersion: 6 }, + output: null, + errors + }, + { + code: "function foo() { arguments.callee }", + output: "function foo() { foo }", + errors + }, + { + code: "var foo = function foo() { arguments.callee }", + output: "var foo = function foo() { foo }", + errors + } + ] +}); diff --git a/packages/autofix/tests/lib/rules/no-console.js b/packages/autofix/tests/lib/rules/no-console.js index fe43a8c..5fb0ed5 100644 --- a/packages/autofix/tests/lib/rules/no-console.js +++ b/packages/autofix/tests/lib/rules/no-console.js @@ -1,77 +1,77 @@ -/** - * @fileoverview Tests for rule "no-console" - * @author Pig Fang - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require("../../../lib/rules/no-console"); -const RuleTester = require("eslint").RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new RuleTester(); - -ruleTester.run("no-console", rule, { - valid: [ - "console;", - "if (true) console;" - ], - invalid: [ - { - code: "console.log", - output: "", - errors: [{ messageId: "unexpected", type: "MemberExpression" }] - }, - { - code: "console.log()", - output: "", - errors: [{ messageId: "unexpected", type: "MemberExpression" }] - }, - { - code: "if (true) console.log", - output: "if (true) {}", - errors: [{ messageId: "unexpected", type: "MemberExpression" }] - }, - { - code: "if (true) { console.log }", - output: "if (true) { }", - errors: [{ messageId: "unexpected", type: "MemberExpression" }] - }, - { - code: "if (true) console.log()", - output: "if (true) {}", - errors: [{ messageId: "unexpected", type: "MemberExpression" }] - }, - { - code: "if (true) { console.log() }", - output: "if (true) { }", - errors: [{ messageId: "unexpected", type: "MemberExpression" }] - }, - { - code: "console.log(foo)", - output: "", - errors: [{ messageId: "unexpected", type: "MemberExpression" }] - }, - { - code: "console.log(233)", - output: "", - errors: [{ messageId: "unexpected", type: "MemberExpression" }] - }, - { - code: "console.log(foo())", - output: null, - errors: [{ messageId: "unexpected", type: "MemberExpression" }] - }, - { - code: "var log = console.log", - output: null, - errors: [{ messageId: "unexpected", type: "MemberExpression" }] - } - ] -}); +/** + * @fileoverview Tests for rule "no-console" + * @author Pig Fang + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require("../../../lib/rules/no-console"); +const RuleTester = require("eslint").RuleTester; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const ruleTester = new RuleTester(); + +ruleTester.run("no-console", rule, { + valid: [ + "console;", + "if (true) console;" + ], + invalid: [ + { + code: "console.log", + output: "", + errors: [{ messageId: "unexpected", type: "MemberExpression" }] + }, + { + code: "console.log()", + output: "", + errors: [{ messageId: "unexpected", type: "MemberExpression" }] + }, + { + code: "if (true) console.log", + output: "if (true) {}", + errors: [{ messageId: "unexpected", type: "MemberExpression" }] + }, + { + code: "if (true) { console.log }", + output: "if (true) { }", + errors: [{ messageId: "unexpected", type: "MemberExpression" }] + }, + { + code: "if (true) console.log()", + output: "if (true) {}", + errors: [{ messageId: "unexpected", type: "MemberExpression" }] + }, + { + code: "if (true) { console.log() }", + output: "if (true) { }", + errors: [{ messageId: "unexpected", type: "MemberExpression" }] + }, + { + code: "console.log(foo)", + output: "", + errors: [{ messageId: "unexpected", type: "MemberExpression" }] + }, + { + code: "console.log(233)", + output: "", + errors: [{ messageId: "unexpected", type: "MemberExpression" }] + }, + { + code: "console.log(foo())", + output: null, + errors: [{ messageId: "unexpected", type: "MemberExpression" }] + }, + { + code: "var log = console.log", + output: null, + errors: [{ messageId: "unexpected", type: "MemberExpression" }] + } + ] +}); diff --git a/packages/autofix/tests/lib/rules/no-debugger.js b/packages/autofix/tests/lib/rules/no-debugger.js index 5a7ccfd..bc899bc 100644 --- a/packages/autofix/tests/lib/rules/no-debugger.js +++ b/packages/autofix/tests/lib/rules/no-debugger.js @@ -1,36 +1,36 @@ -/** - * @fileoverview tests for rule no-debugger - * @author 唯然 - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require("../../../lib/rules/no-debugger"); -const RuleTester = require("eslint").RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new RuleTester(); - -ruleTester.run("no-debugger", rule, { - valid: [ - "var test = { debugger: 1 }; test.debugger;" - ], - invalid: [ - { - code: "debugger", - output: "", - errors: [{ messageId: "unexpected", type: "DebuggerStatement" }] - }, - { - code: "if (foo) debugger", - output: null, - errors: [{ messageId: "unexpected", type: "DebuggerStatement" }] - } - ] -}); +/** + * @fileoverview tests for rule no-debugger + * @author 唯然 + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require("../../../lib/rules/no-debugger"); +const RuleTester = require("eslint").RuleTester; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const ruleTester = new RuleTester(); + +ruleTester.run("no-debugger", rule, { + valid: [ + "var test = { debugger: 1 }; test.debugger;" + ], + invalid: [ + { + code: "debugger", + output: "", + errors: [{ messageId: "unexpected", type: "DebuggerStatement" }] + }, + { + code: "if (foo) debugger", + output: null, + errors: [{ messageId: "unexpected", type: "DebuggerStatement" }] + } + ] +}); diff --git a/packages/autofix/tests/lib/rules/no-eq-null.js b/packages/autofix/tests/lib/rules/no-eq-null.js index a44bdd0..bf94efe 100644 --- a/packages/autofix/tests/lib/rules/no-eq-null.js +++ b/packages/autofix/tests/lib/rules/no-eq-null.js @@ -1,48 +1,48 @@ -/** - * @fileoverview Tests for rule no-eq-null. - * @author Pig Fang - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require("../../../lib/rules/no-eq-null"); -const RuleTester = require("eslint").RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new RuleTester(); -const errors = [{ messageId: "unexpected", type: "BinaryExpression" }]; - -ruleTester.run("no-eq-null", rule, { - valid: [ - "a === null", - "a !== null" - ], - invalid: [ - { - code: "a == null", - output: "a === null", - errors - }, - { - code: "a != null", - output: "a !== null", - errors - }, - { - code: "null == a", - output: "null === a", - errors - }, - { - code: "null != a", - output: "null !== a", - errors - } - ] -}); +/** + * @fileoverview Tests for rule no-eq-null. + * @author Pig Fang + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require("../../../lib/rules/no-eq-null"); +const RuleTester = require("eslint").RuleTester; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const ruleTester = new RuleTester(); +const errors = [{ messageId: "unexpected", type: "BinaryExpression" }]; + +ruleTester.run("no-eq-null", rule, { + valid: [ + "a === null", + "a !== null" + ], + invalid: [ + { + code: "a == null", + output: "a === null", + errors + }, + { + code: "a != null", + output: "a !== null", + errors + }, + { + code: "null == a", + output: "null === a", + errors + }, + { + code: "null != a", + output: "null !== a", + errors + } + ] +}); diff --git a/packages/autofix/tests/lib/rules/no-new-symbol.js b/packages/autofix/tests/lib/rules/no-new-symbol.js index 7a11d68..b1fcac8 100644 --- a/packages/autofix/tests/lib/rules/no-new-symbol.js +++ b/packages/autofix/tests/lib/rules/no-new-symbol.js @@ -1,32 +1,32 @@ -/** - * @fileoverview Tests for rule no-new-symbol - * @author Pig Fang - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require("../../../lib/rules/no-new-symbol"); -const RuleTester = require("eslint").RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new RuleTester({ env: { es6: true } }); -const errors = [{ type: "Identifier" }]; - -ruleTester.run("no-new-symbol", rule, { - valid: [ - "Symbol('a')" - ], - invalid: [ - { - code: "new Symbol('a')", - output: "Symbol('a')", - errors - } - ] -}); +/** + * @fileoverview Tests for rule no-new-symbol + * @author Pig Fang + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require("../../../lib/rules/no-new-symbol"); +const RuleTester = require("eslint").RuleTester; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const ruleTester = new RuleTester({ env: { es6: true } }); +const errors = [{ type: "Identifier" }]; + +ruleTester.run("no-new-symbol", rule, { + valid: [ + "Symbol('a')" + ], + invalid: [ + { + code: "new Symbol('a')", + output: "Symbol('a')", + errors + } + ] +}); diff --git a/packages/autofix/tests/lib/rules/no-plusplus.js b/packages/autofix/tests/lib/rules/no-plusplus.js index 0340439..0553598 100644 --- a/packages/autofix/tests/lib/rules/no-plusplus.js +++ b/packages/autofix/tests/lib/rules/no-plusplus.js @@ -1,66 +1,66 @@ -/** - * @fileoverview Tests for no-plusplus. - * @author 唯然 - */ - -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require("../../../lib/rules/no-plusplus"), - RuleTester = require("eslint").RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new RuleTester(); - -ruleTester.run("no-plusplus", rule, { - valid: [ - "var foo = 0; foo=+1;", - - // With "allowForLoopAfterthoughts" allowed - { code: "var foo = 0; foo=+1;", options: [{ allowForLoopAfterthoughts: true }] }, - { code: "for (i = 0; i < l; i++) { console.log(i); }", options: [{ allowForLoopAfterthoughts: true }] } - ], - - invalid: [ - { - code: "var foo = 0; foo++;", - output: "var foo = 0; foo+=1;", - errors: [{ message: "Unary operator '++' used.", type: "UpdateExpression" }] - }, - { - code: "var foo = 0; foo--;", - output: "var foo = 0; foo-=1;", - errors: [{ message: "Unary operator '--' used.", type: "UpdateExpression" }] - }, - { - code: "for (i = 0; i < l; i++) { console.log(i); }", - output: "for (i = 0; i < l; i+=1) { console.log(i); }", - errors: [{ message: "Unary operator '++' used.", type: "UpdateExpression" }] - }, - - // With "allowForLoopAfterthoughts" allowed - { - code: "var foo = 0; foo++;", - output: "var foo = 0; foo+=1;", - options: [{ allowForLoopAfterthoughts: true }], - errors: [{ message: "Unary operator '++' used.", type: "UpdateExpression" }] - }, - { - code: "for (i = 0; i < l; i++) { v++; }", - output: "for (i = 0; i < l; i++) { v+=1; }", - options: [{ allowForLoopAfterthoughts: true }], - errors: [{ message: "Unary operator '++' used.", type: "UpdateExpression" }] - }, - { - code: "var bar = foo++;", - output: null, - errors: [{ message: "Unary operator '++' used.", type: "UpdateExpression" }] - } - ] -}); +/** + * @fileoverview Tests for no-plusplus. + * @author 唯然 + */ + +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require("../../../lib/rules/no-plusplus"), + RuleTester = require("eslint").RuleTester; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const ruleTester = new RuleTester(); + +ruleTester.run("no-plusplus", rule, { + valid: [ + "var foo = 0; foo=+1;", + + // With "allowForLoopAfterthoughts" allowed + { code: "var foo = 0; foo=+1;", options: [{ allowForLoopAfterthoughts: true }] }, + { code: "for (i = 0; i < l; i++) { console.log(i); }", options: [{ allowForLoopAfterthoughts: true }] } + ], + + invalid: [ + { + code: "var foo = 0; foo++;", + output: "var foo = 0; foo+=1;", + errors: [{ message: "Unary operator '++' used.", type: "UpdateExpression" }] + }, + { + code: "var foo = 0; foo--;", + output: "var foo = 0; foo-=1;", + errors: [{ message: "Unary operator '--' used.", type: "UpdateExpression" }] + }, + { + code: "for (i = 0; i < l; i++) { console.log(i); }", + output: "for (i = 0; i < l; i+=1) { console.log(i); }", + errors: [{ message: "Unary operator '++' used.", type: "UpdateExpression" }] + }, + + // With "allowForLoopAfterthoughts" allowed + { + code: "var foo = 0; foo++;", + output: "var foo = 0; foo+=1;", + options: [{ allowForLoopAfterthoughts: true }], + errors: [{ message: "Unary operator '++' used.", type: "UpdateExpression" }] + }, + { + code: "for (i = 0; i < l; i++) { v++; }", + output: "for (i = 0; i < l; i++) { v+=1; }", + options: [{ allowForLoopAfterthoughts: true }], + errors: [{ message: "Unary operator '++' used.", type: "UpdateExpression" }] + }, + { + code: "var bar = foo++;", + output: null, + errors: [{ message: "Unary operator '++' used.", type: "UpdateExpression" }] + } + ] +}); diff --git a/packages/autofix/tests/lib/rules/no-proto.js b/packages/autofix/tests/lib/rules/no-proto.js index 9488fa7..b6dbefe 100644 --- a/packages/autofix/tests/lib/rules/no-proto.js +++ b/packages/autofix/tests/lib/rules/no-proto.js @@ -1,37 +1,37 @@ -/** - * @fileoverview Tests for rule no-proto. - * @author Pig Fang - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require("../../../lib/rules/no-proto"); -const RuleTester = require("eslint").RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new RuleTester(); -const errors = [{ type: "MemberExpression" }]; - -ruleTester.run("no-proto", rule, { - valid: [ - "Object.getPrototypeOf(obj)" - ], - invalid: [ - { - code: "obj.__proto__", - output: "Object.getPrototypeOf(obj)", - errors - }, - { - code: "obj['__proto__']", - output: "Object.getPrototypeOf(obj)", - errors - } - ] -}); +/** + * @fileoverview Tests for rule no-proto. + * @author Pig Fang + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require("../../../lib/rules/no-proto"); +const RuleTester = require("eslint").RuleTester; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const ruleTester = new RuleTester(); +const errors = [{ type: "MemberExpression" }]; + +ruleTester.run("no-proto", rule, { + valid: [ + "Object.getPrototypeOf(obj)" + ], + invalid: [ + { + code: "obj.__proto__", + output: "Object.getPrototypeOf(obj)", + errors + }, + { + code: "obj['__proto__']", + output: "Object.getPrototypeOf(obj)", + errors + } + ] +}); diff --git a/packages/autofix/tests/lib/rules/no-prototype-builtins.js b/packages/autofix/tests/lib/rules/no-prototype-builtins.js index 437d18f..0057649 100644 --- a/packages/autofix/tests/lib/rules/no-prototype-builtins.js +++ b/packages/autofix/tests/lib/rules/no-prototype-builtins.js @@ -1,47 +1,47 @@ -/** - * @fileoverview Tests for rule no-prototype-builtins - * @author Pig Fang - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require("../../../lib/rules/no-prototype-builtins"); -const RuleTester = require("eslint").RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new RuleTester(); -const errors = [{ type: "CallExpression" }]; - -ruleTester.run("no-prototype-builtins", rule, { - valid: [ - "Object.prototype.hasOwnProperty.call(foo, \"bar\")" - ], - invalid: [ - { - code: "foo.hasOwnProperty('bar')", - output: "Object.prototype.hasOwnProperty.call(foo, 'bar')", - errors - }, - { - code: "foo.bar.hasOwnProperty('baz')", - output: "Object.prototype.hasOwnProperty.call(foo.bar, 'baz')", - errors - }, - { - code: "foo.isPrototypeOf('bar')", - output: "Object.prototype.isPrototypeOf.call(foo, 'bar')", - errors - }, - { - code: "foo.propertyIsEnumerable('bar')", - output: "Object.prototype.propertyIsEnumerable.call(foo, 'bar')", - errors - } - ] -}); +/** + * @fileoverview Tests for rule no-prototype-builtins + * @author Pig Fang + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require("../../../lib/rules/no-prototype-builtins"); +const RuleTester = require("eslint").RuleTester; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const ruleTester = new RuleTester(); +const errors = [{ type: "CallExpression" }]; + +ruleTester.run("no-prototype-builtins", rule, { + valid: [ + "Object.prototype.hasOwnProperty.call(foo, \"bar\")" + ], + invalid: [ + { + code: "foo.hasOwnProperty('bar')", + output: "Object.prototype.hasOwnProperty.call(foo, 'bar')", + errors + }, + { + code: "foo.bar.hasOwnProperty('baz')", + output: "Object.prototype.hasOwnProperty.call(foo.bar, 'baz')", + errors + }, + { + code: "foo.isPrototypeOf('bar')", + output: "Object.prototype.isPrototypeOf.call(foo, 'bar')", + errors + }, + { + code: "foo.propertyIsEnumerable('bar')", + output: "Object.prototype.propertyIsEnumerable.call(foo, 'bar')", + errors + } + ] +}); diff --git a/packages/autofix/tests/lib/rules/no-throw-literal.js b/packages/autofix/tests/lib/rules/no-throw-literal.js index 1980576..f1c5584 100644 --- a/packages/autofix/tests/lib/rules/no-throw-literal.js +++ b/packages/autofix/tests/lib/rules/no-throw-literal.js @@ -1,43 +1,43 @@ -/** - * @fileoverview Tests for rule no-throw-literal. - * @author Pig Fang - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require("../../../lib/rules/no-throw-literal"); -const RuleTester = require("eslint").RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new RuleTester(); -const errors = [{ type: "ThrowStatement" }]; - -ruleTester.run("no-throw-literal", rule, { - valid: [ - "throw new Error()", - "throw error" - ], - invalid: [ - { - code: "throw 'error'", - output: "throw new Error('error');", - errors - }, - { - code: "throw ''", - output: "throw new Error('');", - errors - }, - { - code: "throw 0", - output: "throw new Error(0);", - errors - } - ] -}); +/** + * @fileoverview Tests for rule no-throw-literal. + * @author Pig Fang + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require("../../../lib/rules/no-throw-literal"); +const RuleTester = require("eslint").RuleTester; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const ruleTester = new RuleTester(); +const errors = [{ type: "ThrowStatement" }]; + +ruleTester.run("no-throw-literal", rule, { + valid: [ + "throw new Error()", + "throw error" + ], + invalid: [ + { + code: "throw 'error'", + output: "throw new Error('error');", + errors + }, + { + code: "throw ''", + output: "throw new Error('');", + errors + }, + { + code: "throw 0", + output: "throw new Error(0);", + errors + } + ] +}); diff --git a/packages/autofix/tests/lib/rules/no-unused-vars.js b/packages/autofix/tests/lib/rules/no-unused-vars.js index fc2b8a1..a655661 100644 --- a/packages/autofix/tests/lib/rules/no-unused-vars.js +++ b/packages/autofix/tests/lib/rules/no-unused-vars.js @@ -1,323 +1,323 @@ -/** - * @fileoverview Tests for rule no-unused-vars. - * @author Pig Fang - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require("../../../lib/rules/no-unused-vars"); -const RuleTester = require("eslint").RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 9 } }); - -ruleTester.run("no-unused-vars", rule, { - valid: [ - { - code: "import 'm'", - parserOptions: { sourceType: "module", ecmaVersion: 6 } - }, - - // https://github.com/aladdin-add/eslint-plugin/issues/58 - { - code: `const { _ } = require('lib/locale.js'); - class OneDriveApi { - async example() { - throw new Error(_('oops')); - } - } - module.exports = { OneDriveApi };` - } - ], - invalid: [ - { - code: "import * as m from 'm'", - parserOptions: { sourceType: "module", ecmaVersion: 6 }, - output: "", - errors: [{ type: "Identifier" }] - }, - { - code: "import m from 'm'", - parserOptions: { sourceType: "module", ecmaVersion: 6 }, - output: "", - errors: [{ type: "Identifier" }] - }, - { - code: "import {m} from 'm'", - parserOptions: { sourceType: "module", ecmaVersion: 6 }, - output: "", - errors: [{ type: "Identifier" }] - }, - { - code: "import {m1 as m2} from 'm'", - parserOptions: { sourceType: "module", ecmaVersion: 6 }, - output: "", - errors: [{ type: "Identifier" }] - }, - { - code: "import m, {b} from 'm'; b;", - parserOptions: { sourceType: "module", ecmaVersion: 6 }, - output: "import {b} from 'm'; b;", - errors: [{ type: "Identifier" }] - }, - { - code: "import {a, b} from 'm'; b;", - parserOptions: { sourceType: "module", ecmaVersion: 6 }, - output: "import { b} from 'm'; b;", - errors: [{ type: "Identifier" }] - }, - { - code: "import {a1 as a2, b} from 'm'; b;", - parserOptions: { sourceType: "module", ecmaVersion: 6 }, - output: "import { b} from 'm'; b;", - errors: [{ type: "Identifier" }] - }, - { - code: "import {a, b} from 'm'; a;", - parserOptions: { sourceType: "module", ecmaVersion: 6 }, - output: "import {a} from 'm'; a;", - errors: [{ type: "Identifier" }] - }, - { - code: "import m, {a} from 'm'; m;", - parserOptions: { sourceType: "module", ecmaVersion: 6 }, - output: "import m from 'm'; m;", - errors: [{ type: "Identifier" }] - }, - { - code: "var a", - output: "", - errors: [{ type: "Identifier" }] - }, - { - code: "var a = b", - output: "", - errors: [{ type: "Identifier" }] - }, - { - code: "var a = undefined", - output: "", - errors: [{ type: "Identifier" }] - }, - { - code: "var a = null", - output: "", - errors: [{ type: "Identifier" }] - }, - { - code: "var a = 'b'", - output: "", - errors: [{ type: "Identifier" }] - }, - { - code: "var a = this", - output: "", - errors: [{ type: "Identifier" }] - }, - { - code: "var a = `template`", - parserOptions: { ecmaVersion: 6 }, - output: "", - errors: [{ type: "Identifier" }] - }, - { - code: "var a = this.value", - output: "", - errors: [{ type: "Identifier" }] - }, - { - code: "var a = b = c", - output: null, - errors: [{ type: "Identifier" }] - }, - { - code: "var a = b = c()", - output: null, - errors: [{ type: "Identifier" }] - }, - { - code: "var a = `template-${value}`", - parserOptions: { ecmaVersion: 6 }, - output: null, - errors: [{ type: "Identifier" }] - }, - { - code: "var a = b()", - output: null, - errors: [{ type: "Identifier" }] - }, - { - code: "var a = (b()).c", - output: null, - errors: [{ type: "Identifier" }] - }, - { - code: "var [a] = c", - output: "var [] = c", - parserOptions: { ecmaVersion: 6 }, - errors: [{ type: "Identifier" }] - }, - { - code: "var [a, b] = c", - output: "var [, ] = c", - parserOptions: { ecmaVersion: 6 }, - errors: [{ type: "Identifier" }, { type: "Identifier" }] - }, - { - code: "var [a, b] = c; a;", - output: "var [a, ] = c; a;", - parserOptions: { ecmaVersion: 6 }, - errors: [{ type: "Identifier" }] - }, - { - code: "var [a, b] = c; b;", - output: "var [, b] = c; b;", - parserOptions: { ecmaVersion: 6 }, - errors: [{ type: "Identifier" }] - }, - { - code: "var a = b, c = d; c;", - output: "var c = d; c;", - errors: [{ type: "Identifier" }] - }, - { - code: "var a = b, c = d; a;", - output: "var a = b ; a;", - errors: [{ type: "Identifier" }] - }, - { - code: "let {...a} = b", - output: "let {} = b", - parserOptions: { ecmaVersion: 2018 }, - errors: [{ type: "Identifier" }] - }, - { - code: "function foo(...args){}", - output: null, - parserOptions: { ecmaVersion: 2018 }, - errors: [{ type: "Identifier" }, { type: "Identifier" }] - }, - { - code: "function foo(a, b, c){console.log(b);}; foo(1, 2, 3);", - output: "function foo(a, b ){console.log(b);}; foo(1, 2, 3);", - parserOptions: { ecmaVersion: 2018 }, - errors: [ - { type: "Identifier" } - ], - options: [{ - args: "after-used", - argsIgnorePattern: "^_" - }] - }, - { - code: "const foo = function(a, b, c) {console.log(b);}; foo(1, 2, 3);", - output: "const foo = function(a, b ) {console.log(b);}; foo(1, 2, 3);", - parserOptions: { ecmaVersion: 2018 }, - errors: [ - { type: "Identifier" } - ], - options: [{ - args: "after-used", - argsIgnorePattern: "^_" - }] - }, - { - code: "const foo = (a, b, c) => {console.log(b);}; foo(1, 2, 3);", - output: "const foo = (a, b ) => {console.log(b);}; foo(1, 2, 3);", - parserOptions: { ecmaVersion: 2018 }, - errors: [ - { type: "Identifier" } - ], - options: [{ - args: "after-used", - argsIgnorePattern: "^_" - }] - }, - { - code: "function foo(a, b, c){console.log(b);}; foo(1, 2, 3);", - output: "function foo(_a, b, _c){console.log(b);}; foo(1, 2, 3);", - parserOptions: { ecmaVersion: 2018 }, - errors: [ - { type: "Identifier" }, - { type: "Identifier" } - ], - options: [{ - args: "all", - argsIgnorePattern: "^_" - }] - }, - { - code: "function foo(a, b, c){console.log(b);}; foo(1, 2, 3);", - output: null, - parserOptions: { ecmaVersion: 2018 }, - errors: [ - { type: "Identifier" }, - { type: "Identifier" } - ], - options: [{ - args: "all", - argsIgnorePattern: "_$" - }] - }, - { - code: "function foo(a, b, c){console.log(b);}; foo(1, 2, 3);", - output: null, - parserOptions: { ecmaVersion: 2018 }, - errors: [ - { type: "Identifier" }, - { type: "Identifier" } - ], - options: [{ - args: "all" - }] - }, - { - code: "let {a} = b", - output: "let {} = b", - parserOptions: { ecmaVersion: 6 }, - errors: [{ type: "Identifier" }] - }, - { - code: "let {a,} = b", - output: "let {} = b", - parserOptions: { ecmaVersion: 6 }, - errors: [{ type: "Identifier" }] - }, - { - code: "let {a1: a2} = b", - output: "let {} = b", - parserOptions: { ecmaVersion: 6 }, - errors: [{ type: "Identifier" }] - }, - { - code: "let {a = b} = c", - output: "let {} = c", - parserOptions: { ecmaVersion: 6 }, - errors: [{ type: "Identifier" }] - }, - { - code: "let {a = b()} = c", - output: null, - parserOptions: { ecmaVersion: 6 }, - errors: [{ type: "Identifier" }] - }, - { - code: "let {a, b} = c; b;", - output: "let { b} = c; b;", - parserOptions: { ecmaVersion: 6 }, - errors: [{ type: "Identifier" }] - }, - { - code: "let {a, b} = c; a;", - output: "let {a } = c; a;", - parserOptions: { ecmaVersion: 6 }, - errors: [{ type: "Identifier" }] - } - ] -}); +/** + * @fileoverview Tests for rule no-unused-vars. + * @author Pig Fang + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require("../../../lib/rules/no-unused-vars"); +const RuleTester = require("eslint").RuleTester; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 9 } }); + +ruleTester.run("no-unused-vars", rule, { + valid: [ + { + code: "import 'm'", + parserOptions: { sourceType: "module", ecmaVersion: 6 } + }, + + // https://github.com/aladdin-add/eslint-plugin/issues/58 + { + code: `const { _ } = require('lib/locale.js'); + class OneDriveApi { + async example() { + throw new Error(_('oops')); + } + } + module.exports = { OneDriveApi };` + } + ], + invalid: [ + { + code: "import * as m from 'm'", + parserOptions: { sourceType: "module", ecmaVersion: 6 }, + output: "", + errors: [{ type: "Identifier" }] + }, + { + code: "import m from 'm'", + parserOptions: { sourceType: "module", ecmaVersion: 6 }, + output: "", + errors: [{ type: "Identifier" }] + }, + { + code: "import {m} from 'm'", + parserOptions: { sourceType: "module", ecmaVersion: 6 }, + output: "", + errors: [{ type: "Identifier" }] + }, + { + code: "import {m1 as m2} from 'm'", + parserOptions: { sourceType: "module", ecmaVersion: 6 }, + output: "", + errors: [{ type: "Identifier" }] + }, + { + code: "import m, {b} from 'm'; b;", + parserOptions: { sourceType: "module", ecmaVersion: 6 }, + output: "import {b} from 'm'; b;", + errors: [{ type: "Identifier" }] + }, + { + code: "import {a, b} from 'm'; b;", + parserOptions: { sourceType: "module", ecmaVersion: 6 }, + output: "import { b} from 'm'; b;", + errors: [{ type: "Identifier" }] + }, + { + code: "import {a1 as a2, b} from 'm'; b;", + parserOptions: { sourceType: "module", ecmaVersion: 6 }, + output: "import { b} from 'm'; b;", + errors: [{ type: "Identifier" }] + }, + { + code: "import {a, b} from 'm'; a;", + parserOptions: { sourceType: "module", ecmaVersion: 6 }, + output: "import {a} from 'm'; a;", + errors: [{ type: "Identifier" }] + }, + { + code: "import m, {a} from 'm'; m;", + parserOptions: { sourceType: "module", ecmaVersion: 6 }, + output: "import m from 'm'; m;", + errors: [{ type: "Identifier" }] + }, + { + code: "var a", + output: "", + errors: [{ type: "Identifier" }] + }, + { + code: "var a = b", + output: "", + errors: [{ type: "Identifier" }] + }, + { + code: "var a = undefined", + output: "", + errors: [{ type: "Identifier" }] + }, + { + code: "var a = null", + output: "", + errors: [{ type: "Identifier" }] + }, + { + code: "var a = 'b'", + output: "", + errors: [{ type: "Identifier" }] + }, + { + code: "var a = this", + output: "", + errors: [{ type: "Identifier" }] + }, + { + code: "var a = `template`", + parserOptions: { ecmaVersion: 6 }, + output: "", + errors: [{ type: "Identifier" }] + }, + { + code: "var a = this.value", + output: "", + errors: [{ type: "Identifier" }] + }, + { + code: "var a = b = c", + output: null, + errors: [{ type: "Identifier" }] + }, + { + code: "var a = b = c()", + output: null, + errors: [{ type: "Identifier" }] + }, + { + code: "var a = `template-${value}`", + parserOptions: { ecmaVersion: 6 }, + output: null, + errors: [{ type: "Identifier" }] + }, + { + code: "var a = b()", + output: null, + errors: [{ type: "Identifier" }] + }, + { + code: "var a = (b()).c", + output: null, + errors: [{ type: "Identifier" }] + }, + { + code: "var [a] = c", + output: "var [] = c", + parserOptions: { ecmaVersion: 6 }, + errors: [{ type: "Identifier" }] + }, + { + code: "var [a, b] = c", + output: "var [, ] = c", + parserOptions: { ecmaVersion: 6 }, + errors: [{ type: "Identifier" }, { type: "Identifier" }] + }, + { + code: "var [a, b] = c; a;", + output: "var [a, ] = c; a;", + parserOptions: { ecmaVersion: 6 }, + errors: [{ type: "Identifier" }] + }, + { + code: "var [a, b] = c; b;", + output: "var [, b] = c; b;", + parserOptions: { ecmaVersion: 6 }, + errors: [{ type: "Identifier" }] + }, + { + code: "var a = b, c = d; c;", + output: "var c = d; c;", + errors: [{ type: "Identifier" }] + }, + { + code: "var a = b, c = d; a;", + output: "var a = b ; a;", + errors: [{ type: "Identifier" }] + }, + { + code: "let {...a} = b", + output: "let {} = b", + parserOptions: { ecmaVersion: 2018 }, + errors: [{ type: "Identifier" }] + }, + { + code: "function foo(...args){}", + output: null, + parserOptions: { ecmaVersion: 2018 }, + errors: [{ type: "Identifier" }, { type: "Identifier" }] + }, + { + code: "function foo(a, b, c){console.log(b);}; foo(1, 2, 3);", + output: "function foo(a, b ){console.log(b);}; foo(1, 2, 3);", + parserOptions: { ecmaVersion: 2018 }, + errors: [ + { type: "Identifier" } + ], + options: [{ + args: "after-used", + argsIgnorePattern: "^_" + }] + }, + { + code: "const foo = function(a, b, c) {console.log(b);}; foo(1, 2, 3);", + output: "const foo = function(a, b ) {console.log(b);}; foo(1, 2, 3);", + parserOptions: { ecmaVersion: 2018 }, + errors: [ + { type: "Identifier" } + ], + options: [{ + args: "after-used", + argsIgnorePattern: "^_" + }] + }, + { + code: "const foo = (a, b, c) => {console.log(b);}; foo(1, 2, 3);", + output: "const foo = (a, b ) => {console.log(b);}; foo(1, 2, 3);", + parserOptions: { ecmaVersion: 2018 }, + errors: [ + { type: "Identifier" } + ], + options: [{ + args: "after-used", + argsIgnorePattern: "^_" + }] + }, + { + code: "function foo(a, b, c){console.log(b);}; foo(1, 2, 3);", + output: "function foo(_a, b, _c){console.log(b);}; foo(1, 2, 3);", + parserOptions: { ecmaVersion: 2018 }, + errors: [ + { type: "Identifier" }, + { type: "Identifier" } + ], + options: [{ + args: "all", + argsIgnorePattern: "^_" + }] + }, + { + code: "function foo(a, b, c){console.log(b);}; foo(1, 2, 3);", + output: null, + parserOptions: { ecmaVersion: 2018 }, + errors: [ + { type: "Identifier" }, + { type: "Identifier" } + ], + options: [{ + args: "all", + argsIgnorePattern: "_$" + }] + }, + { + code: "function foo(a, b, c){console.log(b);}; foo(1, 2, 3);", + output: null, + parserOptions: { ecmaVersion: 2018 }, + errors: [ + { type: "Identifier" }, + { type: "Identifier" } + ], + options: [{ + args: "all" + }] + }, + { + code: "let {a} = b", + output: "let {} = b", + parserOptions: { ecmaVersion: 6 }, + errors: [{ type: "Identifier" }] + }, + { + code: "let {a,} = b", + output: "let {} = b", + parserOptions: { ecmaVersion: 6 }, + errors: [{ type: "Identifier" }] + }, + { + code: "let {a1: a2} = b", + output: "let {} = b", + parserOptions: { ecmaVersion: 6 }, + errors: [{ type: "Identifier" }] + }, + { + code: "let {a = b} = c", + output: "let {} = c", + parserOptions: { ecmaVersion: 6 }, + errors: [{ type: "Identifier" }] + }, + { + code: "let {a = b()} = c", + output: null, + parserOptions: { ecmaVersion: 6 }, + errors: [{ type: "Identifier" }] + }, + { + code: "let {a, b} = c; b;", + output: "let { b} = c; b;", + parserOptions: { ecmaVersion: 6 }, + errors: [{ type: "Identifier" }] + }, + { + code: "let {a, b} = c; a;", + output: "let {a } = c; a;", + parserOptions: { ecmaVersion: 6 }, + errors: [{ type: "Identifier" }] + } + ] +}); diff --git a/packages/autofix/tests/lib/rules/no-useless-catch.js b/packages/autofix/tests/lib/rules/no-useless-catch.js index 2d5b1a2..62f389b 100644 --- a/packages/autofix/tests/lib/rules/no-useless-catch.js +++ b/packages/autofix/tests/lib/rules/no-useless-catch.js @@ -1,47 +1,47 @@ -/** - * @fileoverview Tests for rule no-useless-catch. - * @author Pig Fang - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require("../../../lib/rules/no-useless-catch"); -const RuleTester = require("eslint").RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new RuleTester(); - -ruleTester.run("no-useless-catch", rule, { - valid: [ - "try {} catch (e) {}" - ], - invalid: [ - { - code: "try {} catch (e) { throw e }", - output: "{}", - errors: [{ type: "TryStatement" }] - }, - { - code: "try { fn() } catch (e) { throw e }", - output: "{ fn() }", - errors: [{ type: "TryStatement" }] - }, - { - code: "let a; try { let a } catch (e) { throw e };", - output: "let a; { let a };", - parserOptions: { ecmaVersion: 6 }, - errors: [{ type: "TryStatement" }] - }, - { - code: "try {} catch (e) { throw e } finally {}", - output: "try {} finally {}", - errors: [{ type: "CatchClause" }] - } - ] -}); +/** + * @fileoverview Tests for rule no-useless-catch. + * @author Pig Fang + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require("../../../lib/rules/no-useless-catch"); +const RuleTester = require("eslint").RuleTester; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const ruleTester = new RuleTester(); + +ruleTester.run("no-useless-catch", rule, { + valid: [ + "try {} catch (e) {}" + ], + invalid: [ + { + code: "try {} catch (e) { throw e }", + output: "{}", + errors: [{ type: "TryStatement" }] + }, + { + code: "try { fn() } catch (e) { throw e }", + output: "{ fn() }", + errors: [{ type: "TryStatement" }] + }, + { + code: "let a; try { let a } catch (e) { throw e };", + output: "let a; { let a };", + parserOptions: { ecmaVersion: 6 }, + errors: [{ type: "TryStatement" }] + }, + { + code: "try {} catch (e) { throw e } finally {}", + output: "try {} finally {}", + errors: [{ type: "CatchClause" }] + } + ] +}); diff --git a/packages/autofix/tests/lib/rules/no-useless-concat.js b/packages/autofix/tests/lib/rules/no-useless-concat.js index 79ded05..f2cc681 100644 --- a/packages/autofix/tests/lib/rules/no-useless-concat.js +++ b/packages/autofix/tests/lib/rules/no-useless-concat.js @@ -1,33 +1,33 @@ -/** - * @fileoverview Tests for rule no-useless-concat - * @author Pig Fang - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require("../../../lib/rules/no-useless-concat"); -const RuleTester = require("eslint").RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new RuleTester(); -const errors = [{ type: "BinaryExpression" }]; - -ruleTester.run("no-useless-concat", rule, { - valid: [ - "2 + 3", - "2 + '3'" - ], - invalid: [ - { - code: "'a' + 'b'", - output: "\"ab\"", - errors - } - ] -}); +/** + * @fileoverview Tests for rule no-useless-concat + * @author Pig Fang + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require("../../../lib/rules/no-useless-concat"); +const RuleTester = require("eslint").RuleTester; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const ruleTester = new RuleTester(); +const errors = [{ type: "BinaryExpression" }]; + +ruleTester.run("no-useless-concat", rule, { + valid: [ + "2 + 3", + "2 + '3'" + ], + invalid: [ + { + code: "'a' + 'b'", + output: "\"ab\"", + errors + } + ] +}); diff --git a/packages/autofix/tests/lib/rules/prefer-spread.js b/packages/autofix/tests/lib/rules/prefer-spread.js index 1992da8..eae1cb7 100644 --- a/packages/autofix/tests/lib/rules/prefer-spread.js +++ b/packages/autofix/tests/lib/rules/prefer-spread.js @@ -1,95 +1,95 @@ -/** - * @fileoverview Tests for prefer-spread rule. - * @author Toru Nagashima - * source: https://github.com/eslint/eslint/blob/master/tests/lib/rules/prefer-spread.js - */ - -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require("../../../lib/rules/prefer-spread"); -const RuleTester = require("eslint").RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const errors = [{ message: "Use the spread operator instead of '.apply()'.", type: "CallExpression" }]; - -const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } }); - -ruleTester.run("prefer-spread", rule, { - valid: [ - "foo.apply(obj, args);", - "obj.foo.apply(null, args);", - "obj.foo.apply(otherObj, args);", - "a.b(x, y).c.foo.apply(a.b(x, z).c, args);", - "a.b.foo.apply(a.b.c, args);", - - // ignores non variadic. - "foo.apply(undefined, [1, 2]);", - "foo.apply(null, [1, 2]);", - "obj.foo.apply(obj, [1, 2]);", - - // ignores computed property. - "var apply; foo[apply](null, args);", - - // ignores incomplete things. - "foo.apply();", - "obj.foo.apply();", - "obj.foo.apply(obj, ...args)" - ], - invalid: [ - { - code: "foo.apply(undefined, args);", - output: "foo(...args);", - errors - }, - { - code: "foo.apply(void 0, args);", - output: "foo(...args);", - errors - }, - { - code: "foo.apply(null, args);", - output: "foo(...args);", - errors - }, - { - code: "obj.foo.apply(obj, args);", - output: "obj.foo(...args);", - errors - }, - { - - // Not fixed: a.b.c might activate getters - code: "a.b.c.foo.apply(a.b.c, args);", - output: null, - errors - }, - { - - // Not fixed: a.b(x, y).c might activate getters - code: "a.b(x, y).c.foo.apply(a.b(x, y).c, args);", - output: null, - errors - }, - { - - // Not fixed (not an identifier) - code: "[].concat.apply([ ], args);", - output: null, - errors - }, - { - - // Not fixed (not an identifier) - code: "[].concat.apply([\n/*empty*/\n], args);", - output: null, - errors - } - ] -}); +/** + * @fileoverview Tests for prefer-spread rule. + * @author Toru Nagashima + * source: https://github.com/eslint/eslint/blob/master/tests/lib/rules/prefer-spread.js + */ + +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require("../../../lib/rules/prefer-spread"); +const RuleTester = require("eslint").RuleTester; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const errors = [{ message: "Use the spread operator instead of '.apply()'.", type: "CallExpression" }]; + +const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } }); + +ruleTester.run("prefer-spread", rule, { + valid: [ + "foo.apply(obj, args);", + "obj.foo.apply(null, args);", + "obj.foo.apply(otherObj, args);", + "a.b(x, y).c.foo.apply(a.b(x, z).c, args);", + "a.b.foo.apply(a.b.c, args);", + + // ignores non variadic. + "foo.apply(undefined, [1, 2]);", + "foo.apply(null, [1, 2]);", + "obj.foo.apply(obj, [1, 2]);", + + // ignores computed property. + "var apply; foo[apply](null, args);", + + // ignores incomplete things. + "foo.apply();", + "obj.foo.apply();", + "obj.foo.apply(obj, ...args)" + ], + invalid: [ + { + code: "foo.apply(undefined, args);", + output: "foo(...args);", + errors + }, + { + code: "foo.apply(void 0, args);", + output: "foo(...args);", + errors + }, + { + code: "foo.apply(null, args);", + output: "foo(...args);", + errors + }, + { + code: "obj.foo.apply(obj, args);", + output: "obj.foo(...args);", + errors + }, + { + + // Not fixed: a.b.c might activate getters + code: "a.b.c.foo.apply(a.b.c, args);", + output: null, + errors + }, + { + + // Not fixed: a.b(x, y).c might activate getters + code: "a.b(x, y).c.foo.apply(a.b(x, y).c, args);", + output: null, + errors + }, + { + + // Not fixed (not an identifier) + code: "[].concat.apply([ ], args);", + output: null, + errors + }, + { + + // Not fixed (not an identifier) + code: "[].concat.apply([\n/*empty*/\n], args);", + output: null, + errors + } + ] +}); diff --git a/packages/autofix/tests/lib/rules/radix.js b/packages/autofix/tests/lib/rules/radix.js index 19b3d31..91dd56c 100644 --- a/packages/autofix/tests/lib/rules/radix.js +++ b/packages/autofix/tests/lib/rules/radix.js @@ -1,43 +1,43 @@ -/** - * @fileoverview Tests for rule radix - * @author Pig Fang - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require("../../../lib/rules/radix"); -const RuleTester = require("eslint").RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new RuleTester(); - -ruleTester.run("radix", rule, { - valid: [ - "parseInt(a, 10)", - { code: "parseInt(a)", options: ["as-needed"] } - ], - invalid: [ - { - code: "parseInt(a)", - output: "parseInt(a, 10)", - errors: [{ type: "CallExpression" }] - }, - { - code: "parseInt(a, 10)", - options: ["as-needed"], - output: "parseInt(a)", - errors: [{ type: "CallExpression" }] - }, - { - code: "parseInt(a, '10')", - output: null, - errors: [{ type: "CallExpression" }] - } - ] -}); +/** + * @fileoverview Tests for rule radix + * @author Pig Fang + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require("../../../lib/rules/radix"); +const RuleTester = require("eslint").RuleTester; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const ruleTester = new RuleTester(); + +ruleTester.run("radix", rule, { + valid: [ + "parseInt(a, 10)", + { code: "parseInt(a)", options: ["as-needed"] } + ], + invalid: [ + { + code: "parseInt(a)", + output: "parseInt(a, 10)", + errors: [{ type: "CallExpression" }] + }, + { + code: "parseInt(a, 10)", + options: ["as-needed"], + output: "parseInt(a)", + errors: [{ type: "CallExpression" }] + }, + { + code: "parseInt(a, '10')", + output: null, + errors: [{ type: "CallExpression" }] + } + ] +}); diff --git a/packages/autofix/tests/lib/rules/valid-typeof.js b/packages/autofix/tests/lib/rules/valid-typeof.js index edcb2e4..40a6096 100644 --- a/packages/autofix/tests/lib/rules/valid-typeof.js +++ b/packages/autofix/tests/lib/rules/valid-typeof.js @@ -1,63 +1,63 @@ -/** - * @fileoverview Tests for rule valid-typeof - * @author Pig Fang - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require("../../../lib/rules/valid-typeof"); -const RuleTester = require("eslint").RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new RuleTester(); -const errors = [{ messageId: "invalidValue", type: "Literal" }]; - -ruleTester.run("valid-typeof", rule, { - valid: [ - "typeof a === 'string'" - ], - invalid: [ - { - code: "typeof a === 'strnig'", - output: "typeof a === \"string\"", - errors - }, - { - code: "'strnig' === typeof a", - output: "\"string\" === typeof a", - errors - }, - { - code: "typeof a === `strnig`", - parserOptions: { ecmaVersion: 6 }, - output: "typeof a === \"string\"", - errors: [{ messageId: "invalidValue", type: "TemplateLiteral" }] - }, - { - code: "typeof a === 'nunber'", - output: "typeof a === \"number\"", - errors - }, - { - code: "typeof a === 'fucntion'", - output: "typeof a === \"function\"", - errors - }, - { - code: "typeof a === 'undefimed'", - output: "typeof a === \"undefined\"", - errors - }, - { - code: "typeof a === 'biilean'", - output: "typeof a === \"boolean\"", - errors - } - ] -}); +/** + * @fileoverview Tests for rule valid-typeof + * @author Pig Fang + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require("../../../lib/rules/valid-typeof"); +const RuleTester = require("eslint").RuleTester; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const ruleTester = new RuleTester(); +const errors = [{ messageId: "invalidValue", type: "Literal" }]; + +ruleTester.run("valid-typeof", rule, { + valid: [ + "typeof a === 'string'" + ], + invalid: [ + { + code: "typeof a === 'strnig'", + output: "typeof a === \"string\"", + errors + }, + { + code: "'strnig' === typeof a", + output: "\"string\" === typeof a", + errors + }, + { + code: "typeof a === `strnig`", + parserOptions: { ecmaVersion: 6 }, + output: "typeof a === \"string\"", + errors: [{ messageId: "invalidValue", type: "TemplateLiteral" }] + }, + { + code: "typeof a === 'nunber'", + output: "typeof a === \"number\"", + errors + }, + { + code: "typeof a === 'fucntion'", + output: "typeof a === \"function\"", + errors + }, + { + code: "typeof a === 'undefimed'", + output: "typeof a === \"undefined\"", + errors + }, + { + code: "typeof a === 'biilean'", + output: "typeof a === \"boolean\"", + errors + } + ] +}); diff --git a/packages/autofix/tools/eslint-rules/rule-def-format.js b/packages/autofix/tools/eslint-rules/rule-def-format.js index 85b2e08..c8e9bf4 100644 --- a/packages/autofix/tools/eslint-rules/rule-def-format.js +++ b/packages/autofix/tools/eslint-rules/rule-def-format.js @@ -1,81 +1,81 @@ -/** - * @fileoverview Rule definition of "rule-def-format" - * @author Pig Fang - */ -"use strict"; - -const path = require("path"); - -module.exports = { - meta: { - fixable: "code" - }, - create: context => ({ - CallExpression: node => { - if (node.callee.type !== "MemberExpression") { - return; - } - - const { callee: { object: { name: objectName }, property: { name: propertyName } }, arguments: args } = node; - const sourceCode = context.getSourceCode(); - - if (objectName === "utils" && propertyName === "getFixableRule") { - const arg0 = args[0]; - const ruleName = path.basename(context.getFilename(), ".js"); - - if (arg0.value !== ruleName) { - context.report({ - node: arg0, - message: `Rule name should be '${ruleName}'`, - fix: fixer => fixer.replaceText(arg0, `"${ruleName}"`) - }); - } - return; - } - - if (objectName === "ruleComposer") { - if (propertyName === "mapReports") { - const [, predicate] = args; - - if (predicate.type === "ArrowFunctionExpression" || predicate.type === "FunctionExpression") { - if (predicate.body.type !== "BlockStatement") { - context.report({ - node: predicate.body, - message: "Body of predicate should be a block statement." - }); - return; - } - const param0 = predicate.params[0].name; - - const statements = predicate.body.body; - - const returnStatement = statements.find(stmt => stmt.type === "ReturnStatement"); - - if (!returnStatement) { - context.report({ - node: predicate, - message: "Should returning the problem object.", - fix: fixer => { - const lastToken = sourceCode.getLastToken(predicate); - - return fixer.insertTextBefore(lastToken, `\nreturn ${param0};\n`); - } - }); - } else if (returnStatement.argument.name !== param0) { - context.report({ - node: predicate, - message: "Should returning the problem object.", - fix: fixer => fixer.replaceText(returnStatement, `return ${param0};`) - }); - } - } - } else if (propertyName === "joinReports" && args[0].type !== "ArrayExpression") { - context.report({ - node: args[0], - message: "The first argument of 'joinReports' should be an array." - }); - } - } - } - }) -}; +/** + * @fileoverview Rule definition of "rule-def-format" + * @author Pig Fang + */ +"use strict"; + +const path = require("path"); + +module.exports = { + meta: { + fixable: "code" + }, + create: context => ({ + CallExpression: node => { + if (node.callee.type !== "MemberExpression") { + return; + } + + const { callee: { object: { name: objectName }, property: { name: propertyName } }, arguments: args } = node; + const sourceCode = context.getSourceCode(); + + if (objectName === "utils" && propertyName === "getFixableRule") { + const arg0 = args[0]; + const ruleName = path.basename(context.getFilename(), ".js"); + + if (arg0.value !== ruleName) { + context.report({ + node: arg0, + message: `Rule name should be '${ruleName}'`, + fix: fixer => fixer.replaceText(arg0, `"${ruleName}"`) + }); + } + return; + } + + if (objectName === "ruleComposer") { + if (propertyName === "mapReports") { + const [, predicate] = args; + + if (predicate.type === "ArrowFunctionExpression" || predicate.type === "FunctionExpression") { + if (predicate.body.type !== "BlockStatement") { + context.report({ + node: predicate.body, + message: "Body of predicate should be a block statement." + }); + return; + } + const param0 = predicate.params[0].name; + + const statements = predicate.body.body; + + const returnStatement = statements.find(stmt => stmt.type === "ReturnStatement"); + + if (!returnStatement) { + context.report({ + node: predicate, + message: "Should returning the problem object.", + fix: fixer => { + const lastToken = sourceCode.getLastToken(predicate); + + return fixer.insertTextBefore(lastToken, `\nreturn ${param0};\n`); + } + }); + } else if (returnStatement.argument.name !== param0) { + context.report({ + node: predicate, + message: "Should returning the problem object.", + fix: fixer => fixer.replaceText(returnStatement, `return ${param0};`) + }); + } + } + } else if (propertyName === "joinReports" && args[0].type !== "ArrayExpression") { + context.report({ + node: args[0], + message: "The first argument of 'joinReports' should be an array." + }); + } + } + } + }) +}; diff --git a/packages/autofix/tools/generate-readme-table.js b/packages/autofix/tools/generate-readme-table.js index 6261c99..aa7f9ca 100644 --- a/packages/autofix/tools/generate-readme-table.js +++ b/packages/autofix/tools/generate-readme-table.js @@ -1,45 +1,45 @@ -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const rules = require("../lib/rules"); - -const README_LOCATION = path.resolve(__dirname, "..", "readme.md"); -const BEGIN_TABLE_MARKER = "\n"; -const END_TABLE_MARKER = "\n"; - -const expectedTableLines = Object.keys(rules) - .sort() - .reduce((lines, ruleId) => { - const rule = rules[ruleId]; - - lines.push([ - `[${ruleId}](https://eslint.org/docs/rules/${ruleId})`, - rule.meta.recommended ? "✔️" : "", - "🛠", - rule.meta.docs.description - ].join(" | ")); - - return lines; - }, ["Name | ✔️ | 🛠 | Description", "----- | ----- | ----- | -----"]) - .join("\n"); - -const readmeContents = fs.readFileSync(README_LOCATION, "utf8"); - -if (!readmeContents.includes(BEGIN_TABLE_MARKER)) { - throw new Error(`Could not find '${BEGIN_TABLE_MARKER}' marker in README.md.`); -} - -if (!readmeContents.includes(END_TABLE_MARKER)) { - throw new Error(`Could not find '${END_TABLE_MARKER}' marker in README.md.`); -} - -const linesStartIndex = readmeContents.indexOf(BEGIN_TABLE_MARKER) + BEGIN_TABLE_MARKER.length; -const linesEndIndex = readmeContents.indexOf(END_TABLE_MARKER); - -const updatedReadmeContents = readmeContents.slice(0, linesStartIndex) + - expectedTableLines + - readmeContents.slice(linesEndIndex); - - -fs.writeFileSync(README_LOCATION, updatedReadmeContents); +"use strict"; + +const fs = require("fs"); +const path = require("path"); +const rules = require("../lib/rules"); + +const README_LOCATION = path.resolve(__dirname, "..", "readme.md"); +const BEGIN_TABLE_MARKER = "\n"; +const END_TABLE_MARKER = "\n"; + +const expectedTableLines = Object.keys(rules) + .sort() + .reduce((lines, ruleId) => { + const rule = rules[ruleId]; + + lines.push([ + `[${ruleId}](https://eslint.org/docs/rules/${ruleId})`, + rule.meta.recommended ? "✔️" : "", + "🛠", + rule.meta.docs.description + ].join(" | ")); + + return lines; + }, ["Name | ✔️ | 🛠 | Description", "----- | ----- | ----- | -----"]) + .join("\n"); + +const readmeContents = fs.readFileSync(README_LOCATION, "utf8"); + +if (!readmeContents.includes(BEGIN_TABLE_MARKER)) { + throw new Error(`Could not find '${BEGIN_TABLE_MARKER}' marker in README.md.`); +} + +if (!readmeContents.includes(END_TABLE_MARKER)) { + throw new Error(`Could not find '${END_TABLE_MARKER}' marker in README.md.`); +} + +const linesStartIndex = readmeContents.indexOf(BEGIN_TABLE_MARKER) + BEGIN_TABLE_MARKER.length; +const linesEndIndex = readmeContents.indexOf(END_TABLE_MARKER); + +const updatedReadmeContents = readmeContents.slice(0, linesStartIndex) + + expectedTableLines + + readmeContents.slice(linesEndIndex); + + +fs.writeFileSync(README_LOCATION, updatedReadmeContents); diff --git a/packages/autofix/tools/new-rule.js b/packages/autofix/tools/new-rule.js index 3f88f83..8b3a3f0 100644 --- a/packages/autofix/tools/new-rule.js +++ b/packages/autofix/tools/new-rule.js @@ -1,95 +1,95 @@ -/* eslint no-console: 0, no-process-exit: 0*/ -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const util = require("util"); -const gitConfig = require("git-config"); -const execa = require("execa"); -const argv = require("minimist")(process.argv.slice(2), { boolean: true, alias: { safe: "recommended" } }); - -// git user.name user.email -const gitconfig = gitConfig.sync() || { user: { name: "", email: "" } }; -const name = gitconfig.user.name; -const email = gitconfig.user.email; - -// file paths to generated! -const ruleName = argv._[0]; -const rulePath = path.join(__dirname, `../lib/rules/${ruleName}.js`); -const testsPath = path.join(__dirname, `../tests/lib/rules/${ruleName}.js`); - -try { - require.resolve(`eslint/lib/rules/${ruleName}`); -} catch (_) { - console.error(`Cannot find ESLint rule '\u001b[31m${ruleName}\u001b[39m'.`); - process.exit(1); -} - -if (argv.git) { - execa.sync("git", ["checkout", "master"]); - execa.sync("git", ["checkout", "-b", ruleName]); -} - -const rule = -`/** - * @fileoverview Add fixer to rule ${ruleName}. - * @author ${name} <${email}> - */ -"use strict"; - -const ruleComposer = require("eslint-rule-composer"); -const utils = require("../utils"); - -const rule = utils.getFixableRule("${ruleName}", ${!!argv.safe}); - -module.exports = ruleComposer.mapReports( - rule, - problem => { - problem.fix = fixer => { - - // TODO: add rule fixer here; - }; - return problem; - } -); -`; - -const test = -`/** - * @fileoverview Tests for rule ${ruleName}. - * @author ${name} <${email}> - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require("../../../lib/rules/${ruleName}"); -const RuleTester = require("eslint").RuleTester; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new RuleTester(); - -ruleTester.run("${ruleName}", rule, { - valid: [ - - // TODO: add valid tests here - ], - invalid: [ - - // TODO: add invalid tests here - ] -}); -`; - -// generate rule def & tests & docs -const writeFile = util.promisify(fs.writeFile); - -Promise.all([ - writeFile(rulePath, rule, "utf-8"), - writeFile(testsPath, test, "utf-8") -]).then(() => require("./generate-readme-table")); +/* eslint no-console: 0, no-process-exit: 0*/ +"use strict"; + +const fs = require("fs"); +const path = require("path"); +const util = require("util"); +const gitConfig = require("git-config"); +const execa = require("execa"); +const argv = require("minimist")(process.argv.slice(2), { boolean: true, alias: { safe: "recommended" } }); + +// git user.name user.email +const gitconfig = gitConfig.sync() || { user: { name: "", email: "" } }; +const name = gitconfig.user.name; +const email = gitconfig.user.email; + +// file paths to generated! +const ruleName = argv._[0]; +const rulePath = path.join(__dirname, `../lib/rules/${ruleName}.js`); +const testsPath = path.join(__dirname, `../tests/lib/rules/${ruleName}.js`); + +try { + require.resolve(`eslint/lib/rules/${ruleName}`); +} catch (_) { + console.error(`Cannot find ESLint rule '\u001b[31m${ruleName}\u001b[39m'.`); + process.exit(1); +} + +if (argv.git) { + execa.sync("git", ["checkout", "master"]); + execa.sync("git", ["checkout", "-b", ruleName]); +} + +const rule = +`/** + * @fileoverview Add fixer to rule ${ruleName}. + * @author ${name} <${email}> + */ +"use strict"; + +const ruleComposer = require("eslint-rule-composer"); +const utils = require("../utils"); + +const rule = utils.getFixableRule("${ruleName}", ${!!argv.safe}); + +module.exports = ruleComposer.mapReports( + rule, + problem => { + problem.fix = fixer => { + + // TODO: add rule fixer here; + }; + return problem; + } +); +`; + +const test = +`/** + * @fileoverview Tests for rule ${ruleName}. + * @author ${name} <${email}> + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require("../../../lib/rules/${ruleName}"); +const RuleTester = require("eslint").RuleTester; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const ruleTester = new RuleTester(); + +ruleTester.run("${ruleName}", rule, { + valid: [ + + // TODO: add valid tests here + ], + invalid: [ + + // TODO: add invalid tests here + ] +}); +`; + +// generate rule def & tests & docs +const writeFile = util.promisify(fs.writeFile); + +Promise.all([ + writeFile(rulePath, rule, "utf-8"), + writeFile(testsPath, test, "utf-8") +]).then(() => require("./generate-readme-table")); diff --git a/packages/no-autofix/package.json b/packages/no-autofix/package.json index 646ca9f..0e0cf81 100644 --- a/packages/no-autofix/package.json +++ b/packages/no-autofix/package.json @@ -5,9 +5,7 @@ "description": "built-in eslint rules without fixer.", "main": "./lib/index.js", "scripts": { - "lint": "eslint .", "test": "mocha \"tests/**/*.js\"", - "pretest": "npm run lint", "generate-release": "node-release-script" }, "files": [ @@ -20,15 +18,7 @@ }, "devDependencies": { "@not-an-aardvark/node-release-script": "^0.1.0", - "@types/eslint": "^4.16.8", - "eslint": "^6.0.0", - "eslint-config-eslint": "^5.0.1", - "eslint-plugin-autofix": "0.0.9", - "eslint-plugin-eslint-plugin": "^2.1.0", - "eslint-plugin-node": "^9.1.0", - "eslint-plugin-self": "^1.2.0", - "husky": "^3.0.3", - "mocha": "^6.1.4" + "mocha": "^7.0.1" }, "peerDependencies": { "eslint": ">= 5.12.1" @@ -37,33 +27,6 @@ "eslint plugin", "autofix" ], - "eslintConfig": { - "extends": [ - "eslint" - ], - "plugins": [ - "autofix", - "self" - ], - "rules": { - "self/prefer-const": 2 - }, - "overrides": [ - { - "files": [ - "tests/**/*.js" - ], - "env": { - "mocha": true - } - } - ] - }, - "husky": { - "hooks": { - "pre-commit": "npm run lint -- --fix && git add ." - } - }, "license": "MIT", "repository": "https://github.com/aladdin-add/eslint-plugin/tree/master/packages/no-autofix", "homepage": "https://github.com/aladdin-add/eslint-plugin/tree/master/packages/no-autofix", diff --git a/packages/web/.npmrc b/packages/web/.npmrc index c1ca392..7f5fa5a 100644 --- a/packages/web/.npmrc +++ b/packages/web/.npmrc @@ -1 +1 @@ -package-lock = false +package-lock = false diff --git a/packages/web/LICENSE b/packages/web/LICENSE index 7fe552a..f1cb361 100644 --- a/packages/web/LICENSE +++ b/packages/web/LICENSE @@ -1,19 +1,19 @@ -Copyright JS Foundation and other contributors, https://js.foundation - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +Copyright JS Foundation and other contributors, https://js.foundation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/packages/web/README.md b/packages/web/README.md index 7ddcf0a..eb6e54e 100644 --- a/packages/web/README.md +++ b/packages/web/README.md @@ -1,58 +1,58 @@ -# eslint-plugin-web - -wip: Additional ESLint's rules for web. - -## 💿 Install & Usage - -```bash - npm install --save-dev eslint eslint-plugin-web -``` - -- Requires Node.js `>=8.10.0` -- Requires ESLint `>=5.16.0` - -**.eslintrc.json** (An example) - -```json -{ - "extends": [ - "plugin:web/all" - ] -} -``` - -## 📖 Rules - -- ⭐️ - the mark of recommended rules. -- ✒️ - the mark of fixable rules. - - -- no-alert -- no-script-url - - -## 🔧 Configs - -TBF. - -## 👫 FAQ - -TBF. - -## 🚥 Semantic Versioning Policy - -`eslint-plugin-web` follows [semantic versioning](http://semver.org/) and [ESLint's Semantic Versioning Policy](https://github.com/eslint/eslint#semantic-versioning-policy). - -## 📰 Changelog - -TBF. - -## 💎 Contributing - -Welcome contributing! - -Please use GitHub's Issues/PRs. - -### Development Tools - -- `npm test` runs tests and measures coverage. +# eslint-plugin-web + +wip: Additional ESLint's rules for web. + +## 💿 Install & Usage + +```bash + npm install --save-dev eslint eslint-plugin-web +``` + +- Requires Node.js `>=8.10.0` +- Requires ESLint `>=5.16.0` + +**.eslintrc.json** (An example) + +```json +{ + "extends": [ + "plugin:web/all" + ] +} +``` + +## 📖 Rules + +- ⭐️ - the mark of recommended rules. +- ✒️ - the mark of fixable rules. + + +- no-alert +- no-script-url + + +## 🔧 Configs + +TBF. + +## 👫 FAQ + +TBF. + +## 🚥 Semantic Versioning Policy + +`eslint-plugin-web` follows [semantic versioning](http://semver.org/) and [ESLint's Semantic Versioning Policy](https://github.com/eslint/eslint#semantic-versioning-policy). + +## 📰 Changelog + +TBF. + +## 💎 Contributing + +Welcome contributing! + +Please use GitHub's Issues/PRs. + +### Development Tools + +- `npm test` runs tests and measures coverage. diff --git a/packages/web/lib/index.js b/packages/web/lib/index.js index 5706e5a..9f0907d 100644 --- a/packages/web/lib/index.js +++ b/packages/web/lib/index.js @@ -1,25 +1,25 @@ -/** - * @fileoverview eslint-plugin-web - * @author 唯然 - */ -"use strict"; - -const ruleIds = ["no-alert", "no-script-url"]; -const all = {}; -const rules = {}; - -for (const ruleId of ruleIds) { - all[ruleId] = 2; - rules[ruleId] = require(`../lib/rules/${ruleId}.js`); -} - -module.exports = { - configs: { - all: { - plugins: ["web"], - rules: all - } - - }, - rules -}; +/** + * @fileoverview eslint-plugin-web + * @author 唯然 + */ +"use strict"; + +const ruleIds = ["no-alert", "no-script-url"]; +const all = {}; +const rules = {}; + +for (const ruleId of ruleIds) { + all[ruleId] = 2; + rules[ruleId] = require(`../lib/rules/${ruleId}.js`); +} + +module.exports = { + configs: { + all: { + plugins: ["web"], + rules: all + } + + }, + rules +}; diff --git a/packages/web/lib/rules/no-alert.js b/packages/web/lib/rules/no-alert.js index 391ef26..92c6341 100644 --- a/packages/web/lib/rules/no-alert.js +++ b/packages/web/lib/rules/no-alert.js @@ -1,127 +1,127 @@ -/** - * @fileoverview Rule to flag use of alert, confirm, prompt - * @author Nicholas C. Zakas - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const getPropertyName = require("../utils").getStaticPropertyName; - -//------------------------------------------------------------------------------ -// Helpers -//------------------------------------------------------------------------------ - -/** - * Checks if the given name is a prohibited identifier. - * @param {string} name The name to check - * @returns {boolean} Whether or not the name is prohibited. - */ -function isProhibitedIdentifier(name) { - return /^(alert|confirm|prompt)$/u.test(name); -} - -/** - * Finds the eslint-scope reference in the given scope. - * @param {Object} scope The scope to search. - * @param {ASTNode} node The identifier node. - * @returns {Reference|null} Returns the found reference or null if none were found. - */ -function findReference(scope, node) { - const references = scope.references.filter(reference => reference.identifier.range[0] === node.range[0] && - reference.identifier.range[1] === node.range[1]); - - if (references.length === 1) { - return references[0]; - } - return null; -} - -/** - * Checks if the given identifier node is shadowed in the given scope. - * @param {Object} scope The current scope. - * @param {string} node The identifier node to check - * @returns {boolean} Whether or not the name is shadowed. - */ -function isShadowed(scope, node) { - const reference = findReference(scope, node); - - return reference && reference.resolved && reference.resolved.defs.length > 0; -} - -/** - * Checks if the given identifier node is a ThisExpression in the global scope or the global window property. - * @param {Object} scope The current scope. - * @param {string} node The identifier node to check - * @returns {boolean} Whether or not the node is a reference to the global object. - */ -function isGlobalThisReferenceOrGlobalWindow(scope, node) { - if (scope.type === "global" && node.type === "ThisExpression") { - return true; - } - if (node.name === "window") { - return !isShadowed(scope, node); - } - - return false; -} - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -module.exports = { - meta: { - type: "suggestion", - - docs: { - description: "disallow the use of `alert`, `confirm`, and `prompt`", - category: "Best Practices", - recommended: false, - url: "https://eslint.org/docs/rules/no-alert" - }, - - schema: [], - - messages: { - unexpected: "Unexpected {{name}}." - } - }, - - create(context) { - return { - CallExpression(node) { - const callee = node.callee, - currentScope = context.getScope(); - - // without window. - if (callee.type === "Identifier") { - const name = callee.name; - - if (!isShadowed(currentScope, callee) && isProhibitedIdentifier(callee.name)) { - context.report({ - node, - messageId: "unexpected", - data: { name } - }); - } - - } else if (callee.type === "MemberExpression" && isGlobalThisReferenceOrGlobalWindow(currentScope, callee.object)) { - const name = getPropertyName(callee); - - if (isProhibitedIdentifier(name)) { - context.report({ - node, - messageId: "unexpected", - data: { name } - }); - } - } - - } - }; - - } -}; +/** + * @fileoverview Rule to flag use of alert, confirm, prompt + * @author Nicholas C. Zakas + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const getPropertyName = require("../utils").getStaticPropertyName; + +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +/** + * Checks if the given name is a prohibited identifier. + * @param {string} name The name to check + * @returns {boolean} Whether or not the name is prohibited. + */ +function isProhibitedIdentifier(name) { + return /^(alert|confirm|prompt)$/u.test(name); +} + +/** + * Finds the eslint-scope reference in the given scope. + * @param {Object} scope The scope to search. + * @param {ASTNode} node The identifier node. + * @returns {Reference|null} Returns the found reference or null if none were found. + */ +function findReference(scope, node) { + const references = scope.references.filter(reference => reference.identifier.range[0] === node.range[0] && + reference.identifier.range[1] === node.range[1]); + + if (references.length === 1) { + return references[0]; + } + return null; +} + +/** + * Checks if the given identifier node is shadowed in the given scope. + * @param {Object} scope The current scope. + * @param {string} node The identifier node to check + * @returns {boolean} Whether or not the name is shadowed. + */ +function isShadowed(scope, node) { + const reference = findReference(scope, node); + + return reference && reference.resolved && reference.resolved.defs.length > 0; +} + +/** + * Checks if the given identifier node is a ThisExpression in the global scope or the global window property. + * @param {Object} scope The current scope. + * @param {string} node The identifier node to check + * @returns {boolean} Whether or not the node is a reference to the global object. + */ +function isGlobalThisReferenceOrGlobalWindow(scope, node) { + if (scope.type === "global" && node.type === "ThisExpression") { + return true; + } + if (node.name === "window") { + return !isShadowed(scope, node); + } + + return false; +} + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +module.exports = { + meta: { + type: "suggestion", + + docs: { + description: "disallow the use of `alert`, `confirm`, and `prompt`", + category: "Best Practices", + recommended: false, + url: "https://eslint.org/docs/rules/no-alert" + }, + + schema: [], + + messages: { + unexpected: "Unexpected {{name}}." + } + }, + + create(context) { + return { + CallExpression(node) { + const callee = node.callee, + currentScope = context.getScope(); + + // without window. + if (callee.type === "Identifier") { + const name = callee.name; + + if (!isShadowed(currentScope, callee) && isProhibitedIdentifier(callee.name)) { + context.report({ + node, + messageId: "unexpected", + data: { name } + }); + } + + } else if (callee.type === "MemberExpression" && isGlobalThisReferenceOrGlobalWindow(currentScope, callee.object)) { + const name = getPropertyName(callee); + + if (isProhibitedIdentifier(name)) { + context.report({ + node, + messageId: "unexpected", + data: { name } + }); + } + } + + } + }; + + } +}; diff --git a/packages/web/lib/rules/no-script-url.js b/packages/web/lib/rules/no-script-url.js index fedd55c..40b2fce 100644 --- a/packages/web/lib/rules/no-script-url.js +++ b/packages/web/lib/rules/no-script-url.js @@ -1,43 +1,43 @@ -/** - * @fileoverview Rule to flag when using javascript: urls - * @author Ilya Volodin - */ -/* eslint no-script-url:0*/ - -"use strict"; - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -module.exports = { - meta: { - type: "suggestion", - - docs: { - description: "disallow `javascript:` urls", - category: "Best Practices", - recommended: false, - url: "https://eslint.org/docs/rules/no-script-url" - }, - - schema: [] - }, - - create(context) { - - return { - - Literal(node) { - if (node.value && typeof node.value === "string") { - const value = node.value.toLowerCase(); - - if (value.indexOf("javascript:") === 0) { - context.report({ node, message: "Script URL is a form of eval." }); - } - } - } - }; - - } -}; +/** + * @fileoverview Rule to flag when using javascript: urls + * @author Ilya Volodin + */ +/* eslint no-script-url:0*/ + +"use strict"; + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +module.exports = { + meta: { + type: "suggestion", + + docs: { + description: "disallow `javascript:` urls", + category: "Best Practices", + recommended: false, + url: "https://eslint.org/docs/rules/no-script-url" + }, + + schema: [] + }, + + create(context) { + + return { + + Literal(node) { + if (node.value && typeof node.value === "string") { + const value = node.value.toLowerCase(); + + if (value.indexOf("javascript:") === 0) { + context.report({ node, message: "Script URL is a form of eval." }); + } + } + } + }; + + } +}; diff --git a/packages/web/lib/utils.js b/packages/web/lib/utils.js index 8bda86a..29c8b8f 100644 --- a/packages/web/lib/utils.js +++ b/packages/web/lib/utils.js @@ -1,1355 +1,1329 @@ -/** - * @fileoverview Common utils for AST. - * @author Gyandeep Singh - */ - -"use strict"; - -const esutils = require("esutils"); -const espree = require("espree"); -const lodash = require("lodash"); - -const breakableTypePattern = /^(?:(?:Do)?While|For(?:In|Of)?|Switch)Statement$/u; -const lineBreakPattern = /\r\n|[\r\n\u2028\u2029]/u; -const shebangPattern = /^#!([^\r\n]+)/u; - -/** - * Creates a version of the `lineBreakPattern` regex with the global flag. - * Global regexes are mutable, so this needs to be a function instead of a constant. - * @returns {RegExp} A global regular expression that matches line terminators - */ -function createGlobalLinebreakMatcher() { - return new RegExp(lineBreakPattern.source, "gu"); -} - -//------------------------------------------------------------------------------ -// Helpers -//------------------------------------------------------------------------------ - -const anyFunctionPattern = /^(?:Function(?:Declaration|Expression)|ArrowFunctionExpression)$/u; -const anyLoopPattern = /^(?:DoWhile|For|ForIn|ForOf|While)Statement$/u; -const arrayOrTypedArrayPattern = /Array$/u; -const arrayMethodPattern = /^(?:every|filter|find|findIndex|forEach|map|some)$/u; -const bindOrCallOrApplyPattern = /^(?:bind|call|apply)$/u; -const thisTagPattern = /^[\s*]*@this/mu; - - -const COMMENTS_IGNORE_PATTERN = /^\s*(?:eslint|jshint\s+|jslint\s+|istanbul\s+|globals?\s+|exported\s+|jscs)/u; -const LINEBREAKS = new Set(["\r\n", "\r", "\n", "\u2028", "\u2029"]); - -// A set of node types that can contain a list of statements -const STATEMENT_LIST_PARENTS = new Set(["Program", "BlockStatement", "SwitchCase"]); - -/** - * Checks reference if is non initializer and writable. - * @param {Reference} reference - A reference to check. - * @param {int} index - The index of the reference in the references. - * @param {Reference[]} references - The array that the reference belongs to. - * @returns {boolean} Success/Failure - * @private - */ -function isModifyingReference(reference, index, references) { - const identifier = reference.identifier; - - /* - * Destructuring assignments can have multiple default value, so - * possibly there are multiple writeable references for the same - * identifier. - */ - const modifyingDifferentIdentifier = index === 0 || - references[index - 1].identifier !== identifier; - - return (identifier && - reference.init === false && - reference.isWrite() && - modifyingDifferentIdentifier - ); -} - -/** - * Checks whether the given string starts with uppercase or not. - * - * @param {string} s - The string to check. - * @returns {boolean} `true` if the string starts with uppercase. - */ -function startsWithUpperCase(s) { - return s[0] !== s[0].toLocaleLowerCase(); -} - -/** - * Checks whether or not a node is a constructor. - * @param {ASTNode} node - A function node to check. - * @returns {boolean} Wehether or not a node is a constructor. - */ -function isES5Constructor(node) { - return (node.id && startsWithUpperCase(node.id.name)); -} - -/** - * Finds a function node from ancestors of a node. - * @param {ASTNode} node - A start node to find. - * @returns {Node|null} A found function node. - */ -function getUpperFunction(node) { - for (let currentNode = node; currentNode; currentNode = currentNode.parent) { - if (anyFunctionPattern.test(currentNode.type)) { - return currentNode; - } - } - return null; -} - -/** - * Checks whether a given node is a function node or not. - * The following types are function nodes: - * - * - ArrowFunctionExpression - * - FunctionDeclaration - * - FunctionExpression - * - * @param {ASTNode|null} node - A node to check. - * @returns {boolean} `true` if the node is a function node. - */ -function isFunction(node) { - return Boolean(node && anyFunctionPattern.test(node.type)); -} - -/** - * Checks whether a given node is a loop node or not. - * The following types are loop nodes: - * - * - DoWhileStatement - * - ForInStatement - * - ForOfStatement - * - ForStatement - * - WhileStatement - * - * @param {ASTNode|null} node - A node to check. - * @returns {boolean} `true` if the node is a loop node. - */ -function isLoop(node) { - return Boolean(node && anyLoopPattern.test(node.type)); -} - -/** - * Checks whether the given node is in a loop or not. - * - * @param {ASTNode} node - The node to check. - * @returns {boolean} `true` if the node is in a loop. - */ -function isInLoop(node) { - for (let currentNode = node; currentNode && !isFunction(currentNode); currentNode = currentNode.parent) { - if (isLoop(currentNode)) { - return true; - } - } - - return false; -} - -/** - * Checks whether or not a node is `null` or `undefined`. - * @param {ASTNode} node - A node to check. - * @returns {boolean} Whether or not the node is a `null` or `undefined`. - * @public - */ -function isNullOrUndefined(node) { - return ( - module.exports.isNullLiteral(node) || - (node.type === "Identifier" && node.name === "undefined") || - (node.type === "UnaryExpression" && node.operator === "void") - ); -} - -/** - * Checks whether or not a node is callee. - * @param {ASTNode} node - A node to check. - * @returns {boolean} Whether or not the node is callee. - */ -function isCallee(node) { - return node.parent.type === "CallExpression" && node.parent.callee === node; -} - -/** - * Checks whether or not a node is `Reflect.apply`. - * @param {ASTNode} node - A node to check. - * @returns {boolean} Whether or not the node is a `Reflect.apply`. - */ -function isReflectApply(node) { - return ( - node.type === "MemberExpression" && - node.object.type === "Identifier" && - node.object.name === "Reflect" && - node.property.type === "Identifier" && - node.property.name === "apply" && - node.computed === false - ); -} - -/** - * Checks whether or not a node is `Array.from`. - * @param {ASTNode} node - A node to check. - * @returns {boolean} Whether or not the node is a `Array.from`. - */ -function isArrayFromMethod(node) { - return ( - node.type === "MemberExpression" && - node.object.type === "Identifier" && - arrayOrTypedArrayPattern.test(node.object.name) && - node.property.type === "Identifier" && - node.property.name === "from" && - node.computed === false - ); -} - -/** - * Checks whether or not a node is a method which has `thisArg`. - * @param {ASTNode} node - A node to check. - * @returns {boolean} Whether or not the node is a method which has `thisArg`. - */ -function isMethodWhichHasThisArg(node) { - for ( - let currentNode = node; - currentNode.type === "MemberExpression" && !currentNode.computed; - currentNode = currentNode.property - ) { - if (currentNode.property.type === "Identifier") { - return arrayMethodPattern.test(currentNode.property.name); - } - } - - return false; -} - -/** - * Creates the negate function of the given function. - * @param {Function} f - The function to negate. - * @returns {Function} Negated function. - */ -function negate(f) { - return token => !f(token); -} - -/** - * Checks whether or not a node has a `@this` tag in its comments. - * @param {ASTNode} node - A node to check. - * @param {SourceCode} sourceCode - A SourceCode instance to get comments. - * @returns {boolean} Whether or not the node has a `@this` tag in its comments. - */ -function hasJSDocThisTag(node, sourceCode) { - const jsdocComment = sourceCode.getJSDocComment(node); - - if (jsdocComment && thisTagPattern.test(jsdocComment.value)) { - return true; - } - - // Checks `@this` in its leading comments for callbacks, - // because callbacks don't have its JSDoc comment. - // e.g. - // sinon.test(/* @this sinon.Sandbox */function() { this.spy(); }); - return sourceCode.getCommentsBefore(node).some(comment => thisTagPattern.test(comment.value)); -} - -/** - * Determines if a node is surrounded by parentheses. - * @param {SourceCode} sourceCode The ESLint source code object - * @param {ASTNode} node The node to be checked. - * @returns {boolean} True if the node is parenthesised. - * @private - */ -function isParenthesised(sourceCode, node) { - const previousToken = sourceCode.getTokenBefore(node), - nextToken = sourceCode.getTokenAfter(node); - - return Boolean(previousToken && nextToken) && - previousToken.value === "(" && previousToken.range[1] <= node.range[0] && - nextToken.value === ")" && nextToken.range[0] >= node.range[1]; -} - -/** - * Checks if the given token is an arrow token or not. - * - * @param {Token} token - The token to check. - * @returns {boolean} `true` if the token is an arrow token. - */ -function isArrowToken(token) { - return token.value === "=>" && token.type === "Punctuator"; -} - -/** - * Checks if the given token is a comma token or not. - * - * @param {Token} token - The token to check. - * @returns {boolean} `true` if the token is a comma token. - */ -function isCommaToken(token) { - return token.value === "," && token.type === "Punctuator"; -} - -/** - * Checks if the given token is a semicolon token or not. - * - * @param {Token} token - The token to check. - * @returns {boolean} `true` if the token is a semicolon token. - */ -function isSemicolonToken(token) { - return token.value === ";" && token.type === "Punctuator"; -} - -/** - * Checks if the given token is a colon token or not. - * - * @param {Token} token - The token to check. - * @returns {boolean} `true` if the token is a colon token. - */ -function isColonToken(token) { - return token.value === ":" && token.type === "Punctuator"; -} - -/** - * Checks if the given token is an opening parenthesis token or not. - * - * @param {Token} token - The token to check. - * @returns {boolean} `true` if the token is an opening parenthesis token. - */ -function isOpeningParenToken(token) { - return token.value === "(" && token.type === "Punctuator"; -} - -/** - * Checks if the given token is a closing parenthesis token or not. - * - * @param {Token} token - The token to check. - * @returns {boolean} `true` if the token is a closing parenthesis token. - */ -function isClosingParenToken(token) { - return token.value === ")" && token.type === "Punctuator"; -} - -/** - * Checks if the given token is an opening square bracket token or not. - * - * @param {Token} token - The token to check. - * @returns {boolean} `true` if the token is an opening square bracket token. - */ -function isOpeningBracketToken(token) { - return token.value === "[" && token.type === "Punctuator"; -} - -/** - * Checks if the given token is a closing square bracket token or not. - * - * @param {Token} token - The token to check. - * @returns {boolean} `true` if the token is a closing square bracket token. - */ -function isClosingBracketToken(token) { - return token.value === "]" && token.type === "Punctuator"; -} - -/** - * Checks if the given token is an opening brace token or not. - * - * @param {Token} token - The token to check. - * @returns {boolean} `true` if the token is an opening brace token. - */ -function isOpeningBraceToken(token) { - return token.value === "{" && token.type === "Punctuator"; -} - -/** - * Checks if the given token is a closing brace token or not. - * - * @param {Token} token - The token to check. - * @returns {boolean} `true` if the token is a closing brace token. - */ -function isClosingBraceToken(token) { - return token.value === "}" && token.type === "Punctuator"; -} - -/** - * Checks if the given token is a comment token or not. - * - * @param {Token} token - The token to check. - * @returns {boolean} `true` if the token is a comment token. - */ -function isCommentToken(token) { - return token.type === "Line" || token.type === "Block" || token.type === "Shebang"; -} - -/** - * Checks if the given token is a keyword token or not. - * - * @param {Token} token - The token to check. - * @returns {boolean} `true` if the token is a keyword token. - */ -function isKeywordToken(token) { - return token.type === "Keyword"; -} - -/** - * Gets the `(` token of the given function node. - * - * @param {ASTNode} node - The function node to get. - * @param {SourceCode} sourceCode - The source code object to get tokens. - * @returns {Token} `(` token. - */ -function getOpeningParenOfParams(node, sourceCode) { - return node.id - ? sourceCode.getTokenAfter(node.id, isOpeningParenToken) - : sourceCode.getFirstToken(node, isOpeningParenToken); -} - -/** - * Checks whether or not the tokens of two given nodes are same. - * @param {ASTNode} left - A node 1 to compare. - * @param {ASTNode} right - A node 2 to compare. - * @param {SourceCode} sourceCode - The ESLint source code object. - * @returns {boolean} the source code for the given node. - */ -function equalTokens(left, right, sourceCode) { - const tokensL = sourceCode.getTokens(left); - const tokensR = sourceCode.getTokens(right); - - if (tokensL.length !== tokensR.length) { - return false; - } - for (let i = 0; i < tokensL.length; ++i) { - if (tokensL[i].type !== tokensR[i].type || - tokensL[i].value !== tokensR[i].value - ) { - return false; - } - } - - return true; -} - -//------------------------------------------------------------------------------ -// Public Interface -//------------------------------------------------------------------------------ - -module.exports = { - COMMENTS_IGNORE_PATTERN, - LINEBREAKS, - LINEBREAK_MATCHER: lineBreakPattern, - SHEBANG_MATCHER: shebangPattern, - STATEMENT_LIST_PARENTS, - - /** - * Determines whether two adjacent tokens are on the same line. - * @param {Object} left - The left token object. - * @param {Object} right - The right token object. - * @returns {boolean} Whether or not the tokens are on the same line. - * @public - */ - isTokenOnSameLine(left, right) { - return left.loc.end.line === right.loc.start.line; - }, - - isNullOrUndefined, - isCallee, - isES5Constructor, - getUpperFunction, - isFunction, - isLoop, - isInLoop, - isArrayFromMethod, - isParenthesised, - createGlobalLinebreakMatcher, - equalTokens, - - isArrowToken, - isClosingBraceToken, - isClosingBracketToken, - isClosingParenToken, - isColonToken, - isCommaToken, - isCommentToken, - isKeywordToken, - isNotClosingBraceToken: negate(isClosingBraceToken), - isNotClosingBracketToken: negate(isClosingBracketToken), - isNotClosingParenToken: negate(isClosingParenToken), - isNotColonToken: negate(isColonToken), - isNotCommaToken: negate(isCommaToken), - isNotOpeningBraceToken: negate(isOpeningBraceToken), - isNotOpeningBracketToken: negate(isOpeningBracketToken), - isNotOpeningParenToken: negate(isOpeningParenToken), - isNotSemicolonToken: negate(isSemicolonToken), - isOpeningBraceToken, - isOpeningBracketToken, - isOpeningParenToken, - isSemicolonToken, - - /** - * Checks whether or not a given node is a string literal. - * @param {ASTNode} node - A node to check. - * @returns {boolean} `true` if the node is a string literal. - */ - isStringLiteral(node) { - return ( - (node.type === "Literal" && typeof node.value === "string") || - node.type === "TemplateLiteral" - ); - }, - - /** - * Checks whether a given node is a breakable statement or not. - * The node is breakable if the node is one of the following type: - * - * - DoWhileStatement - * - ForInStatement - * - ForOfStatement - * - ForStatement - * - SwitchStatement - * - WhileStatement - * - * @param {ASTNode} node - A node to check. - * @returns {boolean} `true` if the node is breakable. - */ - isBreakableStatement(node) { - return breakableTypePattern.test(node.type); - }, - - /** - * Gets references which are non initializer and writable. - * @param {Reference[]} references - An array of references. - * @returns {Reference[]} An array of only references which are non initializer and writable. - * @public - */ - getModifyingReferences(references) { - return references.filter(isModifyingReference); - }, - - /** - * Validate that a string passed in is surrounded by the specified character - * @param {string} val The text to check. - * @param {string} character The character to see if it's surrounded by. - * @returns {boolean} True if the text is surrounded by the character, false if not. - * @private - */ - isSurroundedBy(val, character) { - return val[0] === character && val[val.length - 1] === character; - }, - - /** - * Returns whether the provided node is an ESLint directive comment or not - * @param {Line|Block} node The comment token to be checked - * @returns {boolean} `true` if the node is an ESLint directive comment - */ - isDirectiveComment(node) { - const comment = node.value.trim(); - - return ( - node.type === "Line" && comment.indexOf("eslint-") === 0 || - node.type === "Block" && ( - comment.indexOf("global ") === 0 || - comment.indexOf("eslint ") === 0 || - comment.indexOf("eslint-") === 0 - ) - ); - }, - - /** - * Gets the trailing statement of a given node. - * - * if (code) - * consequent; - * - * When taking this `IfStatement`, returns `consequent;` statement. - * - * @param {ASTNode} A node to get. - * @returns {ASTNode|null} The trailing statement's node. - */ - getTrailingStatement: esutils.ast.trailingStatement, - - /** - * Finds the variable by a given name in a given scope and its upper scopes. - * - * @param {eslint-scope.Scope} initScope - A scope to start find. - * @param {string} name - A variable name to find. - * @returns {eslint-scope.Variable|null} A found variable or `null`. - */ - getVariableByName(initScope, name) { - let scope = initScope; - - while (scope) { - const variable = scope.set.get(name); - - if (variable) { - return variable; - } - - scope = scope.upper; - } - - return null; - }, - - /** - * Checks whether or not a given function node is the default `this` binding. - * - * First, this checks the node: - * - * - The function name does not start with uppercase (it's a constructor). - * - The function does not have a JSDoc comment that has a @this tag. - * - * Next, this checks the location of the node. - * If the location is below, this judges `this` is valid. - * - * - The location is not on an object literal. - * - The location is not assigned to a variable which starts with an uppercase letter. - * - The location is not on an ES2015 class. - * - Its `bind`/`call`/`apply` method is not called directly. - * - The function is not a callback of array methods (such as `.forEach()`) if `thisArg` is given. - * - * @param {ASTNode} node - A function node to check. - * @param {SourceCode} sourceCode - A SourceCode instance to get comments. - * @returns {boolean} The function node is the default `this` binding. - */ - isDefaultThisBinding(node, sourceCode) { - if (isES5Constructor(node) || hasJSDocThisTag(node, sourceCode)) { - return false; - } - const isAnonymous = node.id === null; - let currentNode = node; - - while (currentNode) { - const parent = currentNode.parent; - - switch (parent.type) { - - /* - * Looks up the destination. - * e.g., obj.foo = nativeFoo || function foo() { ... }; - */ - case "LogicalExpression": - case "ConditionalExpression": - currentNode = parent; - break; - - /* - * If the upper function is IIFE, checks the destination of the return value. - * e.g. - * obj.foo = (function() { - * // setup... - * return function foo() { ... }; - * })(); - * obj.foo = (() => - * function foo() { ... } - * )(); - */ - case "ReturnStatement": { - const func = getUpperFunction(parent); - - if (func === null || !isCallee(func)) { - return true; - } - currentNode = func.parent; - break; - } - case "ArrowFunctionExpression": - if (currentNode !== parent.body || !isCallee(parent)) { - return true; - } - currentNode = parent.parent; - break; - - /* - * e.g. - * var obj = { foo() { ... } }; - * var obj = { foo: function() { ... } }; - * class A { constructor() { ... } } - * class A { foo() { ... } } - * class A { get foo() { ... } } - * class A { set foo() { ... } } - * class A { static foo() { ... } } - */ - case "Property": - case "MethodDefinition": - return parent.value !== currentNode; - - /* - * e.g. - * obj.foo = function foo() { ... }; - * Foo = function() { ... }; - * [obj.foo = function foo() { ... }] = a; - * [Foo = function() { ... }] = a; - */ - case "AssignmentExpression": - case "AssignmentPattern": - if (parent.left.type === "MemberExpression") { - return false; - } - if ( - isAnonymous && - parent.left.type === "Identifier" && - startsWithUpperCase(parent.left.name) - ) { - return false; - } - return true; - - /* - * e.g. - * var Foo = function() { ... }; - */ - case "VariableDeclarator": - return !( - isAnonymous && - parent.init === currentNode && - parent.id.type === "Identifier" && - startsWithUpperCase(parent.id.name) - ); - - /* - * e.g. - * var foo = function foo() { ... }.bind(obj); - * (function foo() { ... }).call(obj); - * (function foo() { ... }).apply(obj, []); - */ - case "MemberExpression": - return ( - parent.object !== currentNode || - parent.property.type !== "Identifier" || - !bindOrCallOrApplyPattern.test(parent.property.name) || - !isCallee(parent) || - parent.parent.arguments.length === 0 || - isNullOrUndefined(parent.parent.arguments[0]) - ); - - /* - * e.g. - * Reflect.apply(function() {}, obj, []); - * Array.from([], function() {}, obj); - * list.forEach(function() {}, obj); - */ - case "CallExpression": - if (isReflectApply(parent.callee)) { - return ( - parent.arguments.length !== 3 || - parent.arguments[0] !== currentNode || - isNullOrUndefined(parent.arguments[1]) - ); - } - if (isArrayFromMethod(parent.callee)) { - return ( - parent.arguments.length !== 3 || - parent.arguments[1] !== currentNode || - isNullOrUndefined(parent.arguments[2]) - ); - } - if (isMethodWhichHasThisArg(parent.callee)) { - return ( - parent.arguments.length !== 2 || - parent.arguments[0] !== currentNode || - isNullOrUndefined(parent.arguments[1]) - ); - } - return true; - - // Otherwise `this` is default. - default: - return true; - } - } - - /* istanbul ignore next */ - return true; - }, - - /** - * Get the precedence level based on the node type - * @param {ASTNode} node node to evaluate - * @returns {int} precedence level - * @private - */ - getPrecedence(node) { - switch (node.type) { - case "SequenceExpression": - return 0; - - case "AssignmentExpression": - case "ArrowFunctionExpression": - case "YieldExpression": - return 1; - - case "ConditionalExpression": - return 3; - - case "LogicalExpression": - switch (node.operator) { - case "||": - return 4; - case "&&": - return 5; - - // no default - } - - /* falls through */ - - case "BinaryExpression": - - switch (node.operator) { - case "|": - return 6; - case "^": - return 7; - case "&": - return 8; - case "==": - case "!=": - case "===": - case "!==": - return 9; - case "<": - case "<=": - case ">": - case ">=": - case "in": - case "instanceof": - return 10; - case "<<": - case ">>": - case ">>>": - return 11; - case "+": - case "-": - return 12; - case "*": - case "/": - case "%": - return 13; - case "**": - return 15; - - // no default - } - - /* falls through */ - - case "UnaryExpression": - case "AwaitExpression": - return 16; - - case "UpdateExpression": - return 17; - - case "CallExpression": - return 18; - - case "NewExpression": - return 19; - - default: - return 20; - } - }, - - /** - * Checks whether the given node is an empty block node or not. - * - * @param {ASTNode|null} node - The node to check. - * @returns {boolean} `true` if the node is an empty block. - */ - isEmptyBlock(node) { - return Boolean(node && node.type === "BlockStatement" && node.body.length === 0); - }, - - /** - * Checks whether the given node is an empty function node or not. - * - * @param {ASTNode|null} node - The node to check. - * @returns {boolean} `true` if the node is an empty function. - */ - isEmptyFunction(node) { - return isFunction(node) && module.exports.isEmptyBlock(node.body); - }, - - /** - * Gets the property name of a given node. - * The node can be a MemberExpression, a Property, or a MethodDefinition. - * - * If the name is dynamic, this returns `null`. - * - * For examples: - * - * a.b // => "b" - * a["b"] // => "b" - * a['b'] // => "b" - * a[`b`] // => "b" - * a[100] // => "100" - * a[b] // => null - * a["a" + "b"] // => null - * a[tag`b`] // => null - * a[`${b}`] // => null - * - * let a = {b: 1} // => "b" - * let a = {["b"]: 1} // => "b" - * let a = {['b']: 1} // => "b" - * let a = {[`b`]: 1} // => "b" - * let a = {[100]: 1} // => "100" - * let a = {[b]: 1} // => null - * let a = {["a" + "b"]: 1} // => null - * let a = {[tag`b`]: 1} // => null - * let a = {[`${b}`]: 1} // => null - * - * @param {ASTNode} node - The node to get. - * @returns {string|null} The property name if static. Otherwise, null. - */ - getStaticPropertyName(node) { - let prop; - - switch (node && node.type) { - case "Property": - case "MethodDefinition": - prop = node.key; - break; - - case "MemberExpression": - prop = node.property; - break; - - // no default - } - - switch (prop && prop.type) { - case "Literal": - return String(prop.value); - - case "TemplateLiteral": - if (prop.expressions.length === 0 && prop.quasis.length === 1) { - return prop.quasis[0].value.cooked; - } - break; - - case "Identifier": - if (!node.computed) { - return prop.name; - } - break; - - // no default - } - - return null; - }, - - /** - * Get directives from directive prologue of a Program or Function node. - * @param {ASTNode} node - The node to check. - * @returns {ASTNode[]} The directives found in the directive prologue. - */ - getDirectivePrologue(node) { - const directives = []; - - // Directive prologues only occur at the top of files or functions. - if ( - node.type === "Program" || - node.type === "FunctionDeclaration" || - node.type === "FunctionExpression" || - - /* - * Do not check arrow functions with implicit return. - * `() => "use strict";` returns the string `"use strict"`. - */ - (node.type === "ArrowFunctionExpression" && node.body.type === "BlockStatement") - ) { - const statements = node.type === "Program" ? node.body : node.body.body; - - for (const statement of statements) { - if ( - statement.type === "ExpressionStatement" && - statement.expression.type === "Literal" - ) { - directives.push(statement); - } else { - break; - } - } - } - - return directives; - }, - - - /** - * Determines whether this node is a decimal integer literal. If a node is a decimal integer literal, a dot added - * after the node will be parsed as a decimal point, rather than a property-access dot. - * @param {ASTNode} node - The node to check. - * @returns {boolean} `true` if this node is a decimal integer. - * @example - * - * 5 // true - * 5. // false - * 5.0 // false - * 05 // false - * 0x5 // false - * 0b101 // false - * 0o5 // false - * 5e0 // false - * '5' // false - */ - isDecimalInteger(node) { - return node.type === "Literal" && typeof node.value === "number" && /^(0|[1-9]\d*)$/u.test(node.raw); - }, - - /** - * Gets the name and kind of the given function node. - * - * - `function foo() {}` .................... `function 'foo'` - * - `(function foo() {})` .................. `function 'foo'` - * - `(function() {})` ...................... `function` - * - `function* foo() {}` ................... `generator function 'foo'` - * - `(function* foo() {})` ................. `generator function 'foo'` - * - `(function*() {})` ..................... `generator function` - * - `() => {}` ............................. `arrow function` - * - `async () => {}` ....................... `async arrow function` - * - `({ foo: function foo() {} })` ......... `method 'foo'` - * - `({ foo: function() {} })` ............. `method 'foo'` - * - `({ ['foo']: function() {} })` ......... `method 'foo'` - * - `({ [foo]: function() {} })` ........... `method` - * - `({ foo() {} })` ....................... `method 'foo'` - * - `({ foo: function* foo() {} })` ........ `generator method 'foo'` - * - `({ foo: function*() {} })` ............ `generator method 'foo'` - * - `({ ['foo']: function*() {} })` ........ `generator method 'foo'` - * - `({ [foo]: function*() {} })` .......... `generator method` - * - `({ *foo() {} })` ...................... `generator method 'foo'` - * - `({ foo: async function foo() {} })` ... `async method 'foo'` - * - `({ foo: async function() {} })` ....... `async method 'foo'` - * - `({ ['foo']: async function() {} })` ... `async method 'foo'` - * - `({ [foo]: async function() {} })` ..... `async method` - * - `({ async foo() {} })` ................. `async method 'foo'` - * - `({ get foo() {} })` ................... `getter 'foo'` - * - `({ set foo(a) {} })` .................. `setter 'foo'` - * - `class A { constructor() {} }` ......... `constructor` - * - `class A { foo() {} }` ................. `method 'foo'` - * - `class A { *foo() {} }` ................ `generator method 'foo'` - * - `class A { async foo() {} }` ........... `async method 'foo'` - * - `class A { ['foo']() {} }` ............. `method 'foo'` - * - `class A { *['foo']() {} }` ............ `generator method 'foo'` - * - `class A { async ['foo']() {} }` ....... `async method 'foo'` - * - `class A { [foo]() {} }` ............... `method` - * - `class A { *[foo]() {} }` .............. `generator method` - * - `class A { async [foo]() {} }` ......... `async method` - * - `class A { get foo() {} }` ............. `getter 'foo'` - * - `class A { set foo(a) {} }` ............ `setter 'foo'` - * - `class A { static foo() {} }` .......... `static method 'foo'` - * - `class A { static *foo() {} }` ......... `static generator method 'foo'` - * - `class A { static async foo() {} }` .... `static async method 'foo'` - * - `class A { static get foo() {} }` ...... `static getter 'foo'` - * - `class A { static set foo(a) {} }` ..... `static setter 'foo'` - * - * @param {ASTNode} node - The function node to get. - * @returns {string} The name and kind of the function node. - */ - getFunctionNameWithKind(node) { - const parent = node.parent; - const tokens = []; - - if (parent.type === "MethodDefinition" && parent.static) { - tokens.push("static"); - } - if (node.async) { - tokens.push("async"); - } - if (node.generator) { - tokens.push("generator"); - } - - if (node.type === "ArrowFunctionExpression") { - tokens.push("arrow", "function"); - } else if (parent.type === "Property" || parent.type === "MethodDefinition") { - if (parent.kind === "constructor") { - return "constructor"; - } - if (parent.kind === "get") { - tokens.push("getter"); - } else if (parent.kind === "set") { - tokens.push("setter"); - } else { - tokens.push("method"); - } - } else { - tokens.push("function"); - } - - if (node.id) { - tokens.push(`'${node.id.name}'`); - } else { - const name = module.exports.getStaticPropertyName(parent); - - if (name) { - tokens.push(`'${name}'`); - } - } - - return tokens.join(" "); - }, - - /** - * Gets the location of the given function node for reporting. - * - * - `function foo() {}` - * ^^^^^^^^^^^^ - * - `(function foo() {})` - * ^^^^^^^^^^^^ - * - `(function() {})` - * ^^^^^^^^ - * - `function* foo() {}` - * ^^^^^^^^^^^^^ - * - `(function* foo() {})` - * ^^^^^^^^^^^^^ - * - `(function*() {})` - * ^^^^^^^^^ - * - `() => {}` - * ^^ - * - `async () => {}` - * ^^ - * - `({ foo: function foo() {} })` - * ^^^^^^^^^^^^^^^^^ - * - `({ foo: function() {} })` - * ^^^^^^^^^^^^^ - * - `({ ['foo']: function() {} })` - * ^^^^^^^^^^^^^^^^^ - * - `({ [foo]: function() {} })` - * ^^^^^^^^^^^^^^^ - * - `({ foo() {} })` - * ^^^ - * - `({ foo: function* foo() {} })` - * ^^^^^^^^^^^^^^^^^^ - * - `({ foo: function*() {} })` - * ^^^^^^^^^^^^^^ - * - `({ ['foo']: function*() {} })` - * ^^^^^^^^^^^^^^^^^^ - * - `({ [foo]: function*() {} })` - * ^^^^^^^^^^^^^^^^ - * - `({ *foo() {} })` - * ^^^^ - * - `({ foo: async function foo() {} })` - * ^^^^^^^^^^^^^^^^^^^^^^^ - * - `({ foo: async function() {} })` - * ^^^^^^^^^^^^^^^^^^^ - * - `({ ['foo']: async function() {} })` - * ^^^^^^^^^^^^^^^^^^^^^^^ - * - `({ [foo]: async function() {} })` - * ^^^^^^^^^^^^^^^^^^^^^ - * - `({ async foo() {} })` - * ^^^^^^^^^ - * - `({ get foo() {} })` - * ^^^^^^^ - * - `({ set foo(a) {} })` - * ^^^^^^^ - * - `class A { constructor() {} }` - * ^^^^^^^^^^^ - * - `class A { foo() {} }` - * ^^^ - * - `class A { *foo() {} }` - * ^^^^ - * - `class A { async foo() {} }` - * ^^^^^^^^^ - * - `class A { ['foo']() {} }` - * ^^^^^^^ - * - `class A { *['foo']() {} }` - * ^^^^^^^^ - * - `class A { async ['foo']() {} }` - * ^^^^^^^^^^^^^ - * - `class A { [foo]() {} }` - * ^^^^^ - * - `class A { *[foo]() {} }` - * ^^^^^^ - * - `class A { async [foo]() {} }` - * ^^^^^^^^^^^ - * - `class A { get foo() {} }` - * ^^^^^^^ - * - `class A { set foo(a) {} }` - * ^^^^^^^ - * - `class A { static foo() {} }` - * ^^^^^^^^^^ - * - `class A { static *foo() {} }` - * ^^^^^^^^^^^ - * - `class A { static async foo() {} }` - * ^^^^^^^^^^^^^^^^ - * - `class A { static get foo() {} }` - * ^^^^^^^^^^^^^^ - * - `class A { static set foo(a) {} }` - * ^^^^^^^^^^^^^^ - * - * @param {ASTNode} node - The function node to get. - * @param {SourceCode} sourceCode - The source code object to get tokens. - * @returns {string} The location of the function node for reporting. - */ - getFunctionHeadLoc(node, sourceCode) { - const parent = node.parent; - let start = null; - let end = null; - - if (node.type === "ArrowFunctionExpression") { - const arrowToken = sourceCode.getTokenBefore(node.body, isArrowToken); - - start = arrowToken.loc.start; - end = arrowToken.loc.end; - } else if (parent.type === "Property" || parent.type === "MethodDefinition") { - start = parent.loc.start; - end = getOpeningParenOfParams(node, sourceCode).loc.start; - } else { - start = node.loc.start; - end = getOpeningParenOfParams(node, sourceCode).loc.start; - } - - return { - start: Object.assign({}, start), - end: Object.assign({}, end) - }; - }, - - /** - * Gets the parenthesized text of a node. This is similar to sourceCode.getText(node), but it also includes any parentheses - * surrounding the node. - * @param {SourceCode} sourceCode The source code object - * @param {ASTNode} node An expression node - * @returns {string} The text representing the node, with all surrounding parentheses included - */ - getParenthesisedText(sourceCode, node) { - let leftToken = sourceCode.getFirstToken(node); - let rightToken = sourceCode.getLastToken(node); - - while ( - sourceCode.getTokenBefore(leftToken) && - sourceCode.getTokenBefore(leftToken).type === "Punctuator" && - sourceCode.getTokenBefore(leftToken).value === "(" && - sourceCode.getTokenAfter(rightToken) && - sourceCode.getTokenAfter(rightToken).type === "Punctuator" && - sourceCode.getTokenAfter(rightToken).value === ")" - ) { - leftToken = sourceCode.getTokenBefore(leftToken); - rightToken = sourceCode.getTokenAfter(rightToken); - } - - return sourceCode.getText().slice(leftToken.range[0], rightToken.range[1]); - }, - - /* - * Determine if a node has a possiblity to be an Error object - * @param {ASTNode} node ASTNode to check - * @returns {boolean} True if there is a chance it contains an Error obj - */ - couldBeError(node) { - switch (node.type) { - case "Identifier": - case "CallExpression": - case "NewExpression": - case "MemberExpression": - case "TaggedTemplateExpression": - case "YieldExpression": - case "AwaitExpression": - return true; // possibly an error object. - - case "AssignmentExpression": - return module.exports.couldBeError(node.right); - - case "SequenceExpression": { - const exprs = node.expressions; - - return exprs.length !== 0 && module.exports.couldBeError(exprs[exprs.length - 1]); - } - - case "LogicalExpression": - return module.exports.couldBeError(node.left) || module.exports.couldBeError(node.right); - - case "ConditionalExpression": - return module.exports.couldBeError(node.consequent) || module.exports.couldBeError(node.alternate); - - default: - return false; - } - }, - - /** - * Determines whether the given node is a `null` literal. - * @param {ASTNode} node The node to check - * @returns {boolean} `true` if the node is a `null` literal - */ - isNullLiteral(node) { - - /* - * Checking `node.value === null` does not guarantee that a literal is a null literal. - * When parsing values that cannot be represented in the current environment (e.g. unicode - * regexes in Node 4), `node.value` is set to `null` because it wouldn't be possible to - * set `node.value` to a unicode regex. To make sure a literal is actually `null`, check - * `node.regex` instead. Also see: https://github.com/eslint/eslint/issues/8020 - */ - return node.type === "Literal" && node.value === null && !node.regex; - }, - - /** - * Determines whether two tokens can safely be placed next to each other without merging into a single token - * @param {Token|string} leftValue The left token. If this is a string, it will be tokenized and the last token will be used. - * @param {Token|string} rightValue The right token. If this is a string, it will be tokenized and the first token will be used. - * @returns {boolean} If the tokens cannot be safely placed next to each other, returns `false`. If the tokens can be placed - * next to each other, behavior is undefined (although it should return `true` in most cases). - */ - canTokensBeAdjacent(leftValue, rightValue) { - let leftToken; - - if (typeof leftValue === "string") { - const leftTokens = espree.tokenize(leftValue, { ecmaVersion: 2015 }); - - leftToken = leftTokens[leftTokens.length - 1]; - } else { - leftToken = leftValue; - } - - const rightToken = typeof rightValue === "string" ? espree.tokenize(rightValue, { ecmaVersion: 2015 })[0] : rightValue; - - if (leftToken.type === "Punctuator" || rightToken.type === "Punctuator") { - if (leftToken.type === "Punctuator" && rightToken.type === "Punctuator") { - const PLUS_TOKENS = new Set(["+", "++"]); - const MINUS_TOKENS = new Set(["-", "--"]); - - return !( - PLUS_TOKENS.has(leftToken.value) && PLUS_TOKENS.has(rightToken.value) || - MINUS_TOKENS.has(leftToken.value) && MINUS_TOKENS.has(rightToken.value) - ); - } - return true; - } - - if ( - leftToken.type === "String" || rightToken.type === "String" || - leftToken.type === "Template" || rightToken.type === "Template" - ) { - return true; - } - - if (leftToken.type !== "Numeric" && rightToken.type === "Numeric" && rightToken.value.startsWith(".")) { - return true; - } - - return false; - }, - - /** - * Get the `loc` object of a given name in a `/*globals` directive comment. - * @param {SourceCode} sourceCode The source code to convert index to loc. - * @param {Comment} comment The `/*globals` directive comment which include the name. - * @param {string} name The name to find. - * @returns {SourceLocation} The `loc` object. - */ - getNameLocationInGlobalDirectiveComment(sourceCode, comment, name) { - const namePattern = new RegExp(`[\\s,]${lodash.escapeRegExp(name)}(?:$|[\\s,:])`, "gu"); - - // To ignore the first text "global". - namePattern.lastIndex = comment.value.indexOf("global") + 6; - - // Search a given variable name. - const match = namePattern.exec(comment.value); - - // Convert the index to loc. - return sourceCode.getLocFromIndex( - comment.range[0] + - "/*".length + - (match ? match.index + 1 : 0) - ); - } -}; +/** + * @fileoverview Common utils for AST. + * @author Gyandeep Singh + */ + +"use strict"; + +const esutils = require("esutils"); +const espree = require("espree"); +const lodash = require("lodash"); + +const breakableTypePattern = /^(?:(?:Do)?While|For(?:In|Of)?|Switch)Statement$/u; +const lineBreakPattern = /\r\n|[\r\n\u2028\u2029]/u; +const shebangPattern = /^#!([^\r\n]+)/u; + +/** + * Creates a version of the `lineBreakPattern` regex with the global flag. + * Global regexes are mutable, so this needs to be a function instead of a constant. + * @returns {RegExp} A global regular expression that matches line terminators + */ +function createGlobalLinebreakMatcher() { + return new RegExp(lineBreakPattern.source, "gu"); +} + +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +const anyFunctionPattern = /^(?:Function(?:Declaration|Expression)|ArrowFunctionExpression)$/u; +const anyLoopPattern = /^(?:DoWhile|For|ForIn|ForOf|While)Statement$/u; +const arrayOrTypedArrayPattern = /Array$/u; +const arrayMethodPattern = /^(?:every|filter|find|findIndex|forEach|map|some)$/u; +const bindOrCallOrApplyPattern = /^(?:bind|call|apply)$/u; +const thisTagPattern = /^[\s*]*@this/mu; + + +const COMMENTS_IGNORE_PATTERN = /^\s*(?:eslint|jshint\s+|jslint\s+|istanbul\s+|globals?\s+|exported\s+|jscs)/u; +const LINEBREAKS = new Set(["\r\n", "\r", "\n", "\u2028", "\u2029"]); + +// A set of node types that can contain a list of statements +const STATEMENT_LIST_PARENTS = new Set(["Program", "BlockStatement", "SwitchCase"]); + +/** + * Checks reference if is non initializer and writable. + * @param {Reference} reference A reference to check. + * @param {int} index The index of the reference in the references. + * @param {Reference[]} references The array that the reference belongs to. + * @returns {boolean} Success/Failure + * @private + */ +function isModifyingReference(reference, index, references) { + const identifier = reference.identifier; + + /* + * Destructuring assignments can have multiple default value, so + * possibly there are multiple writeable references for the same + * identifier. + */ + const modifyingDifferentIdentifier = index === 0 || + references[index - 1].identifier !== identifier; + + return (identifier && + reference.init === false && + reference.isWrite() && + modifyingDifferentIdentifier + ); +} + +/** + * Checks whether the given string starts with uppercase or not. + * @param {string} s The string to check. + * @returns {boolean} `true` if the string starts with uppercase. + */ +function startsWithUpperCase(s) { + return s[0] !== s[0].toLocaleLowerCase(); +} + +/** + * Checks whether or not a node is a constructor. + * @param {ASTNode} node A function node to check. + * @returns {boolean} Wehether or not a node is a constructor. + */ +function isES5Constructor(node) { + return (node.id && startsWithUpperCase(node.id.name)); +} + +/** + * Finds a function node from ancestors of a node. + * @param {ASTNode} node A start node to find. + * @returns {Node|null} A found function node. + */ +function getUpperFunction(node) { + for (let currentNode = node; currentNode; currentNode = currentNode.parent) { + if (anyFunctionPattern.test(currentNode.type)) { + return currentNode; + } + } + return null; +} + +/** + * Checks whether a given node is a function node or not. + * The following types are function nodes: + * + * - ArrowFunctionExpression + * - FunctionDeclaration + * - FunctionExpression + * @param {ASTNode|null} node A node to check. + * @returns {boolean} `true` if the node is a function node. + */ +function isFunction(node) { + return Boolean(node && anyFunctionPattern.test(node.type)); +} + +/** + * Checks whether a given node is a loop node or not. + * The following types are loop nodes: + * + * - DoWhileStatement + * - ForInStatement + * - ForOfStatement + * - ForStatement + * - WhileStatement + * @param {ASTNode|null} node A node to check. + * @returns {boolean} `true` if the node is a loop node. + */ +function isLoop(node) { + return Boolean(node && anyLoopPattern.test(node.type)); +} + +/** + * Checks whether the given node is in a loop or not. + * @param {ASTNode} node The node to check. + * @returns {boolean} `true` if the node is in a loop. + */ +function isInLoop(node) { + for (let currentNode = node; currentNode && !isFunction(currentNode); currentNode = currentNode.parent) { + if (isLoop(currentNode)) { + return true; + } + } + + return false; +} + +/** + * Checks whether or not a node is `null` or `undefined`. + * @param {ASTNode} node A node to check. + * @returns {boolean} Whether or not the node is a `null` or `undefined`. + * @public + */ +function isNullOrUndefined(node) { + return ( + module.exports.isNullLiteral(node) || + (node.type === "Identifier" && node.name === "undefined") || + (node.type === "UnaryExpression" && node.operator === "void") + ); +} + +/** + * Checks whether or not a node is callee. + * @param {ASTNode} node A node to check. + * @returns {boolean} Whether or not the node is callee. + */ +function isCallee(node) { + return node.parent.type === "CallExpression" && node.parent.callee === node; +} + +/** + * Checks whether or not a node is `Reflect.apply`. + * @param {ASTNode} node A node to check. + * @returns {boolean} Whether or not the node is a `Reflect.apply`. + */ +function isReflectApply(node) { + return ( + node.type === "MemberExpression" && + node.object.type === "Identifier" && + node.object.name === "Reflect" && + node.property.type === "Identifier" && + node.property.name === "apply" && + node.computed === false + ); +} + +/** + * Checks whether or not a node is `Array.from`. + * @param {ASTNode} node A node to check. + * @returns {boolean} Whether or not the node is a `Array.from`. + */ +function isArrayFromMethod(node) { + return ( + node.type === "MemberExpression" && + node.object.type === "Identifier" && + arrayOrTypedArrayPattern.test(node.object.name) && + node.property.type === "Identifier" && + node.property.name === "from" && + node.computed === false + ); +} + +/** + * Checks whether or not a node is a method which has `thisArg`. + * @param {ASTNode} node A node to check. + * @returns {boolean} Whether or not the node is a method which has `thisArg`. + */ +function isMethodWhichHasThisArg(node) { + for ( + let currentNode = node; + currentNode.type === "MemberExpression" && !currentNode.computed; + currentNode = currentNode.property + ) { + if (currentNode.property.type === "Identifier") { + return arrayMethodPattern.test(currentNode.property.name); + } + } + + return false; +} + +/** + * Creates the negate function of the given function. + * @param {Function} f The function to negate. + * @returns {Function} Negated function. + */ +function negate(f) { + return token => !f(token); +} + +/** + * Checks whether or not a node has a `@this` tag in its comments. + * @param {ASTNode} node A node to check. + * @param {SourceCode} sourceCode A SourceCode instance to get comments. + * @returns {boolean} Whether or not the node has a `@this` tag in its comments. + */ +function hasJSDocThisTag(node, sourceCode) { + const jsdocComment = sourceCode.getJSDocComment(node); + + if (jsdocComment && thisTagPattern.test(jsdocComment.value)) { + return true; + } + + // Checks `@this` in its leading comments for callbacks, + // because callbacks don't have its JSDoc comment. + // e.g. + // sinon.test(/* @this sinon.Sandbox */function() { this.spy(); }); + return sourceCode.getCommentsBefore(node).some(comment => thisTagPattern.test(comment.value)); +} + +/** + * Determines if a node is surrounded by parentheses. + * @param {SourceCode} sourceCode The ESLint source code object + * @param {ASTNode} node The node to be checked. + * @returns {boolean} True if the node is parenthesised. + * @private + */ +function isParenthesised(sourceCode, node) { + const previousToken = sourceCode.getTokenBefore(node), + nextToken = sourceCode.getTokenAfter(node); + + return Boolean(previousToken && nextToken) && + previousToken.value === "(" && previousToken.range[1] <= node.range[0] && + nextToken.value === ")" && nextToken.range[0] >= node.range[1]; +} + +/** + * Checks if the given token is an arrow token or not. + * @param {Token} token The token to check. + * @returns {boolean} `true` if the token is an arrow token. + */ +function isArrowToken(token) { + return token.value === "=>" && token.type === "Punctuator"; +} + +/** + * Checks if the given token is a comma token or not. + * @param {Token} token The token to check. + * @returns {boolean} `true` if the token is a comma token. + */ +function isCommaToken(token) { + return token.value === "," && token.type === "Punctuator"; +} + +/** + * Checks if the given token is a semicolon token or not. + * @param {Token} token The token to check. + * @returns {boolean} `true` if the token is a semicolon token. + */ +function isSemicolonToken(token) { + return token.value === ";" && token.type === "Punctuator"; +} + +/** + * Checks if the given token is a colon token or not. + * @param {Token} token The token to check. + * @returns {boolean} `true` if the token is a colon token. + */ +function isColonToken(token) { + return token.value === ":" && token.type === "Punctuator"; +} + +/** + * Checks if the given token is an opening parenthesis token or not. + * @param {Token} token The token to check. + * @returns {boolean} `true` if the token is an opening parenthesis token. + */ +function isOpeningParenToken(token) { + return token.value === "(" && token.type === "Punctuator"; +} + +/** + * Checks if the given token is a closing parenthesis token or not. + * @param {Token} token The token to check. + * @returns {boolean} `true` if the token is a closing parenthesis token. + */ +function isClosingParenToken(token) { + return token.value === ")" && token.type === "Punctuator"; +} + +/** + * Checks if the given token is an opening square bracket token or not. + * @param {Token} token The token to check. + * @returns {boolean} `true` if the token is an opening square bracket token. + */ +function isOpeningBracketToken(token) { + return token.value === "[" && token.type === "Punctuator"; +} + +/** + * Checks if the given token is a closing square bracket token or not. + * @param {Token} token The token to check. + * @returns {boolean} `true` if the token is a closing square bracket token. + */ +function isClosingBracketToken(token) { + return token.value === "]" && token.type === "Punctuator"; +} + +/** + * Checks if the given token is an opening brace token or not. + * @param {Token} token The token to check. + * @returns {boolean} `true` if the token is an opening brace token. + */ +function isOpeningBraceToken(token) { + return token.value === "{" && token.type === "Punctuator"; +} + +/** + * Checks if the given token is a closing brace token or not. + * @param {Token} token The token to check. + * @returns {boolean} `true` if the token is a closing brace token. + */ +function isClosingBraceToken(token) { + return token.value === "}" && token.type === "Punctuator"; +} + +/** + * Checks if the given token is a comment token or not. + * @param {Token} token The token to check. + * @returns {boolean} `true` if the token is a comment token. + */ +function isCommentToken(token) { + return token.type === "Line" || token.type === "Block" || token.type === "Shebang"; +} + +/** + * Checks if the given token is a keyword token or not. + * @param {Token} token The token to check. + * @returns {boolean} `true` if the token is a keyword token. + */ +function isKeywordToken(token) { + return token.type === "Keyword"; +} + +/** + * Gets the `(` token of the given function node. + * @param {ASTNode} node The function node to get. + * @param {SourceCode} sourceCode The source code object to get tokens. + * @returns {Token} `(` token. + */ +function getOpeningParenOfParams(node, sourceCode) { + return node.id + ? sourceCode.getTokenAfter(node.id, isOpeningParenToken) + : sourceCode.getFirstToken(node, isOpeningParenToken); +} + +/** + * Checks whether or not the tokens of two given nodes are same. + * @param {ASTNode} left A node 1 to compare. + * @param {ASTNode} right A node 2 to compare. + * @param {SourceCode} sourceCode The ESLint source code object. + * @returns {boolean} the source code for the given node. + */ +function equalTokens(left, right, sourceCode) { + const tokensL = sourceCode.getTokens(left); + const tokensR = sourceCode.getTokens(right); + + if (tokensL.length !== tokensR.length) { + return false; + } + for (let i = 0; i < tokensL.length; ++i) { + if (tokensL[i].type !== tokensR[i].type || + tokensL[i].value !== tokensR[i].value + ) { + return false; + } + } + + return true; +} + +//------------------------------------------------------------------------------ +// Public Interface +//------------------------------------------------------------------------------ + +module.exports = { + COMMENTS_IGNORE_PATTERN, + LINEBREAKS, + LINEBREAK_MATCHER: lineBreakPattern, + SHEBANG_MATCHER: shebangPattern, + STATEMENT_LIST_PARENTS, + + /** + * Determines whether two adjacent tokens are on the same line. + * @param {Object} left The left token object. + * @param {Object} right The right token object. + * @returns {boolean} Whether or not the tokens are on the same line. + * @public + */ + isTokenOnSameLine(left, right) { + return left.loc.end.line === right.loc.start.line; + }, + + isNullOrUndefined, + isCallee, + isES5Constructor, + getUpperFunction, + isFunction, + isLoop, + isInLoop, + isArrayFromMethod, + isParenthesised, + createGlobalLinebreakMatcher, + equalTokens, + + isArrowToken, + isClosingBraceToken, + isClosingBracketToken, + isClosingParenToken, + isColonToken, + isCommaToken, + isCommentToken, + isKeywordToken, + isNotClosingBraceToken: negate(isClosingBraceToken), + isNotClosingBracketToken: negate(isClosingBracketToken), + isNotClosingParenToken: negate(isClosingParenToken), + isNotColonToken: negate(isColonToken), + isNotCommaToken: negate(isCommaToken), + isNotOpeningBraceToken: negate(isOpeningBraceToken), + isNotOpeningBracketToken: negate(isOpeningBracketToken), + isNotOpeningParenToken: negate(isOpeningParenToken), + isNotSemicolonToken: negate(isSemicolonToken), + isOpeningBraceToken, + isOpeningBracketToken, + isOpeningParenToken, + isSemicolonToken, + + /** + * Checks whether or not a given node is a string literal. + * @param {ASTNode} node A node to check. + * @returns {boolean} `true` if the node is a string literal. + */ + isStringLiteral(node) { + return ( + (node.type === "Literal" && typeof node.value === "string") || + node.type === "TemplateLiteral" + ); + }, + + /** + * Checks whether a given node is a breakable statement or not. + * The node is breakable if the node is one of the following type: + * + * - DoWhileStatement + * - ForInStatement + * - ForOfStatement + * - ForStatement + * - SwitchStatement + * - WhileStatement + * @param {ASTNode} node A node to check. + * @returns {boolean} `true` if the node is breakable. + */ + isBreakableStatement(node) { + return breakableTypePattern.test(node.type); + }, + + /** + * Gets references which are non initializer and writable. + * @param {Reference[]} references An array of references. + * @returns {Reference[]} An array of only references which are non initializer and writable. + * @public + */ + getModifyingReferences(references) { + return references.filter(isModifyingReference); + }, + + /** + * Validate that a string passed in is surrounded by the specified character + * @param {string} val The text to check. + * @param {string} character The character to see if it's surrounded by. + * @returns {boolean} True if the text is surrounded by the character, false if not. + * @private + */ + isSurroundedBy(val, character) { + return val[0] === character && val[val.length - 1] === character; + }, + + /** + * Returns whether the provided node is an ESLint directive comment or not + * @param {Line|Block} node The comment token to be checked + * @returns {boolean} `true` if the node is an ESLint directive comment + */ + isDirectiveComment(node) { + const comment = node.value.trim(); + + return ( + node.type === "Line" && comment.indexOf("eslint-") === 0 || + node.type === "Block" && ( + comment.indexOf("global ") === 0 || + comment.indexOf("eslint ") === 0 || + comment.indexOf("eslint-") === 0 + ) + ); + }, + + /** + * Gets the trailing statement of a given node. + * + * if (code) + * consequent; + * + * When taking this `IfStatement`, returns `consequent;` statement. + * @param {ASTNode} A node to get. + * @returns {ASTNode|null} The trailing statement's node. + */ + getTrailingStatement: esutils.ast.trailingStatement, + + /** + * Finds the variable by a given name in a given scope and its upper scopes. + * @param {eslint-scope.Scope} initScope A scope to start find. + * @param {string} name A variable name to find. + * @returns {eslint-scope.Variable|null} A found variable or `null`. + */ + getVariableByName(initScope, name) { + let scope = initScope; + + while (scope) { + const variable = scope.set.get(name); + + if (variable) { + return variable; + } + + scope = scope.upper; + } + + return null; + }, + + /** + * Checks whether or not a given function node is the default `this` binding. + * + * First, this checks the node: + * + * - The function name does not start with uppercase (it's a constructor). + * - The function does not have a JSDoc comment that has a @this tag. + * + * Next, this checks the location of the node. + * If the location is below, this judges `this` is valid. + * + * - The location is not on an object literal. + * - The location is not assigned to a variable which starts with an uppercase letter. + * - The location is not on an ES2015 class. + * - Its `bind`/`call`/`apply` method is not called directly. + * - The function is not a callback of array methods (such as `.forEach()`) if `thisArg` is given. + * @param {ASTNode} node A function node to check. + * @param {SourceCode} sourceCode A SourceCode instance to get comments. + * @returns {boolean} The function node is the default `this` binding. + */ + isDefaultThisBinding(node, sourceCode) { + if (isES5Constructor(node) || hasJSDocThisTag(node, sourceCode)) { + return false; + } + const isAnonymous = node.id === null; + let currentNode = node; + + while (currentNode) { + const parent = currentNode.parent; + + switch (parent.type) { + + /* + * Looks up the destination. + * e.g., obj.foo = nativeFoo || function foo() { ... }; + */ + case "LogicalExpression": + case "ConditionalExpression": + currentNode = parent; + break; + + /* + * If the upper function is IIFE, checks the destination of the return value. + * e.g. + * obj.foo = (function() { + * // setup... + * return function foo() { ... }; + * })(); + * obj.foo = (() => + * function foo() { ... } + * )(); + */ + case "ReturnStatement": { + const func = getUpperFunction(parent); + + if (func === null || !isCallee(func)) { + return true; + } + currentNode = func.parent; + break; + } + case "ArrowFunctionExpression": + if (currentNode !== parent.body || !isCallee(parent)) { + return true; + } + currentNode = parent.parent; + break; + + /* + * e.g. + * var obj = { foo() { ... } }; + * var obj = { foo: function() { ... } }; + * class A { constructor() { ... } } + * class A { foo() { ... } } + * class A { get foo() { ... } } + * class A { set foo() { ... } } + * class A { static foo() { ... } } + */ + case "Property": + case "MethodDefinition": + return parent.value !== currentNode; + + /* + * e.g. + * obj.foo = function foo() { ... }; + * Foo = function() { ... }; + * [obj.foo = function foo() { ... }] = a; + * [Foo = function() { ... }] = a; + */ + case "AssignmentExpression": + case "AssignmentPattern": + if (parent.left.type === "MemberExpression") { + return false; + } + if ( + isAnonymous && + parent.left.type === "Identifier" && + startsWithUpperCase(parent.left.name) + ) { + return false; + } + return true; + + /* + * e.g. + * var Foo = function() { ... }; + */ + case "VariableDeclarator": + return !( + isAnonymous && + parent.init === currentNode && + parent.id.type === "Identifier" && + startsWithUpperCase(parent.id.name) + ); + + /* + * e.g. + * var foo = function foo() { ... }.bind(obj); + * (function foo() { ... }).call(obj); + * (function foo() { ... }).apply(obj, []); + */ + case "MemberExpression": + return ( + parent.object !== currentNode || + parent.property.type !== "Identifier" || + !bindOrCallOrApplyPattern.test(parent.property.name) || + !isCallee(parent) || + parent.parent.arguments.length === 0 || + isNullOrUndefined(parent.parent.arguments[0]) + ); + + /* + * e.g. + * Reflect.apply(function() {}, obj, []); + * Array.from([], function() {}, obj); + * list.forEach(function() {}, obj); + */ + case "CallExpression": + if (isReflectApply(parent.callee)) { + return ( + parent.arguments.length !== 3 || + parent.arguments[0] !== currentNode || + isNullOrUndefined(parent.arguments[1]) + ); + } + if (isArrayFromMethod(parent.callee)) { + return ( + parent.arguments.length !== 3 || + parent.arguments[1] !== currentNode || + isNullOrUndefined(parent.arguments[2]) + ); + } + if (isMethodWhichHasThisArg(parent.callee)) { + return ( + parent.arguments.length !== 2 || + parent.arguments[0] !== currentNode || + isNullOrUndefined(parent.arguments[1]) + ); + } + return true; + + // Otherwise `this` is default. + default: + return true; + } + } + + /* istanbul ignore next */ + return true; + }, + + /** + * Get the precedence level based on the node type + * @param {ASTNode} node node to evaluate + * @returns {int} precedence level + * @private + */ + getPrecedence(node) { + switch (node.type) { + case "SequenceExpression": + return 0; + + case "AssignmentExpression": + case "ArrowFunctionExpression": + case "YieldExpression": + return 1; + + case "ConditionalExpression": + return 3; + + case "LogicalExpression": + switch (node.operator) { + case "||": + return 4; + case "&&": + return 5; + + // no default + } + + /* falls through */ + + case "BinaryExpression": + + switch (node.operator) { + case "|": + return 6; + case "^": + return 7; + case "&": + return 8; + case "==": + case "!=": + case "===": + case "!==": + return 9; + case "<": + case "<=": + case ">": + case ">=": + case "in": + case "instanceof": + return 10; + case "<<": + case ">>": + case ">>>": + return 11; + case "+": + case "-": + return 12; + case "*": + case "/": + case "%": + return 13; + case "**": + return 15; + + // no default + } + + /* falls through */ + + case "UnaryExpression": + case "AwaitExpression": + return 16; + + case "UpdateExpression": + return 17; + + case "CallExpression": + return 18; + + case "NewExpression": + return 19; + + default: + return 20; + } + }, + + /** + * Checks whether the given node is an empty block node or not. + * @param {ASTNode|null} node The node to check. + * @returns {boolean} `true` if the node is an empty block. + */ + isEmptyBlock(node) { + return Boolean(node && node.type === "BlockStatement" && node.body.length === 0); + }, + + /** + * Checks whether the given node is an empty function node or not. + * @param {ASTNode|null} node The node to check. + * @returns {boolean} `true` if the node is an empty function. + */ + isEmptyFunction(node) { + return isFunction(node) && module.exports.isEmptyBlock(node.body); + }, + + /** + * Gets the property name of a given node. + * The node can be a MemberExpression, a Property, or a MethodDefinition. + * + * If the name is dynamic, this returns `null`. + * + * For examples: + * + * a.b // => "b" + * a["b"] // => "b" + * a['b'] // => "b" + * a[`b`] // => "b" + * a[100] // => "100" + * a[b] // => null + * a["a" + "b"] // => null + * a[tag`b`] // => null + * a[`${b}`] // => null + * + * let a = {b: 1} // => "b" + * let a = {["b"]: 1} // => "b" + * let a = {['b']: 1} // => "b" + * let a = {[`b`]: 1} // => "b" + * let a = {[100]: 1} // => "100" + * let a = {[b]: 1} // => null + * let a = {["a" + "b"]: 1} // => null + * let a = {[tag`b`]: 1} // => null + * let a = {[`${b}`]: 1} // => null + * @param {ASTNode} node The node to get. + * @returns {string|null} The property name if static. Otherwise, null. + */ + getStaticPropertyName(node) { + let prop; + + switch (node && node.type) { + case "Property": + case "MethodDefinition": + prop = node.key; + break; + + case "MemberExpression": + prop = node.property; + break; + + // no default + } + + switch (prop && prop.type) { + case "Literal": + return String(prop.value); + + case "TemplateLiteral": + if (prop.expressions.length === 0 && prop.quasis.length === 1) { + return prop.quasis[0].value.cooked; + } + break; + + case "Identifier": + if (!node.computed) { + return prop.name; + } + break; + + // no default + } + + return null; + }, + + /** + * Get directives from directive prologue of a Program or Function node. + * @param {ASTNode} node The node to check. + * @returns {ASTNode[]} The directives found in the directive prologue. + */ + getDirectivePrologue(node) { + const directives = []; + + // Directive prologues only occur at the top of files or functions. + if ( + node.type === "Program" || + node.type === "FunctionDeclaration" || + node.type === "FunctionExpression" || + + /* + * Do not check arrow functions with implicit return. + * `() => "use strict";` returns the string `"use strict"`. + */ + (node.type === "ArrowFunctionExpression" && node.body.type === "BlockStatement") + ) { + const statements = node.type === "Program" ? node.body : node.body.body; + + for (const statement of statements) { + if ( + statement.type === "ExpressionStatement" && + statement.expression.type === "Literal" + ) { + directives.push(statement); + } else { + break; + } + } + } + + return directives; + }, + + + /** + * Determines whether this node is a decimal integer literal. If a node is a decimal integer literal, a dot added + * after the node will be parsed as a decimal point, rather than a property-access dot. + * @param {ASTNode} node The node to check. + * @returns {boolean} `true` if this node is a decimal integer. + * @example + * + * 5 // true + * 5. // false + * 5.0 // false + * 05 // false + * 0x5 // false + * 0b101 // false + * 0o5 // false + * 5e0 // false + * '5' // false + */ + isDecimalInteger(node) { + return node.type === "Literal" && typeof node.value === "number" && /^(0|[1-9]\d*)$/u.test(node.raw); + }, + + /** + * Gets the name and kind of the given function node. + * + * - `function foo() {}` .................... `function 'foo'` + * - `(function foo() {})` .................. `function 'foo'` + * - `(function() {})` ...................... `function` + * - `function* foo() {}` ................... `generator function 'foo'` + * - `(function* foo() {})` ................. `generator function 'foo'` + * - `(function*() {})` ..................... `generator function` + * - `() => {}` ............................. `arrow function` + * - `async () => {}` ....................... `async arrow function` + * - `({ foo: function foo() {} })` ......... `method 'foo'` + * - `({ foo: function() {} })` ............. `method 'foo'` + * - `({ ['foo']: function() {} })` ......... `method 'foo'` + * - `({ [foo]: function() {} })` ........... `method` + * - `({ foo() {} })` ....................... `method 'foo'` + * - `({ foo: function* foo() {} })` ........ `generator method 'foo'` + * - `({ foo: function*() {} })` ............ `generator method 'foo'` + * - `({ ['foo']: function*() {} })` ........ `generator method 'foo'` + * - `({ [foo]: function*() {} })` .......... `generator method` + * - `({ *foo() {} })` ...................... `generator method 'foo'` + * - `({ foo: async function foo() {} })` ... `async method 'foo'` + * - `({ foo: async function() {} })` ....... `async method 'foo'` + * - `({ ['foo']: async function() {} })` ... `async method 'foo'` + * - `({ [foo]: async function() {} })` ..... `async method` + * - `({ async foo() {} })` ................. `async method 'foo'` + * - `({ get foo() {} })` ................... `getter 'foo'` + * - `({ set foo(a) {} })` .................. `setter 'foo'` + * - `class A { constructor() {} }` ......... `constructor` + * - `class A { foo() {} }` ................. `method 'foo'` + * - `class A { *foo() {} }` ................ `generator method 'foo'` + * - `class A { async foo() {} }` ........... `async method 'foo'` + * - `class A { ['foo']() {} }` ............. `method 'foo'` + * - `class A { *['foo']() {} }` ............ `generator method 'foo'` + * - `class A { async ['foo']() {} }` ....... `async method 'foo'` + * - `class A { [foo]() {} }` ............... `method` + * - `class A { *[foo]() {} }` .............. `generator method` + * - `class A { async [foo]() {} }` ......... `async method` + * - `class A { get foo() {} }` ............. `getter 'foo'` + * - `class A { set foo(a) {} }` ............ `setter 'foo'` + * - `class A { static foo() {} }` .......... `static method 'foo'` + * - `class A { static *foo() {} }` ......... `static generator method 'foo'` + * - `class A { static async foo() {} }` .... `static async method 'foo'` + * - `class A { static get foo() {} }` ...... `static getter 'foo'` + * - `class A { static set foo(a) {} }` ..... `static setter 'foo'` + * @param {ASTNode} node The function node to get. + * @returns {string} The name and kind of the function node. + */ + getFunctionNameWithKind(node) { + const parent = node.parent; + const tokens = []; + + if (parent.type === "MethodDefinition" && parent.static) { + tokens.push("static"); + } + if (node.async) { + tokens.push("async"); + } + if (node.generator) { + tokens.push("generator"); + } + + if (node.type === "ArrowFunctionExpression") { + tokens.push("arrow", "function"); + } else if (parent.type === "Property" || parent.type === "MethodDefinition") { + if (parent.kind === "constructor") { + return "constructor"; + } + if (parent.kind === "get") { + tokens.push("getter"); + } else if (parent.kind === "set") { + tokens.push("setter"); + } else { + tokens.push("method"); + } + } else { + tokens.push("function"); + } + + if (node.id) { + tokens.push(`'${node.id.name}'`); + } else { + const name = module.exports.getStaticPropertyName(parent); + + if (name) { + tokens.push(`'${name}'`); + } + } + + return tokens.join(" "); + }, + + /** + * Gets the location of the given function node for reporting. + * + * - `function foo() {}` + * ^^^^^^^^^^^^ + * - `(function foo() {})` + * ^^^^^^^^^^^^ + * - `(function() {})` + * ^^^^^^^^ + * - `function* foo() {}` + * ^^^^^^^^^^^^^ + * - `(function* foo() {})` + * ^^^^^^^^^^^^^ + * - `(function*() {})` + * ^^^^^^^^^ + * - `() => {}` + * ^^ + * - `async () => {}` + * ^^ + * - `({ foo: function foo() {} })` + * ^^^^^^^^^^^^^^^^^ + * - `({ foo: function() {} })` + * ^^^^^^^^^^^^^ + * - `({ ['foo']: function() {} })` + * ^^^^^^^^^^^^^^^^^ + * - `({ [foo]: function() {} })` + * ^^^^^^^^^^^^^^^ + * - `({ foo() {} })` + * ^^^ + * - `({ foo: function* foo() {} })` + * ^^^^^^^^^^^^^^^^^^ + * - `({ foo: function*() {} })` + * ^^^^^^^^^^^^^^ + * - `({ ['foo']: function*() {} })` + * ^^^^^^^^^^^^^^^^^^ + * - `({ [foo]: function*() {} })` + * ^^^^^^^^^^^^^^^^ + * - `({ *foo() {} })` + * ^^^^ + * - `({ foo: async function foo() {} })` + * ^^^^^^^^^^^^^^^^^^^^^^^ + * - `({ foo: async function() {} })` + * ^^^^^^^^^^^^^^^^^^^ + * - `({ ['foo']: async function() {} })` + * ^^^^^^^^^^^^^^^^^^^^^^^ + * - `({ [foo]: async function() {} })` + * ^^^^^^^^^^^^^^^^^^^^^ + * - `({ async foo() {} })` + * ^^^^^^^^^ + * - `({ get foo() {} })` + * ^^^^^^^ + * - `({ set foo(a) {} })` + * ^^^^^^^ + * - `class A { constructor() {} }` + * ^^^^^^^^^^^ + * - `class A { foo() {} }` + * ^^^ + * - `class A { *foo() {} }` + * ^^^^ + * - `class A { async foo() {} }` + * ^^^^^^^^^ + * - `class A { ['foo']() {} }` + * ^^^^^^^ + * - `class A { *['foo']() {} }` + * ^^^^^^^^ + * - `class A { async ['foo']() {} }` + * ^^^^^^^^^^^^^ + * - `class A { [foo]() {} }` + * ^^^^^ + * - `class A { *[foo]() {} }` + * ^^^^^^ + * - `class A { async [foo]() {} }` + * ^^^^^^^^^^^ + * - `class A { get foo() {} }` + * ^^^^^^^ + * - `class A { set foo(a) {} }` + * ^^^^^^^ + * - `class A { static foo() {} }` + * ^^^^^^^^^^ + * - `class A { static *foo() {} }` + * ^^^^^^^^^^^ + * - `class A { static async foo() {} }` + * ^^^^^^^^^^^^^^^^ + * - `class A { static get foo() {} }` + * ^^^^^^^^^^^^^^ + * - `class A { static set foo(a) {} }` + * ^^^^^^^^^^^^^^ + * @param {ASTNode} node The function node to get. + * @param {SourceCode} sourceCode The source code object to get tokens. + * @returns {string} The location of the function node for reporting. + */ + getFunctionHeadLoc(node, sourceCode) { + const parent = node.parent; + let start = null; + let end = null; + + if (node.type === "ArrowFunctionExpression") { + const arrowToken = sourceCode.getTokenBefore(node.body, isArrowToken); + + start = arrowToken.loc.start; + end = arrowToken.loc.end; + } else if (parent.type === "Property" || parent.type === "MethodDefinition") { + start = parent.loc.start; + end = getOpeningParenOfParams(node, sourceCode).loc.start; + } else { + start = node.loc.start; + end = getOpeningParenOfParams(node, sourceCode).loc.start; + } + + return { + start: Object.assign({}, start), + end: Object.assign({}, end) + }; + }, + + /** + * Gets the parenthesized text of a node. This is similar to sourceCode.getText(node), but it also includes any parentheses + * surrounding the node. + * @param {SourceCode} sourceCode The source code object + * @param {ASTNode} node An expression node + * @returns {string} The text representing the node, with all surrounding parentheses included + */ + getParenthesisedText(sourceCode, node) { + let leftToken = sourceCode.getFirstToken(node); + let rightToken = sourceCode.getLastToken(node); + + while ( + sourceCode.getTokenBefore(leftToken) && + sourceCode.getTokenBefore(leftToken).type === "Punctuator" && + sourceCode.getTokenBefore(leftToken).value === "(" && + sourceCode.getTokenAfter(rightToken) && + sourceCode.getTokenAfter(rightToken).type === "Punctuator" && + sourceCode.getTokenAfter(rightToken).value === ")" + ) { + leftToken = sourceCode.getTokenBefore(leftToken); + rightToken = sourceCode.getTokenAfter(rightToken); + } + + return sourceCode.getText().slice(leftToken.range[0], rightToken.range[1]); + }, + + /* + * Determine if a node has a possiblity to be an Error object + * @param {ASTNode} node ASTNode to check + * @returns {boolean} True if there is a chance it contains an Error obj + */ + couldBeError(node) { + switch (node.type) { + case "Identifier": + case "CallExpression": + case "NewExpression": + case "MemberExpression": + case "TaggedTemplateExpression": + case "YieldExpression": + case "AwaitExpression": + return true; // possibly an error object. + + case "AssignmentExpression": + return module.exports.couldBeError(node.right); + + case "SequenceExpression": { + const exprs = node.expressions; + + return exprs.length !== 0 && module.exports.couldBeError(exprs[exprs.length - 1]); + } + + case "LogicalExpression": + return module.exports.couldBeError(node.left) || module.exports.couldBeError(node.right); + + case "ConditionalExpression": + return module.exports.couldBeError(node.consequent) || module.exports.couldBeError(node.alternate); + + default: + return false; + } + }, + + /** + * Determines whether the given node is a `null` literal. + * @param {ASTNode} node The node to check + * @returns {boolean} `true` if the node is a `null` literal + */ + isNullLiteral(node) { + + /* + * Checking `node.value === null` does not guarantee that a literal is a null literal. + * When parsing values that cannot be represented in the current environment (e.g. unicode + * regexes in Node 4), `node.value` is set to `null` because it wouldn't be possible to + * set `node.value` to a unicode regex. To make sure a literal is actually `null`, check + * `node.regex` instead. Also see: https://github.com/eslint/eslint/issues/8020 + */ + return node.type === "Literal" && node.value === null && !node.regex; + }, + + /** + * Determines whether two tokens can safely be placed next to each other without merging into a single token + * @param {Token|string} leftValue The left token. If this is a string, it will be tokenized and the last token will be used. + * @param {Token|string} rightValue The right token. If this is a string, it will be tokenized and the first token will be used. + * @returns {boolean} If the tokens cannot be safely placed next to each other, returns `false`. If the tokens can be placed + * next to each other, behavior is undefined (although it should return `true` in most cases). + */ + canTokensBeAdjacent(leftValue, rightValue) { + let leftToken; + + if (typeof leftValue === "string") { + const leftTokens = espree.tokenize(leftValue, { ecmaVersion: 2015 }); + + leftToken = leftTokens[leftTokens.length - 1]; + } else { + leftToken = leftValue; + } + + const rightToken = typeof rightValue === "string" ? espree.tokenize(rightValue, { ecmaVersion: 2015 })[0] : rightValue; + + if (leftToken.type === "Punctuator" || rightToken.type === "Punctuator") { + if (leftToken.type === "Punctuator" && rightToken.type === "Punctuator") { + const PLUS_TOKENS = new Set(["+", "++"]); + const MINUS_TOKENS = new Set(["-", "--"]); + + return !( + PLUS_TOKENS.has(leftToken.value) && PLUS_TOKENS.has(rightToken.value) || + MINUS_TOKENS.has(leftToken.value) && MINUS_TOKENS.has(rightToken.value) + ); + } + return true; + } + + if ( + leftToken.type === "String" || rightToken.type === "String" || + leftToken.type === "Template" || rightToken.type === "Template" + ) { + return true; + } + + if (leftToken.type !== "Numeric" && rightToken.type === "Numeric" && rightToken.value.startsWith(".")) { + return true; + } + + return false; + }, + + /** + * Get the `loc` object of a given name in a `/*globals` directive comment. + * @param {SourceCode} sourceCode The source code to convert index to loc. + * @param {Comment} comment The `/*globals` directive comment which include the name. + * @param {string} name The name to find. + * @returns {SourceLocation} The `loc` object. + */ + getNameLocationInGlobalDirectiveComment(sourceCode, comment, name) { + const namePattern = new RegExp(`[\\s,]${lodash.escapeRegExp(name)}(?:$|[\\s,:])`, "gu"); + + // To ignore the first text "global". + namePattern.lastIndex = comment.value.indexOf("global") + 6; + + // Search a given variable name. + const match = namePattern.exec(comment.value); + + // Convert the index to loc. + return sourceCode.getLocFromIndex( + comment.range[0] + + "/*".length + + (match ? match.index + 1 : 0) + ); + } +}; diff --git a/packages/web/package.json b/packages/web/package.json index 42ed04c..bd87246 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,57 +1,57 @@ -{ - "name": "eslint-plugin-web", - "version": "0.0.2", - "author": "唯然=11.10.1" - }, - "eslintConfig": { - "extends": "eslint" - } -} +{ + "name": "eslint-plugin-web", + "version": "0.0.2", + "author": "唯然=11.10.1" + }, + "eslintConfig": { + "extends": "eslint" + } +} diff --git a/packages/web/tests/lib/rules/no-alert.js b/packages/web/tests/lib/rules/no-alert.js index 2467d9f..8f5ef1b 100644 --- a/packages/web/tests/lib/rules/no-alert.js +++ b/packages/web/tests/lib/rules/no-alert.js @@ -1,109 +1,109 @@ -/** - * @fileoverview Tests for no-alert rule. - * @author Nicholas C. Zakas - */ - -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require("../../../lib/rules/no-alert"), - { RuleTester } = require("eslint"); - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new RuleTester(); - -ruleTester.run("no-alert", rule, { - valid: [ - "a[o.k](1)", - "foo.alert(foo)", - "foo.confirm(foo)", - "foo.prompt(foo)", - "function alert() {} alert();", - "var alert = function() {}; alert();", - "function foo() { var alert = bar; alert(); }", - "function foo(alert) { alert(); }", - "var alert = function() {}; function test() { alert(); }", - "function foo() { var alert = function() {}; function test() { alert(); } }", - "function confirm() {} confirm();", - "function prompt() {} prompt();", - "window[alert]();", - "function foo() { this.alert(); }", - "function foo() { var window = bar; window.alert(); }" - ], - invalid: [ - { - code: "alert(foo)", - errors: [{ messageId: "unexpected", data: { name: "alert" }, type: "CallExpression", line: 1, column: 1 }] - }, - { - code: "window.alert(foo)", - errors: [{ messageId: "unexpected", data: { name: "alert" }, type: "CallExpression", line: 1, column: 1 }] - }, - { - code: "window['alert'](foo)", - errors: [{ messageId: "unexpected", data: { name: "alert" }, type: "CallExpression", line: 1, column: 1 }] - }, - { - code: "confirm(foo)", - errors: [{ messageId: "unexpected", data: { name: "confirm" }, type: "CallExpression", line: 1, column: 1 }] - }, - { - code: "window.confirm(foo)", - errors: [{ messageId: "unexpected", data: { name: "confirm" }, type: "CallExpression", line: 1, column: 1 }] - }, - { - code: "window['confirm'](foo)", - errors: [{ messageId: "unexpected", data: { name: "confirm" }, type: "CallExpression", line: 1, column: 1 }] - }, - { - code: "prompt(foo)", - errors: [{ messageId: "unexpected", data: { name: "prompt" }, type: "CallExpression", line: 1, column: 1 }] - }, - { - code: "window.prompt(foo)", - errors: [{ messageId: "unexpected", data: { name: "prompt" }, type: "CallExpression", line: 1, column: 1 }] - }, - { - code: "window['prompt'](foo)", - errors: [{ messageId: "unexpected", data: { name: "prompt" }, type: "CallExpression", line: 1, column: 1 }] - }, - { - code: "function alert() {} window.alert(foo)", - errors: [{ messageId: "unexpected", data: { name: "alert" }, type: "CallExpression", line: 1, column: 21 }] - }, - { - code: "var alert = function() {};\nwindow.alert(foo)", - errors: [{ messageId: "unexpected", data: { name: "alert" }, type: "CallExpression", line: 2, column: 1 }] - }, - { - code: "function foo(alert) { window.alert(); }", - errors: [{ messageId: "unexpected", data: { name: "alert" }, type: "CallExpression", line: 1, column: 23 }] - }, - { - code: "function foo() { alert(); }", - errors: [{ messageId: "unexpected", data: { name: "alert" }, type: "CallExpression", line: 1, column: 18 }] - }, - { - code: "function foo() { var alert = function() {}; }\nalert();", - errors: [{ messageId: "unexpected", data: { name: "alert" }, type: "CallExpression", line: 2, column: 1 }] - }, - { - code: "this.alert(foo)", - errors: [{ messageId: "unexpected", data: { name: "alert" }, type: "CallExpression", line: 1, column: 1 }] - }, - { - code: "this['alert'](foo)", - errors: [{ messageId: "unexpected", data: { name: "alert" }, type: "CallExpression", line: 1, column: 1 }] - }, - { - code: "function foo() { var window = bar; window.alert(); }\nwindow.alert();", - errors: [{ messageId: "unexpected", data: { name: "alert" }, type: "CallExpression", line: 2, column: 1 }] - } - ] -}); +/** + * @fileoverview Tests for no-alert rule. + * @author Nicholas C. Zakas + */ + +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require("../../../lib/rules/no-alert"), + { RuleTester } = require("eslint"); + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const ruleTester = new RuleTester(); + +ruleTester.run("no-alert", rule, { + valid: [ + "a[o.k](1)", + "foo.alert(foo)", + "foo.confirm(foo)", + "foo.prompt(foo)", + "function alert() {} alert();", + "var alert = function() {}; alert();", + "function foo() { var alert = bar; alert(); }", + "function foo(alert) { alert(); }", + "var alert = function() {}; function test() { alert(); }", + "function foo() { var alert = function() {}; function test() { alert(); } }", + "function confirm() {} confirm();", + "function prompt() {} prompt();", + "window[alert]();", + "function foo() { this.alert(); }", + "function foo() { var window = bar; window.alert(); }" + ], + invalid: [ + { + code: "alert(foo)", + errors: [{ messageId: "unexpected", data: { name: "alert" }, type: "CallExpression", line: 1, column: 1 }] + }, + { + code: "window.alert(foo)", + errors: [{ messageId: "unexpected", data: { name: "alert" }, type: "CallExpression", line: 1, column: 1 }] + }, + { + code: "window['alert'](foo)", + errors: [{ messageId: "unexpected", data: { name: "alert" }, type: "CallExpression", line: 1, column: 1 }] + }, + { + code: "confirm(foo)", + errors: [{ messageId: "unexpected", data: { name: "confirm" }, type: "CallExpression", line: 1, column: 1 }] + }, + { + code: "window.confirm(foo)", + errors: [{ messageId: "unexpected", data: { name: "confirm" }, type: "CallExpression", line: 1, column: 1 }] + }, + { + code: "window['confirm'](foo)", + errors: [{ messageId: "unexpected", data: { name: "confirm" }, type: "CallExpression", line: 1, column: 1 }] + }, + { + code: "prompt(foo)", + errors: [{ messageId: "unexpected", data: { name: "prompt" }, type: "CallExpression", line: 1, column: 1 }] + }, + { + code: "window.prompt(foo)", + errors: [{ messageId: "unexpected", data: { name: "prompt" }, type: "CallExpression", line: 1, column: 1 }] + }, + { + code: "window['prompt'](foo)", + errors: [{ messageId: "unexpected", data: { name: "prompt" }, type: "CallExpression", line: 1, column: 1 }] + }, + { + code: "function alert() {} window.alert(foo)", + errors: [{ messageId: "unexpected", data: { name: "alert" }, type: "CallExpression", line: 1, column: 21 }] + }, + { + code: "var alert = function() {};\nwindow.alert(foo)", + errors: [{ messageId: "unexpected", data: { name: "alert" }, type: "CallExpression", line: 2, column: 1 }] + }, + { + code: "function foo(alert) { window.alert(); }", + errors: [{ messageId: "unexpected", data: { name: "alert" }, type: "CallExpression", line: 1, column: 23 }] + }, + { + code: "function foo() { alert(); }", + errors: [{ messageId: "unexpected", data: { name: "alert" }, type: "CallExpression", line: 1, column: 18 }] + }, + { + code: "function foo() { var alert = function() {}; }\nalert();", + errors: [{ messageId: "unexpected", data: { name: "alert" }, type: "CallExpression", line: 2, column: 1 }] + }, + { + code: "this.alert(foo)", + errors: [{ messageId: "unexpected", data: { name: "alert" }, type: "CallExpression", line: 1, column: 1 }] + }, + { + code: "this['alert'](foo)", + errors: [{ messageId: "unexpected", data: { name: "alert" }, type: "CallExpression", line: 1, column: 1 }] + }, + { + code: "function foo() { var window = bar; window.alert(); }\nwindow.alert();", + errors: [{ messageId: "unexpected", data: { name: "alert" }, type: "CallExpression", line: 2, column: 1 }] + } + ] +}); diff --git a/packages/web/tests/lib/rules/no-script-url.js b/packages/web/tests/lib/rules/no-script-url.js index 9d6d6a0..b130482 100644 --- a/packages/web/tests/lib/rules/no-script-url.js +++ b/packages/web/tests/lib/rules/no-script-url.js @@ -1,41 +1,41 @@ -/** - * @fileoverview Tests for no-script-url rule. - * @author Ilya Volodin - */ - -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require("../../../lib/rules/no-script-url"), - { RuleTester } = require("eslint"); - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new RuleTester(); - -ruleTester.run("no-script-url", rule, { - valid: [ - "var a = 'Hello World!';", - "var a = 10;", - "var url = 'xjavascript:'" - ], - invalid: [ - { - code: "var a = 'javascript:void(0);';", - errors: [ - { message: "Script URL is a form of eval.", type: "Literal" } - ] - }, - { - code: "var a = 'javascript:';", - errors: [ - { message: "Script URL is a form of eval.", type: "Literal" } - ] - } - ] -}); +/** + * @fileoverview Tests for no-script-url rule. + * @author Ilya Volodin + */ + +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require("../../../lib/rules/no-script-url"), + { RuleTester } = require("eslint"); + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const ruleTester = new RuleTester(); + +ruleTester.run("no-script-url", rule, { + valid: [ + "var a = 'Hello World!';", + "var a = 10;", + "var url = 'xjavascript:'" + ], + invalid: [ + { + code: "var a = 'javascript:void(0);';", + errors: [ + { message: "Script URL is a form of eval.", type: "Literal" } + ] + }, + { + code: "var a = 'javascript:';", + errors: [ + { message: "Script URL is a form of eval.", type: "Literal" } + ] + } + ] +}); diff --git a/readme.md b/readme.md index 141c866..0e153f9 100644 --- a/readme.md +++ b/readme.md @@ -1,8 +1,8 @@ - -# eslint-plugin - -## this repo contains several eslint plugins - -* [eslint-plugin-autofix](./packages/autofix) -* [eslint-plugin-no-autofix](./packages/no-autofix) -* [eslint-plugin-web](./packages/web) + +# eslint-plugin + +## this repo contains several eslint plugins + +* [eslint-plugin-autofix](./packages/autofix) +* [eslint-plugin-no-autofix](./packages/no-autofix) +* [eslint-plugin-web](./packages/web) diff --git a/shims.d.ts b/shims.d.ts index 652c3c3..36f2aa6 100644 --- a/shims.d.ts +++ b/shims.d.ts @@ -1,30 +1,30 @@ -declare module 'eslint-rule-composer' { - import * as estree from 'estree' - import * as eslint from 'eslint' - - interface Problem { - node: estree.Node | null - message: string - messageId: string | null - data: object | null - loc: eslint.AST.SourceLocation - fix?: (fixer: eslint.Rule.RuleFixer) => (eslint.Rule.Fix | null) - } - - interface Metadata { - sourceCode: eslint.SourceCode - settings?: object - options: any[] - filename: string - } - - interface Predicate { - (problem: Problem, metadata: Metadata): T - } - - export function mapReports(rule: eslint.Rule.RuleModule, predicate: Predicate): eslint.Rule.RuleModule - - export function filterReports(rule: eslint.Rule.RuleModule, predicate: Predicate): eslint.Rule.RuleModule - - export function joinReports(rules: eslint.Rule.RuleModule[]): eslint.Rule.RuleModule -} +declare module 'eslint-rule-composer' { + import * as estree from 'estree' + import * as eslint from 'eslint' + + interface Problem { + node: estree.Node | null + message: string + messageId: string | null + data: object | null + loc: eslint.AST.SourceLocation + fix?: (fixer: eslint.Rule.RuleFixer) => (eslint.Rule.Fix | null) + } + + interface Metadata { + sourceCode: eslint.SourceCode + settings?: object + options: any[] + filename: string + } + + interface Predicate { + (problem: Problem, metadata: Metadata): T + } + + export function mapReports(rule: eslint.Rule.RuleModule, predicate: Predicate): eslint.Rule.RuleModule + + export function filterReports(rule: eslint.Rule.RuleModule, predicate: Predicate): eslint.Rule.RuleModule + + export function joinReports(rules: eslint.Rule.RuleModule[]): eslint.Rule.RuleModule +}