source: imaps-frontend/node_modules/eslint/lib/rules/no-restricted-imports.js

main
Last change on this file was d565449, checked in by stefan toskovski <stefantoska84@…>, 4 weeks ago

Update repo after prototype presentation

  • Property mode set to 100644
File size: 16.6 KB
Line 
1/**
2 * @fileoverview Restrict usage of specified node imports.
3 * @author Guy Ellis
4 */
5"use strict";
6
7//------------------------------------------------------------------------------
8// Requirements
9//------------------------------------------------------------------------------
10
11const astUtils = require("./utils/ast-utils");
12
13//------------------------------------------------------------------------------
14// Rule Definition
15//------------------------------------------------------------------------------
16
17const ignore = require("ignore");
18
19const arrayOfStringsOrObjects = {
20 type: "array",
21 items: {
22 anyOf: [
23 { type: "string" },
24 {
25 type: "object",
26 properties: {
27 name: { type: "string" },
28 message: {
29 type: "string",
30 minLength: 1
31 },
32 importNames: {
33 type: "array",
34 items: {
35 type: "string"
36 }
37 }
38 },
39 additionalProperties: false,
40 required: ["name"]
41 }
42 ]
43 },
44 uniqueItems: true
45};
46
47const arrayOfStringsOrObjectPatterns = {
48 anyOf: [
49 {
50 type: "array",
51 items: {
52 type: "string"
53 },
54 uniqueItems: true
55 },
56 {
57 type: "array",
58 items: {
59 type: "object",
60 properties: {
61 importNames: {
62 type: "array",
63 items: {
64 type: "string"
65 },
66 minItems: 1,
67 uniqueItems: true
68 },
69 group: {
70 type: "array",
71 items: {
72 type: "string"
73 },
74 minItems: 1,
75 uniqueItems: true
76 },
77 importNamePattern: {
78 type: "string"
79 },
80 message: {
81 type: "string",
82 minLength: 1
83 },
84 caseSensitive: {
85 type: "boolean"
86 }
87 },
88 additionalProperties: false,
89 required: ["group"]
90 },
91 uniqueItems: true
92 }
93 ]
94};
95
96/** @type {import('../shared/types').Rule} */
97module.exports = {
98 meta: {
99 type: "suggestion",
100
101 docs: {
102 description: "Disallow specified modules when loaded by `import`",
103 recommended: false,
104 url: "https://eslint.org/docs/latest/rules/no-restricted-imports"
105 },
106
107 messages: {
108 path: "'{{importSource}}' import is restricted from being used.",
109 // eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
110 pathWithCustomMessage: "'{{importSource}}' import is restricted from being used. {{customMessage}}",
111
112 patterns: "'{{importSource}}' import is restricted from being used by a pattern.",
113 // eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
114 patternWithCustomMessage: "'{{importSource}}' import is restricted from being used by a pattern. {{customMessage}}",
115
116 patternAndImportName: "'{{importName}}' import from '{{importSource}}' is restricted from being used by a pattern.",
117 // eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
118 patternAndImportNameWithCustomMessage: "'{{importName}}' import from '{{importSource}}' is restricted from being used by a pattern. {{customMessage}}",
119
120 patternAndEverything: "* import is invalid because '{{importNames}}' from '{{importSource}}' is restricted from being used by a pattern.",
121
122 patternAndEverythingWithRegexImportName: "* import is invalid because import name matching '{{importNames}}' pattern from '{{importSource}}' is restricted from being used.",
123 // eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
124 patternAndEverythingWithCustomMessage: "* import is invalid because '{{importNames}}' from '{{importSource}}' is restricted from being used by a pattern. {{customMessage}}",
125 // eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
126 patternAndEverythingWithRegexImportNameAndCustomMessage: "* import is invalid because import name matching '{{importNames}}' pattern from '{{importSource}}' is restricted from being used. {{customMessage}}",
127
128 everything: "* import is invalid because '{{importNames}}' from '{{importSource}}' is restricted.",
129 // eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
130 everythingWithCustomMessage: "* import is invalid because '{{importNames}}' from '{{importSource}}' is restricted. {{customMessage}}",
131
132 importName: "'{{importName}}' import from '{{importSource}}' is restricted.",
133 // eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
134 importNameWithCustomMessage: "'{{importName}}' import from '{{importSource}}' is restricted. {{customMessage}}"
135 },
136
137 schema: {
138 anyOf: [
139 arrayOfStringsOrObjects,
140 {
141 type: "array",
142 items: [{
143 type: "object",
144 properties: {
145 paths: arrayOfStringsOrObjects,
146 patterns: arrayOfStringsOrObjectPatterns
147 },
148 additionalProperties: false
149 }],
150 additionalItems: false
151 }
152 ]
153 }
154 },
155
156 create(context) {
157 const sourceCode = context.sourceCode;
158 const options = Array.isArray(context.options) ? context.options : [];
159 const isPathAndPatternsObject =
160 typeof options[0] === "object" &&
161 (Object.prototype.hasOwnProperty.call(options[0], "paths") || Object.prototype.hasOwnProperty.call(options[0], "patterns"));
162
163 const restrictedPaths = (isPathAndPatternsObject ? options[0].paths : context.options) || [];
164 const restrictedPathMessages = restrictedPaths.reduce((memo, importSource) => {
165 if (typeof importSource === "string") {
166 memo[importSource] = { message: null };
167 } else {
168 memo[importSource.name] = {
169 message: importSource.message,
170 importNames: importSource.importNames
171 };
172 }
173 return memo;
174 }, {});
175
176 // Handle patterns too, either as strings or groups
177 let restrictedPatterns = (isPathAndPatternsObject ? options[0].patterns : []) || [];
178
179 // standardize to array of objects if we have an array of strings
180 if (restrictedPatterns.length > 0 && typeof restrictedPatterns[0] === "string") {
181 restrictedPatterns = [{ group: restrictedPatterns }];
182 }
183
184 // relative paths are supported for this rule
185 const restrictedPatternGroups = restrictedPatterns.map(({ group, message, caseSensitive, importNames, importNamePattern }) => ({
186 matcher: ignore({ allowRelativePaths: true, ignorecase: !caseSensitive }).add(group),
187 customMessage: message,
188 importNames,
189 importNamePattern
190 }));
191
192 // if no imports are restricted we don't need to check
193 if (Object.keys(restrictedPaths).length === 0 && restrictedPatternGroups.length === 0) {
194 return {};
195 }
196
197 /**
198 * Report a restricted path.
199 * @param {string} importSource path of the import
200 * @param {Map<string,Object[]>} importNames Map of import names that are being imported
201 * @param {node} node representing the restricted path reference
202 * @returns {void}
203 * @private
204 */
205 function checkRestrictedPathAndReport(importSource, importNames, node) {
206 if (!Object.prototype.hasOwnProperty.call(restrictedPathMessages, importSource)) {
207 return;
208 }
209
210 const customMessage = restrictedPathMessages[importSource].message;
211 const restrictedImportNames = restrictedPathMessages[importSource].importNames;
212
213 if (restrictedImportNames) {
214 if (importNames.has("*")) {
215 const specifierData = importNames.get("*")[0];
216
217 context.report({
218 node,
219 messageId: customMessage ? "everythingWithCustomMessage" : "everything",
220 loc: specifierData.loc,
221 data: {
222 importSource,
223 importNames: restrictedImportNames,
224 customMessage
225 }
226 });
227 }
228
229 restrictedImportNames.forEach(importName => {
230 if (importNames.has(importName)) {
231 const specifiers = importNames.get(importName);
232
233 specifiers.forEach(specifier => {
234 context.report({
235 node,
236 messageId: customMessage ? "importNameWithCustomMessage" : "importName",
237 loc: specifier.loc,
238 data: {
239 importSource,
240 customMessage,
241 importName
242 }
243 });
244 });
245 }
246 });
247 } else {
248 context.report({
249 node,
250 messageId: customMessage ? "pathWithCustomMessage" : "path",
251 data: {
252 importSource,
253 customMessage
254 }
255 });
256 }
257 }
258
259 /**
260 * Report a restricted path specifically for patterns.
261 * @param {node} node representing the restricted path reference
262 * @param {Object} group contains an Ignore instance for paths, the customMessage to show on failure,
263 * and any restricted import names that have been specified in the config
264 * @param {Map<string,Object[]>} importNames Map of import names that are being imported
265 * @returns {void}
266 * @private
267 */
268 function reportPathForPatterns(node, group, importNames) {
269 const importSource = node.source.value.trim();
270
271 const customMessage = group.customMessage;
272 const restrictedImportNames = group.importNames;
273 const restrictedImportNamePattern = group.importNamePattern ? new RegExp(group.importNamePattern, "u") : null;
274
275 /*
276 * If we are not restricting to any specific import names and just the pattern itself,
277 * report the error and move on
278 */
279 if (!restrictedImportNames && !restrictedImportNamePattern) {
280 context.report({
281 node,
282 messageId: customMessage ? "patternWithCustomMessage" : "patterns",
283 data: {
284 importSource,
285 customMessage
286 }
287 });
288 return;
289 }
290
291 importNames.forEach((specifiers, importName) => {
292 if (importName === "*") {
293 const [specifier] = specifiers;
294
295 if (restrictedImportNames) {
296 context.report({
297 node,
298 messageId: customMessage ? "patternAndEverythingWithCustomMessage" : "patternAndEverything",
299 loc: specifier.loc,
300 data: {
301 importSource,
302 importNames: restrictedImportNames,
303 customMessage
304 }
305 });
306 } else {
307 context.report({
308 node,
309 messageId: customMessage ? "patternAndEverythingWithRegexImportNameAndCustomMessage" : "patternAndEverythingWithRegexImportName",
310 loc: specifier.loc,
311 data: {
312 importSource,
313 importNames: restrictedImportNamePattern,
314 customMessage
315 }
316 });
317 }
318
319 return;
320 }
321
322 if (
323 (restrictedImportNames && restrictedImportNames.includes(importName)) ||
324 (restrictedImportNamePattern && restrictedImportNamePattern.test(importName))
325 ) {
326 specifiers.forEach(specifier => {
327 context.report({
328 node,
329 messageId: customMessage ? "patternAndImportNameWithCustomMessage" : "patternAndImportName",
330 loc: specifier.loc,
331 data: {
332 importSource,
333 customMessage,
334 importName
335 }
336 });
337 });
338 }
339 });
340 }
341
342 /**
343 * Check if the given importSource is restricted by a pattern.
344 * @param {string} importSource path of the import
345 * @param {Object} group contains a Ignore instance for paths, and the customMessage to show if it fails
346 * @returns {boolean} whether the variable is a restricted pattern or not
347 * @private
348 */
349 function isRestrictedPattern(importSource, group) {
350 return group.matcher.ignores(importSource);
351 }
352
353 /**
354 * Checks a node to see if any problems should be reported.
355 * @param {ASTNode} node The node to check.
356 * @returns {void}
357 * @private
358 */
359 function checkNode(node) {
360 const importSource = node.source.value.trim();
361 const importNames = new Map();
362
363 if (node.type === "ExportAllDeclaration") {
364 const starToken = sourceCode.getFirstToken(node, 1);
365
366 importNames.set("*", [{ loc: starToken.loc }]);
367 } else if (node.specifiers) {
368 for (const specifier of node.specifiers) {
369 let name;
370 const specifierData = { loc: specifier.loc };
371
372 if (specifier.type === "ImportDefaultSpecifier") {
373 name = "default";
374 } else if (specifier.type === "ImportNamespaceSpecifier") {
375 name = "*";
376 } else if (specifier.imported) {
377 name = astUtils.getModuleExportName(specifier.imported);
378 } else if (specifier.local) {
379 name = astUtils.getModuleExportName(specifier.local);
380 }
381
382 if (typeof name === "string") {
383 if (importNames.has(name)) {
384 importNames.get(name).push(specifierData);
385 } else {
386 importNames.set(name, [specifierData]);
387 }
388 }
389 }
390 }
391
392 checkRestrictedPathAndReport(importSource, importNames, node);
393 restrictedPatternGroups.forEach(group => {
394 if (isRestrictedPattern(importSource, group)) {
395 reportPathForPatterns(node, group, importNames);
396 }
397 });
398 }
399
400 return {
401 ImportDeclaration: checkNode,
402 ExportNamedDeclaration(node) {
403 if (node.source) {
404 checkNode(node);
405 }
406 },
407 ExportAllDeclaration: checkNode
408 };
409 }
410};
Note: See TracBrowser for help on using the repository browser.