@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
JavaScript
/**
* 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;