@@ -15,6 +15,68 @@ function localPolyfill(name) {
15
15
return path . resolve ( __dirname , 'polyfills' , ...name . split ( '/' ) , 'index.ts' ) ;
16
16
}
17
17
18
+ function normalizeDepName ( name ) {
19
+ return name . replaceAll ( '@' , '' ) . replaceAll ( '/' , '__' ) ;
20
+ }
21
+
22
+ function resolveDepEntry ( name ) {
23
+ const monorepoPackagesDir = path . resolve ( __dirname , '..' ) ;
24
+ const resolvedPath = require . resolve ( name ) ;
25
+ if ( resolvedPath . startsWith ( monorepoPackagesDir ) ) {
26
+ const packageJson = require ( `${ name } /package.json` ) ;
27
+ const packageRoot = path . dirname ( require . resolve ( `${ name } /package.json` ) ) ;
28
+ const entrypoint = path . join (
29
+ packageRoot ,
30
+ packageJson [ 'compass:main' ] ?? packageJson [ 'main' ]
31
+ ) ;
32
+ return entrypoint ;
33
+ }
34
+ return require . resolve ( name ) ;
35
+ }
36
+
37
+ /**
38
+ * Takes in a webpack configuration for a library package and creates a
39
+ * multi-compiler config that splits the library into multiple parts that can be
40
+ * properly processed by another webpack compilation.
41
+ *
42
+ * This is opposed to using a webpack chunk splitting feature that will generate
43
+ * the code that uses internal webpack module runtime that will not be handled
44
+ * by any other bundler (see TODO). This custom code splitting is way less
45
+ * advanced, but works well for leaf node dependencies of the package.
46
+ *
47
+ * TODO(COMPASS-9445): This naive implementation works well only for leaf
48
+ * dependencies with a single export file. A better approach would be to coerce
49
+ * webpack to produce a require-able web bundle, which in theory should be
50
+ * possible with a combination of `splitChunks`, `chunkFormat: 'commonjs'`, and
51
+ * `target: 'web'`, but in practice produced bundle doesn't work due to webpack
52
+ * runtime exports not being built correctly. We should investigate and try to
53
+ * fix this to remove this custom chunk splitting logic.
54
+ */
55
+ function createSiblingBundleFromLeafDeps (
56
+ config ,
57
+ deps ,
58
+ moduleType = 'commonjs2'
59
+ ) {
60
+ const siblings = Object . fromEntries (
61
+ deps . map ( ( depName ) => {
62
+ return [ depName , `${ moduleType } ./${ normalizeDepName ( depName ) } .js` ] ;
63
+ } )
64
+ ) ;
65
+ const baseConfig = merge ( config , { externals : siblings } ) ;
66
+ const configs = [ baseConfig ] . concat (
67
+ deps . map ( ( depName ) => {
68
+ return merge ( baseConfig , {
69
+ entry : resolveDepEntry ( depName ) ,
70
+ output : {
71
+ filename : `${ normalizeDepName ( depName ) } .js` ,
72
+ library : { type : moduleType } ,
73
+ } ,
74
+ } ) ;
75
+ } )
76
+ ) ;
77
+ return configs ;
78
+ }
79
+
18
80
/**
19
81
* Atlas Cloud uses in-flight compression that doesn't compress anything that is
20
82
* bigger than 10MB, we want to make sure that compass-web assets stay under the
@@ -222,7 +284,7 @@ module.exports = (env, args) => {
222
284
} ,
223
285
} ;
224
286
225
- return merge ( config , {
287
+ const compassWebConfig = merge ( config , {
226
288
externals : {
227
289
react : 'commonjs2 react' ,
228
290
'react-dom' : 'commonjs2 react-dom' ,
@@ -258,4 +320,19 @@ module.exports = (env, args) => {
258
320
} ,
259
321
] ,
260
322
} ) ;
323
+
324
+ // Split production bundle into more chunks to make sure it's easier for us to
325
+ // stay under the max chunk limit. Be careful when adding new packages here,
326
+ // make sure you're only selecting big packages with the smallest amount of
327
+ // shared dependencies possible
328
+ const bundles = createSiblingBundleFromLeafDeps ( compassWebConfig , [
329
+ '@mongodb-js/compass-components' ,
330
+ 'ag-grid-community' ,
331
+ 'bson-transpilers' ,
332
+ // bson is not that big, but is a shared dependency of compass-web,
333
+ // compass-components and bson-transpilers, so splitting it out
334
+ 'bson' ,
335
+ ] ) ;
336
+
337
+ return bundles ;
261
338
} ;
0 commit comments