UNPKG

eslint-plugin-no-barrel-files

Version:

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

134 lines 6.67 kB
"use strict"; 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