@@ -25,6 +25,7 @@ function normalizeBareSpecifier(p: string): string {
2525 return p . split ( '/' ) [ 0 ]
2626}
2727
28+ // Function to generate @types package names, includes logic to prevent infinite recursion.
2829function toTypesScopedName ( pkg : string ) : string {
2930 if ( pkg . startsWith ( '@types/' ) ) return pkg
3031 if ( pkg . startsWith ( '@' ) ) return '@types/' + pkg . slice ( 1 ) . replace ( '/' , '__' )
@@ -45,11 +46,13 @@ async function fetchJson<T = any>(url: string): Promise<T> {
4546 return res . json ( )
4647}
4748
49+ // Guesses potential type definition file (.d.ts) paths from a JS file path.
4850function guessDtsFromJs ( jsPath : string ) : string [ ] {
4951 const base = stripJsLike ( jsPath )
5052 return [ `${ base } .d.ts` , `${ base } .ts` , `${ base } /index.d.ts` , `${ base } /index.ts` ]
5153}
5254
55+ // Analyzes the 'exports' field of package.json to create a map of subpaths to their type file URLs.
5356function buildExportTypeMap ( pkgName : string , pkgJson : PackageJson ) : Record < string , string [ ] > {
5457 const map : Record < string , string [ ] > = { }
5558 const base = `${ CDN_BASE } ${ pkgName } /`
@@ -68,6 +71,7 @@ function buildExportTypeMap(pkgName: string, pkgJson: PackageJson): Record<strin
6871 }
6972 }
7073 }
74+ // Fallback to 'types'/'typings' field if 'exports' is missing or has no main '.' path defined.
7175 if ( ! map [ '.' ] && ( pkgJson . types || pkgJson . typings ) ) {
7276 push ( '.' , pkgJson . types || pkgJson . typings )
7377 }
@@ -83,6 +87,7 @@ async function tryFetchOne(urls: string[]): Promise<ResolveResult | null> {
8387 return null
8488}
8589
90+ // A crawler that recursively follows imports/exports within a type definition file (.d.ts).
8691async function crawl ( entryUrl : string , pkgName : string , visited : Set < string > , enqueuePackage : ( name : string ) => void ) : Promise < Library [ ] > {
8792 if ( visited . has ( entryUrl ) ) return [ ]
8893 visited . add ( entryUrl )
@@ -97,13 +102,16 @@ async function crawl(entryUrl: string, pkgName: string, visited: Set<string>, en
97102 const crawlNext = ( nextUrl : string ) => {
98103 if ( ! visited . has ( nextUrl ) ) subPromises . push ( crawl ( nextUrl , pkgName , visited , enqueuePackage ) )
99104 }
105+ // Handles triple-slash directives like '/// <reference ... />'.
100106 for ( const m of content . matchAll ( TRIPLE_SLASH_REF_RE ) ) crawlNext ( new URL ( m [ 1 ] , finalUrl ) . href )
101107 for ( const m of content . matchAll ( IMPORT_ANY_RE ) ) {
102108 const spec = ( m [ 1 ] || m [ 2 ] || m [ 3 ] || '' ) . trim ( )
103109 if ( ! spec ) continue
110+ // Continues crawling for relative path imports, and queues up external package imports.
104111 if ( isRelative ( spec ) ) crawlNext ( new URL ( spec , finalUrl ) . href )
105112 else {
106113 const bare = normalizeBareSpecifier ( spec )
114+ // Ignores Node.js built-in modules that use the 'node:' protocol.
107115 if ( bare && ! bare . startsWith ( 'node:' ) ) enqueuePackage ( bare )
108116 }
109117 }
@@ -113,11 +121,13 @@ async function crawl(entryUrl: string, pkgName: string, visited: Set<string>, en
113121 return out
114122}
115123
124+ // Main function for the type loading process. Fetches type files for a package and all its dependencies.
116125export async function startTypeLoadingProcess ( packageName : string ) : Promise < { mainVirtualPath : string ; libs : Library [ ] ; subpathMap : Record < string , string > } | void > {
117126 const visitedPackages = new Set < string > ( )
118127 const collected : Library [ ] = [ ]
119128 const subpathMap : Record < string , string > = { }
120129
130+ // An inner function that recursively loads packages.
121131 async function loadPackage ( pkgNameToLoad : string ) {
122132 if ( visitedPackages . has ( pkgNameToLoad ) ) return
123133 visitedPackages . add ( pkgNameToLoad )
@@ -128,12 +138,14 @@ export async function startTypeLoadingProcess(packageName: string): Promise<{ ma
128138 pkgJson = await fetchJson < PackageJson > ( pkgJsonUrl )
129139 } catch ( e ) {
130140 console . log ( `- Package '${ pkgNameToLoad } ' not found. Attempting @types fallback.` )
141+ // If the package is not found, attempt to find its @types equivalent.
131142 try { await loadPackage ( toTypesScopedName ( pkgNameToLoad ) ) } catch ( ee ) { }
132143 return
133144 }
134145
135146 const exportMap = buildExportTypeMap ( pkgNameToLoad , pkgJson )
136147
148+ // If the package is found but contains no type information, attempt the @types fallback.
137149 if ( Object . keys ( exportMap ) . length === 0 ) {
138150 console . log ( `- No type declarations in '${ pkgNameToLoad } '. Attempting @types fallback.` )
139151 try { await loadPackage ( toTypesScopedName ( pkgNameToLoad ) ) } catch ( ee ) { }
@@ -145,6 +157,7 @@ export async function startTypeLoadingProcess(packageName: string): Promise<{ ma
145157 const enqueuePackage = ( p : string ) => { if ( ! visitedPackages . has ( p ) ) pendingDependencies . add ( p ) }
146158
147159 const crawlPromises : Promise < Library [ ] > [ ] = [ ]
160+ // Crawl all entry points of the package to gather complete type information.
148161 for ( const [ subpath , urls ] of Object . entries ( exportMap ) ) {
149162 const entryPointUrl = urls [ 0 ]
150163 if ( entryPointUrl ) {
@@ -157,6 +170,7 @@ export async function startTypeLoadingProcess(packageName: string): Promise<{ ma
157170 const libsArrays = await Promise . all ( crawlPromises )
158171 libsArrays . forEach ( libs => collected . push ( ...libs ) )
159172
173+ // Recursively load any discovered dependency packages.
160174 if ( pendingDependencies . size > 0 ) {
161175 console . log ( `- Found dependencies for '${ pkgNameToLoad } ': ${ Array . from ( pendingDependencies ) . join ( ', ' ) } ` )
162176 await Promise . all ( Array . from ( pendingDependencies ) . map ( loadPackage ) )
0 commit comments