Path: blob/main/src/rules/no-internal-modules.js
829 views
import minimatch from 'minimatch';12import resolve from 'eslint-module-utils/resolve';3import importType from '../core/importType';4import moduleVisitor from 'eslint-module-utils/moduleVisitor';5import docsUrl from '../docsUrl';67module.exports = {8meta: {9type: 'suggestion',10docs: {11url: docsUrl('no-internal-modules'),12},1314schema: [15{16oneOf: [17{18type: 'object',19properties: {20allow: {21type: 'array',22items: {23type: 'string',24},25},26},27additionalProperties: false,28},29{30type: 'object',31properties: {32forbid: {33type: 'array',34items: {35type: 'string',36},37},38},39additionalProperties: false,40},41],42},43],44},4546create: function noReachingInside(context) {47const options = context.options[0] || {};48const allowRegexps = (options.allow || []).map(p => minimatch.makeRe(p));49const forbidRegexps = (options.forbid || []).map(p => minimatch.makeRe(p));5051// minimatch patterns are expected to use / path separators, like import52// statements, so normalize paths to use the same53function normalizeSep(somePath) {54return somePath.split('\\').join('/');55}5657function toSteps(somePath) {58return normalizeSep(somePath)59.split('/')60.reduce((acc, step) => {61if (!step || step === '.') {62return acc;63} else if (step === '..') {64return acc.slice(0, -1);65} else {66return acc.concat(step);67}68}, []);69}7071// test if reaching to this destination is allowed72function reachingAllowed(importPath) {73return allowRegexps.some(re => re.test(importPath));74}7576// test if reaching to this destination is forbidden77function reachingForbidden(importPath) {78return forbidRegexps.some(re => re.test(importPath));79}8081function isAllowViolation(importPath) {82const steps = toSteps(importPath);8384const nonScopeSteps = steps.filter(step => step.indexOf('@') !== 0);85if (nonScopeSteps.length <= 1) return false;8687// before trying to resolve, see if the raw import (with relative88// segments resolved) matches an allowed pattern89const justSteps = steps.join('/');90if (reachingAllowed(justSteps) || reachingAllowed(`/${justSteps}`)) return false;9192// if the import statement doesn't match directly, try to match the93// resolved path if the import is resolvable94const resolved = resolve(importPath, context);95if (!resolved || reachingAllowed(normalizeSep(resolved))) return false;9697// this import was not allowed by the allowed paths, and reaches98// so it is a violation99return true;100}101102function isForbidViolation(importPath) {103const steps = toSteps(importPath);104105// before trying to resolve, see if the raw import (with relative106// segments resolved) matches a forbidden pattern107const justSteps = steps.join('/');108109if (reachingForbidden(justSteps) || reachingForbidden(`/${justSteps}`)) return true;110111// if the import statement doesn't match directly, try to match the112// resolved path if the import is resolvable113const resolved = resolve(importPath, context);114if (resolved && reachingForbidden(normalizeSep(resolved))) return true;115116// this import was not forbidden by the forbidden paths so it is not a violation117return false;118}119120// find a directory that is being reached into, but which shouldn't be121const isReachViolation = options.forbid ? isForbidViolation : isAllowViolation;122123function checkImportForReaching(importPath, node) {124const potentialViolationTypes = ['parent', 'index', 'sibling', 'external', 'internal'];125if (potentialViolationTypes.indexOf(importType(importPath, context)) !== -1 &&126isReachViolation(importPath)127) {128context.report({129node,130message: `Reaching to "${importPath}" is not allowed.`,131});132}133}134135return moduleVisitor((source) => {136checkImportForReaching(source.value, source);137}, { commonjs: true });138},139};140141142