UNPKG

intellify

Version:

Detect JavaScript & TypeScript errors and make codes more optimized.

224 lines (170 loc) 5.27 kB
import * as acorn from 'acorn'; import { simple as walk } from 'acorn-walk'; import { debugLog, getLineFromLoc } from '../utils/helpers.js'; export function findUnusedVariables(code, filePath) { const unusedVars = []; try { const ast = acorn.parse(code, { ecmaVersion: 'latest', sourceType: 'module', locations: true }); const declarations = new Map(); const imports = new Map(); const references = new Set(); walk(ast, { VariableDeclarator(node) { if (node.id.type === 'Identifier') { declarations.set(node.id.name, { name: node.id.name, file: filePath, line: getLineFromLoc(node.loc), type: 'variable' }); } }, ImportDeclaration(node) { if (node.specifiers) { node.specifiers.forEach(specifier => { if (specifier.type === 'ImportDefaultSpecifier' || specifier.type === 'ImportSpecifier' || specifier.type === 'ImportNamespaceSpecifier') { const importName = specifier.local.name; imports.set(importName, { name: importName, source: node.source.value, file: filePath, line: getLineFromLoc(node.loc), type: 'import' }); } }); } }, CallExpression(node) { if (node.callee && node.callee.type === 'Identifier' && node.callee.name === 'require' && node.arguments.length > 0 && node.arguments[0].type === 'Literal') { if (node.parent && node.parent.type === 'VariableDeclarator' && node.parent.id.type === 'Identifier') { const requireName = node.parent.id.name; imports.set(requireName, { name: requireName, source: node.arguments[0].value, file: filePath, line: getLineFromLoc(node.loc), type: 'require' }); } } } }); walk(ast, { Identifier(node) { if (node.parent && (node.parent.type === 'ImportSpecifier' || node.parent.type === 'ImportDefaultSpecifier' || node.parent.type === 'ImportNamespaceSpecifier')) { return; } if (node.name === 'require' && node.parent && node.parent.type === 'CallExpression') { return; } references.add(node.name); } }); for (const [name, info] of declarations.entries()) { if (!references.has(name)) { unusedVars.push(info); } } for (const [name, info] of imports.entries()) { if (!references.has(name)) { unusedVars.push(info); } } } catch (error) { debugLog(`Error finding unused variables in ${filePath}: ${error.message}`); } return unusedVars; } export function findComments(code, filePath) { const comments = []; try { acorn.parse(code, { ecmaVersion: 'latest', sourceType: 'module', locations: true, onComment: (isBlock, text, start, end, startLoc, endLoc) => { comments.push({ file: filePath, line: startLoc.line, text: isBlock ? `/*${text}*/` : `//${text}`, isBlock: isBlock, start: start, end: end }); } }); } catch (error) { debugLog(`Error finding comments in ${filePath}: ${error.message}`); } return comments; } export function fixComments(code, comments) { let result = code; let removed = 0; let skipped = 0; const sortedComments = [...comments].sort((a, b) => b.start - a.start); for (const comment of sortedComments) { try { const before = result.substring(0, comment.start); const after = result.substring(comment.end); const testCode = before + after; try { acorn.parse(testCode, { ecmaVersion: 'latest', sourceType: 'module' }); result = testCode; removed++; debugLog(`Removed comment on line ${comment.line}`); } catch (syntaxError) { skipped++; debugLog(`Skipped comment on line ${comment.line} (potential syntax issue)`); } } catch (error) { skipped++; debugLog(`Error processing comment on line ${comment.line}: ${error.message}`); } } return { code: result, stats: { removed, skipped } }; } export function findSyntaxErrors(code, filePath) { const errors = []; try { acorn.parse(code, { ecmaVersion: 'latest', sourceType: 'module', locations: true }); } catch (error) { errors.push({ file: filePath, line: error.loc?.line || 'unknown', message: error.message }); } return errors; } export default { findUnusedVariables, findComments, fixComments, findSyntaxErrors };