eslint-plugin-no-barrel-files
Version:
ESLint plugin for reducing barrel-file usage in two ways:
134 lines • 6.67 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.isTypeOnlyImport = isTypeOnlyImport;
exports.serializeImportBinding = serializeImportBinding;
exports.serializeImportBindings = serializeImportBindings;
exports.getMergeableImportDeclaration = getMergeableImportDeclaration;
exports.buildAutofix = buildAutofix;
const utils_1 = require("@typescript-eslint/utils");
function isTypeOnlyImport(declaration, specifier = null) {
return declaration.importKind === 'type' || (specifier === null || specifier === void 0 ? void 0 : specifier.importKind) === 'type';
}
function serializeImportBinding(importedName, localName, isTypeOnly) {
if (importedName === 'default') {
return `${isTypeOnly ? 'type ' : ''}default as ${localName}`;
}
const binding = importedName === localName ? importedName : `${importedName} as ${localName}`;
return isTypeOnly ? `type ${binding}` : binding;
}
function serializeImportBindings(bindings, allTypeOnly) {
return bindings
.map(binding => serializeImportBinding(binding.importedName, binding.localName, allTypeOnly ? false : binding.isTypeOnly))
.join(', ');
}
function getMergeableImportDeclaration(node, sourceSpecifier) {
if (node.source.value !== sourceSpecifier) {
return null;
}
if (node.specifiers.some(specifier => 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)) {
return null;
}
return node;
}
function buildAutofix(sourceCode, currentNode, matchedSpecifiers) {
const groupedImports = new Map();
matchedSpecifiers.forEach(({ preferredSourceSpecifier, specifier, reExportTarget }) => {
var _a;
if (!preferredSourceSpecifier) {
return;
}
const importBindings = (_a = groupedImports.get(preferredSourceSpecifier)) !== null && _a !== void 0 ? _a : [];
importBindings.push({
importedName: reExportTarget.importedName,
isTypeOnly: reExportTarget.isTypeOnly,
localName: specifier.local.name,
});
groupedImports.set(preferredSourceSpecifier, importBindings);
});
const mergeTargets = new Map();
for (const sourceSpecifier of groupedImports.keys()) {
const matchingDeclarations = sourceCode.ast.body.filter(statement => statement.type === utils_1.AST_NODE_TYPES.ImportDeclaration &&
statement !== currentNode &&
getMergeableImportDeclaration(statement, sourceSpecifier));
if (matchingDeclarations.length > 1) {
return null;
}
if (matchingDeclarations.length === 1) {
mergeTargets.set(sourceSpecifier, matchingDeclarations[0]);
}
}
return fixer => {
var _a, _b;
const fixes = [];
for (const [sourceSpecifier, bindings] of groupedImports.entries()) {
const mergeTarget = mergeTargets.get(sourceSpecifier);
if (!mergeTarget) {
continue;
}
const existingBindings = new Map();
const existingTypeOnlyByLocalName = new Map();
let hasConflict = false;
mergeTarget.specifiers.forEach(specifier => {
const importSpecifier = specifier;
const localName = importSpecifier.local.name;
const importedName = importSpecifier.imported.name;
const typeOnly = isTypeOnlyImport(mergeTarget, importSpecifier);
const currentImportedName = existingBindings.get(localName);
const currentTypeOnly = existingTypeOnlyByLocalName.get(localName);
if (currentImportedName && (currentImportedName !== importedName || currentTypeOnly !== typeOnly)) {
hasConflict = true;
return;
}
existingBindings.set(localName, importedName);
existingTypeOnlyByLocalName.set(localName, typeOnly);
});
if (hasConflict) {
return null;
}
bindings.forEach(binding => {
const currentImportedName = existingBindings.get(binding.localName);
const currentTypeOnly = existingTypeOnlyByLocalName.get(binding.localName);
if (currentImportedName &&
(currentImportedName !== binding.importedName || currentTypeOnly !== binding.isTypeOnly)) {
hasConflict = true;
return;
}
existingBindings.set(binding.localName, binding.importedName);
existingTypeOnlyByLocalName.set(binding.localName, binding.isTypeOnly);
});
if (hasConflict) {
return null;
}
const quote = ((_a = mergeTarget.source.raw) === null || _a === void 0 ? void 0 : _a.startsWith("'")) ? "'" : '"';
const allTypeOnly = Array.from(existingTypeOnlyByLocalName.values()).every(Boolean);
const serializedBindings = serializeImportBindings(Array.from(existingBindings.entries()).map(([localName, importedName]) => ({
importedName,
isTypeOnly: existingTypeOnlyByLocalName.get(localName),
localName,
})), allTypeOnly);
const mergedImport = `import${allTypeOnly ? ' type' : ''} { ${serializedBindings} } from ${quote}${sourceSpecifier}${quote};`;
fixes.push(fixer.replaceText(mergeTarget, mergedImport));
groupedImports.delete(sourceSpecifier);
}
const quote = ((_b = currentNode.source.raw) === null || _b === void 0 ? void 0 : _b.startsWith("'")) ? "'" : '"';
const replacement = Array.from(groupedImports.entries())
.map(([sourceSpecifier, bindings]) => {
const allTypeOnly = bindings.every(binding => binding.isTypeOnly);
const serializedBindings = serializeImportBindings(bindings, allTypeOnly);
return `import${allTypeOnly ? ' type' : ''} { ${serializedBindings} } from ${quote}${sourceSpecifier}${quote};`;
})
.join('\n');
if (replacement) {
fixes.push(fixer.replaceText(currentNode, replacement));
}
else {
const [start, end] = currentNode.range;
const adjustedEnd = sourceCode.text.slice(end, end + 1) === '\n' ? end + 1 : end;
fixes.push(fixer.removeRange([start, adjustedEnd]));
}
return fixes;
};
}
//# sourceMappingURL=autofix.js.map