]> git.proxmox.com Git - pve-eslint.git/blobdiff - eslint/lib/rules/no-restricted-imports.js
import 8.23.1 source
[pve-eslint.git] / eslint / lib / rules / no-restricted-imports.js
index c205dad8bdb79d83b18e832beeaa713c7f82f808..f4838679efcf6d13b5602d815409753ffa642d49 100644 (file)
@@ -4,18 +4,18 @@
  */
 "use strict";
 
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("./utils/ast-utils");
+
 //------------------------------------------------------------------------------
 // Rule Definition
 //------------------------------------------------------------------------------
 
 const ignore = require("ignore");
 
-const arrayOfStrings = {
-    type: "array",
-    items: { type: "string" },
-    uniqueItems: true
-};
-
 const arrayOfStringsOrObjects = {
     type: "array",
     items: {
@@ -44,30 +44,86 @@ const arrayOfStringsOrObjects = {
     uniqueItems: true
 };
 
+const arrayOfStringsOrObjectPatterns = {
+    anyOf: [
+        {
+            type: "array",
+            items: {
+                type: "string"
+            },
+            uniqueItems: true
+        },
+        {
+            type: "array",
+            items: {
+                type: "object",
+                properties: {
+                    importNames: {
+                        type: "array",
+                        items: {
+                            type: "string"
+                        },
+                        minItems: 1,
+                        uniqueItems: true
+                    },
+                    group: {
+                        type: "array",
+                        items: {
+                            type: "string"
+                        },
+                        minItems: 1,
+                        uniqueItems: true
+                    },
+                    message: {
+                        type: "string",
+                        minLength: 1
+                    },
+                    caseSensitive: {
+                        type: "boolean"
+                    }
+                },
+                additionalProperties: false,
+                required: ["group"]
+            },
+            uniqueItems: true
+        }
+    ]
+};
+
+/** @type {import('../shared/types').Rule} */
 module.exports = {
     meta: {
         type: "suggestion",
 
         docs: {
-            description: "disallow specified modules when loaded by `import`",
-            category: "ECMAScript 6",
+            description: "Disallow specified modules when loaded by `import`",
             recommended: false,
             url: "https://eslint.org/docs/rules/no-restricted-imports"
         },
 
         messages: {
             path: "'{{importSource}}' import is restricted from being used.",
-            // eslint-disable-next-line eslint-plugin/report-message-format
+            // eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
             pathWithCustomMessage: "'{{importSource}}' import is restricted from being used. {{customMessage}}",
 
             patterns: "'{{importSource}}' import is restricted from being used by a pattern.",
+            // eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
+            patternWithCustomMessage: "'{{importSource}}' import is restricted from being used by a pattern. {{customMessage}}",
+
+            patternAndImportName: "'{{importName}}' import from '{{importSource}}' is restricted from being used by a pattern.",
+            // eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
+            patternAndImportNameWithCustomMessage: "'{{importName}}' import from '{{importSource}}' is restricted from being used by a pattern. {{customMessage}}",
+
+            patternAndEverything: "* import is invalid because '{{importNames}}' from '{{importSource}}' is restricted from being used by a pattern.",
+            // eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
+            patternAndEverythingWithCustomMessage: "* import is invalid because '{{importNames}}' from '{{importSource}}' is restricted from being used by a pattern. {{customMessage}}",
 
             everything: "* import is invalid because '{{importNames}}' from '{{importSource}}' is restricted.",
-            // eslint-disable-next-line eslint-plugin/report-message-format
+            // eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
             everythingWithCustomMessage: "* import is invalid because '{{importNames}}' from '{{importSource}}' is restricted. {{customMessage}}",
 
             importName: "'{{importName}}' import from '{{importSource}}' is restricted.",
-            // eslint-disable-next-line eslint-plugin/report-message-format
+            // eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
             importNameWithCustomMessage: "'{{importName}}' import from '{{importSource}}' is restricted. {{customMessage}}"
         },
 
@@ -80,7 +136,7 @@ module.exports = {
                         type: "object",
                         properties: {
                             paths: arrayOfStringsOrObjects,
-                            patterns: arrayOfStrings
+                            patterns: arrayOfStringsOrObjectPatterns
                         },
                         additionalProperties: false
                     }],
@@ -98,13 +154,6 @@ module.exports = {
             (Object.prototype.hasOwnProperty.call(options[0], "paths") || Object.prototype.hasOwnProperty.call(options[0], "patterns"));
 
         const restrictedPaths = (isPathAndPatternsObject ? options[0].paths : context.options) || [];
-        const restrictedPatterns = (isPathAndPatternsObject ? options[0].patterns : []) || [];
-
-        // if no imports are restricted we don"t need to check
-        if (Object.keys(restrictedPaths).length === 0 && restrictedPatterns.length === 0) {
-            return {};
-        }
-
         const restrictedPathMessages = restrictedPaths.reduce((memo, importSource) => {
             if (typeof importSource === "string") {
                 memo[importSource] = { message: null };
@@ -117,7 +166,25 @@ module.exports = {
             return memo;
         }, {});
 
-        const restrictedPatternsMatcher = ignore().add(restrictedPatterns);
+        // Handle patterns too, either as strings or groups
+        let restrictedPatterns = (isPathAndPatternsObject ? options[0].patterns : []) || [];
+
+        // standardize to array of objects if we have an array of strings
+        if (restrictedPatterns.length > 0 && typeof restrictedPatterns[0] === "string") {
+            restrictedPatterns = [{ group: restrictedPatterns }];
+        }
+
+        // relative paths are supported for this rule
+        const restrictedPatternGroups = restrictedPatterns.map(({ group, message, caseSensitive, importNames }) => ({
+            matcher: ignore({ allowRelativePaths: true, ignorecase: !caseSensitive }).add(group),
+            customMessage: message,
+            importNames
+        }));
+
+        // if no imports are restricted we don't need to check
+        if (Object.keys(restrictedPaths).length === 0 && restrictedPatternGroups.length === 0) {
+            return {};
+        }
 
         /**
          * Report a restricted path.
@@ -184,29 +251,80 @@ module.exports = {
         /**
          * Report a restricted path specifically for patterns.
          * @param {node} node representing the restricted path reference
+         * @param {Object} group contains an Ignore instance for paths, the customMessage to show on failure,
+         * and any restricted import names that have been specified in the config
+         * @param {Map<string,Object[]>} importNames Map of import names that are being imported
          * @returns {void}
          * @private
          */
-        function reportPathForPatterns(node) {
+        function reportPathForPatterns(node, group, importNames) {
             const importSource = node.source.value.trim();
 
-            context.report({
-                node,
-                messageId: "patterns",
-                data: {
-                    importSource
+            const customMessage = group.customMessage;
+            const restrictedImportNames = group.importNames;
+
+            /*
+             * If we are not restricting to any specific import names and just the pattern itself,
+             * report the error and move on
+             */
+            if (!restrictedImportNames) {
+                context.report({
+                    node,
+                    messageId: customMessage ? "patternWithCustomMessage" : "patterns",
+                    data: {
+                        importSource,
+                        customMessage
+                    }
+                });
+                return;
+            }
+
+            if (importNames.has("*")) {
+                const specifierData = importNames.get("*")[0];
+
+                context.report({
+                    node,
+                    messageId: customMessage ? "patternAndEverythingWithCustomMessage" : "patternAndEverything",
+                    loc: specifierData.loc,
+                    data: {
+                        importSource,
+                        importNames: restrictedImportNames,
+                        customMessage
+                    }
+                });
+            }
+
+            restrictedImportNames.forEach(importName => {
+                if (!importNames.has(importName)) {
+                    return;
                 }
+
+                const specifiers = importNames.get(importName);
+
+                specifiers.forEach(specifier => {
+                    context.report({
+                        node,
+                        messageId: customMessage ? "patternAndImportNameWithCustomMessage" : "patternAndImportName",
+                        loc: specifier.loc,
+                        data: {
+                            importSource,
+                            customMessage,
+                            importName
+                        }
+                    });
+                });
             });
         }
 
         /**
          * Check if the given importSource is restricted by a pattern.
          * @param {string} importSource path of the import
+         * @param {Object} group contains a Ignore instance for paths, and the customMessage to show if it fails
          * @returns {boolean} whether the variable is a restricted pattern or not
          * @private
          */
-        function isRestrictedPattern(importSource) {
-            return restrictedPatterns.length > 0 && restrictedPatternsMatcher.ignores(importSource);
+        function isRestrictedPattern(importSource, group) {
+            return group.matcher.ignores(importSource);
         }
 
         /**
@@ -233,12 +351,12 @@ module.exports = {
                     } else if (specifier.type === "ImportNamespaceSpecifier") {
                         name = "*";
                     } else if (specifier.imported) {
-                        name = specifier.imported.name;
+                        name = astUtils.getModuleExportName(specifier.imported);
                     } else if (specifier.local) {
-                        name = specifier.local.name;
+                        name = astUtils.getModuleExportName(specifier.local);
                     }
 
-                    if (name) {
+                    if (typeof name === "string") {
                         if (importNames.has(name)) {
                             importNames.get(name).push(specifierData);
                         } else {
@@ -249,10 +367,11 @@ module.exports = {
             }
 
             checkRestrictedPathAndReport(importSource, importNames, node);
-
-            if (isRestrictedPattern(importSource)) {
-                reportPathForPatterns(node);
-            }
+            restrictedPatternGroups.forEach(group => {
+                if (isRestrictedPattern(importSource, group)) {
+                    reportPathForPatterns(node, group, importNames);
+                }
+            });
         }
 
         return {