@module-federation/vite 1.12.1 → 1.12.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
@@ -58,6 +58,7 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
58
58
  let _command;
59
59
  let emitFileId;
60
60
  let viteConfig;
61
+ let clientInjected = false;
61
62
  function injectHtml() {
62
63
  return inject === "html" && htmlFilePath;
63
64
  }
@@ -88,6 +89,7 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
88
89
  },
89
90
  transformIndexHtml(c) {
90
91
  if (!injectHtml()) return;
92
+ clientInjected = true;
91
93
  return c.replace("<head>", `<head><script type="module" src=${JSON.stringify(devEntryPath.replace(/.+?\:([/\\])[/\\]?/, "$1").replace(/\\\\?/g, "/"))}><\/script>`);
92
94
  },
93
95
  transform(code, id) {
@@ -160,9 +162,10 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
160
162
  }
161
163
  },
162
164
  transform(code, id) {
163
- if (injectEntry() && entryFiles.some((file) => id.endsWith(file))) return mapCodeToCodeWithSourcemap(`
164
- import ${JSON.stringify(getEntryPath())};
165
- ` + code);
165
+ if (injectEntry() && entryFiles.some((file) => id.endsWith(file)) || _command === "serve" && inject === "html" && !clientInjected && !id.includes("node_modules") && /\.(js|ts|mjs|vue|jsx|tsx)(\?|$)/.test(id)) {
166
+ clientInjected = true;
167
+ return mapCodeToCodeWithSourcemap(`import ${JSON.stringify(getEntryPath())};\n` + code);
168
+ }
166
169
  }
167
170
  }];
168
171
  };
@@ -856,10 +859,14 @@ var VirtualModule = class {
856
859
  writeSync(code, force) {
857
860
  if (!force && this.inited) return;
858
861
  if (!this.inited) this.inited = true;
859
- (0, fs.writeFileSync)(this.getPath(), code);
862
+ const path = this.getPath();
863
+ (0, fs.mkdirSync)((0, pathe.dirname)(path), { recursive: true });
864
+ (0, fs.writeFileSync)(path, code);
860
865
  }
861
866
  write(code) {
862
- (0, fs.writeFile)(this.getPath(), code, function() {});
867
+ const path = this.getPath();
868
+ (0, fs.mkdirSync)((0, pathe.dirname)(path), { recursive: true });
869
+ (0, fs.writeFile)(path, code, function() {});
863
870
  }
864
871
  };
865
872
  //#endregion
@@ -907,6 +914,14 @@ if (!globalThis[globalKey]) {
907
914
  initResolve,
908
915
  initReject,
909
916
  };
917
+ // In SSR (no window), resolve immediately with a stub runtime
918
+ // so modules don't hang waiting for browser-only init
919
+ if (typeof window === 'undefined') {
920
+ initResolve({
921
+ loadRemote: function() { return Promise.resolve(undefined); },
922
+ loadShare: function() { return Promise.resolve(undefined); },
923
+ });
924
+ }
910
925
  }
911
926
  ${exportStatement}
