--- /dev/null
+/**
+ * @fileoverview Restrict usage of specified node modules.
+ * @author Christian Schulz
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const DEFAULT_MESSAGE_TEMPLATE = "'{{moduleName}}' module is restricted from being used.";
+const CUSTOM_MESSAGE_TEMPLATE = "'{{moduleName}}' module is restricted from being used. {{customMessage}}";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+const ignore = require("ignore");
+
+const arrayOfStrings = {
+ type: "array",
+ items: { type: "string" },
+ uniqueItems: true
+};
+
+const arrayOfStringsOrObjects = {
+ type: "array",
+ items: {
+ anyOf: [
+ { type: "string" },
+ {
+ type: "object",
+ properties: {
+ name: { type: "string" },
+ message: {
+ type: "string",
+ minLength: 1
+ }
+ },
+ additionalProperties: false,
+ required: ["name"]
+ }
+ ]
+ },
+ uniqueItems: true
+};
+
+module.exports = {
+ meta: {
+ type: "suggestion",
+
+ docs: {
+ description: "disallow specified modules when loaded by `require`",
+ category: "Node.js and CommonJS",
+ recommended: false,
+ url: "https://eslint.org/docs/rules/no-restricted-modules"
+ },
+
+ schema: {
+ anyOf: [
+ arrayOfStringsOrObjects,
+ {
+ type: "array",
+ items: {
+ type: "object",
+ properties: {
+ paths: arrayOfStringsOrObjects,
+ patterns: arrayOfStrings
+ },
+ additionalProperties: false
+ },
+ additionalItems: false
+ }
+ ]
+ }
+ },
+
+ create(context) {
+ const options = Array.isArray(context.options) ? context.options : [];
+ const isPathAndPatternsObject =
+ typeof options[0] === "object" &&
+ (Object.prototype.hasOwnProperty.call(options[0], "paths") || Object.prototype.hasOwnProperty.call(options[0], "patterns"));
+
+ const restrictedPaths = (isPathAndPatternsObject ? options[0].paths : context.options) || [];
+ const restrictedPatterns = (isPathAndPatternsObject ? options[0].patterns : []) || [];
+
+ const restrictedPathMessages = restrictedPaths.reduce((memo, importName) => {
+ if (typeof importName === "string") {
+ memo[importName] = null;
+ } else {
+ memo[importName.name] = importName.message;
+ }
+ return memo;
+ }, {});
+
+ // if no imports are restricted we don"t need to check
+ if (Object.keys(restrictedPaths).length === 0 && restrictedPatterns.length === 0) {
+ return {};
+ }
+
+ const ig = ignore().add(restrictedPatterns);
+
+
+ /**
+ * Function to check if a node is a string literal.
+ * @param {ASTNode} node The node to check.
+ * @returns {boolean} If the node is a string literal.
+ */
+ function isString(node) {
+ return node && node.type === "Literal" && typeof node.value === "string";
+ }
+
+ /**
+ * Function to check if a node is a require call.
+ * @param {ASTNode} node The node to check.
+ * @returns {boolean} If the node is a require call.
+ */
+ function isRequireCall(node) {
+ return node.callee.type === "Identifier" && node.callee.name === "require";
+ }
+
+ /**
+ * Report a restricted path.
+ * @param {node} node representing the restricted path reference
+ * @returns {void}
+ * @private
+ */
+ function reportPath(node) {
+ const moduleName = node.arguments[0].value.trim();
+ const customMessage = restrictedPathMessages[moduleName];
+ const message = customMessage
+ ? CUSTOM_MESSAGE_TEMPLATE
+ : DEFAULT_MESSAGE_TEMPLATE;
+
+ context.report({
+ node,
+ message,
+ data: {
+ moduleName,
+ customMessage
+ }
+ });
+ }
+
+ /**
+ * Check if the given name is a restricted path name
+ * @param {string} name name of a variable
+ * @returns {boolean} whether the variable is a restricted path or not
+ * @private
+ */
+ function isRestrictedPath(name) {
+ return Object.prototype.hasOwnProperty.call(restrictedPathMessages, name);
+ }
+
+ return {
+ CallExpression(node) {
+ if (isRequireCall(node)) {
+
+ // node has arguments and first argument is string
+ if (node.arguments.length && isString(node.arguments[0])) {
+ const moduleName = node.arguments[0].value.trim();
+
+ // check if argument value is in restricted modules array
+ if (isRestrictedPath(moduleName)) {
+ reportPath(node);
+ }
+
+ if (restrictedPatterns.length > 0 && ig.ignores(moduleName)) {
+ context.report({
+ node,
+ message: "'{{moduleName}}' module is restricted from being used by a pattern.",
+ data: { moduleName }
+ });
+ }
+ }
+ }
+ }
+ };
+ }
+};