Path: blob/main/src/rules/no-restricted-paths.js
829 views
import path from 'path';12import resolve from 'eslint-module-utils/resolve';3import moduleVisitor from 'eslint-module-utils/moduleVisitor';4import isGlob from 'is-glob';5import { Minimatch, default as minimatch } from 'minimatch';6import docsUrl from '../docsUrl';7import importType from '../core/importType';89const containsPath = (filepath, target) => {10const relative = path.relative(target, filepath);11return relative === '' || !relative.startsWith('..');12};1314module.exports = {15meta: {16type: 'problem',17docs: {18url: docsUrl('no-restricted-paths'),19},2021schema: [22{23type: 'object',24properties: {25zones: {26type: 'array',27minItems: 1,28items: {29type: 'object',30properties: {31target: { type: 'string' },32from: { type: 'string' },33except: {34type: 'array',35items: {36type: 'string',37},38uniqueItems: true,39},40message: { type: 'string' },41},42additionalProperties: false,43},44},45basePath: { type: 'string' },46},47additionalProperties: false,48},49],50},5152create: function noRestrictedPaths(context) {53const options = context.options[0] || {};54const restrictedPaths = options.zones || [];55const basePath = options.basePath || process.cwd();56const currentFilename = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename();57const matchingZones = restrictedPaths.filter((zone) => {58const targetPath = path.resolve(basePath, zone.target);5960if (isGlob(targetPath)) {61return minimatch(currentFilename, targetPath);62}6364return containsPath(currentFilename, targetPath);65});6667function isValidExceptionPath(absoluteFromPath, absoluteExceptionPath) {68const relativeExceptionPath = path.relative(absoluteFromPath, absoluteExceptionPath);6970return importType(relativeExceptionPath, context) !== 'parent';71}7273function reportInvalidExceptionPath(node) {74context.report({75node,76message: 'Restricted path exceptions must be descendants of the configured `from` path for that zone.',77});78}7980function reportInvalidExceptionGlob(node) {81context.report({82node,83message: 'Restricted path exceptions must be glob patterns when`from` is a glob pattern',84});85}8687const makePathValidator = (zoneFrom, zoneExcept = []) => {88const absoluteFrom = path.resolve(basePath, zoneFrom);89const isGlobPattern = isGlob(zoneFrom);90let isPathRestricted;91let hasValidExceptions;92let isPathException;93let reportInvalidException;9495if (isGlobPattern) {96const mm = new Minimatch(absoluteFrom);97isPathRestricted = (absoluteImportPath) => mm.match(absoluteImportPath);9899hasValidExceptions = zoneExcept.every(isGlob);100101if (hasValidExceptions) {102const exceptionsMm = zoneExcept.map((except) => new Minimatch(except));103isPathException = (absoluteImportPath) => exceptionsMm.some((mm) => mm.match(absoluteImportPath));104}105106reportInvalidException = reportInvalidExceptionGlob;107} else {108isPathRestricted = (absoluteImportPath) => containsPath(absoluteImportPath, absoluteFrom);109110const absoluteExceptionPaths = zoneExcept111.map((exceptionPath) => path.resolve(absoluteFrom, exceptionPath));112hasValidExceptions = absoluteExceptionPaths113.every((absoluteExceptionPath) => isValidExceptionPath(absoluteFrom, absoluteExceptionPath));114115if (hasValidExceptions) {116isPathException = (absoluteImportPath) => absoluteExceptionPaths.some(117(absoluteExceptionPath) => containsPath(absoluteImportPath, absoluteExceptionPath),118);119}120121reportInvalidException = reportInvalidExceptionPath;122}123124return {125isPathRestricted,126hasValidExceptions,127isPathException,128reportInvalidException,129};130};131132const validators = [];133134function checkForRestrictedImportPath(importPath, node) {135const absoluteImportPath = resolve(importPath, context);136137if (!absoluteImportPath) {138return;139}140141matchingZones.forEach((zone, index) => {142if (!validators[index]) {143validators[index] = makePathValidator(zone.from, zone.except);144}145146const {147isPathRestricted,148hasValidExceptions,149isPathException,150reportInvalidException,151} = validators[index];152153if (!isPathRestricted(absoluteImportPath)) {154return;155}156157if (!hasValidExceptions) {158reportInvalidException(node);159return;160}161162const pathIsExcepted = isPathException(absoluteImportPath);163if (pathIsExcepted) {164return;165}166167context.report({168node,169message: `Unexpected path "{{importPath}}" imported in restricted zone.${zone.message ? ` ${zone.message}` : ''}`,170data: { importPath },171});172});173}174175return moduleVisitor((source) => {176checkForRestrictedImportPath(source.value, source);177}, { commonjs: true });178},179};180181182