UNPKG

5.93 kBJavaScriptView Raw
1/**
2 * @fileoverview Restrict usage of specified node modules.
3 * @author Christian Schulz
4 */
5"use strict";
6
7//------------------------------------------------------------------------------
8// Helpers
9//------------------------------------------------------------------------------
10
11const DEFAULT_MESSAGE_TEMPLATE = "'{{moduleName}}' module is restricted from being used.";
12const CUSTOM_MESSAGE_TEMPLATE = "'{{moduleName}}' module is restricted from being used. {{customMessage}}";
13
14//------------------------------------------------------------------------------
15// Rule Definition
16//------------------------------------------------------------------------------
17
18const ignore = require("ignore");
19
20const arrayOfStrings = {
21 type: "array",
22 items: { type: "string" },
23 uniqueItems: true
24};
25
26const arrayOfStringsOrObjects = {
27 type: "array",
28 items: {
29 anyOf: [
30 { type: "string" },
31 {
32 type: "object",
33 properties: {
34 name: { type: "string" },
35 message: {
36 type: "string",
37 minLength: 1
38 }
39 },
40 additionalProperties: false,
41 required: ["name"]
42 }
43 ]
44 },
45 uniqueItems: true
46};
47
48module.exports = {
49 meta: {
50 docs: {
51 description: "disallow specified modules when loaded by `require`",
52 category: "Node.js and CommonJS",
53 recommended: false,
54 url: "https://eslint.org/docs/rules/no-restricted-modules"
55 },
56
57 schema: {
58 anyOf: [
59 arrayOfStringsOrObjects,
60 {
61 type: "array",
62 items: {
63 type: "object",
64 properties: {
65 paths: arrayOfStringsOrObjects,
66 patterns: arrayOfStrings
67 },
68 additionalProperties: false
69 },
70 additionalItems: false
71 }
72 ]
73 }
74 },
75
76 create(context) {
77 const options = Array.isArray(context.options) ? context.options : [];
78 const isPathAndPatternsObject =
79 typeof options[0] === "object" &&
80 (options[0].hasOwnProperty("paths") || options[0].hasOwnProperty("patterns"));
81
82 const restrictedPaths = (isPathAndPatternsObject ? options[0].paths : context.options) || [];
83 const restrictedPatterns = (isPathAndPatternsObject ? options[0].patterns : []) || [];
84
85 const restrictedPathMessages = restrictedPaths.reduce((memo, importName) => {
86 if (typeof importName === "string") {
87 memo[importName] = null;
88 } else {
89 memo[importName.name] = importName.message;
90 }
91 return memo;
92 }, {});
93
94 // if no imports are restricted we don"t need to check
95 if (Object.keys(restrictedPaths).length === 0 && restrictedPatterns.length === 0) {
96 return {};
97 }
98
99 const ig = ignore().add(restrictedPatterns);
100
101
102 /**
103 * Function to check if a node is a string literal.
104 * @param {ASTNode} node The node to check.
105 * @returns {boolean} If the node is a string literal.
106 */
107 function isString(node) {
108 return node && node.type === "Literal" && typeof node.value === "string";
109 }
110
111 /**
112 * Function to check if a node is a require call.
113 * @param {ASTNode} node The node to check.
114 * @returns {boolean} If the node is a require call.
115 */
116 function isRequireCall(node) {
117 return node.callee.type === "Identifier" && node.callee.name === "require";
118 }
119
120 /**
121 * Report a restricted path.
122 * @param {node} node representing the restricted path reference
123 * @returns {void}
124 * @private
125 */
126 function reportPath(node) {
127 const moduleName = node.arguments[0].value.trim();
128 const customMessage = restrictedPathMessages[moduleName];
129 const message = customMessage
130 ? CUSTOM_MESSAGE_TEMPLATE
131 : DEFAULT_MESSAGE_TEMPLATE;
132
133 context.report({
134 node,
135 message,
136 data: {
137 moduleName,
138 customMessage
139 }
140 });
141 }
142
143 /**
144 * Check if the given name is a restricted path name
145 * @param {string} name name of a variable
146 * @returns {boolean} whether the variable is a restricted path or not
147 * @private
148 */
149 function isRestrictedPath(name) {
150 return Object.prototype.hasOwnProperty.call(restrictedPathMessages, name);
151 }
152
153 return {
154 CallExpression(node) {
155 if (isRequireCall(node)) {
156
157 // node has arguments and first argument is string
158 if (node.arguments.length && isString(node.arguments[0])) {
159 const moduleName = node.arguments[0].value.trim();
160
161 // check if argument value is in restricted modules array
162 if (isRestrictedPath(moduleName)) {
163 reportPath(node);
164 }
165
166 if (restrictedPatterns.length > 0 && ig.ignores(moduleName)) {
167 context.report({
168 node,
169 message: "'{{moduleName}}' module is restricted from being used by a pattern.",
170 data: { moduleName }
171 });
172 }
173 }
174 }
175 }
176 };
177 }
178};