@module-federation/metro 2.0.0 → 2.1.0

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.
Files changed (63) hide show
  1. package/dist/babel/transformer.js +1 -1
  2. package/dist/commands/bundle-host/index.js +5 -4
  3. package/dist/commands/bundle-host/index.mjs +5 -26
  4. package/dist/commands/bundle-remote/index.js +22 -11
  5. package/dist/commands/bundle-remote/index.mjs +22 -32
  6. package/dist/commands/utils/create-resolver.d.ts +1 -1
  7. package/dist/commands/utils/get-community-plugin.d.ts +1 -2
  8. package/dist/commands/utils/get-community-plugin.js +3 -4
  9. package/dist/commands/utils/get-community-plugin.mjs +3 -4
  10. package/dist/commands/utils/path-utils.d.ts +2 -0
  11. package/dist/commands/utils/path-utils.js +47 -0
  12. package/dist/commands/utils/path-utils.mjs +12 -0
  13. package/dist/commands/utils/save-bundle-and-map.d.ts +1 -1
  14. package/dist/commands/utils/save-bundle-and-map.js +2 -3
  15. package/dist/commands/utils/save-bundle-and-map.mjs +2 -25
  16. package/dist/logger.d.ts +2 -0
  17. package/dist/logger.js +39 -0
  18. package/dist/logger.mjs +7 -0
  19. package/dist/modules/asyncRequire.ts +0 -3
  20. package/dist/modules/asyncStartup.tsx +1 -0
  21. package/dist/modules/metroCorePlugin.ts +3 -3
  22. package/dist/plugin/babel-transformer.js +1 -1
  23. package/dist/plugin/babel-transformer.mjs +1 -1
  24. package/dist/plugin/constants.d.ts +1 -0
  25. package/dist/plugin/constants.js +4 -0
  26. package/dist/plugin/constants.mjs +2 -1
  27. package/dist/plugin/generators.js +6 -3
  28. package/dist/plugin/generators.mjs +6 -3
  29. package/dist/plugin/helpers.d.ts +1 -0
  30. package/dist/plugin/helpers.js +11 -5
  31. package/dist/plugin/helpers.mjs +4 -1
  32. package/dist/plugin/index.d.ts +2 -0
  33. package/dist/plugin/index.js +33 -1
  34. package/dist/plugin/index.mjs +33 -1
  35. package/dist/plugin/manifest.js +8 -2
  36. package/dist/plugin/manifest.mjs +8 -2
  37. package/dist/plugin/normalize-options.js +57 -8
  38. package/dist/plugin/normalize-options.mjs +57 -8
  39. package/dist/plugin/resolver.js +1 -1
  40. package/dist/plugin/resolver.mjs +2 -2
  41. package/dist/plugin/rewrite-request.d.ts +5 -1
  42. package/dist/plugin/rewrite-request.js +11 -2
  43. package/dist/plugin/rewrite-request.mjs +12 -3
  44. package/dist/plugin/serializer.js +24 -9
  45. package/dist/plugin/serializer.mjs +24 -31
  46. package/dist/plugin/validate-options.js +106 -5
  47. package/dist/plugin/validate-options.mjs +95 -5
  48. package/dist/types.d.ts +15 -15
  49. package/dist/utils/federated-remote-types.d.ts +12 -0
  50. package/dist/utils/federated-remote-types.js +159 -0
  51. package/dist/utils/federated-remote-types.mjs +91 -0
  52. package/dist/utils/metro-compat.d.ts +23 -0
  53. package/dist/utils/metro-compat.js +70 -0
  54. package/dist/utils/metro-compat.mjs +26 -0
  55. package/dist/utils/vm-manager.d.ts +1 -1
  56. package/package.json +7 -3
  57. package/dist/babel/transformer.d.ts +0 -2
  58. package/dist/runtime/host-entry.d.ts +0 -1
  59. package/dist/runtime/init-host.d.ts +0 -1
  60. package/dist/runtime/remote-entry.d.ts +0 -1
  61. package/dist/runtime/remote-hmr.d.ts +0 -1
  62. package/dist/runtime/remote-module-registry.d.ts +0 -7
  63. package/dist/runtime/remote-module.d.ts +0 -1
@@ -37,13 +37,9 @@ __webpack_require__.d(__webpack_exports__, {
37
37
  });
38
38
  const external_node_path_namespaceObject = require("node:path");
39
39
  var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
40
- const baseJSBundle_namespaceObject = require("metro/src/DeltaBundler/Serializers/baseJSBundle");
41
- var baseJSBundle_default = /*#__PURE__*/ __webpack_require__.n(baseJSBundle_namespaceObject);
42
- const CountingSet_namespaceObject = require("metro/src/lib/CountingSet");
43
- var CountingSet_default = /*#__PURE__*/ __webpack_require__.n(CountingSet_namespaceObject);
44
- const bundleToString_namespaceObject = require("metro/src/lib/bundleToString");
45
- var bundleToString_default = /*#__PURE__*/ __webpack_require__.n(bundleToString_namespaceObject);
46
40
  const errors_js_namespaceObject = require("../utils/errors.js");
