2 * @fileoverview Restrict usage of specified node modules.
3 * @author Christian Schulz
7 //------------------------------------------------------------------------------
9 //------------------------------------------------------------------------------
11 const DEFAULT_MESSAGE_TEMPLATE = "'{{moduleName}}' module is restricted from being used.";
12 const CUSTOM_MESSAGE_TEMPLATE = "'{{moduleName}}' module is restricted from being used. {{customMessage}}";
14 //------------------------------------------------------------------------------
16 //------------------------------------------------------------------------------
18 const ignore = require("ignore");
20 const arrayOfStrings = {
22 items: { type: "string" },
26 const arrayOfStringsOrObjects = {
34 name: { type: "string" },
40 additionalProperties: false,
53 description: "disallow specified modules when loaded by `require`",
54 category: "Node.js and CommonJS",
56 url: "https://eslint.org/docs/rules/no-restricted-modules"
61 arrayOfStringsOrObjects,
67 paths: arrayOfStringsOrObjects,
68 patterns: arrayOfStrings
70 additionalProperties: false
72 additionalItems: false
79 const options = Array.isArray(context.options) ? context.options : [];
80 const isPathAndPatternsObject =
81 typeof options[0] === "object" &&
82 (Object.prototype.hasOwnProperty.call(options[0], "paths") || Object.prototype.hasOwnProperty.call(options[0], "patterns"));
84 const restrictedPaths = (isPathAndPatternsObject ? options[0].paths : context.options) || [];
85 const restrictedPatterns = (isPathAndPatternsObject ? options[0].patterns : []) || [];
87 const restrictedPathMessages = restrictedPaths.reduce((memo, importName) => {
88 if (typeof importName === "string") {
89 memo[importName] = null;
91 memo[importName.name] = importName.message;
96 // if no imports are restricted we don"t need to check
97 if (Object.keys(restrictedPaths).length === 0 && restrictedPatterns.length === 0) {
101 const ig = ignore().add(restrictedPatterns);
105 * Function to check if a node is a string literal.
106 * @param {ASTNode} node The node to check.
107 * @returns {boolean} If the node is a string literal.
109 function isString(node) {
110 return node && node.type === "Literal" && typeof node.value === "string";
114 * Function to check if a node is a require call.
115 * @param {ASTNode} node The node to check.
116 * @returns {boolean} If the node is a require call.
118 function isRequireCall(node) {
119 return node.callee.type === "Identifier" && node.callee.name === "require";
123 * Report a restricted path.
124 * @param {node} node representing the restricted path reference
128 function reportPath(node) {
129 const moduleName = node.arguments[0].value.trim();
130 const customMessage = restrictedPathMessages[moduleName];
131 const message = customMessage
132 ? CUSTOM_MESSAGE_TEMPLATE
133 : DEFAULT_MESSAGE_TEMPLATE;
146 * Check if the given name is a restricted path name
147 * @param {string} name name of a variable
148 * @returns {boolean} whether the variable is a restricted path or not
151 function isRestrictedPath(name) {
152 return Object.prototype.hasOwnProperty.call(restrictedPathMessages, name);
156 CallExpression(node) {
157 if (isRequireCall(node)) {
159 // node has arguments and first argument is string
160 if (node.arguments.length && isString(node.arguments[0])) {
161 const moduleName = node.arguments[0].value.trim();
163 // check if argument value is in restricted modules array
164 if (isRestrictedPath(moduleName)) {
168 if (restrictedPatterns.length > 0 && ig.ignores(moduleName)) {
171 message: "'{{moduleName}}' module is restricted from being used by a pattern.",