@openmrs/webpack-config 10.0.1-pre.5199 → 10.0.1-pre.5227

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
@@ -50,11 +50,44 @@ const fork_ts_checker_webpack_plugin_1 = __importDefault(require("fork-ts-checke
50
50
  // eslint-disable-next-line no-restricted-imports
51
51
  const lodash_1 = require("lodash");
52
52
  const semver_1 = require("semver");
53
- const webpack_1 = require("webpack");
53
+ const webpack_1 = require("@module-federation/enhanced/webpack");
54
+ const webpack_2 = require("webpack");
54
55
  const webpack_bundle_analyzer_1 = require("webpack-bundle-analyzer");
55
56
  const webpack_stats_plugin_1 = require("webpack-stats-plugin");
56
57
  const production = 'production';
57
- const { ModuleFederationPlugin } = webpack_1.container;
58
+ // Read from our own pin rather than `@module-federation/enhanced/package.json`, which its `exports` map
59
+ // makes unreadable. `parse` rather than `coerce`, so that loosening the pin to a range disables the skew
60
+ // warning instead of misreporting it: `coerce` turns `2.x` and `^2` into 2.0, which would warn forever.
61
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
62
+ const moduleFederationPin = require('../package.json').dependencies['@module-federation/enhanced'];
63
+ const moduleFederationVersion = (0, semver_1.parse)(moduleFederationPin);
64
+ /**
65
+ * Prepended to this app's entry chunks. Without it, an app running under an app shell too old to
66
+ * publish the runtime globals fails with a `TypeError` from inside minified runtime code. All three
67
+ * globals are checked because `_OPENMRS_FEDERATION_ERROR_CODES` is what describes the failure, and a runtime
68
+ * minor differing from the app shell's warns. `@openmrs/rspack-config` has a copy of this; keep them in step.
69
+ */
70
+ function buildFederationRuntimeGuard(appName, expectedMinor) {
71
+ const missingRuntime = "(function(){var g=typeof globalThis!=='undefined'?globalThis:self;" +
72
+ "if(typeof g._FEDERATION_RUNTIME_CORE==='undefined'||typeof g._OPENMRS_FEDERATION_SDK==='undefined'||typeof g._OPENMRS_FEDERATION_ERROR_CODES==='undefined'){" +
73
+ 'throw new Error(' +
74
+ JSON.stringify(appName) +
75
+ " + ' cannot start: the OpenMRS app shell serving this page does not provide the Module Federation runtime. " +
76
+ 'This app was built with newer OpenMRS tooling than the app shell, so either upgrade @openmrs/esm-app-shell, ' +
77
+ "or rebuild this app with tooling matching the app shell.');}";
78
+ // Skipped rather than always warning if the pin couldn't be coerced to a version.
79
+ const skew = expectedMinor
80
+ ? 'var from=g._FEDERATION_RUNTIME_CORE_FROM;' +
81
+ 'if(from&&from.version&&String(from.version).split(".").slice(0,2).join(".")!==' +
82
+ JSON.stringify(expectedMinor) +
83
+ '){console.warn(' +
84
+ JSON.stringify(appName) +
85
+ " + ' was built against Module Federation " +
86
+ expectedMinor +
87
+ ".x but the app shell provides ' + from.version + '. Shared dependencies may not de-duplicate correctly.');}"
88
+ : '';
89
+ return missingRuntime + skew + '})();';
90
+ }
58
91
  function getFrameworkVersion() {
59
92
  try {
60
93
  // eslint-disable-next-line @typescript-eslint/no-require-imports
@@ -238,15 +271,23 @@ exports.default = (env, argv = {}) => {
238
271
  new webpack_bundle_analyzer_1.BundleAnalyzerPlugin({
239
272
  analyzerMode: env && env.analyze ? 'server' : 'disabled',
240
273
  }),
241
- new webpack_1.DefinePlugin({
274
+ new webpack_2.DefinePlugin({
242
275
  'process.env.FRAMEWORK_VERSION': JSON.stringify(frameworkVersion),
243
276
  }),
244
- new ModuleFederationPlugin({
277
+ new webpack_1.ModuleFederationPlugin({
245
278
  // Look in the `esm-dynamic-loading` framework package for an explanation of how modules
246
279
  // get loaded into the application.
247
280
  name,
248
281
  library: { type: 'var', name: slugify(name) },
249
282
  filename,
283
+ // Building on `@module-federation/enhanced` gives webpack remotes the MF 2.0 runtime, which they
284
+ // read from the globals the app shell publishes rather than embedding. See `@openmrs/rspack-config`.
285
+ experiments: {
286
+ externalRuntime: true,
287
+ },
288
+ // Nothing consumes an mf-manifest or federated types here, so skip both plugins.
289
+ manifest: false,
290
+ dts: false,
250
291
  exposes: {
251
292
  './start': srcFile,
252
293
  },
@@ -292,6 +333,19 @@ exports.default = (env, argv = {}) => {
292
333
  },
293
334
  ],
294
335
  }),
336
+ // The two runtime packages a remote can safely borrow from the app shell; see the
337
+ // `ExternalsPlugin` block in `@openmrs/rspack-config` for why only these two are shareable,
338
+ // what still ships per remote, and why this is a plugin rather than an `externals` entry.
339
+ new webpack_2.ExternalsPlugin('global', {
340
+ '@module-federation/sdk': '_OPENMRS_FEDERATION_SDK',
341
+ '@module-federation/error-codes': '_OPENMRS_FEDERATION_ERROR_CODES',
342
+ }),
343
+ new webpack_2.BannerPlugin({
344
+ raw: true,
345
+ entryOnly: true,
346
+ test: /\.[cm]?js$/,
347
+ banner: buildFederationRuntimeGuard(name, moduleFederationVersion ? `${moduleFederationVersion.major}.${moduleFederationVersion.minor}` : undefined),
348
+ }),
295
349
  new webpack_stats_plugin_1.StatsWriterPlugin({
296
350
  filename: `${filename}.buildmanifest.json`,
297
351
  stats: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/webpack-config",
3
- "version": "10.0.1-pre.5199",
3
+ "version": "10.0.1-pre.5227",
4
4
  "license": "MPL-2.0",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -30,6 +30,7 @@
30
30
  ],
31
31
  "homepage": "https://github.com/openmrs/openmrs-esm-core#readme",
32
32
  "dependencies": {
33
+ "@module-federation/enhanced": "2.8.1",
33
34
  "@swc/core": "1.15.21",
34
35
  "clean-webpack-plugin": "4.0.0",
35
36
  "copy-webpack-plugin": "11.0.0",
@@ -38,6 +39,7 @@
38
39
  "lodash": "4.17.21",
39
40
  "sass-embedded": "1.89.2",
40
41
  "sass-loader": "16.0.5",
42
+ "semver": "^7.7.3",
41
43
  "style-loader": "3.3.4",
42
44
  "swc-loader": "0.2.7",
43
45
  "webpack": "5.105.3",
@@ -47,6 +49,7 @@
47
49
  },
48
50
  "devDependencies": {
49
51
  "@types/lodash-es": "4.17.12",
52
+ "@types/semver": "^7.7.3",
50
53
  "typescript": "^5.8.3"
51
54
  },
52
55
  "stableVersion": "10.0.0"
package/src/index.ts CHANGED
@@ -43,10 +43,12 @@ import CopyWebpackPlugin from 'copy-webpack-plugin';
43
43
  import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
44
44
  // eslint-disable-next-line no-restricted-imports
45
45
  import { isArray, merge, mergeWith } from 'lodash';
46
- import { inc } from 'semver';
46
+ import { inc, parse } from 'semver';
47
+ import { ModuleFederationPlugin } from '@module-federation/enhanced/webpack';
47
48
  import {
48
- container,
49
+ BannerPlugin,
49
50
  DefinePlugin,
51
+ ExternalsPlugin,
50
52
  type ModuleOptions,
51
53
  type RuleSetRule,
52
54
  type WebpackOptionsNormalized as WebpackConfiguration,
@@ -60,7 +62,44 @@ type OpenmrsWebpackConfig = Omit<Partial<WebpackConfiguration>, 'module' | 'outp
60
62
  };
61
63
 
62
64
  const production = 'production';
63
- const { ModuleFederationPlugin } = container;
65
+
66
+ // Read from our own pin rather than `@module-federation/enhanced/package.json`, which its `exports` map
67
+ // makes unreadable. `parse` rather than `coerce`, so that loosening the pin to a range disables the skew
68
+ // warning instead of misreporting it: `coerce` turns `2.x` and `^2` into 2.0, which would warn forever.
69
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
70
+ const moduleFederationPin: string = require('../package.json').dependencies['@module-federation/enhanced'];
71
+ const moduleFederationVersion = parse(moduleFederationPin);
72
+
73
+ /**
74
+ * Prepended to this app's entry chunks. Without it, an app running under an app shell too old to
75
+ * publish the runtime globals fails with a `TypeError` from inside minified runtime code. All three
76
+ * globals are checked because `_OPENMRS_FEDERATION_ERROR_CODES` is what describes the failure, and a runtime
77
+ * minor differing from the app shell's warns. `@openmrs/rspack-config` has a copy of this; keep them in step.
78
+ */
79
+ function buildFederationRuntimeGuard(appName: string, expectedMinor: string | undefined) {
80
+ const missingRuntime =
81
+ "(function(){var g=typeof globalThis!=='undefined'?globalThis:self;" +
82
+ "if(typeof g._FEDERATION_RUNTIME_CORE==='undefined'||typeof g._OPENMRS_FEDERATION_SDK==='undefined'||typeof g._OPENMRS_FEDERATION_ERROR_CODES==='undefined'){" +
83
+ 'throw new Error(' +
84
+ JSON.stringify(appName) +
85
+ " + ' cannot start: the OpenMRS app shell serving this page does not provide the Module Federation runtime. " +
86
+ 'This app was built with newer OpenMRS tooling than the app shell, so either upgrade @openmrs/esm-app-shell, ' +
87
+ "or rebuild this app with tooling matching the app shell.');}";
88
+
89
+ // Skipped rather than always warning if the pin couldn't be coerced to a version.
90
+ const skew = expectedMinor
91
+ ? 'var from=g._FEDERATION_RUNTIME_CORE_FROM;' +
92
+ 'if(from&&from.version&&String(from.version).split(".").slice(0,2).join(".")!==' +
93
+ JSON.stringify(expectedMinor) +
94
+ '){console.warn(' +
95
+ JSON.stringify(appName) +
96
+ " + ' was built against Module Federation " +
97
+ expectedMinor +
98
+ ".x but the app shell provides ' + from.version + '. Shared dependencies may not de-duplicate correctly.');}"
99
+ : '';
100
+
101
+ return missingRuntime + skew + '})();';
102
+ }
64
103
 
65
104
  function getFrameworkVersion() {
66
105
  try {
@@ -297,6 +336,14 @@ export default (env: Record<string, string>, argv: Record<string, string> = {})
297
336
  name,
298
337
  library: { type: 'var', name: slugify(name) },
299
338
  filename,
339
+ // Building on `@module-federation/enhanced` gives webpack remotes the MF 2.0 runtime, which they
340
+ // read from the globals the app shell publishes rather than embedding. See `@openmrs/rspack-config`.
341
+ experiments: {
342
+ externalRuntime: true,
343
+ },
344
+ // Nothing consumes an mf-manifest or federated types here, so skip both plugins.
345
+ manifest: false,
346
+ dts: false,
300
347
  exposes: {
301
348
  './start': srcFile,
302
349
  },
@@ -344,6 +391,22 @@ export default (env: Record<string, string>, argv: Record<string, string> = {})
344
391
  },
345
392
  ],
346
393
  }),
394
+ // The two runtime packages a remote can safely borrow from the app shell; see the
395
+ // `ExternalsPlugin` block in `@openmrs/rspack-config` for why only these two are shareable,
396
+ // what still ships per remote, and why this is a plugin rather than an `externals` entry.
397
+ new ExternalsPlugin('global', {
398
+ '@module-federation/sdk': '_OPENMRS_FEDERATION_SDK',
399
+ '@module-federation/error-codes': '_OPENMRS_FEDERATION_ERROR_CODES',
400
+ }),
401
+ new BannerPlugin({
402
+ raw: true,
403
+ entryOnly: true,
404
+ test: /\.[cm]?js$/,
405
+ banner: buildFederationRuntimeGuard(
406
+ name,
407
+ moduleFederationVersion ? `${moduleFederationVersion.major}.${moduleFederationVersion.minor}` : undefined,
408
+ ),
409
+ }),
347
410
  new StatsWriterPlugin({
348
411
  filename: `${filename}.buildmanifest.json`,
349
412
  stats: {