@@ -395,12 +395,23 @@ async function backfillTypeMap(filePath, source) {
395395 }
396396 const parsers = await createParsers ( ) ;
397397 const extracted = wasmExtractSymbols ( parsers , filePath , code ) ;
398- if ( ! extracted ?. symbols ?. typeMap ) return { typeMap : [ ] , backfilled : false } ;
399- const tm = extracted . symbols . typeMap ;
400- return {
401- typeMap : tm instanceof Map ? tm : new Map ( tm . map ( ( e ) => [ e . name , e . typeName ] ) ) ,
402- backfilled : true ,
403- } ;
398+ try {
399+ if ( ! extracted ?. symbols ?. typeMap ) {
400+ return { typeMap : [ ] , backfilled : false } ;
401+ }
402+ const tm = extracted . symbols . typeMap ;
403+ return {
404+ typeMap : tm instanceof Map ? tm : new Map ( tm . map ( ( e ) => [ e . name , e . typeName ] ) ) ,
405+ backfilled : true ,
406+ } ;
407+ } finally {
408+ // Free the WASM tree to prevent memory accumulation across repeated builds
409+ if ( extracted ?. tree && typeof extracted . tree . delete === 'function' ) {
410+ try {
411+ extracted . tree . delete ( ) ;
412+ } catch { }
413+ }
414+ }
404415}
405416
406417/**
@@ -441,7 +452,13 @@ export async function parseFileAuto(filePath, source, opts = {}) {
441452 const result = native . parseFile ( filePath , source , ! ! opts . dataflow , opts . ast !== false ) ;
442453 if ( ! result ) return null ;
443454 const patched = patchNativeResult ( result ) ;
444- if ( ! patched . typeMap || patched . typeMap . length === 0 ) {
455+ // Only backfill typeMap for TS/TSX — JS files have no type annotations,
456+ // and the native engine already handles `new Expr()` patterns.
457+ const TS_BACKFILL_EXTS = new Set ( [ '.ts' , '.tsx' ] ) ;
458+ if (
459+ ( ! patched . typeMap || patched . typeMap . length === 0 ) &&
460+ TS_BACKFILL_EXTS . has ( path . extname ( filePath ) )
461+ ) {
445462 const { typeMap, backfilled } = await backfillTypeMap ( filePath , source ) ;
446463 patched . typeMap = typeMap ;
447464 if ( backfilled ) patched . _typeMapBackfilled = true ;
@@ -486,21 +503,35 @@ export async function parseFilesAuto(filePaths, rootDir, opts = {}) {
486503 }
487504 // Backfill typeMap via WASM for native binaries that predate the type-map feature
488505 if ( needsTypeMap . length > 0 ) {
489- const parsers = await createParsers ( ) ;
490- for ( const { filePath, relPath } of needsTypeMap ) {
491- try {
492- const code = fs . readFileSync ( filePath , 'utf-8' ) ;
493- const extracted = wasmExtractSymbols ( parsers , filePath , code ) ;
494- if ( extracted ?. symbols ?. typeMap ) {
495- const symbols = result . get ( relPath ) ;
496- symbols . typeMap =
497- extracted . symbols . typeMap instanceof Map
498- ? extracted . symbols . typeMap
499- : new Map ( extracted . symbols . typeMap . map ( ( e ) => [ e . name , e . typeName ] ) ) ;
500- symbols . _typeMapBackfilled = true ;
506+ // Only backfill for languages where WASM extraction can produce typeMap
507+ // (TS/TSX have type annotations; JS only has `new Expr()` which native already handles)
508+ const TS_EXTS = new Set ( [ '.ts' , '.tsx' ] ) ;
509+ const tsFiles = needsTypeMap . filter ( ( { filePath } ) => TS_EXTS . has ( path . extname ( filePath ) ) ) ;
510+ if ( tsFiles . length > 0 ) {
511+ const parsers = await createParsers ( ) ;
512+ for ( const { filePath, relPath } of tsFiles ) {
513+ let extracted ;
514+ try {
515+ const code = fs . readFileSync ( filePath , 'utf-8' ) ;
516+ extracted = wasmExtractSymbols ( parsers , filePath , code ) ;
517+ if ( extracted ?. symbols ?. typeMap ) {
518+ const symbols = result . get ( relPath ) ;
519+ symbols . typeMap =
520+ extracted . symbols . typeMap instanceof Map
521+ ? extracted . symbols . typeMap
522+ : new Map ( extracted . symbols . typeMap . map ( ( e ) => [ e . name , e . typeName ] ) ) ;
523+ symbols . _typeMapBackfilled = true ;
524+ }
525+ } catch {
526+ /* skip — typeMap is a best-effort backfill */
527+ } finally {
528+ // Free the WASM tree to prevent memory accumulation across repeated builds
529+ if ( extracted ?. tree && typeof extracted . tree . delete === 'function' ) {
530+ try {
531+ extracted . tree . delete ( ) ;
532+ } catch { }
533+ }
501534 }
502- } catch {
503- /* skip — typeMap is a best-effort backfill */
504535 }
505536 }
506537 }
@@ -578,7 +609,13 @@ export async function parseFileIncremental(cache, filePath, source, opts = {}) {
578609 const result = cache . parseFile ( filePath , source ) ;
579610 if ( ! result ) return null ;
580611 const patched = patchNativeResult ( result ) ;
581- if ( ! patched . typeMap || patched . typeMap . length === 0 ) {
612+ // Only backfill typeMap for TS/TSX — JS files have no type annotations,
613+ // and the native engine already handles `new Expr()` patterns.
614+ const TS_BACKFILL_EXTS = new Set ( [ '.ts' , '.tsx' ] ) ;
615+ if (
616+ ( ! patched . typeMap || patched . typeMap . length === 0 ) &&
617+ TS_BACKFILL_EXTS . has ( path . extname ( filePath ) )
618+ ) {
582619 const { typeMap, backfilled } = await backfillTypeMap ( filePath , source ) ;
583620 patched . typeMap = typeMap ;
584621 if ( backfilled ) patched . _typeMapBackfilled = true ;
0 commit comments