912
927
  `);
@@ -957,7 +972,7 @@ function generateRemotes(id, command, isRolldown) {
957
972
  function getPackageNamedExports(pkg) {
958
973
  try {
959
974
  const mod = (0, module$1.createRequire)(new URL("file://" + process.cwd() + "/package.json"))(pkg);
960
- return Object.keys(mod).filter((k) => k !== "default" && k !== "__esModule");
975
+ return Object.keys(mod).filter((k) => k !== "default" && k !== "__esModule" && /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(k));
961
976
  } catch {
962
977
  return [];
963
978
  }
@@ -986,9 +1001,9 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
986
1001
  const namedExports = getPackageNamedExports(pkg);
987
1002
  let exportLine;
988
1003
  if (namedExports.length > 0) {
989
- const destructure = `const { ${namedExports.join(", ")} } = exportModule;`;
990
- const namedExportLine = `export { ${namedExports.join(", ")} };`;
991
- exportLine = useESM ? `export default exportModule;\n ${destructure}\n ${namedExportLine}` : `module.exports = exportModule;\n ${destructure}\n Object.assign(module.exports, { ${namedExports.join(", ")} });`;
1004
+ const destructure = `const { ${namedExports.map((name, i) => `${name}: __mf_${i}`).join(", ")} } = exportModule;`;
1005
+ const namedExportLine = `export { ${namedExports.map((name, i) => `__mf_${i} as ${name}`).join(", ")} };`;
1006
+ exportLine = useESM ? `export default exportModule;\n ${destructure}\n ${namedExportLine}` : `module.exports = exportModule;\n ${destructure}\n Object.assign(module.exports, { ${namedExports.map((name, i) => `"${name}": __mf_${i}`).join(", ")} });`;
992
1007
  } else exportLine = useESM ? `export default exportModule\n export * from ${JSON.stringify(getPreBuildLibImportId(pkg))}` : "module.exports = exportModule";
993
1008
  loadShareCacheMap[pkg].writeSync(`
994
1009
  import ${JSON.stringify(getPreBuildLibImportId(pkg))};
@@ -1191,6 +1206,20 @@ function initVirtualModules(command, remoteEntryId) {
1191
1206
  }
1192
1207
  //#endregion
1193
1208
  //#region src/utils/bundleHelpers.ts
1209
+ /**
1210
+ * Resolve the local alias for a non-inlineable proxy binding.
1211
+ * If Rollup's deconflict renamed the alias but didn't update references
1212
+ * in the code body, fall back to proxyLocal so they stay in sync.
1213
+ */
1214
+ function resolveProxyAlias(binding, proxyLocal, code, fullImport) {
1215
+ const codeWithoutImport = code.replace(fullImport, "");
1216
+ const escapedLocal = binding.local.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1217
+ const localUsedInCode = new RegExp(`\\b${escapedLocal}\\b`).test(codeWithoutImport);
1218
+ return {
1219
+ imported: binding.imported,
1220
+ local: localUsedInCode ? binding.local : proxyLocal
1221
+ };
1222
+ }
1194
1223
  function findRemoteEntryFile(filename, bundle) {
1195
1224
  for (const [_, fileData] of Object.entries(bundle)) if (filename.replace(/[\[\]]/g, "_").replace(/\.[^/.]+$/, "") === fileData.name || fileData.name === "remoteEntry") return fileData.fileName;
1196
1225
  }
