@module-federation/vite 1.9.3 → 1.9.5

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.
@@ -6,6 +6,9 @@ import path__default, { resolve, basename, parse, join, dirname } from 'pathe';
6
6
  import MagicString from 'magic-string';
7
7
  import { createFilter } from '@rollup/pluginutils';
8
8
  import { walk } from 'estree-walker';
9
+ import { normalizeOptions } from '@module-federation/sdk';
10
+ import { normalizeDtsOptions, normalizeConsumeTypesOptions, consumeTypesAPI, normalizeGenerateTypesOptions, generateTypesAPI, isTSProject } from '@module-federation/dts-plugin';
11
+ import { rpc } from '@module-federation/dts-plugin/core';
9
12
 
10
13
  async function mapCodeToCodeWithSourcemap(code) {
11
14
  const resolvedCode = await code;
@@ -121,10 +124,12 @@ const addEntry = ({
121
124
  }
122
125
  },
123
126
  generateBundle(options, bundle) {
127
+ var _viteConfig$experimen, _viteConfig$experimen2;
124
128
  if (!injectHtml()) return;
125
129
  const file = this.getFileName(emitFileId);
130
+ const path = (_viteConfig$experimen = viteConfig.experimental) != null && _viteConfig$experimen.renderBuiltUrl ? (_viteConfig$experimen2 = viteConfig.experimental) == null ? void 0 : _viteConfig$experimen2.renderBuiltUrl(file) : viteConfig.base + file;
126
131
  const scriptContent = `
127
- <script type="module" src="${viteConfig.base + file}"></script>
132
+ <script type="module" src="${path}"></script>
128
133
  `;
129
134
  for (const fileName in bundle) {
130
135
  if (fileName.endsWith('.html')) {
@@ -535,6 +540,92 @@ function createFile(filePath, content) {
535
540
  writeFileSync(filePath, content);
536
541
  }
537
542
 
543
+ /**
544
+ * Serializes a JavaScript object into a string of source code that can be evaluated.
545
+ * This function is used to create runtime plugin options without relying solely on JSON.stringify,
546
+ * allowing support for non-JSON types like RegExp, Date, Map, Set, and Functions.
547
+ * It also safely handles circular references.
548
+ *
549
+ * @param {Record<string, unknown>} options - The options object to serialize.
550
+ * @returns {string} The resulting JavaScript source code string.
551
+ */
552
+ function serializeRuntimeOptions(options) {
553
+ // Use a WeakSet to track objects already encountered, which helps in detecting circular references.
554
+ const seenObjects = new WeakSet();
555
+ /**
556
+ * Recursive inner function to serialize any value into a source code string.
557
+ */
558
+ function valueToCode(val) {
559
+ // 1. Handle primitive values
560
+ if (val === null) return 'null';
561
+ const type = typeof val;
562
+ if (type === 'string') return JSON.stringify(val);
563
+ if (type === 'number' || type === 'boolean') return String(val);
564
+ if (type === 'undefined') return 'undefined';
565
+ // Handle Symbol
566
+ if (type === 'symbol') {
567
+ var _val$description;
568
+ const desc = (_val$description = val.description) != null ? _val$description : '';
569
+ return `Symbol(${JSON.stringify(desc)})`;
570
+ }
571
+ // Handle Function (returns the function's source code)
572
+ if (type === 'function') return val.toString();
573
+ // 2. Handle special built-in objects
574
+ if (val instanceof Date) return `new Date(${JSON.stringify(val.toISOString())})`;
575
+ if (val instanceof RegExp) {
576
+ return `new RegExp(${JSON.stringify(val.source)}, ${JSON.stringify(val.flags)})`;
577
+ }
578
+ // 3. Check for circular references and mark object as seen
579
+ // This applies to objects, arrays, maps, and sets.
580
+ if (type === 'object') {
581
+ if (seenObjects.has(val)) {
582
+ // This object has been seen previously in the recursion path
583
+ return `"__circular__"`;
584
+ }
585
+ seenObjects.add(val);
586
+ }
587
+ // 4. Handle Array, Map, Set
588
+ if (Array.isArray(val)) {
589
+ // Recursively serialize each element
590
+ return `[${val.map(valueToCode).join(', ')}]`;
591
+ }
592
+ if (val instanceof Map) {
593
+ // Serialize Map entries into an array of [key, value] pairs
594
+ const entries = Array.from(val.entries()).map(([k, v]) => `[${valueToCode(k)}, ${valueToCode(v)}]`);
595
+ return `new Map([${entries.join(', ')}])`;
596
+ }
597
+ if (val instanceof Set) {
598
+ // Serialize Set values into an array
599
+ const items = Array.from(val.values()).map(valueToCode);
600
+ return `new Set([${items.join(', ')}])`;
601
+ }
602
+ // 5. Handle plain objects (the default object type)
603
+ if (type === 'object') {
604
+ const properties = [];
605
+ // Iterate over the object's own enumerable properties
606
+ for (const key in val) {
607
+ if (Object.prototype.hasOwnProperty.call(val, key)) {
608
+ // Wrap the key in JSON.stringify to handle non-identifier keys
609
+ properties.push(`${JSON.stringify(key)}: ${valueToCode(val[key])}`);
610
+ }
611
+ }
612
+ return `{${properties.join(', ')}}`;
613
+ }
614
+ // 6. Fallback case (e.g., BigInt, other object types)
615
+ // Coerce to string and then JSON.stringify that string for safety
616
+ return JSON.stringify(String(val));
617
+ }
618
+ // Start serialization for the top-level object
619
+ const topLevelProps = [];
620
+ // Iterate over the properties of the root 'options' object
621
+ for (const key in options) {
622
+ if (Object.prototype.hasOwnProperty.call(options, key)) {
623
+ topLevelProps.push(`${JSON.stringify(key)}: ${valueToCode(options[key])}`);
624
+ }
625
+ }
626
+ return `{${topLevelProps.join(', ')}}`;
627
+ }
628
+
538
629
  // Cache root path
539
630
  let rootDir;
540
631
  function findNodeModulesDir(root = process.cwd()) {
@@ -857,7 +948,13 @@ function generateLocalSharedImportMap() {
857
948
  }
858
949
  const REMOTE_ENTRY_ID = 'virtual:mf-REMOTE_ENTRY_ID';
859
950
  function generateRemoteEntry(options) {
860
- const pluginImportNames = options.runtimePlugins.map((p, i) => [`$runtimePlugin_${i}`, `import $runtimePlugin_${i} from "${p}";`]);
951
+ const pluginImportNames = options.runtimePlugins.map((p, i) => {
952
+ if (typeof p === 'string') {
953
+ return [`$runtimePlugin_${i}`, `import $runtimePlugin_${i} from "${p}";`, `undefined`];
954
+ } else {
955
+ return [`$runtimePlugin_${i}`, `import $runtimePlugin_${i} from "${p[0]}";`, serializeRuntimeOptions(p[1])];
956
+ }
957
+ });
861
958
  return `
862
959
  import {init as runtimeInit, loadRemote} from "@module-federation/runtime";
863
960
  ${pluginImportNames.map(item => item[1]).join('\n')}
@@ -874,7 +971,7 @@ function generateRemoteEntry(options) {
874
971
  name: mfName,
875
972
  remotes: usedRemotes,
876
973
  shared: usedShared,
877
- plugins: [${pluginImportNames.map(item => `${item[0]}()`).join(', ')}],
974
+ plugins: [${pluginImportNames.map(item => `${item[0]}(${item[2]})`).join(', ')}],
878
975
  ${options.shareStrategy ? `shareStrategy: '${options.shareStrategy}'` : ''}
879
976
  });
880
977
  // handling circular init calls
@@ -1494,6 +1591,279 @@ function pluginProxyRemotes (options) {
1494
1591
  };
1495
1592
  }
1496
1593
 
1594
+ const DEFAULT_DEV_OPTIONS = {
1595
+ disableLiveReload: true,
1596
+ disableHotTypesReload: false,
1597
+ disableDynamicRemoteTypeHints: false
1598
+ };
1599
+ const DYNAMIC_HINTS_PLUGIN = '@module-federation/dts-plugin/dynamic-remote-type-hints-plugin';
1600
+ const getIPv4 = () => process.env['FEDERATION_IPV4'] || '127.0.0.1';
1601
+ const forkDevWorkerPath = (() => {
1602
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
1603
+ return require.resolve('@module-federation/dts-plugin/dist/fork-dev-worker.js');
1604
+ })();
1605
+ class DevWorker {
1606
+ constructor(options) {
1607
+ this.worker = rpc.createRpcWorker(forkDevWorkerPath, {}, undefined, false);
1608
+ this.worker.connect(options);
1609
+ }
1610
+ update() {
1611
+ var _this$worker$process;
1612
+ (_this$worker$process = this.worker.process) == null || _this$worker$process.send == null || _this$worker$process.send({
1613
+ type: rpc.RpcGMCallTypes.CALL,
1614
+ id: this.worker.id,
1615
+ args: [undefined, 'update']
1616
+ });
1617
+ }
1618
+ exit() {
1619
+ this.worker.terminate();
1620
+ }
1621
+ }
1622
+ const normalizeDevOptions = dev => {
1623
+ if (dev === false) {
1624
+ return false;
1625
+ }
1626
+ if (dev === true || typeof dev === 'undefined') {
1627
+ return _extends({}, DEFAULT_DEV_OPTIONS);
1628
+ }
1629
+ return _extends({}, DEFAULT_DEV_OPTIONS, dev);
1630
+ };
1631
+ const buildDtsModuleFederationConfig = options => {
1632
+ const exposes = {};
1633
+ Object.entries(options.exposes).forEach(([key, value]) => {
1634
+ if (typeof value === 'string') {
1635
+ exposes[key] = value;
1636
+ return;
1637
+ }
1638
+ const importValue = Array.isArray(value.import) ? value.import[0] : value.import;
1639
+ if (importValue) {
1640
+ exposes[key] = importValue;
1641
+ }
1642
+ });
1643
+ const remotes = {};
1644
+ Object.entries(options.remotes).forEach(([key, remote]) => {
1645
+ var _remote$entryGlobalNa, _remote$entryGlobalNa2;
1646
+ if (typeof remote === 'string') {
1647
+ remotes[key] = remote;
1648
+ return;
1649
+ }
1650
+ if (!remote.entry) {
1651
+ return;
1652
+ }
1653
+ const entryLooksLikeUrl = ((_remote$entryGlobalNa = remote.entryGlobalName) == null ? void 0 : _remote$entryGlobalNa.startsWith('http')) || ((_remote$entryGlobalNa2 = remote.entryGlobalName) == null ? void 0 : _remote$entryGlobalNa2.includes('.json'));
1654
+ const entryGlobalName = entryLooksLikeUrl ? remote.name || key : remote.entryGlobalName || remote.name || key;
1655
+ remotes[key] = `${entryGlobalName}@${remote.entry}`;
1656
+ });
1657
+ return _extends({}, options, {
1658
+ exposes,
1659
+ remotes
1660
+ });
1661
+ };
1662
+ const resolveOutputDir = config => {
1663
+ const {
1664
+ outDir
1665
+ } = config.build;
1666
+ if (path.isAbsolute(outDir)) {
1667
+ return path.relative(config.root, outDir);
1668
+ }
1669
+ return outDir;
1670
+ };
1671
+ const ensureRuntimePlugin = (options, pluginId) => {
1672
+ const hasPlugin = options.runtimePlugins.some(plugin => {
1673
+ if (typeof plugin === 'string') {
1674
+ return plugin === pluginId;
1675
+ }
1676
+ return plugin[0] === pluginId;
1677
+ });
1678
+ if (!hasPlugin) {
1679
+ options.runtimePlugins.push(pluginId);
1680
+ }
1681
+ };
1682
+ const normalizeDevDtsOptions = (dts, context) => {
1683
+ const defaultGenerateTypes = {
1684
+ compileInChildProcess: true
1685
+ };
1686
+ const defaultConsumeTypes = {
1687
+ consumeAPITypes: true
1688
+ };
1689
+ return normalizeOptions(isTSProject(dts, context), {
1690
+ generateTypes: defaultGenerateTypes,
1691
+ consumeTypes: defaultConsumeTypes,
1692
+ extraOptions: {},
1693
+ displayErrorInTerminal: typeof dts === 'object' && dts ? dts.displayErrorInTerminal : undefined
1694
+ }, 'mfOptions.dts')(dts);
1695
+ };
1696
+ const logDtsError = (error, dtsOptions) => {
1697
+ if (dtsOptions && dtsOptions.displayErrorInTerminal !== false) {
1698
+ console.error(error);
1699
+ }
1700
+ };
1701
+ function pluginDts(options) {
1702
+ if (options.dts === false) {
1703
+ return [];
1704
+ }
1705
+ const dtsModuleFederationConfig = buildDtsModuleFederationConfig(options);
1706
+ let resolvedConfig;
1707
+ let devWorker;
1708
+ let normalizedDevOptions;
1709
+ const devPlugin = {
1710
+ name: 'module-federation-dts-dev',
1711
+ apply: 'serve',
1712
+ config(config) {
1713
+ normalizedDevOptions = normalizeDevOptions(options.dev);
1714
+ if (!normalizedDevOptions) {
1715
+ return;
1716
+ }
1717
+ if (normalizedDevOptions.disableDynamicRemoteTypeHints) {
1718
+ return;
1719
+ }
1720
+ ensureRuntimePlugin(options, DYNAMIC_HINTS_PLUGIN);
1721
+ const define = config.define ? _extends({}, config.define) : {};
1722
+ if (!('FEDERATION_IPV4' in define)) {
1723
+ define.FEDERATION_IPV4 = JSON.stringify(getIPv4());
1724
+ }
1725
+ config.define = define;
1726
+ },
1727
+ configResolved(config) {
1728
+ resolvedConfig = config;
1729
+ },
1730
+ configureServer(server) {
1731
+ if (!normalizedDevOptions || !resolvedConfig) {
1732
+ return;
1733
+ }
1734
+ const devOptions = normalizedDevOptions;
1735
+ if (devOptions.disableDynamicRemoteTypeHints && devOptions.disableHotTypesReload && devOptions.disableLiveReload) {
1736
+ return;
1737
+ }
1738
+ if (!options.name) {
1739
+ throw new Error('name is required if you want to enable dev server!');
1740
+ }
1741
+ const outputDir = resolveOutputDir(resolvedConfig);
1742
+ const normalizedDtsOptions = normalizeDevDtsOptions(options.dts, resolvedConfig.root);
1743
+ if (typeof normalizedDtsOptions !== 'object') {
1744
+ return;
1745
+ }
1746
+ const normalizedGenerateTypes = normalizeOptions(Boolean(normalizedDtsOptions), {
1747
+ compileInChildProcess: true
1748
+ }, 'mfOptions.dts.generateTypes')(normalizedDtsOptions.generateTypes);
1749
+ const remote = normalizedGenerateTypes === false ? undefined : _extends({
1750
+ implementation: normalizedDtsOptions.implementation,
1751
+ context: resolvedConfig.root,
1752
+ outputDir,
1753
+ moduleFederationConfig: _extends({}, dtsModuleFederationConfig),
1754
+ hostRemoteTypesFolder: normalizedGenerateTypes.typesFolder || '@mf-types'
1755
+ }, normalizedGenerateTypes, {
1756
+ typesFolder: '.dev-server'
1757
+ });
1758
+ if (remote && !remote.tsConfigPath && typeof normalizedDtsOptions === 'object' && normalizedDtsOptions.tsConfigPath) {
1759
+ remote.tsConfigPath = normalizedDtsOptions.tsConfigPath;
1760
+ }
1761
+ const normalizedConsumeTypes = normalizeOptions(Boolean(normalizedDtsOptions), {
1762
+ consumeAPITypes: true
1763
+ }, 'mfOptions.dts.consumeTypes')(normalizedDtsOptions.consumeTypes);
1764
+ const host = normalizedConsumeTypes === false ? undefined : _extends({
1765
+ implementation: normalizedDtsOptions.implementation,
1766
+ context: resolvedConfig.root,
1767
+ moduleFederationConfig: dtsModuleFederationConfig,
1768
+ typesFolder: normalizedConsumeTypes.typesFolder || '@mf-types',
1769
+ abortOnError: false
1770
+ }, normalizedConsumeTypes);
1771
+ const extraOptions = normalizedDtsOptions.extraOptions || {};
1772
+ if (!remote && !host && devOptions.disableLiveReload) {
1773
+ return;
1774
+ }
1775
+ const startDevWorker = async () => {
1776
+ var _server$httpServer;
1777
+ let remoteTypeUrls;
1778
+ if (host) {
1779
+ remoteTypeUrls = await new Promise(resolve => {
1780
+ consumeTypesAPI({
1781
+ host,
1782
+ extraOptions,
1783
+ displayErrorInTerminal: normalizedDtsOptions.displayErrorInTerminal
1784
+ }, resolve);
1785
+ });
1786
+ }
1787
+ devWorker = new DevWorker({
1788
+ name: options.name,
1789
+ remote,
1790
+ host: host ? _extends({}, host, {
1791
+ remoteTypeUrls
1792
+ }) : undefined,
1793
+ extraOptions,
1794
+ disableLiveReload: devOptions.disableLiveReload,
1795
+ disableHotTypesReload: devOptions.disableHotTypesReload
1796
+ });
1797
+ const update = () => {
1798
+ var _devWorker;
1799
+ return (_devWorker = devWorker) == null ? void 0 : _devWorker.update();
1800
+ };
1801
+ server.watcher.on('change', update);
1802
+ server.watcher.on('add', update);
1803
+ server.watcher.on('unlink', update);
1804
+ (_server$httpServer = server.httpServer) == null || _server$httpServer.once('close', () => {
1805
+ var _devWorker2;
1806
+ (_devWorker2 = devWorker) == null || _devWorker2.exit();
1807
+ server.watcher.off('change', update);
1808
+ server.watcher.off('add', update);
1809
+ server.watcher.off('unlink', update);
1810
+ });
1811
+ };
1812
+ startDevWorker().catch(error => {
1813
+ logDtsError(error, normalizedDtsOptions);
1814
+ });
1815
+ }
1816
+ };
1817
+ const buildPlugin = {
1818
+ name: 'module-federation-dts-build',
1819
+ apply: 'build',
1820
+ configResolved(config) {
1821
+ resolvedConfig = config;
1822
+ },
1823
+ async closeBundle() {
1824
+ var _consumeOptions$host;
1825
+ if (!resolvedConfig) {
1826
+ return;
1827
+ }
1828
+ const normalizedDtsOptions = normalizeDtsOptions(dtsModuleFederationConfig, resolvedConfig.root);
1829
+ if (typeof normalizedDtsOptions !== 'object') {
1830
+ return;
1831
+ }
1832
+ const context = resolvedConfig.root;
1833
+ const outputDir = resolveOutputDir(resolvedConfig);
1834
+ const consumeOptions = normalizeConsumeTypesOptions({
1835
+ context,
1836
+ dtsOptions: normalizedDtsOptions,
1837
+ pluginOptions: dtsModuleFederationConfig
1838
+ });
1839
+ if (consumeOptions != null && (_consumeOptions$host = consumeOptions.host) != null && _consumeOptions$host.typesOnBuild) {
1840
+ try {
1841
+ await consumeTypesAPI(consumeOptions);
1842
+ } catch (error) {
1843
+ logDtsError(error, normalizedDtsOptions);
1844
+ }
1845
+ }
1846
+ const generateOptions = normalizeGenerateTypesOptions({
1847
+ context,
1848
+ outputDir,
1849
+ dtsOptions: normalizedDtsOptions,
1850
+ pluginOptions: dtsModuleFederationConfig
1851
+ });
1852
+ if (!generateOptions) {
1853
+ return;
1854
+ }
1855
+ try {
1856
+ await generateTypesAPI({
1857
+ dtsManagerOptions: generateOptions
1858
+ });
1859
+ } catch (error) {
1860
+ logDtsError(error, normalizedDtsOptions);
1861
+ }
1862
+ }
1863
+ };
1864
+ return [devPlugin, buildPlugin];
1865
+ }
1866
+
1497
1867
  /**
1498
1868
  * example:
1499
1869
  * const store = new PromiseStore<number>();
@@ -1663,7 +2033,7 @@ function federation(mfUserOptions) {
1663
2033
  }
1664
2034
  }, aliasToArrayPlugin, checkAliasConflicts({
1665
2035
  shared
1666
- }), normalizeOptimizeDepsPlugin, ...addEntry({
2036
+ }), normalizeOptimizeDepsPlugin, ...pluginDts(options), ...addEntry({
1667
2037
  entryName: 'remoteEntry',
1668
2038
  entryPath: REMOTE_ENTRY_ID,
1669
2039
  fileName: filename