massive update, probably broken
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / no-restricted-imports.js
1 /**
2  * @fileoverview Restrict usage of specified node imports.
3  * @author Guy Ellis
4  */
5 "use strict";
6
7 //------------------------------------------------------------------------------
8 // Rule Definition
9 //------------------------------------------------------------------------------
10
11 const ignore = require("ignore");
12
13 const arrayOfStringsOrObjects = {
14     type: "array",
15     items: {
16         anyOf: [
17             { type: "string" },
18             {
19                 type: "object",
20                 properties: {
21                     name: { type: "string" },
22                     message: {
23                         type: "string",
24                         minLength: 1
25                     },
26                     importNames: {
27                         type: "array",
28                         items: {
29                             type: "string"
30                         }
31                     }
32                 },
33                 additionalProperties: false,
34                 required: ["name"]
35             }
36         ]
37     },
38     uniqueItems: true
39 };
40
41 const arrayOfStringsOrObjectPatterns = {
42     anyOf: [
43         {
44             type: "array",
45             items: {
46                 type: "string"
47             },
48             uniqueItems: true
49         },
50         {
51             type: "array",
52             items: {
53                 type: "object",
54                 properties: {
55                     group: {
56                         type: "array",
57                         items: {
58                             type: "string"
59                         },
60                         minItems: 1,
61                         uniqueItems: true
62                     },
63                     message: {
64                         type: "string",
65                         minLength: 1
66                     }
67                 },
68                 additionalProperties: false,
69                 required: ["group"]
70             },
71             uniqueItems: true
72         }
73     ]
74 };
75
76 module.exports = {
77     meta: {
78         type: "suggestion",
79
80         docs: {
81             description: "disallow specified modules when loaded by `import`",
82             category: "ECMAScript 6",
83             recommended: false,
84             url: "https://eslint.org/docs/rules/no-restricted-imports"
85         },
86
87         messages: {
88             path: "'{{importSource}}' import is restricted from being used.",
89             // eslint-disable-next-line eslint-plugin/report-message-format
90             pathWithCustomMessage: "'{{importSource}}' import is restricted from being used. {{customMessage}}",
91
92             patterns: "'{{importSource}}' import is restricted from being used by a pattern.",
93             // eslint-disable-next-line eslint-plugin/report-message-format
94             patternWithCustomMessage: "'{{importSource}}' import is restricted from being used by a pattern. {{customMessage}}",
95
96             everything: "* import is invalid because '{{importNames}}' from '{{importSource}}' is restricted.",
97             // eslint-disable-next-line eslint-plugin/report-message-format
98             everythingWithCustomMessage: "* import is invalid because '{{importNames}}' from '{{importSource}}' is restricted. {{customMessage}}",
99
100             importName: "'{{importName}}' import from '{{importSource}}' is restricted.",
101             // eslint-disable-next-line eslint-plugin/report-message-format
102             importNameWithCustomMessage: "'{{importName}}' import from '{{importSource}}' is restricted. {{customMessage}}"
103         },
104
105         schema: {
106             anyOf: [
107                 arrayOfStringsOrObjects,
108                 {
109                     type: "array",
110                     items: [{
111                         type: "object",
112                         properties: {
113                             paths: arrayOfStringsOrObjects,
114                             patterns: arrayOfStringsOrObjectPatterns
115                         },
116                         additionalProperties: false
117                     }],
118                     additionalItems: false
119                 }
120             ]
121         }
122     },
123
124     create(context) {
125         const sourceCode = context.getSourceCode();
126         const options = Array.isArray(context.options) ? context.options : [];
127         const isPathAndPatternsObject =
128             typeof options[0] === "object" &&
129             (Object.prototype.hasOwnProperty.call(options[0], "paths") || Object.prototype.hasOwnProperty.call(options[0], "patterns"));
130
131         const restrictedPaths = (isPathAndPatternsObject ? options[0].paths : context.options) || [];
132         const restrictedPathMessages = restrictedPaths.reduce((memo, importSource) => {
133             if (typeof importSource === "string") {
134                 memo[importSource] = { message: null };
135             } else {
136                 memo[importSource.name] = {
137                     message: importSource.message,
138                     importNames: importSource.importNames
139                 };
140             }
141             return memo;
142         }, {});
143
144         // Handle patterns too, either as strings or groups
145         const restrictedPatterns = (isPathAndPatternsObject ? options[0].patterns : []) || [];
146         const restrictedPatternGroups = restrictedPatterns.length > 0 && typeof restrictedPatterns[0] === "string"
147             ? [{ matcher: ignore().add(restrictedPatterns) }]
148             : restrictedPatterns.map(({ group, message }) => ({ matcher: ignore().add(group), customMessage: message }));
149
150         // if no imports are restricted we don"t need to check
151         if (Object.keys(restrictedPaths).length === 0 && restrictedPatternGroups.length === 0) {
152             return {};
153         }
154
155         /**
156          * Report a restricted path.
157          * @param {string} importSource path of the import
158          * @param {Map<string,Object[]>} importNames Map of import names that are being imported
159          * @param {node} node representing the restricted path reference
160          * @returns {void}
161          * @private
162          */
163         function checkRestrictedPathAndReport(importSource, importNames, node) {
164             if (!Object.prototype.hasOwnProperty.call(restrictedPathMessages, importSource)) {
165                 return;
166             }
167
168             const customMessage = restrictedPathMessages[importSource].message;
169             const restrictedImportNames = restrictedPathMessages[importSource].importNames;
170
171             if (restrictedImportNames) {
172                 if (importNames.has("*")) {
173                     const specifierData = importNames.get("*")[0];
174
175                     context.report({
176                         node,
177                         messageId: customMessage ? "everythingWithCustomMessage" : "everything",
178                         loc: specifierData.loc,
179                         data: {
180                             importSource,
181                             importNames: restrictedImportNames,
182                             customMessage
183                         }
184                     });
185                 }
186
187                 restrictedImportNames.forEach(importName => {
188                     if (importNames.has(importName)) {
189                         const specifiers = importNames.get(importName);
190
191                         specifiers.forEach(specifier => {
192                             context.report({
193                                 node,
194                                 messageId: customMessage ? "importNameWithCustomMessage" : "importName",
195                                 loc: specifier.loc,
196                                 data: {
197                                     importSource,
198                                     customMessage,
199                                     importName
200                                 }
201                             });
202                         });
203                     }
204                 });
205             } else {
206                 context.report({
207                     node,
208                     messageId: customMessage ? "pathWithCustomMessage" : "path",
209                     data: {
210                         importSource,
211                         customMessage
212                     }
213                 });
214             }
215         }
216
217         /**
218          * Report a restricted path specifically for patterns.
219          * @param {node} node representing the restricted path reference
220          * @param {Object} group contains a Ignore instance for paths, and the customMessage to show if it fails
221          * @returns {void}
222          * @private
223          */
224         function reportPathForPatterns(node, group) {
225             const importSource = node.source.value.trim();
226
227             context.report({
228                 node,
229                 messageId: group.customMessage ? "patternWithCustomMessage" : "patterns",
230                 data: {
231                     importSource,
232                     customMessage: group.customMessage
233                 }
234             });
235         }
236
237         /**
238          * Check if the given importSource is restricted by a pattern.
239          * @param {string} importSource path of the import
240          * @param {Object} group contains a Ignore instance for paths, and the customMessage to show if it fails
241          * @returns {boolean} whether the variable is a restricted pattern or not
242          * @private
243          */
244         function isRestrictedPattern(importSource, group) {
245             return group.matcher.ignores(importSource);
246         }
247
248         /**
249          * Checks a node to see if any problems should be reported.
250          * @param {ASTNode} node The node to check.
251          * @returns {void}
252          * @private
253          */
254         function checkNode(node) {
255             const importSource = node.source.value.trim();
256             const importNames = new Map();
257
258             if (node.type === "ExportAllDeclaration") {
259                 const starToken = sourceCode.getFirstToken(node, 1);
260
261                 importNames.set("*", [{ loc: starToken.loc }]);
262             } else if (node.specifiers) {
263                 for (const specifier of node.specifiers) {
264                     let name;
265                     const specifierData = { loc: specifier.loc };
266
267                     if (specifier.type === "ImportDefaultSpecifier") {
268                         name = "default";
269                     } else if (specifier.type === "ImportNamespaceSpecifier") {
270                         name = "*";
271                     } else if (specifier.imported) {
272                         name = specifier.imported.name;
273                     } else if (specifier.local) {
274                         name = specifier.local.name;
275                     }
276
277                     if (name) {
278                         if (importNames.has(name)) {
279                             importNames.get(name).push(specifierData);
280                         } else {
281                             importNames.set(name, [specifierData]);
282                         }
283                     }
284                 }
285             }
286
287             checkRestrictedPathAndReport(importSource, importNames, node);
288             restrictedPatternGroups.forEach(group => {
289                 if (isRestrictedPattern(importSource, group)) {
290                     reportPathForPatterns(node, group);
291                 }
292             });
293         }
294
295         return {
296             ImportDeclaration: checkNode,
297             ExportNamedDeclaration(node) {
298                 if (node.source) {
299                     checkNode(node);
300                 }
301             },
302             ExportAllDeclaration: checkNode
303         };
304     }
305 };