eslint-plugin-no-barrel-files
Version:
ESLint plugin for reducing barrel-file usage in two ways:
213 lines • 11 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.__private__ = void 0;
const utils_1 = require("@typescript-eslint/utils");
const minimatch_1 = require("minimatch");
const analysis_1 = require("./prefer-source-imports/analysis");
const autofix_1 = require("./prefer-source-imports/autofix");
const path_utils_1 = require("./prefer-source-imports/path-utils");
const resolution_1 = require("./prefer-source-imports/resolution");
function isNamedImportSpecifier(specifier) {
return (specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier &&
specifier.imported.type === utils_1.AST_NODE_TYPES.Identifier &&
specifier.local.type === utils_1.AST_NODE_TYPES.Identifier);
}
function isSupportedImportSpecifier(specifier) {
return specifier.type === utils_1.AST_NODE_TYPES.ImportDefaultSpecifier || isNamedImportSpecifier(specifier);
}
function shouldReportMissingTypeScript(_filename, options) {
return (options === null || options === void 0 ? void 0 : options.tsconfig) !== false && !(0, resolution_1.hasTypeScriptModule)();
}
// ESLint creates a rule instance for every file. Parsed barrel modules are
// immutable for this rule's fixes, so share them across those instances.
const sharedAnalysisCaches = (0, analysis_1.createAnalysisCaches)();
const preferSourceImports = {
defaultOptions: [{}],
meta: {
type: 'suggestion',
fixable: 'code',
docs: {
url: 'https://github.com/art0rz/eslint-plugin-no-barrel-files',
description: 'prefer importing from source modules instead of barrel re-exports',
},
schema: [
{
type: 'object',
properties: {
fixStyle: {
type: 'string',
enum: ['auto', 'preserve-alias', 'relative'],
},
ignore: {
type: 'array',
items: { type: 'string' },
},
tsconfig: {
anyOf: [{ type: 'boolean' }, { type: 'string' }],
},
paths: {
type: 'object',
additionalProperties: {
anyOf: [
{ type: 'string' },
{
type: 'array',
items: { type: 'string' },
},
],
},
},
},
additionalProperties: false,
},
],
messages: {
missingTypeScript: "prefer-source-imports requires the 'typescript' package when tsconfig resolution is enabled. Install 'typescript' in the consuming project or set 'tsconfig: false'.",
preferSourceImport: "Import '{{name}}' directly from '{{source}}' instead of barrel '{{barrel}}'.",
preferSourceImports: "Import directly from source modules instead of barrel '{{barrel}}'.",
},
},
create(context) {
const [options] = context.options;
const sourceCode = context.sourceCode;
const barrelExportCache = new Map();
const analysisCaches = sharedAnalysisCaches;
const resolutionCaches = (0, resolution_1.createSharedResolutionCaches)();
const cwd = process.cwd();
const missingTypeScript = shouldReportMissingTypeScript(context.filename, options);
return {
Program(node) {
if (!missingTypeScript) {
return;
}
context.report({
node,
messageId: 'missingTypeScript',
});
},
ImportDeclaration(node) {
var _a;
if (missingTypeScript) {
return;
}
if (!node.source.value || typeof node.source.value !== 'string' || node.specifiers.length === 0) {
return;
}
if ((_a = options === null || options === void 0 ? void 0 : options.ignore) === null || _a === void 0 ? void 0 : _a.some(pattern => (0, minimatch_1.minimatch)(node.source.value, pattern))) {
return;
}
const supportedSpecifiers = node.specifiers.filter(isSupportedImportSpecifier);
if (supportedSpecifiers.length === 0) {
return;
}
const importerFilename = context.filename;
const barrelFilePath = (0, resolution_1.resolveImport)(options, resolutionCaches, importerFilename, node.source.value, cwd);
if (!barrelFilePath) {
return;
}
const barrelAnalysis = (0, resolution_1.getBarrelAnalysis)(options, barrelFilePath, barrelExportCache, resolutionCaches, analysisCaches, cwd);
if (!barrelAnalysis) {
return;
}
const matchedSpecifiers = [];
supportedSpecifiers.forEach(specifier => {
const specifierIsTypeOnly = specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier ? (0, autofix_1.isTypeOnlyImport)(node, specifier) : false;
const importedName = specifier.type === utils_1.AST_NODE_TYPES.ImportDefaultSpecifier ? 'default' : specifier.imported.name;
const reExportKey = (0, analysis_1.getReExportKey)(importedName, specifierIsTypeOnly);
const explicitReExport = barrelAnalysis.explicitReExports.get(reExportKey);
if (explicitReExport) {
matchedSpecifiers.push({
preferredSourceSpecifier: (0, resolution_1.getPreferredSourceSpecifier)(options, importerFilename, node.source.value, explicitReExport, resolutionCaches.tsconfigInfo, cwd, resolutionCaches.tsconfigPaths),
specifier,
reExportTarget: explicitReExport,
});
return;
}
const exportAllReExport = barrelAnalysis.exportAllReExports.get(reExportKey);
if (!exportAllReExport) {
return;
}
matchedSpecifiers.push({
preferredSourceSpecifier: (0, resolution_1.getPreferredSourceSpecifier)(options, importerFilename, node.source.value, exportAllReExport, resolutionCaches.tsconfigInfo, cwd, resolutionCaches.tsconfigPaths),
specifier,
reExportTarget: exportAllReExport,
});
});
if (matchedSpecifiers.length === 0) {
return;
}
const canAutoFix = matchedSpecifiers.every(({ preferredSourceSpecifier }) => preferredSourceSpecifier !== null) &&
matchedSpecifiers.length === node.specifiers.length &&
node.specifiers.every(isSupportedImportSpecifier);
const autofix = canAutoFix ? (0, autofix_1.buildAutofix)(sourceCode, node, matchedSpecifiers) : null;
if (autofix) {
context.report({
node,
messageId: 'preferSourceImports',
data: {
barrel: node.source.value,
},
fix: autofix,
});
return;
}
matchedSpecifiers.forEach(({ specifier, reExportTarget }) => {
var _a;
context.report({
node: specifier,
messageId: 'preferSourceImport',
data: {
barrel: node.source.value,
name: specifier.local.name,
source: (_a = (0, resolution_1.getPreferredSourceSpecifier)(options, importerFilename, node.source.value, reExportTarget, resolutionCaches.tsconfigInfo, cwd, resolutionCaches.tsconfigPaths)) !== null && _a !== void 0 ? _a : reExportTarget.sourceSpecifier,
},
});
});
},
};
},
};
exports.__private__ = {
applyAliasTarget: path_utils_1.applyAliasTarget,
buildAliasSpecifier: path_utils_1.buildAliasSpecifier,
buildAutofix: autofix_1.buildAutofix,
collectAllExportedBindings: analysis_1.collectAllExportedBindings,
collectBarrelAnalysis: analysis_1.collectBarrelAnalysis,
collectExportedBindings: analysis_1.collectExportedBindings,
createAnalysisCaches: analysis_1.createAnalysisCaches,
createResolutionCaches: resolution_1.createResolutionCaches,
createSharedResolutionCaches: resolution_1.createSharedResolutionCaches,
getBarrelAnalysis: resolution_1.getBarrelAnalysis,
getManualAliasMappings: resolution_1.getManualAliasMappings,
getTypeScriptModule: resolution_1.getTypeScriptModule,
hasTypeScriptModule: resolution_1.hasTypeScriptModule,
getMergeableImportDeclaration: autofix_1.getMergeableImportDeclaration,
getPreferredSourceSpecifier: resolution_1.getPreferredSourceSpecifier,
getReExportKey: analysis_1.getReExportKey,
getReExportMeta: analysis_1.getReExportMeta,
getTsconfigInfo: resolution_1.getTsconfigInfo,
getTsconfigPath: resolution_1.getTsconfigPath,
isNamedImportSpecifier,
isSupportedImportSpecifier,
isRelativePath: path_utils_1.isRelativePath,
isTypeOnlyImport: autofix_1.isTypeOnlyImport,
matchesAliasPattern: path_utils_1.matchesAliasPattern,
normalizeModulePath: path_utils_1.normalizeModulePath,
parseBarrelFile: analysis_1.parseBarrelFile,
parseExportedBindings: analysis_1.parseExportedBindings,
parseModule: analysis_1.parseModule,
resolveImport: resolution_1.resolveImport,
resolveModuleFile: path_utils_1.resolveModuleFile,
resolveReExportTarget: analysis_1.resolveReExportTarget,
resolveWithManualPaths: resolution_1.resolveWithManualPaths,
resolveWithTsconfig: resolution_1.resolveWithTsconfig,
reverseResolveManualAlias: resolution_1.reverseResolveManualAlias,
reverseResolveTsconfigAlias: resolution_1.reverseResolveTsconfigAlias,
setTypeScriptModuleLoaderForTests: resolution_1.setTypeScriptModuleLoaderForTests,
shouldReportMissingTypeScript,
serializeImportBinding: autofix_1.serializeImportBinding,
serializeImportBindings: autofix_1.serializeImportBindings,
toRelativeImportSpecifier: path_utils_1.toRelativeImportSpecifier,
};
exports.default = preferSourceImports;
//# sourceMappingURL=prefer-source-imports.js.map