From a5b41a75b57572e97476b06ad39b768e15b9d844 Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Fri, 28 Feb 2020 21:51:30 +0100 Subject: [PATCH] Update: no-restricted-modules handle TemplateLiteral (fixes #12926) (#12927) * Update: no-restricted-modules handle TemplateLiteral (fixes #12926) * add fixture using backslashes * finally understood, I think --- lib/rules/no-restricted-modules.js | 66 +++++++++++++++++------- tests/lib/rules/no-restricted-modules.js | 14 ++++- 2 files changed, 61 insertions(+), 19 deletions(-) diff --git a/lib/rules/no-restricted-modules.js b/lib/rules/no-restricted-modules.js index abbc30a05fb..abd8d5cbe29 100644 --- a/lib/rules/no-restricted-modules.js +++ b/lib/rules/no-restricted-modules.js @@ -106,10 +106,19 @@ module.exports = { * @param {ASTNode} node The node to check. * @returns {boolean} If the node is a string literal. */ - function isString(node) { + function isStringLiteral(node) { return node && node.type === "Literal" && typeof node.value === "string"; } + /** + * Function to check if a node is a static string template literal. + * @param {ASTNode} node The node to check. + * @returns {boolean} If the node is a string template literal. + */ + function isStaticTemplateLiteral(node) { + return node && node.type === "TemplateLiteral" && node.expressions.length === 0; + } + /** * Function to check if a node is a require call. * @param {ASTNode} node The node to check. @@ -119,14 +128,31 @@ module.exports = { return node.callee.type === "Identifier" && node.callee.name === "require"; } + /** + * Extract string from Literal or TemplateLiteral node + * @param {ASTNode} node The node to extract from + * @returns {string|null} Extracted string or null if node doesn't represent a string + */ + function getFirstArgumentString(node) { + if (isStringLiteral(node)) { + return node.value.trim(); + } + + if (isStaticTemplateLiteral(node)) { + return node.quasis[0].value.cooked.trim(); + } + + return null; + } + /** * Report a restricted path. * @param {node} node representing the restricted path reference + * @param {string} name restricted path * @returns {void} * @private */ - function reportPath(node) { - const name = node.arguments[0].value.trim(); + function reportPath(node, name) { const customMessage = restrictedPathMessages[name]; const messageId = customMessage ? "customMessage" @@ -156,21 +182,25 @@ module.exports = { CallExpression(node) { if (isRequireCall(node)) { - // node has arguments and first argument is string - if (node.arguments.length && isString(node.arguments[0])) { - const name = node.arguments[0].value.trim(); - - // check if argument value is in restricted modules array - if (isRestrictedPath(name)) { - reportPath(node); - } - - if (restrictedPatterns.length > 0 && ig.ignores(name)) { - context.report({ - node, - messageId: "patternMessage", - data: { name } - }); + // node has arguments + if (node.arguments.length) { + const name = getFirstArgumentString(node.arguments[0]); + + // if first argument is a string literal or a static string template literal + if (name) { + + // check if argument value is in restricted modules array + if (isRestrictedPath(name)) { + reportPath(node, name); + } + + if (restrictedPatterns.length > 0 && ig.ignores(name)) { + context.report({ + node, + messageId: "patternMessage", + data: { name } + }); + } } } } diff --git a/tests/lib/rules/no-restricted-modules.js b/tests/lib/rules/no-restricted-modules.js index f8b0a026bb3..08cc7c092f5 100644 --- a/tests/lib/rules/no-restricted-modules.js +++ b/tests/lib/rules/no-restricted-modules.js @@ -30,7 +30,9 @@ ruleTester.run("no-restricted-modules", rule, { { code: "var withPaths = require(\"foo/bar\");", options: [{ paths: ["foo", "bar"] }] }, { code: "var withPatterns = require(\"foo/bar\");", options: [{ patterns: ["foo/c*"] }] }, { code: "var withPatternsAndPaths = require(\"foo/bar\");", options: [{ paths: ["foo"], patterns: ["foo/c*"] }] }, - { code: "var withGitignores = require(\"foo/bar\");", options: [{ paths: ["foo"], patterns: ["foo/*", "!foo/bar"] }] } + { code: "var withGitignores = require(\"foo/bar\");", options: [{ paths: ["foo"], patterns: ["foo/*", "!foo/bar"] }] }, + { code: "require(`fs`)", options: ["crypto"], parserOptions: { ecmaVersion: 6 } }, + { code: "require(`foo${bar}`)", options: ["foo"], parserOptions: { ecmaVersion: 6 } } ], invalid: [{ code: "require(\"fs\")", @@ -99,5 +101,15 @@ ruleTester.run("no-restricted-modules", rule, { data: { name: "foo", customMessage: "Please use 'bar' module instead." }, type: "CallExpression" }] + }, { + code: "require(`fs`)", + options: ["fs"], + parserOptions: { ecmaVersion: 6 }, + errors: [{ messageId: "defaultMessage", data: { name: "fs" }, type: "CallExpression" }] + }, { + code: "require(`crypt\\o`);", + options: ["crypto"], + parserOptions: { ecmaVersion: 6 }, + errors: [{ messageId: "defaultMessage", data: { name: "crypto" }, type: "CallExpression" }] }] });