@@ -1657,14 +1686,16 @@ function pluginProxyRemoteEntry_default({ options, remoteEntryId, virtualExposes
1657
1686
  const host = typeof viteConfig.server?.host === "string" && viteConfig.server.host !== "0.0.0.0" ? viteConfig.server.host : "localhost";
1658
1687
  const publicPath = JSON.stringify(resolvePublicPath(options, viteConfig.base) + options.filename);
1659
1688
  return `
1660
- const origin = (window && ${!options.ignoreOrigin}) ? window.origin : "//${host}:${viteConfig.server?.port}"
1661
- const remoteEntryPromise = await import(origin + ${publicPath})
1662
- // __tla only serves as a hack for vite-plugin-top-level-await.
1663
- Promise.resolve(remoteEntryPromise)
1664
- .then(remoteEntry => {
1665
- return Promise.resolve(remoteEntry.__tla)
1666
- .then(remoteEntry.init).catch(remoteEntry.init)
1667
- })
1689
+ if (typeof window !== 'undefined') {
1690
+ const origin = (${!options.ignoreOrigin}) ? window.origin : "//${host}:${viteConfig.server?.port}"
1691
+ const remoteEntryPromise = await import(origin + ${publicPath})
1692
+ // __tla only serves as a hack for vite-plugin-top-level-await.
1693
+ Promise.resolve(remoteEntryPromise)
1694
+ .then(remoteEntry => {
1695
+ return Promise.resolve(remoteEntry.__tla)
1696
+ .then(remoteEntry.init).catch(remoteEntry.init)
1697
+ })
1698
+ }
1668
1699
  `;
1669
1700
  }
1670
1701
  return code;
@@ -1926,13 +1957,13 @@ function createEarlyVirtualModulesPlugin(options) {
1926
1957
  name: "vite:module-federation-early-init",
1927
1958
  enforce: "pre",
1928
1959
  config(config, { command: _command }) {
1929
- if (_command !== "serve") return;
1930
- const isRolldown = !!this?.meta?.rolldownVersion;
1931
1960
  const root = config.root || process.cwd();
1932
1961
  initVirtualModuleInfrastructure(root, virtualModuleDir);
1933
1962
  VirtualModule.setRoot(root);
1934
1963
  VirtualModule.ensureVirtualPackageExists();
1935
1964
  initVirtualModules(_command, getRemoteEntryId(options));
1965
+ if (_command !== "serve") return;
1966
+ const isRolldown = !!this?.meta?.rolldownVersion;
1936
1967
  if (remotes && Object.keys(remotes).length > 0) for (const key of Object.keys(remotes)) addUsedRemote(key, key);
1937
1968
  if (shared && Object.keys(shared).length > 0) {
1938
1969
  config.optimizeDeps = config.optimizeDeps || {};
@@ -2148,13 +2179,14 @@ function federation(mfUserOptions) {
2148
2179
  local: b.local,
2149
2180
  funcBody: renamedFunc
2150
2181
  });
2151
- } else nonInlineable.push(b);
2182
+ } else nonInlineable.push(resolveProxyAlias(b, proxyLocal, code, fullImport));
2152
2183
  }
2153
- if (inlineable.length === 0) continue;
2184
+ const hasRenamedAlias = nonInlineable.some((b) => bindings.find((ob) => ob.imported === b.imported)?.local !== b.local);
2185
+ if (inlineable.length === 0 && !hasRenamedAlias) continue;
2154
2186
  let replacement = "";
2155
2187
  if (nonInlineable.length > 0) replacement = `import{${nonInlineable.map((b) => b.imported === b.local ? b.imported : `${b.imported} as ${b.local}`).join(",")}}from"${importMatch[2]}";`;
2156
2188
  replacement += inlineable.map((f) => f.funcBody).join("");
2157
- code = code.replace(fullImport, replacement);
2189
+ code = code.replace(fullImport, () => replacement);
2158
2190
  modified = true;
2159
2191
  }
2160
2192
  if (modified) chunk.code = code;
@@ -2206,6 +2238,9 @@ function federation(mfUserOptions) {
2206
2238
  config.optimizeDeps.include ||= [];
2207
2239
  config.optimizeDeps.include.push("@module-federation/runtime");
2208
2240
  config.optimizeDeps.include.push(virtualDir);
2241
+ config.ssr ||= {};
2242
+ config.ssr.noExternal ||= [];
2243
+ if (Array.isArray(config.ssr.noExternal)) config.ssr.noExternal.push(virtualDir);
2209
2244
  options.runtimePlugins.forEach((p) => {
2210
2245
  const pluginPath = typeof p === "string" ? p : p[0];
2211
2246
  if (pluginPath && !pluginPath.startsWith(".") && !pluginPath.startsWith("/") && !pluginPath.startsWith("\0") && !pluginPath.startsWith("virtual:")) config.optimizeDeps.include.push(pluginPath);
package/lib/index.mjs CHANGED
@@ -37,6 +37,7 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
37
37
  let _command;
38
38
  let emitFileId;
39
39
  let viteConfig;
40
+ let clientInjected = false;
40
41
  function injectHtml() {
41
42
  return inject === "html" && htmlFilePath;
42
43
  }
@@ -67,6 +68,7 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
67
68
  },
68
69
  transformIndexHtml(c) {
69
70
  if (!injectHtml()) return;
71
+ clientInjected = true;
70
72
  return c.replace("<head>", `<head><script type="module" src=${JSON.stringify(devEntryPath.replace(/.+?\:([/\\])[/\\]?/, "$1").replace(/\\\\?/g, "/"))}><\/script>`);
71
73
  },
72
74
  transform(code, id) {
@@ -139,9 +141,10 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
139
141
  }
140
142
  },
141
143
  transform(code, id) {
142
- if (injectEntry() && entryFiles.some((file) => id.endsWith(file))) return mapCodeToCodeWithSourcemap(`
143
- import ${JSON.stringify(getEntryPath())};
144
- ` + code);
144
+ if (injectEntry() && entryFiles.some((file) => id.endsWith(file)) || _command === "serve" && inject === "html" && !clientInjected && !id.includes("node_modules") && /\.(js|ts|mjs|vue|jsx|tsx)(\?|$)/.test(id)) {
145
+ clientInjected = true;
146
+ return mapCodeToCodeWithSourcemap(`import ${JSON.stringify(getEntryPath())};\n` + code);
147
+ }
145
148
  }
146
149
  }];
