UNPKG

crapifyme

Version:

Ultra-fast developer productivity CLI tools - remove comments, logs, and more

187 lines 7.24 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ASTAnalyzer = void 0; const parser_1 = require("@babel/parser"); const traverse_1 = __importDefault(require("@babel/traverse")); class ASTAnalyzer { constructor() { this.usedIdentifiers = new Set(); this.importStatements = []; this.scopeChain = []; } analyzeFile(content, filePath) { this.reset(); try { const ast = this.parseCode(content, filePath); this.extractImports(ast, content); this.analyzeUsage(ast); return { imports: this.importStatements, unusedImports: this.findUnusedImports(), duplicateGroups: this.findDuplicateImports(), usedIdentifiers: this.usedIdentifiers, scopeChain: this.scopeChain }; } catch (error) { throw new Error(`AST analysis failed for ${filePath}: ${error.message}`); } } reset() { this.usedIdentifiers.clear(); this.importStatements = []; this.scopeChain = []; } parseCode(content, filePath) { const isTypeScript = /\.tsx?$/.test(filePath); const isJSX = /\.(jsx|tsx)$/.test(filePath); const plugins = ['objectRestSpread', 'functionBind', 'decorators-legacy']; if (isTypeScript) { plugins.push('typescript'); } if (isJSX) { plugins.push('jsx'); } return (0, parser_1.parse)(content, { sourceType: 'module', allowImportExportEverywhere: true, allowReturnOutsideFunction: true, plugins, errorRecovery: true }); } extractImports(ast, content) { const lines = content.split('\n'); (0, traverse_1.default)(ast, { ImportDeclaration: (path) => { const node = path.node; const startLine = (node.loc?.start.line || 1) - 1; const endLine = (node.loc?.end.line || 1) - 1; const leadingComments = node.leadingComments?.map(c => c.value.trim()) || []; const trailingComments = node.trailingComments?.map(c => c.value.trim()) || []; const importKind = node.importKind; const importStatement = { source: node.source.value, specifiers: this.extractSpecifiers(node.specifiers), importKind: importKind === 'type' || importKind === 'typeof' ? importKind : 'value', startPos: node.start || 0, endPos: node.end || 0, leadingComments, trailingComments }; this.importStatements.push(importStatement); } }); } extractSpecifiers(specifiers) { return specifiers.map(spec => { if (spec.type === 'ImportDefaultSpecifier') { return { type: 'default', local: spec.local.name }; } else if (spec.type === 'ImportNamespaceSpecifier') { return { type: 'namespace', local: spec.local.name }; } else if (spec.type === 'ImportSpecifier') { const importKind = spec.importKind; return { type: 'named', imported: 'name' in spec.imported ? spec.imported.name : spec.imported.value, local: spec.local.name, importKind: importKind === 'typeof' || importKind === 'type' ? importKind : 'value' }; } throw new Error(`Unknown import specifier type: ${spec.type}`); }); } analyzeUsage(ast) { const scopeStack = [[]]; (0, traverse_1.default)(ast, { enter: path => { if (path.isFunction() || path.isBlockStatement() || path.isProgram()) { scopeStack.push([]); } }, exit: path => { if (path.isFunction() || path.isBlockStatement() || path.isProgram()) { this.scopeChain.push(scopeStack.pop() || []); } }, Identifier: (path) => { if (path.isReferencedIdentifier()) { this.usedIdentifiers.add(path.node.name); const currentScope = scopeStack[scopeStack.length - 1]; if (currentScope && !currentScope.includes(path.node.name)) { currentScope.push(path.node.name); } } }, VariableDeclarator: path => { if (path.node.id.type === 'Identifier') { const currentScope = scopeStack[scopeStack.length - 1]; if (currentScope) { currentScope.push(path.node.id.name); } } } }); } findUnusedImports() { return this.importStatements.filter(importStmt => { return importStmt.specifiers.some(spec => { return !this.usedIdentifiers.has(spec.local); }); }); } findDuplicateImports() { const sourceGroups = new Map(); for (const importStmt of this.importStatements) { const source = importStmt.source; if (!sourceGroups.has(source)) { sourceGroups.set(source, []); } sourceGroups.get(source).push(importStmt); } return Array.from(sourceGroups.values()).filter(group => group.length > 1); } canMergeImports(imports) { if (imports.length <= 1) return false; const firstImport = imports[0]; return imports.every(imp => imp.importKind === firstImport.importKind && imp.source === firstImport.source); } mergeImports(imports) { if (!this.canMergeImports(imports)) { throw new Error('Cannot merge incompatible imports'); } const mergedSpecifiers = []; const seenLocals = new Set(); for (const importStmt of imports) { for (const spec of importStmt.specifiers) { if (!seenLocals.has(spec.local)) { mergedSpecifiers.push(spec); seenLocals.add(spec.local); } } } return { source: imports[0].source, specifiers: mergedSpecifiers, importKind: imports[0].importKind, startPos: Math.min(...imports.map(i => i.startPos)), endPos: Math.max(...imports.map(i => i.endPos)), leadingComments: imports[0].leadingComments, trailingComments: imports[imports.length - 1].trailingComments }; } } exports.ASTAnalyzer = ASTAnalyzer; //# sourceMappingURL=ast-analyzer.js.map