UNPKG

@sun-asterisk/sunlint

Version:

☀️ SunLint - Multi-language static analysis tool for code quality and security | Sun* Engineering Standards

154 lines (132 loc) 4.13 kB
/** * ESLint-based JavaScript Parser * Uses the same AST infrastructure as ESLint for reliable parsing * Gracefully handles missing peer dependencies */ class ESLintJavaScriptParser { constructor() { this.parser = null; this.parserType = null; this.initParser(); } initParser() { // Try to dynamically load available parsers const parsers = [ { name: '@babel/parser', type: 'babel' }, { name: 'espree', type: 'espree' } ]; for (const { name, type } of parsers) { try { this.parser = require(name); this.parserType = type; break; } catch (error) { // Continue to next parser continue; } } } async parse(code, filePath) { if (!this.parser) return null; try { if (this.parserType === 'babel') { return this.parser.parse(code, { sourceType: 'unambiguous', allowImportExportEverywhere: true, allowReturnOutsideFunction: true, plugins: [ 'jsx', 'typescript', 'decorators-legacy', 'classProperties', 'objectRestSpread', 'optionalChaining', 'nullishCoalescingOperator' ] }); } else if (this.parserType === 'espree') { return this.parser.parse(code, { ecmaVersion: 'latest', sourceType: 'module', ecmaFeatures: { jsx: true, globalReturn: true } }); } } catch (error) { // Parsing failed, return null for fallback return null; } return null; } async analyzeRule(ruleId, code, filePath) { const ast = await this.parse(code, filePath); if (!ast) return null; // Rule-specific AST analysis switch (ruleId) { case 'C010': return this.analyzeBlockNesting(ast, code); case 'C012': return this.analyzeCyclomaticComplexity(ast, code); case 'C015': return this.analyzeFunctionParameters(ast, code); case 'C017': return this.analyzeConstructorLogic(ast, code); default: return null; } } analyzeBlockNesting(ast, code) { const violations = []; const maxDepth = 3; const traverse = (node, depth = 0, parentType = null) => { if (!node || typeof node !== 'object') return; // Control flow statements that create nesting const controlFlowTypes = [ 'IfStatement', 'ForStatement', 'WhileStatement', 'DoWhileStatement', 'SwitchStatement', 'TryStatement', 'CatchClause', 'WithStatement' ]; let currentDepth = depth; if (controlFlowTypes.includes(node.type)) { currentDepth = depth + 1; if (currentDepth > maxDepth) { violations.push({ line: node.loc ? node.loc.start.line : 1, column: node.loc ? node.loc.start.column + 1 : 1, message: `Block nesting depth ${currentDepth} exceeds maximum of ${maxDepth}. Consider refactoring to reduce complexity.`, severity: 'warning', ruleId: 'C010' }); } } // Recursively traverse child nodes for (const key in node) { if (key === 'parent' || key === 'leadingComments' || key === 'trailingComments') { continue; // Skip circular references and comments } const child = node[key]; if (Array.isArray(child)) { child.forEach(item => traverse(item, currentDepth, node.type)); } else if (child && typeof child === 'object' && child.type) { traverse(child, currentDepth, node.type); } } }; traverse(ast); return violations; } analyzeCyclomaticComplexity(ast, code) { // TODO: Implement cyclomatic complexity analysis return []; } analyzeFunctionParameters(ast, code) { // TODO: Implement function parameter analysis return []; } analyzeConstructorLogic(ast, code) { // TODO: Implement constructor logic analysis return []; } } module.exports = ESLintJavaScriptParser;