147
150
  };
@@ -834,10 +837,14 @@ var VirtualModule = class {
834
837
  writeSync(code, force) {
835
838
  if (!force && this.inited) return;
836
839
  if (!this.inited) this.inited = true;
837
- writeFileSync(this.getPath(), code);
840
+ const path = this.getPath();
841
+ mkdirSync(dirname(path), { recursive: true });
842
+ writeFileSync(path, code);
838
843
  }
839
844
  write(code) {
840
- writeFile(this.getPath(), code, function() {});
845
+ const path = this.getPath();
846
+ mkdirSync(dirname(path), { recursive: true });
847
+ writeFile(path, code, function() {});
841
848
  }
842
849
  };
843
850
  //#endregion
@@ -885,6 +892,14 @@ if (!globalThis[globalKey]) {
885
892
  initResolve,
886
893
  initReject,
887
894
  };
895
+ // In SSR (no window), resolve immediately with a stub runtime
896
+ // so modules don't hang waiting for browser-only init
897
+ if (typeof window === 'undefined') {
898
+ initResolve({
899
+ loadRemote: function() { return Promise.resolve(undefined); },
900
+ loadShare: function() { return Promise.resolve(undefined); },
901
+ });
902
+ }
888
903
  }
889
904
  ${exportStatement}
890
905
  `);
@@ -935,7 +950,7 @@ function generateRemotes(id, command, isRolldown) {
935
950
  function getPackageNamedExports(pkg) {
936
951
  try {
937
952
  const mod = createRequire$1(new URL("file://" + process.cwd() + "/package.json"))(pkg);
938
- return Object.keys(mod).filter((k) => k !== "default" && k !== "__esModule");
953
+ return Object.keys(mod).filter((k) => k !== "default" && k !== "__esModule" && /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(k));
939
954
  } catch {
940
955
  return [];
941
956
  }
@@ -964,9 +979,9 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
964
979
  const namedExports = getPackageNamedExports(pkg);
965
980
  let exportLine;
966
981
  if (namedExports.length > 0) {
967
- const destructure = `const { ${namedExports.join(", ")} } = exportModule;`;
968
- const namedExportLine = `export { ${namedExports.join(", ")} };`;
969
- exportLine = useESM ? `export default exportModule;\n ${destructure}\n ${namedExportLine}` : `module.exports = exportModule;\n ${destructure}\n Object.assign(module.exports, { ${namedExports.join(", ")} });`;
982
+ const destructure = `const { ${namedExports.map((name, i) => `${name}: __mf_${i}`).join(", ")} } = exportModule;`;
983
+ const namedExportLine = `export { ${namedExports.map((name, i) => `__mf_${i} as ${name}`).join(", ")} };`;
984
+ exportLine = useESM ? `export default exportModule;\n ${destructure}\n ${namedExportLine}` : `module.exports = exportModule;\n ${destructure}\n Object.assign(module.exports, { ${namedExports.map((name, i) => `"${name}": __mf_${i}`).join(", ")} });`;
970
985
  } else exportLine = useESM ? `export default exportModule\n export * from ${JSON.stringify(getPreBuildLibImportId(pkg))}` : "module.exports = exportModule";
971
986
  loadShareCacheMap[pkg].writeSync(`
