2 * @fileoverview Restrict usage of specified node imports.
7 //------------------------------------------------------------------------------
9 //------------------------------------------------------------------------------
11 const ignore = require("ignore");
13 const arrayOfStrings = {
15 items: { type: "string" },
19 const arrayOfStringsOrObjects = {
27 name: { type: "string" },
39 additionalProperties: false,
52 description: "disallow specified modules when loaded by `import`",
53 category: "ECMAScript 6",
55 url: "https://eslint.org/docs/rules/no-restricted-imports"
59 path: "'{{importSource}}' import is restricted from being used.",
60 // eslint-disable-next-line eslint-plugin/report-message-format
61 pathWithCustomMessage: "'{{importSource}}' import is restricted from being used. {{customMessage}}",
63 patterns: "'{{importSource}}' import is restricted from being used by a pattern.",
65 everything: "* import is invalid because '{{importNames}}' from '{{importSource}}' is restricted.",
66 // eslint-disable-next-line eslint-plugin/report-message-format
67 everythingWithCustomMessage: "* import is invalid because '{{importNames}}' from '{{importSource}}' is restricted. {{customMessage}}"
72 arrayOfStringsOrObjects,
78 paths: arrayOfStringsOrObjects,
79 patterns: arrayOfStrings
81 additionalProperties: false
83 additionalItems: false
90 const options = Array.isArray(context.options) ? context.options : [];
91 const isPathAndPatternsObject =
92 typeof options[0] === "object" &&
93 (Object.prototype.hasOwnProperty.call(options[0], "paths") || Object.prototype.hasOwnProperty.call(options[0], "patterns"));
95 const restrictedPaths = (isPathAndPatternsObject ? options[0].paths : context.options) || [];
96 const restrictedPatterns = (isPathAndPatternsObject ? options[0].patterns : []) || [];
98 const restrictedPathMessages = restrictedPaths.reduce((memo, importSource) => {
99 if (typeof importSource === "string") {
100 memo[importSource] = { message: null };
102 memo[importSource.name] = {
103 message: importSource.message,
104 importNames: importSource.importNames
110 // if no imports are restricted we don"t need to check
111 if (Object.keys(restrictedPaths).length === 0 && restrictedPatterns.length === 0) {
115 const restrictedPatternsMatcher = ignore().add(restrictedPatterns);
118 * Checks to see if "*" is being used to import everything.
119 * @param {Set.<string>} importNames Set of import names that are being imported
120 * @returns {boolean} whether everything is imported or not
122 function isEverythingImported(importNames) {
123 return importNames.has("*");
127 * Report a restricted path.
128 * @param {node} node representing the restricted path reference
132 function reportPath(node) {
133 const importSource = node.source.value.trim();
134 const customMessage = restrictedPathMessages[importSource] && restrictedPathMessages[importSource].message;
138 messageId: customMessage ? "pathWithCustomMessage" : "path",
147 * Report a restricted path specifically for patterns.
148 * @param {node} node representing the restricted path reference
152 function reportPathForPatterns(node) {
153 const importSource = node.source.value.trim();
157 messageId: "patterns",
165 * Report a restricted path specifically when using the '*' import.
166 * @param {string} importSource path of the import
167 * @param {node} node representing the restricted path reference
171 function reportPathForEverythingImported(importSource, node) {
172 const importNames = restrictedPathMessages[importSource].importNames;
173 const customMessage = restrictedPathMessages[importSource] && restrictedPathMessages[importSource].message;
177 messageId: customMessage ? "everythingWithCustomMessage" : "everything",
187 * Check if the given importSource is restricted because '*' is being imported.
188 * @param {string} importSource path of the import
189 * @param {Set.<string>} importNames Set of import names that are being imported
190 * @returns {boolean} whether the path is restricted
193 function isRestrictedForEverythingImported(importSource, importNames) {
194 return Object.prototype.hasOwnProperty.call(restrictedPathMessages, importSource) &&
195 restrictedPathMessages[importSource].importNames &&
196 isEverythingImported(importNames);
200 * Check if the given importNames are restricted given a list of restrictedImportNames.
201 * @param {Set.<string>} importNames Set of import names that are being imported
202 * @param {string[]} restrictedImportNames array of import names that are restricted for this import
203 * @returns {boolean} whether the objectName is restricted
206 function isRestrictedObject(importNames, restrictedImportNames) {
207 return restrictedImportNames.some(restrictedObjectName => (
208 importNames.has(restrictedObjectName)
213 * Check if the given importSource is a restricted path.
214 * @param {string} importSource path of the import
215 * @param {Set.<string>} importNames Set of import names that are being imported
216 * @returns {boolean} whether the variable is a restricted path or not
219 function isRestrictedPath(importSource, importNames) {
220 let isRestricted = false;
222 if (Object.prototype.hasOwnProperty.call(restrictedPathMessages, importSource)) {
223 if (restrictedPathMessages[importSource].importNames) {
224 isRestricted = isRestrictedObject(importNames, restrictedPathMessages[importSource].importNames);
234 * Check if the given importSource is restricted by a pattern.
235 * @param {string} importSource path of the import
236 * @returns {boolean} whether the variable is a restricted pattern or not
239 function isRestrictedPattern(importSource) {
240 return restrictedPatterns.length > 0 && restrictedPatternsMatcher.ignores(importSource);
244 * Checks a node to see if any problems should be reported.
245 * @param {ASTNode} node The node to check.
249 function checkNode(node) {
250 const importSource = node.source.value.trim();
251 const importNames = node.specifiers ? node.specifiers.reduce((set, specifier) => {
252 if (specifier.type === "ImportDefaultSpecifier") {
254 } else if (specifier.type === "ImportNamespaceSpecifier") {
256 } else if (specifier.imported) {
257 set.add(specifier.imported.name);
258 } else if (specifier.local) {
259 set.add(specifier.local.name);
262 }, new Set()) : new Set();
264 if (isRestrictedForEverythingImported(importSource, importNames)) {
265 reportPathForEverythingImported(importSource, node);
268 if (isRestrictedPath(importSource, importNames)) {
271 if (isRestrictedPattern(importSource)) {
272 reportPathForPatterns(node);
277 ImportDeclaration: checkNode,
278 ExportNamedDeclaration(node) {
283 ExportAllDeclaration: checkNode