@@ -47,6 +47,11 @@ interface LibraryReference {
47
47
readonly shortName : string ;
48
48
}
49
49
50
+ type Export = string | {
51
+ readonly import ?: string ;
52
+ readonly require ?: string ;
53
+ } ;
54
+
50
55
interface PackageJson {
51
56
readonly main ?: string ;
52
57
readonly description ?: string ;
@@ -103,7 +108,7 @@ interface PackageJson {
103
108
*/
104
109
readonly exports ?: Record < string , string > ;
105
110
} ;
106
- exports ?: Record < string , string > ;
111
+ exports ?: Record < string , Export > ;
107
112
}
108
113
109
114
/**
@@ -269,7 +274,10 @@ async function prepareSourceFiles(libraries: readonly LibraryReference[], packag
269
274
// Control 'exports' field of the 'package.json'. This will control what kind of 'import' statements are
270
275
// allowed for this package: we only want to allow the exact import statements that we want to support.
271
276
packageJson . exports = {
272
- '.' : './index.js' ,
277
+ '.' : {
278
+ import : './index.js' ,
279
+ require : './lazy-index.js' ,
280
+ } ,
273
281
274
282
// We need to expose 'package.json' and '.jsii' because 'jsii' and 'jsii-reflect' load them using
275
283
// require(). (-_-). Can be removed after https://github.com/aws/jsii/pull/3205 gets merged.
@@ -281,12 +289,15 @@ async function prepareSourceFiles(libraries: readonly LibraryReference[], packag
281
289
} ;
282
290
283
291
// We use the index.ts to compile type definitions.
284
- // At the very end we replace the compiled index.js file with our fixed version exports.js.
285
- // exports.js has the top level submodules exports defined as a getter function,
286
- // so they are not automatically loaded when importing from `aws-cdk-lib`.
292
+ //
293
+ // We build two indexes: one for eager loading (used by ESM modules), and one
294
+ // for lazy loading (used by CJS modules). The lazy loading will result in faster
295
+ // loading times, because we don't have to load and parse all submodules right away,
296
+ // but is not compatible with ESM's loading algorithm.
297
+ //
287
298
// This improves AWS CDK app performance by ~400ms.
288
299
const indexStatements = new Array < string > ( ) ;
289
- const exportsStatements = new Array < string > ( ) ;
300
+ const lazyExports = new Array < string > ( ) ;
290
301
291
302
for ( const library of libraries ) {
292
303
const libDir = path . join ( libRoot , library . shortName ) ;
@@ -297,19 +308,21 @@ async function prepareSourceFiles(libraries: readonly LibraryReference[], packag
297
308
}
298
309
if ( library . shortName === 'core' ) {
299
310
indexStatements . push ( `export * from './${ library . shortName } ';` ) ;
300
- exportsStatements . unshift ( `export * from './${ library . shortName } ';` ) ;
311
+ lazyExports . unshift ( `export * from './${ library . shortName } ';` ) ;
301
312
} else {
302
- indexStatements . push ( `export * as ${ library . shortName . replace ( / - / g, '_' ) } from './${ library . shortName } ';` ) ;
303
- exportsStatements . push ( `Object.defineProperty(exports, '${ library . shortName . replace ( / - / g, '_' ) } ', { get: function () { return require('./${ library . shortName } '); } });` ) ;
313
+ const exportName = library . shortName . replace ( / - / g, '_' ) ;
314
+
315
+ indexStatements . push ( `export * as ${ exportName } from './${ library . shortName } ';` ) ;
316
+ lazyExports . push ( `Object.defineProperty(exports, '${ exportName } ', { get: function () { return require('./${ library . shortName } '); } });` ) ;
304
317
}
305
318
copySubmoduleExports ( packageJson . exports , library , library . shortName ) ;
306
319
}
307
320
308
321
// make the exports.ts file pass linting
309
- exportsStatements . unshift ( '/* eslint-disable @typescript-eslint/no-require-imports */' ) ;
322
+ lazyExports . unshift ( '/* eslint-disable @typescript-eslint/no-require-imports */' ) ;
310
323
311
324
await fs . writeFile ( path . join ( libRoot , 'index.ts' ) , indexStatements . join ( '\n' ) , { encoding : 'utf8' } ) ;
312
- await fs . writeFile ( path . join ( libRoot , 'exports .ts' ) , exportsStatements . join ( '\n' ) , { encoding : 'utf8' } ) ;
325
+ await fs . writeFile ( path . join ( libRoot , 'lazy-index .ts' ) , lazyExports . join ( '\n' ) , { encoding : 'utf8' } ) ;
313
326
314
327
console . log ( '\t🍺 Success!' ) ;
315
328
}
@@ -320,13 +333,21 @@ async function prepareSourceFiles(libraries: readonly LibraryReference[], packag
320
333
* Replace the original 'main' export with an export of the new '<submodule>/index.ts` file we've written
321
334
* in 'transformPackage'.
322
335
*/
323
- function copySubmoduleExports ( targetExports : Record < string , string > , library : LibraryReference , subdirectory : string ) {
336
+ function copySubmoduleExports ( targetExports : Record < string , Export > , library : LibraryReference , subdirectory : string ) {
324
337
const visibleName = library . shortName ;
325
338
326
339
// Do both REAL "exports" section, as well as virtual, ubergen-only "exports" section
327
340
for ( const exportSet of [ library . packageJson . exports , library . packageJson . ubergen ?. exports ] ) {
328
341
for ( const [ relPath , relSource ] of Object . entries ( exportSet ?? { } ) ) {
329
- targetExports [ `./${ unixPath ( path . join ( visibleName , relPath ) ) } ` ] = `./${ unixPath ( path . join ( subdirectory , relSource ) ) } ` ;
342
+ targetExports [ `./${ unixPath ( path . join ( visibleName , relPath ) ) } ` ] = resolveExport ( relSource ) ;
343
+ }
344
+ }
345
+
346
+ function resolveExport < A extends Export > ( exp : A ) : A {
347
+ if ( typeof exp === 'string' ) {
348
+ return `./${ unixPath ( path . join ( subdirectory , exp ) ) } ` as any ;
349
+ } else {
350
+ return Object . fromEntries ( Object . entries ( exp ) . map ( ( [ k , v ] ) => [ k , v ? resolveExport ( v ) : undefined ] ) ) as any ;
330
351
}
331
352
}
332
353
0 commit comments