972
987
  import ${JSON.stringify(getPreBuildLibImportId(pkg))};
@@ -1169,6 +1184,20 @@ function initVirtualModules(command, remoteEntryId) {
1169
1184
  }
1170
1185
  //#endregion
1171
1186
  //#region src/utils/bundleHelpers.ts
1187
+ /**
1188
+ * Resolve the local alias for a non-inlineable proxy binding.
1189
+ * If Rollup's deconflict renamed the alias but didn't update references
1190
+ * in the code body, fall back to proxyLocal so they stay in sync.
1191
+ */
1192
+ function resolveProxyAlias(binding, proxyLocal, code, fullImport) {
1193
+ const codeWithoutImport = code.replace(fullImport, "");
1194
+ const escapedLocal = binding.local.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1195
+ const localUsedInCode = new RegExp(`\\b${escapedLocal}\\b`).test(codeWithoutImport);
1196
+ return {
1197
+ imported: binding.imported,
1198
+ local: localUsedInCode ? binding.local : proxyLocal
1199
+ };
1200
+ }
1172
1201
  function findRemoteEntryFile(filename, bundle) {
1173
1202
  for (const [_, fileData] of Object.entries(bundle)) if (filename.replace(/[\[\]]/g, "_").replace(/\.[^/.]+$/, "") === fileData.name || fileData.name === "remoteEntry") return fileData.fileName;
1174
1203
  }
@@ -1635,14 +1664,16 @@ function pluginProxyRemoteEntry_default({ options, remoteEntryId, virtualExposes
1635
1664
  const host = typeof viteConfig.server?.host === "string" && viteConfig.server.host !== "0.0.0.0" ? viteConfig.server.host : "localhost";
1636
1665
  const publicPath = JSON.stringify(resolvePublicPath(options, viteConfig.base) + options.filename);
1637
1666
  return `
1638
- const origin = (window && ${!options.ignoreOrigin}) ? window.origin : "//${host}:${viteConfig.server?.port}"
1639
- const remoteEntryPromise = await import(origin + ${publicPath})
1640
- // __tla only serves as a hack for vite-plugin-top-level-await.
1641
- Promise.resolve(remoteEntryPromise)
1642
- .then(remoteEntry => {
1643
- return Promise.resolve(remoteEntry.__tla)
1644
- .then(remoteEntry.init).catch(remoteEntry.init)
1645
- })
1667
+ if (typeof window !== 'undefined') {
1668
+ const origin = (${!options.ignoreOrigin}) ? window.origin : "//${host}:${viteConfig.server?.port}"
1669
+ const remoteEntryPromise = await import(origin + ${publicPath})
1670
+ // __tla only serves as a hack for vite-plugin-top-level-await.
1671
+ Promise.resolve(remoteEntryPromise)
1672
+ .then(remoteEntry => {
1673
+ return Promise.resolve(remoteEntry.__tla)
1674
+ .then(remoteEntry.init).catch(remoteEntry.init)
1675
+ })
1676
+ }
1646
1677
  `;
1647
1678
  }
1648
1679
  return code;
@@ -1904,13 +1935,13 @@ function createEarlyVirtualModulesPlugin(options) {
1904
1935
  name: "vite:module-federation-early-init",
1905
1936
  enforce: "pre",
1906
1937
  config(config, { command: _command }) {
1907
- if (_command !== "serve") return;
1908
- const isRolldown = !!this?.meta?.rolldownVersion;
1909
1938
  const root = config.root || process.cwd();
1910
1939
  initVirtualModuleInfrastructure(root, virtualModuleDir);
1911
1940
  VirtualModule.setRoot(root);
1912
1941
  VirtualModule.ensureVirtualPackageExists();
1913
1942
  initVirtualModules(_command, getRemoteEntryId(options));
1943
+ if (_command !== "serve") return;
1944
+ const isRolldown = !!this?.meta?.rolldownVersion;
1914
1945
  if (remotes && Object.keys(remotes).length > 0) for (const key of Object.keys(remotes)) addUsedRemote(key, key);
1915
1946
  if (shared && Object.keys(shared).length > 0) {
1916
1947
  config.optimizeDeps = config.optimizeDeps || {};
@@ -2126,13 +2157,14 @@ function federation(mfUserOptions) {
2126
2157
  local: b.local,
2127
2158
  funcBody: renamedFunc
2128
2159
  });
2129
- } else nonInlineable.push(b);
2160
+ } else nonInlineable.push(resolveProxyAlias(b, proxyLocal, code, fullImport));
2130
2161
  }
