@@ -15,6 +15,68 @@ function localPolyfill(name) {
1515 return path . resolve ( __dirname , 'polyfills' , ...name . split ( '/' ) , 'index.ts' ) ;
1616}
1717
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+
1880/**
1981 * Atlas Cloud uses in-flight compression that doesn't compress anything that is
2082 * bigger than 10MB, we want to make sure that compass-web assets stay under the
@@ -222,7 +284,7 @@ module.exports = (env, args) => {
222284 } ,
223285 } ;
224286
225- return merge ( config , {
287+ const compassWebConfig = merge ( config , {
226288 externals : {
227289 react : 'commonjs2 react' ,
228290 'react-dom' : 'commonjs2 react-dom' ,
@@ -258,4 +320,19 @@ module.exports = (env, args) => {
258320 } ,
259321 ] ,
260322 } ) ;
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 ;
261338} ;
0 commit comments