@module-federation/vite 1.14.2 → 1.14.3

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/lib/index.cjs CHANGED
@@ -1239,6 +1239,16 @@ function generateExposes(options) {
1239
1239
  return `
1240
1240
  const cssAssetMap = ${JSON.stringify(options.bundleAllCSS ? EXPOSES_CSS_MAP_PLACEHOLDER : {})};
1241
1241
  const injectedCssHrefs = new Set();
1242
+ let exposeLoadQueue = Promise.resolve();
1243
+
1244
+ async function importExposedModule(loader) {
1245
+ const currentLoad = exposeLoadQueue.then(loader, loader);
1246
+ exposeLoadQueue = currentLoad.then(
1247
+ () => undefined,
1248
+ () => undefined
1249
+ );
1250
+ return currentLoad;
1251
+ }
1242
1252
 
1243
1253
  async function injectCssAssets(exposeKey) {
1244
1254
  if (typeof document === "undefined") {
@@ -1283,7 +1293,9 @@ function generateExposes(options) {
1283
1293
  return `
1284
1294
  ${JSON.stringify(key)}: async () => {
1285
1295
  await injectCssAssets(${JSON.stringify(key)})
1286
- const importModule = await import(${JSON.stringify(options.exposes[key].import)})
1296
+ const importModule = await importExposedModule(
1297
+ () => import(${JSON.stringify(options.exposes[key].import)})
1298
+ )
1287
1299
  const exportModule = {}
1288
1300
  Object.assign(exportModule, importModule)
1289
1301
  Object.defineProperty(exportModule, "__esModule", {
@@ -2619,6 +2631,37 @@ var PromiseStore = class {
2619
2631
  function getPrebuildResolutionSource(pkgName, shareItem) {
2620
2632
  return getConcreteSharedImportSource(pkgName, shareItem) || pkgName;
2621
2633
  }
2634
+ /**
2635
+ * Reads the dependencies of an installed package from its package.json.
2636
+ */
2637
+ function getPackageDependencies(pkg) {
2638
+ const packageName = removePathFromNpmPackage(pkg);
2639
+ const cwd = getPackageDetectionCwd();
2640
+ const candidates = [pathe.default.join(cwd, "node_modules", packageName, "package.json")];
2641
+ for (const candidate of candidates) if ((0, fs.existsSync)(candidate)) try {
2642
+ const json = JSON.parse((0, fs.readFileSync)(candidate, "utf-8"));
2643
+ return Object.keys(json.dependencies || {});
2644
+ } catch {}
2645
+ return [];
2646
+ }
2647
+ /**
2648
+ * In dev mode, detects shared packages that are sub-dependencies of other
2649
+ * shared packages and removes them to avoid initialization order issues.
2650
+ * For example, `lit` depends on `lit-html`, `lit-element`, and
2651
+ * `@lit/reactive-element` — sharing them separately causes the child modules
2652
+ * to load before their parent, resulting in `undefined` class extends errors.
2653
+ */
2654
+ function excludeSharedSubDependencies(shared) {
2655
+ const sharedKeys = new Set(Object.keys(shared));
2656
+ for (const parentKey of sharedKeys) {
2657
+ const deps = getPackageDependencies(parentKey);
2658
+ for (const dep of deps) if (sharedKeys.has(dep) && dep !== parentKey) {
2659
+ mfWarn(`"${dep}" is a dependency of shared package "${parentKey}" and is also shared separately. This may cause initialization order issues in dev mode. Consider sharing only "${parentKey}".\n Auto-excluding "${dep}" from shared modules for dev mode.`);
2660
+ delete shared[dep];
2661
+ sharedKeys.delete(dep);
2662
+ }
2663
+ }
2664
+ }
2622
2665
  function proxySharedModule(options) {
2623
2666
  const { shared = {} } = options;
2624
2667
  let _config;
@@ -2644,6 +2687,7 @@ function proxySharedModule(options) {
2644
2687
  const isRolldown = getIsRolldown(this);
2645
2688
  _command = command;
2646
2689
  useDirectReactImport = isVinext || isAstro;
2690
+ if (command === "serve") excludeSharedSubDependencies(shared);
2647
2691
  config.resolve.alias.push(...Object.keys(shared).filter((key) => !(useDirectReactImport && key === "react")).map((key) => {
2648
2692
  const keyBase = key.endsWith("/") ? key.slice(0, -1) : key;
2649
2693
  const escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -3241,6 +3285,44 @@ function escapeUnsafeJsSourceChars(str) {
3241
3285
  return UNSAFE_JS_SOURCE_CHAR_MAP[char] ?? char;
3242
3286
  });
3243
3287
  }
3288
+ function insertAfterLastTopLevelImport(code, snippet) {
3289
+ let cursor = 0;
3290
+ let lastImportEnd = -1;
3291
+ const skipTrivia = () => {
3292
+ while (cursor < code.length) {
3293
+ if (/\s/.test(code[cursor])) {
3294
+ cursor++;
3295
+ continue;
3296
+ }
3297
+ if (code.startsWith("//", cursor)) {
3298
+ const lineEnd = code.indexOf("\n", cursor);
3299
+ cursor = lineEnd === -1 ? code.length : lineEnd + 1;
3300
+ continue;
3301
+ }
3302
+ if (code.startsWith("/*", cursor)) {
3303
+ const commentEnd = code.indexOf("*/", cursor + 2);
3304
+ cursor = commentEnd === -1 ? code.length : commentEnd + 2;
3305
+ continue;
3306
+ }
3307
+ break;
3308
+ }
3309
+ };
3310
+ while (cursor < code.length) {
3311
+ skipTrivia();
3312
+ if (!code.startsWith("import", cursor) || !/[\s"'*{]/.test(code[cursor + 6] ?? "")) break;
3313
+ const statementEnd = code.indexOf(";", cursor);
3314
+ if (statementEnd !== -1) {
3315
+ lastImportEnd = statementEnd + 1;
3316
+ cursor = statementEnd + 1;
3317
+ continue;
3318
+ }
3319
+ const lineEnd = code.indexOf("\n", cursor);
3320
+ lastImportEnd = lineEnd === -1 ? code.length : lineEnd + 1;
3321
+ cursor = lastImportEnd;
3322
+ }
3323
+ if (lastImportEnd === -1) return;
3324
+ return code.slice(0, lastImportEnd) + snippet + code.slice(lastImportEnd);
3325
+ }
3244
3326
  /**
3245
3327
  * Plugin that runs FIRST to create virtual module files in the config hook.
3246
3328
  * This prevents 504 "Outdated Optimize Dep" errors by ensuring files exist
@@ -3475,11 +3557,9 @@ function federation(mfUserOptions) {
3475
3557
  for (const v of importedFromLoadShare) if (new RegExp("\\(" + v + "\\(\\)\\s*,\\s*\\w+\\(\\w+\\)\\)").test(code)) allInits.push(v);
3476
3558
  if (allInits.length === 0) continue;
3477
3559
  const awaits = allInits.map((v) => `await ${v}();`).join("");
3478
- const lastFromRegex = /\bfrom\s*["'][^"']*["']\s*;?/g;
3479
- let lastFromEnd = -1;
3480
- while ((m = lastFromRegex.exec(code)) !== null) lastFromEnd = m.index + m[0].length;
3481
- if (lastFromEnd !== -1) {
3482
- chunk.code = code.slice(0, lastFromEnd) + awaits + code.slice(lastFromEnd);
3560
+ const codeWithAwaits = insertAfterLastTopLevelImport(code, awaits);
3561
+ if (codeWithAwaits) {
3562
+ chunk.code = codeWithAwaits;
3483
3563
  continue;
3484
3564
  }
3485
3565
  const exportIdx = code.search(/\bexport\s*[{d]/);
@@ -3610,14 +3690,7 @@ function federation(mfUserOptions) {
3610
3690
  })) return;
3611
3691
  if (/await\s+init_\w+__loadShare__/.test(code)) return;
3612
3692
  if (code.includes("__esmMin")) return;
3613
- const awaits = [...initFns].map((fn) => `await ${fn}();`).join("\n");
3614
- const topLevelImportRe = /^import\s/gm;
3615
- let lastImportIdx = -1;
3616
- let importMatch;
3617
- while ((importMatch = topLevelImportRe.exec(code)) !== null) lastImportIdx = importMatch.index;
3618
- if (lastImportIdx === -1) return;
3619
- const lineEnd = code.indexOf("\n", lastImportIdx);
3620
- return code.slice(0, lineEnd + 1) + awaits + "\n" + code.slice(lineEnd + 1);
3693
+ return insertAfterLastTopLevelImport(code, [...initFns].map((fn) => `await ${fn}();`).join("\n") + "\n");
3621
3694
  }
3622
3695
  },
3623
3696
  PluginDevProxyModuleTopLevelAwait(),
package/lib/index.mjs CHANGED
@@ -1216,6 +1216,16 @@ function generateExposes(options) {
1216
1216
  return `
1217
1217
  const cssAssetMap = ${JSON.stringify(options.bundleAllCSS ? EXPOSES_CSS_MAP_PLACEHOLDER : {})};
1218
1218
  const injectedCssHrefs = new Set();
1219
+ let exposeLoadQueue = Promise.resolve();
1220
+
1221
+ async function importExposedModule(loader) {
1222
+ const currentLoad = exposeLoadQueue.then(loader, loader);
1223
+ exposeLoadQueue = currentLoad.then(
1224
+ () => undefined,
1225
+ () => undefined
1226
+ );
1227
+ return currentLoad;
1228
+ }
1219
1229
 
1220
1230
  async function injectCssAssets(exposeKey) {
1221
1231
  if (typeof document === "undefined") {
@@ -1260,7 +1270,9 @@ function generateExposes(options) {
1260
1270
  return `
1261
1271
  ${JSON.stringify(key)}: async () => {
1262
1272
  await injectCssAssets(${JSON.stringify(key)})
1263
- const importModule = await import(${JSON.stringify(options.exposes[key].import)})
1273
+ const importModule = await importExposedModule(
1274
+ () => import(${JSON.stringify(options.exposes[key].import)})
1275
+ )
1264
1276
  const exportModule = {}
1265
1277
  Object.assign(exportModule, importModule)
1266
1278
  Object.defineProperty(exportModule, "__esModule", {
@@ -2596,6 +2608,37 @@ var PromiseStore = class {
2596
2608
  function getPrebuildResolutionSource(pkgName, shareItem) {
2597
2609
  return getConcreteSharedImportSource(pkgName, shareItem) || pkgName;
2598
2610
  }
2611
+ /**
2612
+ * Reads the dependencies of an installed package from its package.json.
2613
+ */
2614
+ function getPackageDependencies(pkg) {
2615
+ const packageName = removePathFromNpmPackage(pkg);
2616
+ const cwd = getPackageDetectionCwd();
2617
+ const candidates = [path.join(cwd, "node_modules", packageName, "package.json")];
2618
+ for (const candidate of candidates) if (existsSync(candidate)) try {
2619
+ const json = JSON.parse(readFileSync(candidate, "utf-8"));
2620
+ return Object.keys(json.dependencies || {});
2621
+ } catch {}
2622
+ return [];
2623
+ }
2624
+ /**
2625
+ * In dev mode, detects shared packages that are sub-dependencies of other
2626
+ * shared packages and removes them to avoid initialization order issues.
2627
+ * For example, `lit` depends on `lit-html`, `lit-element`, and
2628
+ * `@lit/reactive-element` — sharing them separately causes the child modules
2629
+ * to load before their parent, resulting in `undefined` class extends errors.
2630
+ */
2631
+ function excludeSharedSubDependencies(shared) {
2632
+ const sharedKeys = new Set(Object.keys(shared));
2633
+ for (const parentKey of sharedKeys) {
2634
+ const deps = getPackageDependencies(parentKey);
2635
+ for (const dep of deps) if (sharedKeys.has(dep) && dep !== parentKey) {
2636
+ mfWarn(`"${dep}" is a dependency of shared package "${parentKey}" and is also shared separately. This may cause initialization order issues in dev mode. Consider sharing only "${parentKey}".\n Auto-excluding "${dep}" from shared modules for dev mode.`);
2637
+ delete shared[dep];
2638
+ sharedKeys.delete(dep);
2639
+ }
2640
+ }
2641
+ }
2599
2642
  function proxySharedModule(options) {
2600
2643
  const { shared = {} } = options;
2601
2644
  let _config;
@@ -2621,6 +2664,7 @@ function proxySharedModule(options) {
2621
2664
  const isRolldown = getIsRolldown(this);
2622
2665
  _command = command;
2623
2666
  useDirectReactImport = isVinext || isAstro;
2667
+ if (command === "serve") excludeSharedSubDependencies(shared);
2624
2668
  config.resolve.alias.push(...Object.keys(shared).filter((key) => !(useDirectReactImport && key === "react")).map((key) => {
2625
2669
  const keyBase = key.endsWith("/") ? key.slice(0, -1) : key;
2626
2670
  const escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -3218,6 +3262,44 @@ function escapeUnsafeJsSourceChars(str) {
3218
3262
  return UNSAFE_JS_SOURCE_CHAR_MAP[char] ?? char;
3219
3263
  });
3220
3264
  }
3265
+ function insertAfterLastTopLevelImport(code, snippet) {
3266
+ let cursor = 0;
3267
+ let lastImportEnd = -1;
3268
+ const skipTrivia = () => {
3269
+ while (cursor < code.length) {
3270
+ if (/\s/.test(code[cursor])) {
3271
+ cursor++;
3272
+ continue;
3273
+ }
3274
+ if (code.startsWith("//", cursor)) {
3275
+ const lineEnd = code.indexOf("\n", cursor);
3276
+ cursor = lineEnd === -1 ? code.length : lineEnd + 1;
3277
+ continue;
3278
+ }
3279
+ if (code.startsWith("/*", cursor)) {
3280
+ const commentEnd = code.indexOf("*/", cursor + 2);
3281
+ cursor = commentEnd === -1 ? code.length : commentEnd + 2;
3282
+ continue;
3283
+ }
3284
+ break;
3285
+ }
3286
+ };
3287
+ while (cursor < code.length) {
3288
+ skipTrivia();
3289
+ if (!code.startsWith("import", cursor) || !/[\s"'*{]/.test(code[cursor + 6] ?? "")) break;
3290
+ const statementEnd = code.indexOf(";", cursor);
3291
+ if (statementEnd !== -1) {
3292
+ lastImportEnd = statementEnd + 1;
3293
+ cursor = statementEnd + 1;
3294
+ continue;
3295
+ }
3296
+ const lineEnd = code.indexOf("\n", cursor);
3297
+ lastImportEnd = lineEnd === -1 ? code.length : lineEnd + 1;
3298
+ cursor = lastImportEnd;
3299
+ }
3300
+ if (lastImportEnd === -1) return;
3301
+ return code.slice(0, lastImportEnd) + snippet + code.slice(lastImportEnd);
3302
+ }
3221
3303
  /**
3222
3304
  * Plugin that runs FIRST to create virtual module files in the config hook.
3223
3305
  * This prevents 504 "Outdated Optimize Dep" errors by ensuring files exist
@@ -3452,11 +3534,9 @@ function federation(mfUserOptions) {
3452
3534
  for (const v of importedFromLoadShare) if (new RegExp("\\(" + v + "\\(\\)\\s*,\\s*\\w+\\(\\w+\\)\\)").test(code)) allInits.push(v);
3453
3535
  if (allInits.length === 0) continue;
3454
3536
  const awaits = allInits.map((v) => `await ${v}();`).join("");
3455
- const lastFromRegex = /\bfrom\s*["'][^"']*["']\s*;?/g;
3456
- let lastFromEnd = -1;
3457
- while ((m = lastFromRegex.exec(code)) !== null) lastFromEnd = m.index + m[0].length;
3458
- if (lastFromEnd !== -1) {
3459
- chunk.code = code.slice(0, lastFromEnd) + awaits + code.slice(lastFromEnd);
3537
+ const codeWithAwaits = insertAfterLastTopLevelImport(code, awaits);
3538
+ if (codeWithAwaits) {
3539
+ chunk.code = codeWithAwaits;
3460
3540
  continue;
3461
3541
  }
3462
3542
  const exportIdx = code.search(/\bexport\s*[{d]/);
@@ -3587,14 +3667,7 @@ function federation(mfUserOptions) {
3587
3667
  })) return;
3588
3668
  if (/await\s+init_\w+__loadShare__/.test(code)) return;
3589
3669
  if (code.includes("__esmMin")) return;
3590
- const awaits = [...initFns].map((fn) => `await ${fn}();`).join("\n");
3591
- const topLevelImportRe = /^import\s/gm;
3592
- let lastImportIdx = -1;
3593
- let importMatch;
3594
- while ((importMatch = topLevelImportRe.exec(code)) !== null) lastImportIdx = importMatch.index;
3595
- if (lastImportIdx === -1) return;
3596
- const lineEnd = code.indexOf("\n", lastImportIdx);
3597
- return code.slice(0, lineEnd + 1) + awaits + "\n" + code.slice(lineEnd + 1);
3670
+ return insertAfterLastTopLevelImport(code, [...initFns].map((fn) => `await ${fn}();`).join("\n") + "\n");
3598
3671
  }
3599
3672
  },
3600
3673
  PluginDevProxyModuleTopLevelAwait(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@module-federation/vite",
3
- "version": "1.14.2",
3
+ "version": "1.14.3",
4
4
  "description": "Vite plugin for Module Federation",
5
5
  "type": "module",
6
6
  "main": "./lib/index.cjs",
@@ -70,9 +70,9 @@
70
70
  "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
71
71
  },
72
72
  "dependencies": {
73
- "@module-federation/dts-plugin": "2.3.1",
74
- "@module-federation/runtime": "2.3.1",
75
- "@module-federation/sdk": "2.3.1",
73
+ "@module-federation/dts-plugin": "2.3.2",
74
+ "@module-federation/runtime": "2.3.2",
75
+ "@module-federation/sdk": "2.3.2",
76
76
  "@rollup/pluginutils": "^5.3.0",
77
77
  "defu": "^6.1.4",
78
78
  "es-module-lexer": "^2.0.0",