2131
- if (inlineable.length === 0) continue;
2162
+ const hasRenamedAlias = nonInlineable.some((b) => bindings.find((ob) => ob.imported === b.imported)?.local !== b.local);
2163
+ if (inlineable.length === 0 && !hasRenamedAlias) continue;
2132
2164
  let replacement = "";
2133
2165
  if (nonInlineable.length > 0) replacement = `import{${nonInlineable.map((b) => b.imported === b.local ? b.imported : `${b.imported} as ${b.local}`).join(",")}}from"${importMatch[2]}";`;
2134
2166
  replacement += inlineable.map((f) => f.funcBody).join("");
2135
- code = code.replace(fullImport, replacement);
2167
+ code = code.replace(fullImport, () => replacement);
2136
2168
  modified = true;
2137
2169
  }
2138
2170
  if (modified) chunk.code = code;
@@ -2184,6 +2216,9 @@ function federation(mfUserOptions) {
2184
2216
  config.optimizeDeps.include ||= [];
2185
2217
  config.optimizeDeps.include.push("@module-federation/runtime");
2186
2218
  config.optimizeDeps.include.push(virtualDir);
2219
+ config.ssr ||= {};
2220
+ config.ssr.noExternal ||= [];
2221
+ if (Array.isArray(config.ssr.noExternal)) config.ssr.noExternal.push(virtualDir);
2187
2222
  options.runtimePlugins.forEach((p) => {
2188
2223
  const pluginPath = typeof p === "string" ? p : p[0];
2189
2224
  if (pluginPath && !pluginPath.startsWith(".") && !pluginPath.startsWith("/") && !pluginPath.startsWith("\0") && !pluginPath.startsWith("virtual:")) config.optimizeDeps.include.push(pluginPath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@module-federation/vite",
3
- "version": "1.12.1",
3
+ "version": "1.12.3",
4
4
  "description": "Vite plugin for Module Federation",
5
5
  "type": "module",
6
6
  "main": "./lib/index.cjs",
@@ -14,7 +14,8 @@
14
14
  },
15
15
  "import": "./lib/index.mjs",
16
16
  "require": "./lib/index.cjs"
17
- }
17
+ },
18
+ "./package.json": "./package.json"
18
19
  },
19
20
  "files": [
20
21
  "lib/**/*"
@@ -65,9 +66,9 @@
65
66
  "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
66
67
  },
67
68
  "dependencies": {
68
- "@module-federation/dts-plugin": "^2.0.1",
69
- "@module-federation/runtime": "^2.0.1",
70
- "@module-federation/sdk": "^2.0.1",
69
+ "@module-federation/dts-plugin": "2.0.1",
70
+ "@module-federation/runtime": "2.0.1",
71
+ "@module-federation/sdk": "2.0.1",
71
72
  "@rollup/pluginutils": "^5.3.0",
72
73
  "defu": "^6.1.4",
73
74
  "estree-walker": "^3.0.3",