UNPKG

eslint-plugin-no-barrel-files

Version:

ESLint plugin for reducing barrel-file usage in two ways:

424 lines 20.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.createAnalysisCaches = createAnalysisCaches; exports.getReExportKey = getReExportKey; exports.getReExportMeta = getReExportMeta; exports.parseModule = parseModule; exports.collectExportedBindings = collectExportedBindings; exports.parseExportedBindings = parseExportedBindings; exports.collectAllExportedBindings = collectAllExportedBindings; exports.resolveReExportTarget = resolveReExportTarget; exports.collectBarrelAnalysis = collectBarrelAnalysis; exports.parseBarrelFile = parseBarrelFile; const node_fs_1 = __importDefault(require("node:fs")); const utils_1 = require("@typescript-eslint/utils"); // eslint-disable-next-line @typescript-eslint/no-require-imports const parser = require('@typescript-eslint/parser'); function createAnalysisCaches() { return { exportedBindings: new Map(), parsedModules: new Map(), }; } function getReExportKey(exportedName, isTypeOnly) { return `${isTypeOnly ? 'type' : 'value'}:${exportedName}`; } function getReExportMeta(reExportKey) { if (reExportKey.startsWith('type:')) { return { exportedName: reExportKey.slice(5), isTypeOnly: true, }; } return { exportedName: reExportKey.slice(6), isTypeOnly: false, }; } function parseModule(filePath, analysisCaches = createAnalysisCaches()) { const cachedProgram = analysisCaches.parsedModules.get(filePath); if (cachedProgram !== undefined) { return cachedProgram; } let sourceText; try { sourceText = node_fs_1.default.readFileSync(filePath, 'utf8'); } catch (_a) { analysisCaches.parsedModules.set(filePath, null); return null; } try { const program = parser.parse(sourceText, { filePath, ecmaVersion: 'latest', sourceType: 'module', range: false, loc: false, comment: false, tokens: false, }); analysisCaches.parsedModules.set(filePath, program); return program; } catch (_b) { analysisCaches.parsedModules.set(filePath, null); return null; } } function collectExportedBindings(program) { const exportedBindings = new Set(); function addExportedBinding(exportedName, isTypeOnly) { exportedBindings.add(getReExportKey(exportedName, isTypeOnly)); } function collectBindingNames(name) { switch (name.type) { case utils_1.AST_NODE_TYPES.Identifier: return [name.name]; case utils_1.AST_NODE_TYPES.ObjectPattern: return name.properties.flatMap(property => { if (property.type === utils_1.AST_NODE_TYPES.Property) { if (property.value.type === utils_1.AST_NODE_TYPES.Identifier || property.value.type === utils_1.AST_NODE_TYPES.ObjectPattern || property.value.type === utils_1.AST_NODE_TYPES.ArrayPattern || property.value.type === utils_1.AST_NODE_TYPES.AssignmentPattern) { return collectBindingNames(property.value); } return []; } if (property.type === utils_1.AST_NODE_TYPES.RestElement) { if (property.argument.type === utils_1.AST_NODE_TYPES.Identifier || property.argument.type === utils_1.AST_NODE_TYPES.ObjectPattern || property.argument.type === utils_1.AST_NODE_TYPES.ArrayPattern || property.argument.type === utils_1.AST_NODE_TYPES.AssignmentPattern) { return collectBindingNames(property.argument); } return []; } return []; }); case utils_1.AST_NODE_TYPES.ArrayPattern: return name.elements.flatMap(element => { if (!element) { return []; } if (element.type === utils_1.AST_NODE_TYPES.RestElement) { if (element.argument.type === utils_1.AST_NODE_TYPES.Identifier || element.argument.type === utils_1.AST_NODE_TYPES.ObjectPattern || element.argument.type === utils_1.AST_NODE_TYPES.ArrayPattern || element.argument.type === utils_1.AST_NODE_TYPES.AssignmentPattern) { return collectBindingNames(element.argument); } return []; } if (element.type === utils_1.AST_NODE_TYPES.Identifier || element.type === utils_1.AST_NODE_TYPES.ObjectPattern || element.type === utils_1.AST_NODE_TYPES.ArrayPattern || element.type === utils_1.AST_NODE_TYPES.AssignmentPattern) { return collectBindingNames(element); } return []; }); case utils_1.AST_NODE_TYPES.AssignmentPattern: return collectBindingNames(name.left); case utils_1.AST_NODE_TYPES.RestElement: if (name.argument.type === utils_1.AST_NODE_TYPES.Identifier || name.argument.type === utils_1.AST_NODE_TYPES.ObjectPattern || name.argument.type === utils_1.AST_NODE_TYPES.ArrayPattern || name.argument.type === utils_1.AST_NODE_TYPES.AssignmentPattern) { return collectBindingNames(name.argument); } return []; } } program.body.forEach(statement => { if (statement.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration) { if (statement.declaration) { switch (statement.declaration.type) { case utils_1.AST_NODE_TYPES.VariableDeclaration: statement.declaration.declarations.forEach(declaration => { collectBindingNames(declaration.id).forEach(bindingName => { addExportedBinding(bindingName, false); }); }); break; case utils_1.AST_NODE_TYPES.FunctionDeclaration: if (statement.declaration.id) { addExportedBinding(statement.declaration.id.name, false); } break; case utils_1.AST_NODE_TYPES.ClassDeclaration: case utils_1.AST_NODE_TYPES.TSEnumDeclaration: if (statement.declaration.id) { addExportedBinding(statement.declaration.id.name, false); addExportedBinding(statement.declaration.id.name, true); } break; case utils_1.AST_NODE_TYPES.TSInterfaceDeclaration: case utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration: if (statement.declaration.id) { addExportedBinding(statement.declaration.id.name, true); } break; } } statement.specifiers.forEach(specifier => { if (specifier.type !== utils_1.AST_NODE_TYPES.ExportSpecifier) { return; } if (specifier.exported.type === utils_1.AST_NODE_TYPES.Identifier) { addExportedBinding(specifier.exported.name, statement.exportKind === 'type' || specifier.exportKind === 'type'); } }); } }); return exportedBindings; } function parseExportedBindings(filePath, analysisCaches = createAnalysisCaches()) { const cachedBindings = analysisCaches.exportedBindings.get(filePath); if (cachedBindings) { return cachedBindings; } const program = parseModule(filePath, analysisCaches); if (!program) { const emptyBindings = new Set(); analysisCaches.exportedBindings.set(filePath, emptyBindings); return emptyBindings; } const exportedBindings = collectExportedBindings(program); analysisCaches.exportedBindings.set(filePath, exportedBindings); return exportedBindings; } function collectAllExportedBindings(filePath, resolveImport, analysisCaches = createAnalysisCaches(), visitedFiles = new Set()) { if (visitedFiles.has(filePath)) { return new Set(); } const program = parseModule(filePath, analysisCaches); if (!program) { return new Set(); } const nextVisitedFiles = new Set(visitedFiles); nextVisitedFiles.add(filePath); const exportedBindings = collectExportedBindings(program); program.body.forEach(statement => { if (statement.type !== utils_1.AST_NODE_TYPES.ExportAllDeclaration || statement.exported || statement.source.type !== utils_1.AST_NODE_TYPES.Literal || typeof statement.source.value !== 'string') { return; } const resolvedSourceFile = resolveImport(filePath, statement.source.value); if (!resolvedSourceFile) { return; } const nestedBindings = collectAllExportedBindings(resolvedSourceFile, resolveImport, analysisCaches, nextVisitedFiles); nestedBindings.forEach(reExportKey => { const { isTypeOnly } = getReExportMeta(reExportKey); if (statement.exportKind === 'type' && !isTypeOnly) { return; } if (exportedBindings.has(reExportKey)) { return; } exportedBindings.add(reExportKey); }); }); return exportedBindings; } function resolveReExportTarget(filePath, exportedName, isTypeOnly, resolveImport, analysisCaches = createAnalysisCaches(), visitedTargets = new Set()) { var _a, _b; const reExportKey = getReExportKey(exportedName, isTypeOnly); const visitedTargetKey = `${filePath}:${reExportKey}`; if (visitedTargets.has(visitedTargetKey)) { return null; } const program = parseModule(filePath, analysisCaches); if (!program) { return null; } const nextVisitedTargets = new Set(visitedTargets); nextVisitedTargets.add(visitedTargetKey); const barrelAnalysis = collectBarrelAnalysis(program, filePath, resolveImport, analysisCaches, nextVisitedTargets); if (!barrelAnalysis) { return null; } return ((_b = (_a = barrelAnalysis.explicitReExports.get(reExportKey)) !== null && _a !== void 0 ? _a : barrelAnalysis.exportAllReExports.get(reExportKey)) !== null && _b !== void 0 ? _b : null); } function collectBarrelAnalysis(program, barrelFilePath, resolveImport, analysisCaches = createAnalysisCaches(), visitedTargets = new Set()) { const explicitReExports = new Map(); const exportAllReExports = new Map(); const importedBindings = new Map(); program.body.forEach(statement => { if (statement.type !== utils_1.AST_NODE_TYPES.ImportDeclaration || statement.source.type !== utils_1.AST_NODE_TYPES.Literal || typeof statement.source.value !== 'string') { return; } const resolvedSourceFile = resolveImport(barrelFilePath, statement.source.value); if (!resolvedSourceFile) { return; } statement.specifiers.forEach(specifier => { const importedName = specifier.type === utils_1.AST_NODE_TYPES.ImportDefaultSpecifier ? 'default' : specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier && specifier.imported.type === utils_1.AST_NODE_TYPES.Identifier ? specifier.imported.name : null; if (!importedName) { return; } importedBindings.set(specifier.local.name, { importedName, isTypeOnly: statement.importKind === 'type' || (specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier && specifier.importKind === 'type'), resolvedFilePath: resolvedSourceFile, sourceSpecifier: statement.source.value, }); }); }); program.body.forEach(statement => { const source = statement.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration ? statement.source : null; if (statement.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration && (source === null || source === void 0 ? void 0 : source.type) === utils_1.AST_NODE_TYPES.Literal && typeof source.value === 'string') { const resolvedSourceFile = resolveImport(barrelFilePath, source.value); if (!resolvedSourceFile) { return; } statement.specifiers.forEach(specifier => { var _a; if (specifier.type !== utils_1.AST_NODE_TYPES.ExportSpecifier || specifier.local.type !== utils_1.AST_NODE_TYPES.Identifier || specifier.exported.type !== utils_1.AST_NODE_TYPES.Identifier) { return; } const specifierIsTypeOnly = statement.exportKind === 'type' || specifier.exportKind === 'type'; const sourceExportedBindings = parseExportedBindings(resolvedSourceFile, analysisCaches); const resolvedTarget = (_a = resolveReExportTarget(resolvedSourceFile, specifier.local.name, specifierIsTypeOnly, resolveImport, analysisCaches, visitedTargets)) !== null && _a !== void 0 ? _a : { importedName: specifier.local.name, resolvedFilePath: resolvedSourceFile, sourceSpecifier: source.value, isTypeOnly: specifierIsTypeOnly, fromExportAll: false, }; if (resolvedTarget.resolvedFilePath === barrelFilePath) { return; } explicitReExports.set(getReExportKey(specifier.exported.name, specifierIsTypeOnly), { importedName: resolvedTarget.importedName, resolvedFilePath: resolvedTarget.resolvedFilePath, sourceSpecifier: resolvedTarget.sourceSpecifier, isTypeOnly: specifierIsTypeOnly, fromExportAll: false, }); if (!specifierIsTypeOnly && sourceExportedBindings.has(getReExportKey(specifier.local.name, true))) { explicitReExports.set(getReExportKey(specifier.exported.name, true), { importedName: resolvedTarget.importedName, resolvedFilePath: resolvedTarget.resolvedFilePath, sourceSpecifier: resolvedTarget.sourceSpecifier, isTypeOnly: true, fromExportAll: false, }); } }); } if (statement.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration && !statement.source) { statement.specifiers.forEach(specifier => { var _a; if (specifier.type !== utils_1.AST_NODE_TYPES.ExportSpecifier || specifier.local.type !== utils_1.AST_NODE_TYPES.Identifier || specifier.exported.type !== utils_1.AST_NODE_TYPES.Identifier) { return; } const importedBinding = importedBindings.get(specifier.local.name); const specifierIsTypeOnly = statement.exportKind === 'type' || specifier.exportKind === 'type'; if (!importedBinding || (importedBinding.isTypeOnly && !specifierIsTypeOnly)) { return; } const sourceExportedBindings = parseExportedBindings(importedBinding.resolvedFilePath, analysisCaches); const resolvedTarget = (_a = resolveReExportTarget(importedBinding.resolvedFilePath, importedBinding.importedName, specifierIsTypeOnly, resolveImport, analysisCaches, visitedTargets)) !== null && _a !== void 0 ? _a : { importedName: importedBinding.importedName, resolvedFilePath: importedBinding.resolvedFilePath, sourceSpecifier: importedBinding.sourceSpecifier, isTypeOnly: specifierIsTypeOnly, fromExportAll: false, }; if (resolvedTarget.resolvedFilePath === barrelFilePath) { return; } explicitReExports.set(getReExportKey(specifier.exported.name, specifierIsTypeOnly), { importedName: resolvedTarget.importedName, resolvedFilePath: resolvedTarget.resolvedFilePath, sourceSpecifier: resolvedTarget.sourceSpecifier, isTypeOnly: specifierIsTypeOnly, fromExportAll: false, }); if (!specifierIsTypeOnly && sourceExportedBindings.has(getReExportKey(importedBinding.importedName, true))) { explicitReExports.set(getReExportKey(specifier.exported.name, true), { importedName: resolvedTarget.importedName, resolvedFilePath: resolvedTarget.resolvedFilePath, sourceSpecifier: resolvedTarget.sourceSpecifier, isTypeOnly: true, fromExportAll: false, }); } }); } if (statement.type === utils_1.AST_NODE_TYPES.ExportAllDeclaration && !statement.exported && statement.source !== null && statement.source.type === utils_1.AST_NODE_TYPES.Literal && typeof statement.source.value === 'string') { const resolvedSourceFile = resolveImport(barrelFilePath, statement.source.value); if (!resolvedSourceFile) { return; } const exportedBindings = collectAllExportedBindings(resolvedSourceFile, resolveImport, analysisCaches, new Set([barrelFilePath])); exportedBindings.forEach(reExportKey => { var _a; const { exportedName, isTypeOnly } = getReExportMeta(reExportKey); if (statement.exportKind === 'type' && !isTypeOnly) { return; } if (explicitReExports.has(reExportKey) || exportAllReExports.has(reExportKey)) { return; } const resolvedTarget = (_a = resolveReExportTarget(resolvedSourceFile, exportedName, isTypeOnly, resolveImport, analysisCaches, visitedTargets)) !== null && _a !== void 0 ? _a : { importedName: exportedName, resolvedFilePath: resolvedSourceFile, sourceSpecifier: statement.source.value, isTypeOnly, fromExportAll: true, }; if (resolvedTarget.resolvedFilePath === barrelFilePath) { return; } exportAllReExports.set(reExportKey, { importedName: resolvedTarget.importedName, resolvedFilePath: resolvedTarget.resolvedFilePath, sourceSpecifier: resolvedTarget.sourceSpecifier, isTypeOnly, fromExportAll: true, }); }); } }); if (explicitReExports.size === 0 && exportAllReExports.size === 0) { return null; } return { explicitReExports, exportAllReExports, }; } function parseBarrelFile(barrelFilePath, resolveImport, analysisCaches = createAnalysisCaches()) { const program = parseModule(barrelFilePath, analysisCaches); if (!program) { return null; } return collectBarrelAnalysis(program, barrelFilePath, resolveImport, analysisCaches); } //# sourceMappingURL=analysis.js.map