41
+ const external_helpers_js_namespaceObject = require("./helpers.js");
42
+ const metro_compat_js_namespaceObject = require("../utils/metro-compat.js");
47
43
  function getModuleFederationSerializer(mfConfig, isUsingMFBundleCommand) {
48
44
  return async (entryPoint, preModules, graph, options)=>{
49
45
  const syncRemoteModules = collectSyncRemoteModules(graph, mfConfig.remotes);
@@ -122,7 +118,7 @@ function generateVirtualModule(name, code) {
122
118
  return {
123
119
  dependencies: new Map(),
124
120
  getSource: ()=>Buffer.from(code),
125
- inverseDependencies: new (CountingSet_default())(),
121
+ inverseDependencies: new metro_compat_js_namespaceObject.CountingSet(),
126
122
  path: name,
127
123
  output: [
128
124
  {
@@ -142,6 +138,7 @@ function isProjectSource(entryPoint, projectRoot) {
142
138
  }
143
139
  function getBundlePath(entryPoint, projectRoot, exposes, isUsingMFBundleCommand) {
144
140
  const relativeEntryPath = external_node_path_default().relative(projectRoot, entryPoint);
141
+ const normalizedRelativeEntryPath = normalizeEntryPath(relativeEntryPath);
145
142
  if (!isUsingMFBundleCommand) {
146
143
  const { dir, name } = external_node_path_default().parse(relativeEntryPath);
147
144
  return external_node_path_default().format({
@@ -150,7 +147,13 @@ function getBundlePath(entryPoint, projectRoot, exposes, isUsingMFBundleCommand)
150
147
  ext: ''
151
148
  });
152
149
  }
153
- const exposedMatchedKey = Object.keys(exposes).find((exposeKey)=>exposes[exposeKey].match(relativeEntryPath));
150
+ const exposeEntries = Object.entries(exposes).map(([exposeKey, exposePath])=>({
151
+ exposeKey,
152
+ normalizedExposePath: normalizeEntryPath(exposePath)
153
+ }));
154
+ const exactMatch = exposeEntries.find(({ normalizedExposePath })=>normalizedExposePath === normalizedRelativeEntryPath);
155
+ const extensionlessMatch = exposeEntries.find(({ normalizedExposePath })=>removePathExtension(normalizedExposePath) === removePathExtension(normalizedRelativeEntryPath));
156
+ const exposedMatchedKey = (null == exactMatch ? void 0 : exactMatch.exposeKey) ?? (null == extensionlessMatch ? void 0 : extensionlessMatch.exposeKey);
154
157
  if (exposedMatchedKey) {
155
158
  let exposedName = exposedMatchedKey;
156
159
  if (exposedName.startsWith('./')) exposedName = exposedName.slice(2);
@@ -158,8 +161,20 @@ function getBundlePath(entryPoint, projectRoot, exposes, isUsingMFBundleCommand)
158
161
  }
159
162
  throw new errors_js_namespaceObject.ConfigError(`Unable to handle entry point: ${relativeEntryPath}. Expected to match an entrypoint with one of the exposed keys, but failed. This is most likely a configuration error. If you believe this is not a configuration issue, please report it as a bug. Debug info: entryPoint="${entryPoint}", projectRoot="${projectRoot}", exposesKeys=[${Object.keys(exposes).join(', ')}]`);
160
163
  }
164
+ function normalizeEntryPath(value) {
165
+ const normalized = (0, external_helpers_js_namespaceObject.toPosixPath)(external_node_path_default().normalize(value));
166
+ return normalized.startsWith('./') ? normalized.slice(2) : normalized;
167
+ }
168
+ function removePathExtension(value) {
169
+ const parsed = external_node_path_default().posix.parse(value);
170
+ return external_node_path_default().posix.format({
171
+ dir: parsed.dir,
172
+ name: parsed.name,
173
+ ext: ''
174
+ });
175
+ }
161
176
  function getBundleCode(entryPoint, preModules, graph, options) {
162
- const { code } = bundleToString_default()(baseJSBundle_default()(entryPoint, preModules, graph, options));
177
+ const { code } = (0, metro_compat_js_namespaceObject.bundleToString)((0, metro_compat_js_namespaceObject.baseJSBundle)(entryPoint, preModules, graph, options));
163
178
  return code;
164
179
  }
165
180
  exports.getModuleFederationSerializer = __webpack_exports__.getModuleFederationSerializer;
@@ -2,34 +2,8 @@ import 'module';
2
2
  /*#__PURE__*/ import.meta.url;
3
3
  import node_path from "node:path";
4
4
  import { ConfigError } from "../utils/errors.mjs";
5
- import { createRequire as __WEBPACK_EXTERNAL_createRequire } from "node:module";
6
- var __webpack_require__ = {};
7
- (()=>{
8
- __webpack_require__.n = (module)=>{
9
- var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
10
- __webpack_require__.d(getter, {
11
- a: getter
12
- });
13
- return getter;
14
- };
15
- })();
16
- (()=>{
17
- __webpack_require__.d = (exports, definition)=>{
18
- for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) Object.defineProperty(exports, key, {
19
- enumerable: true,
20
- get: definition[key]
21
- });
22
- };
23
- })();
24
- (()=>{
25
- __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
26
- })();
27
- const baseJSBundle_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("metro/src/DeltaBundler/Serializers/baseJSBundle");
28
- var baseJSBundle_default = /*#__PURE__*/ __webpack_require__.n(baseJSBundle_namespaceObject);
29
- const CountingSet_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("metro/src/lib/CountingSet");
30
- var CountingSet_default = /*#__PURE__*/ __webpack_require__.n(CountingSet_namespaceObject);
31
- const bundleToString_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("metro/src/lib/bundleToString");
32
- var bundleToString_default = /*#__PURE__*/ __webpack_require__.n(bundleToString_namespaceObject);
5
+ import { toPosixPath } from "./helpers.mjs";
6
+ import { CountingSet, baseJSBundle, bundleToString } from "../utils/metro-compat.mjs";
33
7
  function getModuleFederationSerializer(mfConfig, isUsingMFBundleCommand) {
34
8
  return async (entryPoint, preModules, graph, options)=>{
35
9
  const syncRemoteModules = collectSyncRemoteModules(graph, mfConfig.remotes);
@@ -108,7 +82,7 @@ function generateVirtualModule(name, code) {
108
82
  return {
109
83
  dependencies: new Map(),
110
84
  getSource: ()=>Buffer.from(code),
111
- inverseDependencies: new (CountingSet_default())(),
85
+ inverseDependencies: new CountingSet(),
112
86
  path: name,
113
87
  output: [
114
88
  {
@@ -128,6 +102,7 @@ function isProjectSource(entryPoint, projectRoot) {
128
102
  }
129
103
  function getBundlePath(entryPoint, projectRoot, exposes, isUsingMFBundleCommand) {
130
104
  const relativeEntryPath = node_path.relative(projectRoot, entryPoint);
105
+ const normalizedRelativeEntryPath = normalizeEntryPath(relativeEntryPath);
131
106
  if (!isUsingMFBundleCommand) {
132
107
  const { dir, name } = node_path.parse(relativeEntryPath);
133
108
  return node_path.format({
@@ -136,7 +111,13 @@ function getBundlePath(entryPoint, projectRoot, exposes, isUsingMFBundleCommand)
136
111
  ext: ''
137
112
  });
138
113
  }
139
- const exposedMatchedKey = Object.keys(exposes).find((exposeKey)=>exposes[exposeKey].match(relativeEntryPath));
114
+ const exposeEntries = Object.entries(exposes).map(([exposeKey, exposePath])=>({
115
+ exposeKey,
116
+ normalizedExposePath: normalizeEntryPath(exposePath)
117
+ }));
118
+ const exactMatch = exposeEntries.find(({ normalizedExposePath })=>normalizedExposePath === normalizedRelativeEntryPath);
119
+ const extensionlessMatch = exposeEntries.find(({ normalizedExposePath })=>removePathExtension(normalizedExposePath) === removePathExtension(normalizedRelativeEntryPath));
120
+ const exposedMatchedKey = (null == exactMatch ? void 0 : exactMatch.exposeKey) ?? (null == extensionlessMatch ? void 0 : extensionlessMatch.exposeKey);
140
121
  if (exposedMatchedKey) {
141
122
  let exposedName = exposedMatchedKey;
142
123
  if (exposedName.startsWith('./')) exposedName = exposedName.slice(2);
@@ -144,8 +125,20 @@ function getBundlePath(entryPoint, projectRoot, exposes, isUsingMFBundleCommand)
144
125
  }
145
126
  throw new ConfigError(`Unable to handle entry point: ${relativeEntryPath}. Expected to match an entrypoint with one of the exposed keys, but failed. This is most likely a configuration error. If you believe this is not a configuration issue, please report it as a bug. Debug info: entryPoint="${entryPoint}", projectRoot="${projectRoot}", exposesKeys=[${Object.keys(exposes).join(', ')}]`);
146
127
  }
128
+ function normalizeEntryPath(value) {
129
+ const normalized = toPosixPath(node_path.normalize(value));
130
+ return normalized.startsWith('./') ? normalized.slice(2) : normalized;
131
+ }
132
+ function removePathExtension(value) {
133
+ const parsed = node_path.posix.parse(value);
134
+ return node_path.posix.format({
135
+ dir: parsed.dir,
136
+ name: parsed.name,
137
+ ext: ''
138
+ });
139
+ }
147
140
  function getBundleCode(entryPoint, preModules, graph, options) {
148
- const { code } = bundleToString_default()(baseJSBundle_default()(entryPoint, preModules, graph, options));
141
+ const { code } = bundleToString(baseJSBundle(entryPoint, preModules, graph, options));
149
142
  return code;
150
143
  }
151
144
  export { getModuleFederationSerializer };
@@ -1,5 +1,14 @@
1
1
  "use strict";
2
2
  var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.n = (module)=>{
5
+ var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
6
+ __webpack_require__.d(getter, {
7
+ a: getter
8
+ });
9
+ return getter;
10
+ };
11
+ })();
3
12
  (()=>{
4
13
  __webpack_require__.d = (exports1, definition)=>{
5
14
  for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
@@ -26,8 +35,41 @@ __webpack_require__.r(__webpack_exports__);
26
35
  __webpack_require__.d(__webpack_exports__, {
27
36
  validateOptions: ()=>validateOptions
28
37
  });
38
+ const external_node_path_namespaceObject = require("node:path");
39
+ var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
40
+ const external_logger_js_namespaceObject = require("../logger.js");
41
+ var external_logger_js_default = /*#__PURE__*/ __webpack_require__.n(external_logger_js_namespaceObject);
29
42
  const index_js_namespaceObject = require("../utils/index.js");
43
+ const unsupportedTopLevelOptions = [
44
+ 'library',
45
+ 'remoteType',
46
+ 'runtime',
47
+ 'shareScope',
48
+ 'getPublicPath',
49
+ 'implementation',
50
+ 'manifest',
51
+ 'dev',
52
+ 'dataPrefetch',
53
+ 'virtualRuntimeEntry',
54
+ 'experiments',
55
+ 'bridge',
56
+ 'async',
57
+ 'treeShakingDir',
58
+ 'injectTreeShakingUsedExports',
59
+ 'treeShakingSharedExcludePlugins',
60
+ 'treeShakingSharedPlugins'
61
+ ];
62
+ const unsupportedSharedFields = [
63
+ 'packageName',
64
+ 'shareKey',
65
+ 'shareScope',
66
+ 'strictVersion',
67
+ 'treeShaking',
68
+ 'shareStrategy'
69
+ ];
70
+ const warningSet = new Set();
30
71
  function validateName(name) {
72
+ if (!name || 'string' != typeof name) throw new index_js_namespaceObject.ConfigError("Option 'name' is required.");
31
73
  const validEcmaIdentifierRegex = /^[$_\p{ID_Start}][$_\u{200C}\u{200D}\p{ID_Continue}]*$/u;
32
74
  if (!validEcmaIdentifierRegex.test(name)) throw new index_js_namespaceObject.ConfigError(`Invalid 'name': ${name}. The 'name' must be a valid JavaScript identifier.`);
33
75
  }
@@ -39,17 +81,76 @@ function validateShared(shared) {
39
81
  if (!shared) throw new index_js_namespaceObject.ConfigError('Shared configuration is required.');
40
82
  if ('object' != typeof shared) throw new index_js_namespaceObject.ConfigError('Shared must be an object.');
41
83
  if (Array.isArray(shared)) throw new index_js_namespaceObject.ConfigError('Array format is not supported for shared.');
42
- if (!('react' in shared)) throw new index_js_namespaceObject.ConfigError("Dependency 'react' must be present in shared.");
43
- if (!('react-native' in shared)) throw new index_js_namespaceObject.ConfigError("Dependency 'react-native' must be present in shared.");
44
- for (const sharedName of Object.keys(shared)){
45
- if (sharedName.startsWith('./') || sharedName.startsWith('../')) throw new index_js_namespaceObject.ConfigError('Relative paths are not supported as shared module names.');
46
- if (sharedName.startsWith('/')) throw new index_js_namespaceObject.ConfigError('Absolute paths are not supported as shared module names.');
84
+ if (!isPlainObject(shared)) throw new index_js_namespaceObject.ConfigError('Shared must be an object.');
85
+ const sharedObject = shared;
86
+ if (!('react' in sharedObject)) throw new index_js_namespaceObject.ConfigError("Dependency 'react' must be present in shared.");
87
+ if (!('react-native' in sharedObject)) throw new index_js_namespaceObject.ConfigError("Dependency 'react-native' must be present in shared.");
88
+ for (const sharedName of Object.keys(sharedObject)){
89
+ const sharedConfig = sharedObject[sharedName];
90
+ if (isRelativePathLike(sharedName)) throw new index_js_namespaceObject.ConfigError('Relative paths are not supported as shared module names.');
91
+ if (isAbsolutePathLike(sharedName)) throw new index_js_namespaceObject.ConfigError('Absolute paths are not supported as shared module names.');
47
92
  if (sharedName.endsWith('/')) throw new index_js_namespaceObject.ConfigError("Deep import wildcards are not supported as shared module names. You need to list all deep imports explicitly.");
93
+ if (!isPlainObject(sharedConfig)) throw new index_js_namespaceObject.ConfigError(`Unsupported shared format for '${sharedName}'. Metro only supports object notation for shared modules.`);
94
+ if ('import' in sharedConfig && false !== sharedConfig.import && 'string' != typeof sharedConfig.import) throw new index_js_namespaceObject.ConfigError(`Unsupported shared.import value for '${sharedName}'. Only string and false are supported in Metro.`);
95
+ for (const unsupportedField of unsupportedSharedFields)if (unsupportedField in sharedConfig) warnUnsupported(`shared.${sharedName}.${unsupportedField}`, `Option 'shared.${sharedName}.${unsupportedField}' is not supported in Metro and will have no effect.`);
48
96
  }
49
97
  }
98
+ function validateRemotes(remotes) {
99
+ if (void 0 === remotes) return;
100
+ if (!isPlainObject(remotes)) throw new index_js_namespaceObject.ConfigError('Unsupported remotes format. Metro only supports remotes as Record<string, string>.');
101
+ for (const remoteConfig of Object.values(remotes))if ('string' != typeof remoteConfig) throw new index_js_namespaceObject.ConfigError('Unsupported remotes format. Metro only supports remotes as Record<string, string>.');
102
+ }
103
+ function validateExposes(exposes) {
104
+ if (void 0 === exposes) return;
105
+ if (!isPlainObject(exposes)) throw new index_js_namespaceObject.ConfigError('Unsupported exposes format. Metro only supports exposes as Record<string, string>.');
106
+ for (const exposeConfig of Object.values(exposes))if ('string' != typeof exposeConfig) throw new index_js_namespaceObject.ConfigError('Unsupported exposes format. Metro only supports exposes as Record<string, string>.');
107
+ }
108
+ function validateRuntimePlugins(runtimePlugins) {
109
+ if (void 0 === runtimePlugins) return;
110
+ if (!Array.isArray(runtimePlugins)) throw new index_js_namespaceObject.ConfigError('runtimePlugins must be an array.');
111
+ runtimePlugins.forEach((runtimePlugin, index)=>{
112
+ if ('string' == typeof runtimePlugin) return;
113
+ if (!Array.isArray(runtimePlugin)) throw new index_js_namespaceObject.ConfigError('runtimePlugins entries must be either a string or a tuple of [path, params].');
114
+ const [pluginPath, pluginParams] = runtimePlugin;
115
+ if ('string' != typeof pluginPath) throw new index_js_namespaceObject.ConfigError(`Invalid runtimePlugins[${index}] path. Expected a string path.`);
116
+ if (void 0 !== pluginParams) warnUnsupported(`runtimePlugins[${index}][1]`, `Option 'runtimePlugins[${index}][1]' is not supported in Metro and will have no effect.`);
117
+ });
118
+ }
119
+ function validateDts(dts) {
120
+ if (void 0 === dts || 'boolean' == typeof dts) return;
121
+ if (!isPlainObject(dts)) throw new index_js_namespaceObject.ConfigError("Option 'dts' must be a boolean or a plain object.");
122
+ }
123
+ function validateUnsupportedTopLevelOptions(options) {
124
+ unsupportedTopLevelOptions.forEach((unsupportedOption)=>{
125
+ if (void 0 !== options[unsupportedOption]) warnUnsupported(String(unsupportedOption), `Option '${String(unsupportedOption)}' is not supported in Metro and will have no effect.`);
126
+ });
127
+ }
128
+ function validateDeprecatedOptions(options) {
129
+ if (options.plugins && options.plugins.length > 0) warnUnsupported('deprecated.plugins', "The 'plugins' option is deprecated. Use 'runtimePlugins' instead. Support for 'plugins' will be removed in the next major version.");
130
+ }
131
+ function warnUnsupported(key, message) {
132
+ if (warningSet.has(key)) return;
133
+ warningSet.add(key);
134
+ external_logger_js_default().warn(message);
135
+ }
136
+ function isPlainObject(value) {
137
+ return 'object' == typeof value && null !== value && !Array.isArray(value);
138
+ }
139
+ function isRelativePathLike(value) {
140
+ return /^\.{1,2}[\\/]/.test(value);
141
+ }
142
+ function isAbsolutePathLike(value) {
143
+ return external_node_path_default().posix.isAbsolute(value) || external_node_path_default().win32.isAbsolute(value);
144
+ }
50
145
  function validateOptions(options) {
146
+ validateUnsupportedTopLevelOptions(options);
147
+ validateDeprecatedOptions(options);
51
148
  validateName(options.name);
52
149
  validateFilename(options.filename);
150
+ validateRemotes(options.remotes);
151
+ validateExposes(options.exposes);
152
+ validateRuntimePlugins(options.runtimePlugins);
153
+ validateDts(options.dts);
53
154
  validateShared(options.shared);
54
155
  }
55
156
  exports.validateOptions = __webpack_exports__.validateOptions;
@@ -1,7 +1,38 @@
1
1
  import 'module';
2
2
  /*#__PURE__*/ import.meta.url;
3
+ import node_path from "node:path";
4
+ import logger from "../logger.mjs";
3
5
  import { ConfigError } from "../utils/index.mjs";
6
+ const unsupportedTopLevelOptions = [
7
+ 'library',
8
+ 'remoteType',
9
+ 'runtime',
10
+ 'shareScope',
11
+ 'getPublicPath',
12
+ 'implementation',
13
+ 'manifest',
14
+ 'dev',
15
+ 'dataPrefetch',
16
+ 'virtualRuntimeEntry',
17
+ 'experiments',
18
+ 'bridge',
19
+ 'async',
20
+ 'treeShakingDir',
21
+ 'injectTreeShakingUsedExports',
22
+ 'treeShakingSharedExcludePlugins',
23
+ 'treeShakingSharedPlugins'
24
+ ];
25
+ const unsupportedSharedFields = [
26
+ 'packageName',
27
+ 'shareKey',
28
+ 'shareScope',
29
+ 'strictVersion',
30
+ 'treeShaking',
31
+ 'shareStrategy'
32
+ ];
33
+ const warningSet = new Set();
4
34
  function validateName(name) {
35
+ if (!name || 'string' != typeof name) throw new ConfigError("Option 'name' is required.");
5
36
  const validEcmaIdentifierRegex = /^[$_\p{ID_Start}][$_\u{200C}\u{200D}\p{ID_Continue}]*$/u;
6
37
  if (!validEcmaIdentifierRegex.test(name)) throw new ConfigError(`Invalid 'name': ${name}. The 'name' must be a valid JavaScript identifier.`);
7
38
  }
@@ -13,17 +44,76 @@ function validateShared(shared) {
13
44
  if (!shared) throw new ConfigError('Shared configuration is required.');
14
45
  if ('object' != typeof shared) throw new ConfigError('Shared must be an object.');
15
46
  if (Array.isArray(shared)) throw new ConfigError('Array format is not supported for shared.');
16
- if (!('react' in shared)) throw new ConfigError("Dependency 'react' must be present in shared.");
17
- if (!('react-native' in shared)) throw new ConfigError("Dependency 'react-native' must be present in shared.");
18
- for (const sharedName of Object.keys(shared)){
19
- if (sharedName.startsWith('./') || sharedName.startsWith('../')) throw new ConfigError('Relative paths are not supported as shared module names.');
20
- if (sharedName.startsWith('/')) throw new ConfigError('Absolute paths are not supported as shared module names.');
47
+ if (!isPlainObject(shared)) throw new ConfigError('Shared must be an object.');
48
+ const sharedObject = shared;
49
+ if (!('react' in sharedObject)) throw new ConfigError("Dependency 'react' must be present in shared.");
50
+ if (!('react-native' in sharedObject)) throw new ConfigError("Dependency 'react-native' must be present in shared.");
51
+ for (const sharedName of Object.keys(sharedObject)){
52
+ const sharedConfig = sharedObject[sharedName];
53
+ if (isRelativePathLike(sharedName)) throw new ConfigError('Relative paths are not supported as shared module names.');
54
+ if (isAbsolutePathLike(sharedName)) throw new ConfigError('Absolute paths are not supported as shared module names.');
21
55
  if (sharedName.endsWith('/')) throw new ConfigError("Deep import wildcards are not supported as shared module names. You need to list all deep imports explicitly.");
56
+ if (!isPlainObject(sharedConfig)) throw new ConfigError(`Unsupported shared format for '${sharedName}'. Metro only supports object notation for shared modules.`);
57
+ if ('import' in sharedConfig && false !== sharedConfig.import && 'string' != typeof sharedConfig.import) throw new ConfigError(`Unsupported shared.import value for '${sharedName}'. Only string and false are supported in Metro.`);
58
+ for (const unsupportedField of unsupportedSharedFields)if (unsupportedField in sharedConfig) warnUnsupported(`shared.${sharedName}.${unsupportedField}`, `Option 'shared.${sharedName}.${unsupportedField}' is not supported in Metro and will have no effect.`);
22
59
  }
23
60
  }
61
+ function validateRemotes(remotes) {
62
+ if (void 0 === remotes) return;
63
+ if (!isPlainObject(remotes)) throw new ConfigError('Unsupported remotes format. Metro only supports remotes as Record<string, string>.');
64
+ for (const remoteConfig of Object.values(remotes))if ('string' != typeof remoteConfig) throw new ConfigError('Unsupported remotes format. Metro only supports remotes as Record<string, string>.');
65
+ }
66
+ function validateExposes(exposes) {
67
+ if (void 0 === exposes) return;
68
+ if (!isPlainObject(exposes)) throw new ConfigError('Unsupported exposes format. Metro only supports exposes as Record<string, string>.');
69
+ for (const exposeConfig of Object.values(exposes))if ('string' != typeof exposeConfig) throw new ConfigError('Unsupported exposes format. Metro only supports exposes as Record<string, string>.');
70
+ }
71
+ function validateRuntimePlugins(runtimePlugins) {
72
+ if (void 0 === runtimePlugins) return;
73
+ if (!Array.isArray(runtimePlugins)) throw new ConfigError('runtimePlugins must be an array.');
74
+ runtimePlugins.forEach((runtimePlugin, index)=>{
75
+ if ('string' == typeof runtimePlugin) return;
76
+ if (!Array.isArray(runtimePlugin)) throw new ConfigError('runtimePlugins entries must be either a string or a tuple of [path, params].');
77
+ const [pluginPath, pluginParams] = runtimePlugin;
78
+ if ('string' != typeof pluginPath) throw new ConfigError(`Invalid runtimePlugins[${index}] path. Expected a string path.`);
79
+ if (void 0 !== pluginParams) warnUnsupported(`runtimePlugins[${index}][1]`, `Option 'runtimePlugins[${index}][1]' is not supported in Metro and will have no effect.`);
80
+ });
81
+ }
82
+ function validateDts(dts) {
83
+ if (void 0 === dts || 'boolean' == typeof dts) return;
84
+ if (!isPlainObject(dts)) throw new ConfigError("Option 'dts' must be a boolean or a plain object.");
85
+ }
86
+ function validateUnsupportedTopLevelOptions(options) {
87
+ unsupportedTopLevelOptions.forEach((unsupportedOption)=>{
88
+ if (void 0 !== options[unsupportedOption]) warnUnsupported(String(unsupportedOption), `Option '${String(unsupportedOption)}' is not supported in Metro and will have no effect.`);
89
+ });
90
+ }
91
+ function validateDeprecatedOptions(options) {
92
+ if (options.plugins && options.plugins.length > 0) warnUnsupported('deprecated.plugins', "The 'plugins' option is deprecated. Use 'runtimePlugins' instead. Support for 'plugins' will be removed in the next major version.");
93
+ }
94
+ function warnUnsupported(key, message) {
95
+ if (warningSet.has(key)) return;
96
+ warningSet.add(key);
97
+ logger.warn(message);
98
+ }
99
+ function isPlainObject(value) {
100
+ return 'object' == typeof value && null !== value && !Array.isArray(value);
101
+ }
102
+ function isRelativePathLike(value) {
103
+ return /^\.{1,2}[\\/]/.test(value);
104
+ }
105
+ function isAbsolutePathLike(value) {
106
+ return node_path.posix.isAbsolute(value) || node_path.win32.isAbsolute(value);
107
+ }
24
108
  function validateOptions(options) {
109
+ validateUnsupportedTopLevelOptions(options);
110
+ validateDeprecatedOptions(options);
25
111
  validateName(options.name);
26
112
  validateFilename(options.filename);
113
+ validateRemotes(options.remotes);
114
+ validateExposes(options.exposes);
115
+ validateRuntimePlugins(options.runtimePlugins);
116
+ validateDts(options.dts);
27
117
  validateShared(options.shared);
28
118
  }
29
119
  export { validateOptions };
package/dist/types.d.ts CHANGED
@@ -1,21 +1,21 @@
1
- export interface SharedConfig {
2
- singleton: boolean;
3
- eager: boolean;
4
- version: string;
5
- requiredVersion: string;
6
- import?: false;
1
+ import type { moduleFederationPlugin } from '@module-federation/sdk';
2
+ export interface ModuleFederationConfig extends moduleFederationPlugin.ModuleFederationPluginOptions {
3
+ /**
4
+ * @deprecated Use runtimePlugins instead. Scheduled for removal in the next major version.
5
+ */
6
+ plugins?: string[];
7
7
  }
8
- export type Shared = Record<string, SharedConfig>;
9
- export interface ModuleFederationConfig {
8
+ export type ShareObject = Record<string, moduleFederationPlugin.SharedConfig>;
9
+ export interface ModuleFederationConfigNormalized {
10
10
  name: string;
11
- filename?: string;
12
- remotes?: Record<string, string>;
13
- exposes?: Record<string, string>;
14
- shared?: Shared;
15
- shareStrategy?: 'loaded-first' | 'version-first';
16
- plugins?: string[];
11
+ filename: string;
12
+ remotes: Record<string, string>;
13
+ exposes: Record<string, string>;
14
+ shared: ShareObject;
15
+ shareStrategy: moduleFederationPlugin.SharedStrategy;
16
+ plugins: string[];
17
+ dts: boolean | moduleFederationPlugin.PluginDtsOptions;
17
18
  }
18
- export type ModuleFederationConfigNormalized = Required<ModuleFederationConfig>;
19
19
  export type ModuleFederationExtraOptions = {
20
20
  flags?: MetroMFFlags;
21
21
  };
@@ -0,0 +1,12 @@
1
+ import type { ModuleFederationConfigNormalized } from '../types';
2
+ export type FederatedTypesMeta = {
3
+ zipName: string;
4
+ apiFileName: string;
5
+ };
6
+ export declare function maybeGenerateFederatedRemoteTypes(opts: {
7
+ federationConfig: ModuleFederationConfigNormalized;
8
+ projectRoot: string;
9
+ outputDir: string;
10
+ logger: Pick<Console, 'info' | 'warn'>;
11
+ }): Promise<FederatedTypesMeta | undefined>;
12
+ export declare function applyTypesMetaToManifest(manifest: Record<string, any>, typesMeta: FederatedTypesMeta | undefined): Record<string, any>;
@@ -0,0 +1,159 @@
1
+ "use strict";
2
+ var __webpack_modules__ = {
3
+ "@module-federation/dts-plugin": function(module) {
4
+ module.exports = import("@module-federation/dts-plugin").then(function(module) {
5
+ return module;
6
+ });
7
+ },
8
+ "@module-federation/dts-plugin/core": function(module) {
9
+ module.exports = import("@module-federation/dts-plugin/core").then(function(module) {
10
+ return module;
11
+ });
12
+ }
13
+ };
14
+ var __webpack_module_cache__ = {};
15
+ function __webpack_require__(moduleId) {
16
+ var cachedModule = __webpack_module_cache__[moduleId];
17
+ if (void 0 !== cachedModule) return cachedModule.exports;
18
+ var module = __webpack_module_cache__[moduleId] = {
19
+ exports: {}
20
+ };
21
+ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
22
+ return module.exports;
23
+ }
24
+ (()=>{
25
+ __webpack_require__.n = (module)=>{
26
+ var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
27
+ __webpack_require__.d(getter, {
28
+ a: getter
29
+ });
30
+ return getter;
31
+ };
32
+ })();
33
+ (()=>{
34
+ __webpack_require__.d = (exports1, definition)=>{
35
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
36
+ enumerable: true,
37
+ get: definition[key]
38
+ });
39
+ };
40
+ })();
41
+ (()=>{
42
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
43
+ })();
44
+ (()=>{
45
+ __webpack_require__.r = (exports1)=>{
46
+ if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
47
+ value: 'Module'
48
+ });
49
+ Object.defineProperty(exports1, '__esModule', {
50
+ value: true
51
+ });
52
+ };
53
+ })();
54
+ var __webpack_exports__ = {};
55
+ (()=>{
56
+ __webpack_require__.r(__webpack_exports__);
57
+ __webpack_require__.d(__webpack_exports__, {
58
+ maybeGenerateFederatedRemoteTypes: ()=>maybeGenerateFederatedRemoteTypes,
59
+ applyTypesMetaToManifest: ()=>applyTypesMetaToManifest
60
+ });
61
+ const external_node_fs_namespaceObject = require("node:fs");
62
+ const external_node_util_namespaceObject = require("node:util");
63
+ var external_node_util_default = /*#__PURE__*/ __webpack_require__.n(external_node_util_namespaceObject);
64
+ async function maybeGenerateFederatedRemoteTypes(opts) {
65
+ const { federationConfig, projectRoot, outputDir, logger } = opts;
66
+ if (false === federationConfig.dts) return;
67
+ const dtsPlugin = await Promise.resolve().then(__webpack_require__.bind(__webpack_require__, "@module-federation/dts-plugin"));
68
+ const dtsPluginCore = await Promise.resolve().then(__webpack_require__.bind(__webpack_require__, "@module-federation/dts-plugin/core"));
69
+ const dtsConfig = true === federationConfig.dts ? {
70
+ consumeTypes: false
71
+ } : 'object' != typeof federationConfig.dts || null === federationConfig.dts || Object.prototype.hasOwnProperty.call(federationConfig.dts, 'consumeTypes') ? federationConfig.dts : {
72
+ ...federationConfig.dts,
73
+ consumeTypes: false
74
+ };
75
+ const mfOptions = {
76
+ name: federationConfig.name,
77
+ filename: federationConfig.filename,
78
+ remotes: federationConfig.remotes,
79
+ exposes: federationConfig.exposes,
80
+ shared: federationConfig.shared,
81
+ dts: dtsConfig
82
+ };
83
+ const normalizedDtsOptions = dtsPlugin.normalizeDtsOptions(mfOptions, projectRoot, {
84
+ defaultGenerateOptions: {
85
+ generateAPITypes: true,
86
+ compileInChildProcess: false,
87
+ abortOnError: false,
88
+ extractThirdParty: false,
89
+ extractRemoteTypes: false
90
+ },
91
+ defaultConsumeOptions: {
92
+ abortOnError: true,
93
+ consumeAPITypes: true
94
+ }
95
+ });
96
+ if (!normalizedDtsOptions) return;
97
+ const dtsManagerOptions = dtsPlugin.normalizeGenerateTypesOptions({
98
+ context: projectRoot,
99
+ outputDir,
100
+ dtsOptions: normalizedDtsOptions,
101
+ pluginOptions: mfOptions
102
+ });
103
+ if (!dtsManagerOptions) return;
104
+ if (dtsManagerOptions.host) {
105
+ let remoteTypeUrls = dtsManagerOptions.host.remoteTypeUrls;
106
+ if ('function' == typeof remoteTypeUrls) remoteTypeUrls = await remoteTypeUrls();
107
+ await dtsPluginCore.consumeTypes({
108
+ ...dtsManagerOptions,
109
+ host: {
110
+ ...dtsManagerOptions.host,
111
+ remoteTypeUrls
112
+ }
113
+ });
114
+ }
115
+ logger.info(`${external_node_util_default().styleText('blue', 'Generating federated types (d.ts)')}`);
116
+ await dtsPlugin.generateTypesAPI({
117
+ dtsManagerOptions
118
+ });
119
+ const { zipTypesPath, apiTypesPath, zipName, apiFileName } = dtsPluginCore.retrieveTypesAssetsInfo(dtsManagerOptions.remote);
120
+ const produced = {};
121
+ const fileExists = async (p)=>{
122
+ try {
123
+ await external_node_fs_namespaceObject.promises.stat(p);
124
+ return true;
125
+ } catch {
126
+ return false;
127
+ }
128
+ };
129
+ if (zipTypesPath && zipName && await fileExists(zipTypesPath)) produced.zipName = zipName;
130
+ if (apiTypesPath && apiFileName && await fileExists(apiTypesPath)) produced.apiFileName = apiFileName;
131
+ if (process.env['FEDERATION_DEBUG']) logger.info(`dts debug: zipTypesPath=${zipTypesPath} zipExists=${String(Boolean(produced.zipName))} apiTypesPath=${apiTypesPath} apiExists=${String(Boolean(produced.apiFileName))}`);
132
+ if (!produced.zipName && !produced.apiFileName) return void logger.warn(`${external_node_util_default().styleText('yellow', 'Federated types enabled, but no types files were produced.')}`);
133
+ logger.info(`Done writing federated types:\n${external_node_util_default().styleText('dim', [
134
+ produced.zipName,
135
+ produced.apiFileName
136
+ ].filter(Boolean).join('\n'))}`);
137
+ return {
138
+ zipName: produced.zipName ?? '',
139
+ apiFileName: produced.apiFileName ?? ''
140
+ };
141
+ }
142
+ function applyTypesMetaToManifest(manifest, typesMeta) {
143
+ if (!(null == typesMeta ? void 0 : typesMeta.zipName) && !(null == typesMeta ? void 0 : typesMeta.apiFileName)) return manifest;
144
+ manifest.metaData = manifest.metaData || {};
145
+ manifest.metaData.types = manifest.metaData.types || {};
146
+ if (typesMeta.zipName) manifest.metaData.types.zip = typesMeta.zipName;
147
+ if (typesMeta.apiFileName) manifest.metaData.types.api = typesMeta.apiFileName;
148
+ return manifest;
149
+ }
150
+ })();
151
+ exports.applyTypesMetaToManifest = __webpack_exports__.applyTypesMetaToManifest;
152
+ exports.maybeGenerateFederatedRemoteTypes = __webpack_exports__.maybeGenerateFederatedRemoteTypes;
153
+ for(var __webpack_i__ in __webpack_exports__)if (-1 === [
154
+ "applyTypesMetaToManifest",
155
+ "maybeGenerateFederatedRemoteTypes"
156
+ ].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
157
+ Object.defineProperty(exports, '__esModule', {
158
+ value: true
159
+ });