From 04069feced09509afea00a3a5ee5d7feb43bc531 Mon Sep 17 00:00:00 2001 From: Dimitri Mitropoulos Date: Mon, 6 Jul 2020 22:32:02 -0400 Subject: [PATCH 1/5] Fix: deprecates id-blacklist (and documents deprecation) --- docs/rules/id-blacklist.md | 3 +++ lib/rules/id-blacklist.js | 22 ++++++++++++++++++++++ lib/rules/index.js | 4 +--- tools/rule-types.json | 1 + 4 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 docs/rules/id-blacklist.md create mode 100644 lib/rules/id-blacklist.js diff --git a/docs/rules/id-blacklist.md b/docs/rules/id-blacklist.md new file mode 100644 index 00000000000..688873cb44b --- /dev/null +++ b/docs/rules/id-blacklist.md @@ -0,0 +1,3 @@ +# disallow specified identifiers (id-blacklist) + +This rule has been deprecated. Please see the [`id-denylist`]('./id-blacklist') rule. diff --git a/lib/rules/id-blacklist.js b/lib/rules/id-blacklist.js new file mode 100644 index 00000000000..ca9d7efdac4 --- /dev/null +++ b/lib/rules/id-blacklist.js @@ -0,0 +1,22 @@ +/** + * @fileoverview Rule that warns when identifier names that are + * specified in the configuration are used. + * @author Keith Cirkel (http://keithcirkel.co.uk) + */ + +"use strict"; + +const idDenylist = require("./id-denylist"); + +// `id-blacklist` has been renamed to `id-denylist` +module.exports = { + + // eslint-disable-next-line internal-rules/consistent-docs-url, internal-rules/no-invalid-meta + meta: Object.assign({}, { + deprecated: true, + docs: Object.assign({}, { + url: "https://eslint.org/docs/rules/id-blacklist" + }, idDenylist.meta.docs) + }, idDenylist.meta), + create: idDenylist.create +}; diff --git a/lib/rules/index.js b/lib/rules/index.js index e34d090899a..3cf26e51bc8 100644 --- a/lib/rules/index.js +++ b/lib/rules/index.js @@ -56,9 +56,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({ "grouped-accessor-pairs": () => require("./grouped-accessor-pairs"), "guard-for-in": () => require("./guard-for-in"), "handle-callback-err": () => require("./handle-callback-err"), - - // Renamed to id-denylist. - "id-blacklist": () => require("./id-denylist"), + "id-blacklist": () => require("./id-blacklist"), "id-denylist": () => require("./id-denylist"), "id-length": () => require("./id-length"), "id-match": () => require("./id-match"), diff --git a/tools/rule-types.json b/tools/rule-types.json index 2421b9fdaad..84700de70d0 100644 --- a/tools/rule-types.json +++ b/tools/rule-types.json @@ -43,6 +43,7 @@ "grouped-accessor-pairs": "suggestion", "guard-for-in": "suggestion", "handle-callback-err": "suggestion", + "id-blacklist": "suggestion", "id-denylist": "suggestion", "id-length": "suggestion", "id-match": "suggestion", From 4eed01d15110d703a9a465076a5782f001e4a7f7 Mon Sep 17 00:00:00 2001 From: Dimitri Mitropoulos Date: Sat, 11 Jul 2020 09:50:34 -0400 Subject: [PATCH 2/5] Update docs/rules/id-blacklist.md Co-authored-by: Milos Djermanovic --- docs/rules/id-blacklist.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rules/id-blacklist.md b/docs/rules/id-blacklist.md index 688873cb44b..87a7d9503bb 100644 --- a/docs/rules/id-blacklist.md +++ b/docs/rules/id-blacklist.md @@ -1,3 +1,3 @@ # disallow specified identifiers (id-blacklist) -This rule has been deprecated. Please see the [`id-denylist`]('./id-blacklist') rule. +This rule was **deprecated** in ESLint v7.5.0 and replaced by the [id-denylist](id-denylist.md) rule. From cbde10e7ff0013dc6e31d0eaf88215ce66170b67 Mon Sep 17 00:00:00 2001 From: Dimitri Mitropoulos Date: Sat, 11 Jul 2020 09:54:07 -0400 Subject: [PATCH 3/5] copies contents of id-denylist to id-blacklist verbatim --- lib/rules/id-blacklist.js | 228 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 218 insertions(+), 10 deletions(-) diff --git a/lib/rules/id-blacklist.js b/lib/rules/id-blacklist.js index ca9d7efdac4..112fd8a9d55 100644 --- a/lib/rules/id-blacklist.js +++ b/lib/rules/id-blacklist.js @@ -6,17 +6,225 @@ "use strict"; -const idDenylist = require("./id-denylist"); +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +/** + * Checks whether the given node represents assignment target in a normal assignment or destructuring. + * @param {ASTNode} node The node to check. + * @returns {boolean} `true` if the node is assignment target. + */ +function isAssignmentTarget(node) { + const parent = node.parent; + + return ( + + // normal assignment + ( + parent.type === "AssignmentExpression" && + parent.left === node + ) || + + // destructuring + parent.type === "ArrayPattern" || + parent.type === "RestElement" || + ( + parent.type === "Property" && + parent.value === node && + parent.parent.type === "ObjectPattern" + ) || + ( + parent.type === "AssignmentPattern" && + parent.left === node + ) + ); +} + +/** + * Checks whether the given node represents an imported name that is renamed in the same import/export specifier. + * + * Examples: + * import { a as b } from 'mod'; // node `a` is renamed import + * export { a as b } from 'mod'; // node `a` is renamed import + * @param {ASTNode} node `Identifier` node to check. + * @returns {boolean} `true` if the node is a renamed import. + */ +function isRenamedImport(node) { + const parent = node.parent; + + return ( + ( + parent.type === "ImportSpecifier" && + parent.imported !== parent.local && + parent.imported === node + ) || + ( + parent.type === "ExportSpecifier" && + parent.parent.source && // re-export + parent.local !== parent.exported && + parent.local === node + ) + ); +} + +/** + * Checks whether the given node is a renamed identifier node in an ObjectPattern destructuring. + * + * Examples: + * const { a : b } = foo; // node `a` is renamed node. + * @param {ASTNode} node `Identifier` node to check. + * @returns {boolean} `true` if the node is a renamed node in an ObjectPattern destructuring. + */ +function isRenamedInDestructuring(node) { + const parent = node.parent; + + return ( + ( + !parent.computed && + parent.type === "Property" && + parent.parent.type === "ObjectPattern" && + parent.value !== node && + parent.key === node + ) + ); +} + +/** + * Checks whether the given node represents shorthand definition of a property in an object literal. + * @param {ASTNode} node `Identifier` node to check. + * @returns {boolean} `true` if the node is a shorthand property definition. + */ +function isShorthandPropertyDefinition(node) { + const parent = node.parent; + + return ( + parent.type === "Property" && + parent.parent.type === "ObjectExpression" && + parent.shorthand + ); +} + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ -// `id-blacklist` has been renamed to `id-denylist` module.exports = { + meta: { + type: "suggestion", + + docs: { + description: "disallow specified identifiers", + category: "Stylistic Issues", + recommended: false, + url: "https://eslint.org/docs/rules/id-denylist" + }, + + schema: { + type: "array", + items: { + type: "string" + }, + uniqueItems: true + }, + messages: { + restricted: "Identifier '{{name}}' is restricted." + } + }, + + create(context) { + + const denyList = new Set(context.options); + const reportedNodes = new Set(); + + let globalScope; + + /** + * Checks whether the given name is restricted. + * @param {string} name The name to check. + * @returns {boolean} `true` if the name is restricted. + * @private + */ + function isRestricted(name) { + return denyList.has(name); + } + + /** + * Checks whether the given node represents a reference to a global variable that is not declared in the source code. + * These identifiers will be allowed, as it is assumed that user has no control over the names of external global variables. + * @param {ASTNode} node `Identifier` node to check. + * @returns {boolean} `true` if the node is a reference to a global variable. + */ + function isReferenceToGlobalVariable(node) { + const variable = globalScope.set.get(node.name); + + return variable && variable.defs.length === 0 && + variable.references.some(ref => ref.identifier === node); + } + + /** + * Determines whether the given node should be checked. + * @param {ASTNode} node `Identifier` node. + * @returns {boolean} `true` if the node should be checked. + */ + function shouldCheck(node) { + const parent = node.parent; + + /* + * Member access has special rules for checking property names. + * Read access to a property with a restricted name is allowed, because it can be on an object that user has no control over. + * Write access isn't allowed, because it potentially creates a new property with a restricted name. + */ + if ( + parent.type === "MemberExpression" && + parent.property === node && + !parent.computed + ) { + return isAssignmentTarget(parent); + } + + return ( + parent.type !== "CallExpression" && + parent.type !== "NewExpression" && + !isRenamedImport(node) && + !isRenamedInDestructuring(node) && + !( + isReferenceToGlobalVariable(node) && + !isShorthandPropertyDefinition(node) + ) + ); + } + + /** + * Reports an AST node as a rule violation. + * @param {ASTNode} node The node to report. + * @returns {void} + * @private + */ + function report(node) { + if (!reportedNodes.has(node)) { + context.report({ + node, + messageId: "restricted", + data: { + name: node.name + } + }); + reportedNodes.add(node); + } + } + + return { + + Program() { + globalScope = context.getScope(); + }, - // eslint-disable-next-line internal-rules/consistent-docs-url, internal-rules/no-invalid-meta - meta: Object.assign({}, { - deprecated: true, - docs: Object.assign({}, { - url: "https://eslint.org/docs/rules/id-blacklist" - }, idDenylist.meta.docs) - }, idDenylist.meta), - create: idDenylist.create + Identifier(node) { + if (isRestricted(node.name) && shouldCheck(node)) { + report(node); + } + } + }; + } }; From b9321957b22fccaefa2cff9ff9d074706afe09ba Mon Sep 17 00:00:00 2001 From: Dimitri Mitropoulos Date: Sat, 11 Jul 2020 09:54:56 -0400 Subject: [PATCH 4/5] deprecates id-blacklist and updates docs url --- lib/rules/id-blacklist.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/rules/id-blacklist.js b/lib/rules/id-blacklist.js index 112fd8a9d55..99760c2fdf0 100644 --- a/lib/rules/id-blacklist.js +++ b/lib/rules/id-blacklist.js @@ -111,13 +111,15 @@ function isShorthandPropertyDefinition(node) { module.exports = { meta: { + deprecated: true, + type: "suggestion", docs: { description: "disallow specified identifiers", category: "Stylistic Issues", recommended: false, - url: "https://eslint.org/docs/rules/id-denylist" + url: "https://eslint.org/docs/rules/id-blacklist" }, schema: { From b174551e5d5e17c52352b745fbd3159ef45a3ec9 Mon Sep 17 00:00:00 2001 From: Dimitri Mitropoulos Date: Sat, 11 Jul 2020 15:24:17 -0400 Subject: [PATCH 5/5] adds replacedBy metadata --- lib/rules/id-blacklist.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/rules/id-blacklist.js b/lib/rules/id-blacklist.js index 99760c2fdf0..4fbba909fde 100644 --- a/lib/rules/id-blacklist.js +++ b/lib/rules/id-blacklist.js @@ -112,6 +112,7 @@ function isShorthandPropertyDefinition(node) { module.exports = { meta: { deprecated: true, + replacedBy: ["id-denylist"], type: "suggestion",