eslint-plugin-no-barrel-files
Version:
ESLint plugin for reducing barrel-file usage in two ways:
424 lines • 20.7 kB
JavaScript
"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