@@ -234,17 +234,28 @@ function findFunctionNode(rootNode, startLine, _endLine, rules) {
234234 * @param {object } [engineOpts] - engine options (unused; always uses WASM for AST)
235235 */
236236export async function buildComplexityMetrics ( db , fileSymbols , rootDir , _engineOpts ) {
237- const { createParsers, getParser } = await import ( './parser.js' ) ;
238- const parsers = await createParsers ( ) ;
239-
240- // Map extensions to language IDs
241- const { LANGUAGE_REGISTRY } = await import ( './parser.js' ) ;
242- const extToLang = new Map ( ) ;
243- for ( const entry of LANGUAGE_REGISTRY ) {
244- for ( const ext of entry . extensions ) {
245- extToLang . set ( ext , entry . id ) ;
237+ // Only initialize WASM parsers if some files lack a cached tree (native engine path)
238+ let parsers = null ;
239+ let extToLang = null ;
240+ let needsFallback = false ;
241+ for ( const [ , symbols ] of fileSymbols ) {
242+ if ( ! symbols . _tree ) {
243+ needsFallback = true ;
244+ break ;
246245 }
247246 }
247+ if ( needsFallback ) {
248+ const { createParsers, LANGUAGE_REGISTRY } = await import ( './parser.js' ) ;
249+ parsers = await createParsers ( ) ;
250+ extToLang = new Map ( ) ;
251+ for ( const entry of LANGUAGE_REGISTRY ) {
252+ for ( const ext of entry . extensions ) {
253+ extToLang . set ( ext , entry . id ) ;
254+ }
255+ }
256+ }
257+
258+ const { getParser } = await import ( './parser.js' ) ;
248259
249260 const upsert = db . prepare (
250261 'INSERT OR REPLACE INTO function_complexity (node_id, cognitive, cyclomatic, max_nesting) VALUES (?, ?, ?, ?)' ,
@@ -257,30 +268,35 @@ export async function buildComplexityMetrics(db, fileSymbols, rootDir, _engineOp
257268
258269 const tx = db . transaction ( ( ) => {
259270 for ( const [ relPath , symbols ] of fileSymbols ) {
260- const ext = path . extname ( relPath ) . toLowerCase ( ) ;
261- const langId = extToLang . get ( ext ) ;
262- if ( ! langId ) continue ;
271+ let tree = symbols . _tree ;
272+ let langId = symbols . _langId ;
273+
274+ // Fallback: re-read and re-parse when no cached tree (native engine)
275+ if ( ! tree ) {
276+ const ext = path . extname ( relPath ) . toLowerCase ( ) ;
277+ langId = extToLang . get ( ext ) ;
278+ if ( ! langId ) continue ;
279+
280+ const absPath = path . join ( rootDir , relPath ) ;
281+ let code ;
282+ try {
283+ code = fs . readFileSync ( absPath , 'utf-8' ) ;
284+ } catch {
285+ continue ;
286+ }
263287
264- const rules = COMPLEXITY_RULES . get ( langId ) ;
265- if ( ! rules ) continue ;
288+ const parser = getParser ( parsers , absPath ) ;
289+ if ( ! parser ) continue ;
266290
267- const absPath = path . join ( rootDir , relPath ) ;
268- let code ;
269- try {
270- code = fs . readFileSync ( absPath , 'utf-8' ) ;
271- } catch {
272- continue ;
291+ try {
292+ tree = parser . parse ( code ) ;
293+ } catch {
294+ continue ;
295+ }
273296 }
274297
275- const parser = getParser ( parsers , absPath ) ;
276- if ( ! parser ) continue ;
277-
278- let tree ;
279- try {
280- tree = parser . parse ( code ) ;
281- } catch {
282- continue ;
283- }
298+ const rules = COMPLEXITY_RULES . get ( langId ) ;
299+ if ( ! rules ) continue ;
284300
285301 for ( const def of symbols . definitions ) {
286302 if ( def . kind !== 'function' && def . kind !== 'method' ) continue ;
@@ -298,6 +314,9 @@ export async function buildComplexityMetrics(db, fileSymbols, rootDir, _engineOp
298314 upsert . run ( row . id , result . cognitive , result . cyclomatic , result . maxNesting ) ;
299315 analyzed ++ ;
300316 }
317+
318+ // Release cached tree for GC
319+ symbols . _tree = null ;
301320 }
302321 } ) ;
303322
0 commit comments