/** * @author Toru Nagashima * See LICENSE file in root directory for full license. */ "use strict" const path = require("path") const { Minimatch } = require("minimatch") /** @typedef {import("../util/import-target")} ImportTarget */ /** * @typedef {Object} DefinitionData * @property {string | string[]} name The name to disallow. * @property {string} [message] The custom message to show. */ /** * Check if matched or not. * @param {InstanceType} matcher The matcher. * @param {boolean} absolute The flag that the matcher is for absolute paths. * @param {ImportTarget} importee The importee information. */ function match(matcher, absolute, { filePath, name }) { if (absolute) { return filePath != null && matcher.match(filePath) } return matcher.match(name) } /** Restriction. */ class Restriction { /** * Initialize this restriction. * @param {DefinitionData} def The definition of a restriction. */ constructor({ name, message }) { const names = Array.isArray(name) ? name : [name] const matchers = names.map(raw => { const negate = raw[0] === "!" && raw[1] !== "(" const pattern = negate ? raw.slice(1) : raw const absolute = path.isAbsolute(pattern) const matcher = new Minimatch(pattern, { dot: true }) return { absolute, matcher, negate } }) this.matchers = matchers this.message = message ? ` ${message}` : "" } /** * Check if a given importee is disallowed. * @param {ImportTarget} importee The importee to check. * @returns {boolean} `true` if the importee is disallowed. */ match(importee) { return this.matchers.reduce( (ret, { absolute, matcher, negate }) => negate ? ret && !match(matcher, absolute, importee) : ret || match(matcher, absolute, importee), false ) } } /** * Create a restriction. * @param {string | DefinitionData} def A definition. * @returns {Restriction} Created restriction. */ function createRestriction(def) { if (typeof def === "string") { return new Restriction({ name: def }) } return new Restriction(def) } /** * Create restrictions. * @param {(string | DefinitionData | GlobDefinition)[]} defs Definitions. * @returns {(Restriction | GlobRestriction)[]} Created restrictions. */ function createRestrictions(defs) { return (defs || []).map(createRestriction) } /** * Checks if given importees are disallowed or not. * @param {RuleContext} context - A context to report. * @param {ImportTarget[]} targets - A list of target information to check. * @returns {void} */ exports.checkForRestriction = function checkForRestriction(context, targets) { const restrictions = createRestrictions(context.options[0]) for (const target of targets) { const restriction = restrictions.find(r => r.match(target)) if (restriction) { context.report({ node: target.node, messageId: "restricted", data: { name: target.name, customMessage: restriction.message, }, }) } } } exports.messages = { restricted: "'{{name}}' module is restricted from being used.{{customMessage}}", }