@openmrs/rspack-config 10.0.1-pre.5364 → 10.0.1-pre.5378

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -86,6 +86,7 @@ const ts_checker_rspack_plugin_1 = require("ts-checker-rspack-plugin");
86
86
  const lodash_1 = require("lodash");
87
87
  const semver_1 = require("semver");
88
88
  const rspack_1 = require("@module-federation/enhanced/rspack");
89
+ const carbon_css_guard_1 = require("@openmrs/carbon-css-guard");
89
90
  const core_1 = __importStar(require("@rspack/core"));
90
91
  const webpack_bundle_analyzer_1 = require("webpack-bundle-analyzer");
91
92
  const webpack_stats_plugin_1 = require("webpack-stats-plugin");
@@ -272,14 +273,20 @@ exports.watchConfig = {};
272
273
  * This object will be merged with the webpack optimization
273
274
  * object.
274
275
  * Make sure to modify this object and not reassign it.
276
+ *
277
+ * Arrays here merge by index rather than replacing, so a `minimizer` set on this object lands on top of
278
+ * the defaults entry by entry instead of taking their place. Use `overrides.optimization` to replace
279
+ * them outright.
275
280
  */
276
281
  exports.optimizationConfig = {};
277
282
  exports.default = (env, argv = {}) => {
283
+ var _a;
278
284
  const root = process.cwd();
279
285
  // eslint-disable-next-line @typescript-eslint/no-require-imports
280
286
  const { name, version, peerDependencies, browser, main, types } = require((0, path_1.resolve)(root, 'package.json'));
281
287
  // this typing is provably incorrect, but actually works
282
288
  const mode = (argv.mode || process.env.NODE_ENV || 'development');
289
+ const isProd = mode === production;
283
290
  const devServerPort = argv.port ? Number(argv.port) : undefined;
284
291
  const devServerHost = argv.host || 'localhost';
285
292
  const filename = (0, path_1.basename)(browser || main);
@@ -296,20 +303,27 @@ exports.default = (env, argv = {}) => {
296
303
  // so this (hopefully) doesn't interfere with Webpack-specific exit codes
297
304
  process.exit(9819023573289);
298
305
  }
299
- const cssLoader = {
306
+ // These are factories rather than shared objects because `cssRuleConfig` and `scssRuleConfig` are
307
+ // merged into the rules with lodash `merge`, which mutates: one shared entry would let an override
308
+ // aimed at the CSS rule silently leak into the SCSS rule as well.
309
+ const cssLoader = () => ({
300
310
  loader: require.resolve('css-loader'),
301
311
  options: {
302
312
  modules: {
303
313
  localIdentName: `${ident}__[name]__[local]___[hash:base64:5]`,
304
314
  },
305
315
  },
306
- };
316
+ });
317
+ // Production emits real `.css` assets; development keeps `style-loader` for its better HMR story.
318
+ // See RFC 0033. The chunk-loading runtime resolves a chunk only once its stylesheet has loaded,
319
+ // so nothing mounts unstyled.
320
+ const styleLoader = () => isProd ? { loader: require.resolve(core_1.CssExtractRspackPlugin.loader) } : { loader: require.resolve('style-loader') };
307
321
  const baseConfig = Object.assign(Object.assign({
308
322
  // The only `entry` in the application is the app shell. Everything else is
309
323
  // a Webpack Module Federation "remote." This ensures that there is always
310
324
  // only one container context--i.e., if we had an entry point per module,
311
325
  // WMF could get confused and not resolve shared dependencies correctly.
312
- output: {
326
+ entry: {}, output: {
313
327
  publicPath: 'auto',
314
328
  path: (0, path_1.resolve)(root, outDir),
315
329
  hashFunction: 'xxhash64',
@@ -333,13 +347,13 @@ exports.default = (env, argv = {}) => {
333
347
  }, exports.scriptRuleConfig),
334
348
  (0, lodash_1.merge)({
335
349
  test: /\.css$/,
336
- use: [require.resolve('style-loader'), cssLoader],
350
+ use: [styleLoader(), cssLoader()],
337
351
  }, exports.cssRuleConfig),
338
352
  (0, lodash_1.merge)({
339
353
  test: /\.s[ac]ss$/i,
340
354
  use: [
341
- require.resolve('style-loader'),
342
- cssLoader,
355
+ styleLoader(),
356
+ cssLoader(),
343
357
  {
344
358
  loader: require.resolve('sass-loader'),
345
359
  options: {
@@ -377,11 +391,23 @@ exports.default = (env, argv = {}) => {
377
391
  maxAsyncRequests: 3,
378
392
  maxInitialRequests: 1,
379
393
  },
380
- minimizer: [new core_1.default.SwcJsMinimizerRspackPlugin(), new core_1.default.LightningCssMinimizerRspackPlugin()],
394
+ minimizer: [
395
+ new core_1.default.SwcJsMinimizerRspackPlugin(),
396
+ // `targets` is passed explicitly rather than left to the minimizer's own default
397
+ new core_1.default.LightningCssMinimizerRspackPlugin({
398
+ minimizerOptions: { targets: browserTargets },
399
+ }),
400
+ ],
381
401
  }, exports.optimizationConfig), plugins: [
382
402
  browserslistWarnings.length > 0 && new BrowserslistWarningsPlugin(browserslistWarnings),
383
403
  new ts_checker_rspack_plugin_1.TsCheckerRspackPlugin(),
384
404
  new clean_webpack_plugin_1.CleanWebpackPlugin(),
405
+ isProd &&
406
+ new core_1.CssExtractRspackPlugin({
407
+ // `ignoreOrder` because nearly every class here is uniquely scoped by CSS Modules, so the
408
+ // conflicting-order warnings this would otherwise emit across chunks are almost all noise.
409
+ ignoreOrder: true,
410
+ }),
385
411
  new webpack_bundle_analyzer_1.BundleAnalyzerPlugin({
386
412
  analyzerMode: env && env.analyze ? 'server' : 'disabled',
387
413
  }),
@@ -493,5 +519,12 @@ exports.default = (env, argv = {}) => {
493
519
  serverUrl: `http://${devServerHost}:${devServerPort}`,
494
520
  },
495
521
  })), exports.overrides);
496
- return (0, lodash_1.mergeWith)(baseConfig, exports.additionalConfig, mergeFunction);
522
+ const config = (0, lodash_1.mergeWith)(baseConfig, exports.additionalConfig, mergeFunction);
523
+ // Appended after the merge rather than listed above. `overrides` is spread over the config object, so
524
+ // an app setting `overrides.plugins` replaces the whole array — not a lodash merge, which would keep
525
+ // the entries — and would drop the guard without noticing. Same reasoning as the `ExternalsPlugin`
526
+ // block above, which avoids `externals` for the same reason.
527
+ // Production only: under `style-loader` there are no `.css` assets to check.
528
+ config.plugins = [...((_a = config.plugins) !== null && _a !== void 0 ? _a : []), ...(isProd ? [new carbon_css_guard_1.CarbonCssGuardPlugin(name)] : [])];
529
+ return config;
497
530
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/rspack-config",
3
- "version": "10.0.1-pre.5364",
3
+ "version": "10.0.1-pre.5378",
4
4
  "license": "MPL-2.0",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -32,6 +32,7 @@
32
32
  "homepage": "https://github.com/openmrs/openmrs-esm-core#readme",
33
33
  "dependencies": {
34
34
  "@module-federation/enhanced": "2.8.1",
35
+ "@openmrs/carbon-css-guard": "10.0.1-pre.5378",
35
36
  "@rspack/cli": "1.7.9",
36
37
  "@rspack/core": "1.7.9",
37
38
  "@swc/core": "1.15.21",
package/src/index.ts CHANGED
@@ -47,9 +47,11 @@ import { TsCheckerRspackPlugin } from 'ts-checker-rspack-plugin';
47
47
  import { isArray, merge, mergeWith } from 'lodash';
48
48
  import { inc, parse } from 'semver';
49
49
  import { ModuleFederationPlugin } from '@module-federation/enhanced/rspack';
50
+ import { CarbonCssGuardPlugin } from '@openmrs/carbon-css-guard';
50
51
  import rspack, {
51
52
  type Compiler,
52
53
  CopyRspackPlugin,
54
+ CssExtractRspackPlugin,
53
55
  DefinePlugin,
54
56
  type ModuleOptions,
55
57
  type RuleSetRule,
@@ -292,6 +294,10 @@ export const watchConfig: Partial<OpenmrsRspackConfig['watchOptions']> = {};
292
294
  * This object will be merged with the webpack optimization
293
295
  * object.
294
296
  * Make sure to modify this object and not reassign it.
297
+ *
298
+ * Arrays here merge by index rather than replacing, so a `minimizer` set on this object lands on top of
299
+ * the defaults entry by entry instead of taking their place. Use `overrides.optimization` to replace
300
+ * them outright.
295
301
  */
296
302
  export const optimizationConfig: Partial<OpenmrsRspackConfig['optimization']> = {};
297
303
 
@@ -301,6 +307,7 @@ export default (env: Record<string, string>, argv: Record<string, string> = {})
301
307
  const { name, version, peerDependencies, browser, main, types } = require(resolve(root, 'package.json'));
302
308
  // this typing is provably incorrect, but actually works
303
309
  const mode = (argv.mode || process.env.NODE_ENV || 'development') as OpenmrsRspackConfig['mode'];
310
+ const isProd = mode === production;
304
311
  const devServerPort = argv.port ? Number(argv.port) : undefined;
305
312
  const devServerHost = argv.host || 'localhost';
306
313
  const filename = basename(browser || main);
@@ -321,20 +328,30 @@ export default (env: Record<string, string>, argv: Record<string, string> = {})
321
328
  process.exit(9819023573289);
322
329
  }
323
330
 
324
- const cssLoader = {
331
+ // These are factories rather than shared objects because `cssRuleConfig` and `scssRuleConfig` are
332
+ // merged into the rules with lodash `merge`, which mutates: one shared entry would let an override
333
+ // aimed at the CSS rule silently leak into the SCSS rule as well.
334
+ const cssLoader = () => ({
325
335
  loader: require.resolve('css-loader'),
326
336
  options: {
327
337
  modules: {
328
338
  localIdentName: `${ident}__[name]__[local]___[hash:base64:5]`,
329
339
  },
330
340
  },
331
- };
341
+ });
342
+
343
+ // Production emits real `.css` assets; development keeps `style-loader` for its better HMR story.
344
+ // See RFC 0033. The chunk-loading runtime resolves a chunk only once its stylesheet has loaded,
345
+ // so nothing mounts unstyled.
346
+ const styleLoader = () =>
347
+ isProd ? { loader: require.resolve(CssExtractRspackPlugin.loader) } : { loader: require.resolve('style-loader') };
332
348
 
333
349
  const baseConfig: OpenmrsRspackConfig = {
334
350
  // The only `entry` in the application is the app shell. Everything else is
335
351
  // a Webpack Module Federation "remote." This ensures that there is always
336
352
  // only one container context--i.e., if we had an entry point per module,
337
353
  // WMF could get confused and not resolve shared dependencies correctly.
354
+ entry: {},
338
355
  output: {
339
356
  publicPath: 'auto',
340
357
  path: resolve(root, outDir),
@@ -364,7 +381,7 @@ export default (env: Record<string, string>, argv: Record<string, string> = {})
364
381
  merge(
365
382
  {
366
383
  test: /\.css$/,
367
- use: [require.resolve('style-loader'), cssLoader],
384
+ use: [styleLoader(), cssLoader()],
368
385
  },
369
386
  cssRuleConfig,
370
387
  ),
@@ -372,8 +389,8 @@ export default (env: Record<string, string>, argv: Record<string, string> = {})
372
389
  {
373
390
  test: /\.s[ac]ss$/i,
374
391
  use: [
375
- require.resolve('style-loader'),
376
- cssLoader,
392
+ styleLoader(),
393
+ cssLoader(),
377
394
  {
378
395
  loader: require.resolve('sass-loader'),
379
396
  options: {
@@ -426,7 +443,13 @@ export default (env: Record<string, string>, argv: Record<string, string> = {})
426
443
  maxAsyncRequests: 3,
427
444
  maxInitialRequests: 1,
428
445
  },
429
- minimizer: [new rspack.SwcJsMinimizerRspackPlugin(), new rspack.LightningCssMinimizerRspackPlugin()],
446
+ minimizer: [
447
+ new rspack.SwcJsMinimizerRspackPlugin(),
448
+ // `targets` is passed explicitly rather than left to the minimizer's own default
449
+ new rspack.LightningCssMinimizerRspackPlugin({
450
+ minimizerOptions: { targets: browserTargets },
451
+ }),
452
+ ],
430
453
  },
431
454
  optimizationConfig,
432
455
  ),
@@ -434,6 +457,12 @@ export default (env: Record<string, string>, argv: Record<string, string> = {})
434
457
  browserslistWarnings.length > 0 && new BrowserslistWarningsPlugin(browserslistWarnings),
435
458
  new TsCheckerRspackPlugin(),
436
459
  new CleanWebpackPlugin(),
460
+ isProd &&
461
+ new CssExtractRspackPlugin({
462
+ // `ignoreOrder` because nearly every class here is uniquely scoped by CSS Modules, so the
463
+ // conflicting-order warnings this would otherwise emit across chunks are almost all noise.
464
+ ignoreOrder: true,
465
+ }),
437
466
  new BundleAnalyzerPlugin({
438
467
  analyzerMode: env && env.analyze ? 'server' : 'disabled',
439
468
  }),
@@ -554,5 +583,14 @@ export default (env: Record<string, string>, argv: Record<string, string> = {})
554
583
  }),
555
584
  ...overrides,
556
585
  };
557
- return mergeWith(baseConfig, additionalConfig, mergeFunction);
586
+ const config = mergeWith(baseConfig, additionalConfig, mergeFunction);
587
+
588
+ // Appended after the merge rather than listed above. `overrides` is spread over the config object, so
589
+ // an app setting `overrides.plugins` replaces the whole array — not a lodash merge, which would keep
590
+ // the entries — and would drop the guard without noticing. Same reasoning as the `ExternalsPlugin`
591
+ // block above, which avoids `externals` for the same reason.
592
+ // Production only: under `style-loader` there are no `.css` assets to check.
593
+ config.plugins = [...(config.plugins ?? []), ...(isProd ? [new CarbonCssGuardPlugin(name)] : [])];
594
+
595
+ return config;
558
596
  };