@@ -76,8 +76,9 @@ export class TypingsLoader {
7676
7777 for ( const library of this . librariesToGetTypesFrom ) {
7878 // The library's package.json is where the type definitions are defined
79+ const packageJsonFsPath = `./node_modules/${ library } /package.json` ;
7980 const packageJsonContent = await this . webContainer . fs
80- . readFile ( `./node_modules/ ${ library } /package.json` , 'utf-8' )
81+ . readFile ( packageJsonFsPath , 'utf-8' )
8182 . catch ( ( error ) => {
8283 // Note: "ENOENT" errors occurs:
8384 // - While resetting the NodeRuntimeSandbox.
@@ -94,15 +95,31 @@ export class TypingsLoader {
9495 // if the package.json content is empty, skip this library
9596 if ( ! packageJsonContent ) continue ;
9697
98+ // Ensure the worker VFS also receives the package.json file so NodeNext resolution
99+ // can read "exports"/"types" information when resolving imports like '@angular/core'.
100+ filesToRead . push ( `/node_modules/${ library } /package.json` ) ;
101+
97102 const packageJson = JSON . parse ( packageJsonContent ) ;
98103
99- // If the package.json doesn't have `exports`, skip this library
104+ // If the package exposes a top-level types entry, include that directory as a fallback
105+ const topLevelTypes : string | undefined = packageJson . types ?? packageJson . typings ;
106+ if ( ! packageJson ?. exports && topLevelTypes ) {
107+ const path = `/node_modules/${ library } /${ this . normalizePath ( topLevelTypes ) } ` ;
108+ const directory = path . substring ( 0 , path . lastIndexOf ( '/' ) ) ;
109+ directoriesToRead . push ( directory ) ;
110+ continue ;
111+ }
112+
100113 if ( ! packageJson ?. exports ) continue ;
101114
102115 // Based on `exports` we can identify paths to the types definition files
103116 for ( const exportKey of Object . keys ( packageJson . exports ) ) {
104117 const exportEntry = packageJson . exports [ exportKey ] ;
105- const types : string | undefined = exportEntry . typings ?? exportEntry . types ;
118+ // Handle both object and string entries; for strings we can't infer types, so skip
119+ const types : string | undefined =
120+ exportEntry && typeof exportEntry === 'object'
121+ ? ( exportEntry . typings ?? exportEntry . types )
122+ : undefined ;
106123
107124 if ( types ) {
108125 const path = `/node_modules/${ library } /${ this . normalizePath ( types ) } ` ;
@@ -131,9 +148,38 @@ export class TypingsLoader {
131148 private async getTypeDefinitionFilesFromDirectory ( directory : string ) : Promise < string [ ] > {
132149 if ( ! this . webContainer ) throw new Error ( 'this.webContainer is not defined' ) ;
133150
134- const files = await this . webContainer . fs . readdir ( directory ) ;
151+ return this . getTypeDefinitionFilesRecursively ( directory ) ;
152+ }
153+
154+ private async getTypeDefinitionFilesRecursively ( directory : string ) : Promise < string [ ] > {
155+ if ( ! this . webContainer ) throw new Error ( 'this.webContainer is not defined' ) ;
156+
157+ const results : string [ ] = [ ] ;
158+ const entries = await this . webContainer . fs . readdir ( directory ) . catch ( ( error ) => {
159+ // Directory may not exist (e.g., optional export) — ignore.
160+ if ( error ?. message . startsWith ( 'ENOENT' ) ) return [ ] ;
161+ throw error ;
162+ } ) ;
163+
164+ for ( const entry of entries ) {
165+ const fullPath = `${ directory } /${ entry } ` ;
166+ // Try to read as directory; if it fails, treat as file
167+ const children = await this . webContainer . fs . readdir ( fullPath ) . catch ( ( err ) => {
168+ // Not a directory or doesn't exist
169+ return null ;
170+ } ) ;
171+
172+ if ( Array . isArray ( children ) ) {
173+ const nested = await this . getTypeDefinitionFilesRecursively ( fullPath ) ;
174+ results . push ( ...nested ) ;
175+ } else {
176+ if ( this . isTypeDefinitionFile ( fullPath ) ) {
177+ results . push ( fullPath ) ;
178+ }
179+ }
180+ }
135181
136- return files . filter ( this . isTypeDefinitionFile ) . map ( ( file ) => ` ${ directory } / ${ file } ` ) ;
182+ return results ;
137183 }
138184
139185 private isTypeDefinitionFile ( path : string ) : boolean {
0 commit comments