@module-federation/vite 1.9.4 → 1.9.6

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;
@@ -537,6 +540,92 @@ function createFile(filePath, content) {
537
540
  writeFileSync(filePath, content);
538
541
  }
539
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
+
540
629
  // Cache root path
541
630
  let rootDir;
542
631
  function findNodeModulesDir(root = process.cwd()) {
@@ -859,7 +948,13 @@ function generateLocalSharedImportMap() {
859
948
  }
860
949
  const REMOTE_ENTRY_ID = 'virtual:mf-REMOTE_ENTRY_ID';
861
950
  function generateRemoteEntry(options) {
862
- 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
+ });
863
958
  return `
864
959
  import {init as runtimeInit, loadRemote} from "@module-federation/runtime";
865
960
  ${pluginImportNames.map(item => item[1]).join('\n')}
@@ -876,7 +971,7 @@ function generateRemoteEntry(options) {
876
971
  name: mfName,
877
972
  remotes: usedRemotes,
878
973
  shared: usedShared,
879
- plugins: [${pluginImportNames.map(item => `${item[0]}()`).join(', ')}],
974
+ plugins: [${pluginImportNames.map(item => `${item[0]}(${item[2]})`).join(', ')}],
880
975
  ${options.shareStrategy ? `shareStrategy: '${options.shareStrategy}'` : ''}
881
976
  });
882
977
  // handling circular init calls
@@ -1496,6 +1591,284 @@ function pluginProxyRemotes (options) {
1496
1591
  };
1497
1592
  }
1498
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
+ let hasGeneratedBundle = false;
1710
+ const devPlugin = {
1711
+ name: 'module-federation-dts-dev',
1712
+ apply: 'serve',
1713
+ config(config) {
1714
+ normalizedDevOptions = normalizeDevOptions(options.dev);
1715
+ if (!normalizedDevOptions) {
1716
+ return;
1717
+ }
1718
+ if (normalizedDevOptions.disableDynamicRemoteTypeHints) {
1719
+ return;
1720
+ }
1721
+ ensureRuntimePlugin(options, DYNAMIC_HINTS_PLUGIN);
1722
+ const define = config.define ? _extends({}, config.define) : {};
1723
+ if (!('FEDERATION_IPV4' in define)) {
1724
+ define.FEDERATION_IPV4 = JSON.stringify(getIPv4());
1725
+ }
1726
+ config.define = define;
1727
+ },
1728
+ configResolved(config) {
1729
+ resolvedConfig = config;
1730
+ },
1731
+ configureServer(server) {
1732
+ if (!normalizedDevOptions || !resolvedConfig) {
1733
+ return;
1734
+ }
1735
+ const devOptions = normalizedDevOptions;
1736
+ if (devOptions.disableDynamicRemoteTypeHints && devOptions.disableHotTypesReload && devOptions.disableLiveReload) {
1737
+ return;
1738
+ }
1739
+ if (!options.name) {
1740
+ throw new Error('name is required if you want to enable dev server!');
1741
+ }
1742
+ const outputDir = resolveOutputDir(resolvedConfig);
1743
+ const normalizedDtsOptions = normalizeDevDtsOptions(options.dts, resolvedConfig.root);
1744
+ if (typeof normalizedDtsOptions !== 'object') {
1745
+ return;
1746
+ }
1747
+ const normalizedGenerateTypes = normalizeOptions(Boolean(normalizedDtsOptions), {
1748
+ compileInChildProcess: true
1749
+ }, 'mfOptions.dts.generateTypes')(normalizedDtsOptions.generateTypes);
1750
+ const remote = normalizedGenerateTypes === false ? undefined : _extends({
1751
+ implementation: normalizedDtsOptions.implementation,
1752
+ context: resolvedConfig.root,
1753
+ outputDir,
1754
+ moduleFederationConfig: _extends({}, dtsModuleFederationConfig),
1755
+ hostRemoteTypesFolder: normalizedGenerateTypes.typesFolder || '@mf-types'
1756
+ }, normalizedGenerateTypes, {
1757
+ typesFolder: '.dev-server'
1758
+ });
1759
+ if (remote && !remote.tsConfigPath && typeof normalizedDtsOptions === 'object' && normalizedDtsOptions.tsConfigPath) {
1760
+ remote.tsConfigPath = normalizedDtsOptions.tsConfigPath;
1761
+ }
1762
+ const normalizedConsumeTypes = normalizeOptions(Boolean(normalizedDtsOptions), {
1763
+ consumeAPITypes: true
1764
+ }, 'mfOptions.dts.consumeTypes')(normalizedDtsOptions.consumeTypes);
1765
+ const host = normalizedConsumeTypes === false ? undefined : _extends({
1766
+ implementation: normalizedDtsOptions.implementation,
1767
+ context: resolvedConfig.root,
1768
+ moduleFederationConfig: dtsModuleFederationConfig,
1769
+ typesFolder: normalizedConsumeTypes.typesFolder || '@mf-types',
1770
+ abortOnError: false
1771
+ }, normalizedConsumeTypes);
1772
+ const extraOptions = normalizedDtsOptions.extraOptions || {};
1773
+ if (!remote && !host && devOptions.disableLiveReload) {
1774
+ return;
1775
+ }
1776
+ const startDevWorker = async () => {
1777
+ var _server$httpServer;
1778
+ let remoteTypeUrls;
1779
+ if (host) {
1780
+ remoteTypeUrls = await new Promise(resolve => {
1781
+ consumeTypesAPI({
1782
+ host,
1783
+ extraOptions,
1784
+ displayErrorInTerminal: normalizedDtsOptions.displayErrorInTerminal
1785
+ }, resolve);
1786
+ });
1787
+ }
1788
+ devWorker = new DevWorker({
1789
+ name: options.name,
1790
+ remote,
1791
+ host: host ? _extends({}, host, {
1792
+ remoteTypeUrls
1793
+ }) : undefined,
1794
+ extraOptions,
1795
+ disableLiveReload: devOptions.disableLiveReload,
1796
+ disableHotTypesReload: devOptions.disableHotTypesReload
1797
+ });
1798
+ const update = () => {
1799
+ var _devWorker;
1800
+ return (_devWorker = devWorker) == null ? void 0 : _devWorker.update();
1801
+ };
1802
+ server.watcher.on('change', update);
1803
+ server.watcher.on('add', update);
1804
+ server.watcher.on('unlink', update);
1805
+ (_server$httpServer = server.httpServer) == null || _server$httpServer.once('close', () => {
1806
+ var _devWorker2;
1807
+ (_devWorker2 = devWorker) == null || _devWorker2.exit();
1808
+ server.watcher.off('change', update);
1809
+ server.watcher.off('add', update);
1810
+ server.watcher.off('unlink', update);
1811
+ });
1812
+ };
1813
+ startDevWorker().catch(error => {
1814
+ logDtsError(error, normalizedDtsOptions);
1815
+ });
1816
+ }
1817
+ };
1818
+ const buildPlugin = {
1819
+ name: 'module-federation-dts-build',
1820
+ apply: 'build',
1821
+ configResolved(config) {
1822
+ resolvedConfig = config;
1823
+ },
1824
+ async generateBundle() {
1825
+ var _consumeOptions$host;
1826
+ if (hasGeneratedBundle) {
1827
+ return;
1828
+ }
1829
+ hasGeneratedBundle = true;
1830
+ if (!resolvedConfig) {
1831
+ return;
1832
+ }
1833
+ const normalizedDtsOptions = normalizeDtsOptions(dtsModuleFederationConfig, resolvedConfig.root);
1834
+ if (typeof normalizedDtsOptions !== 'object') {
1835
+ return;
1836
+ }
1837
+ const context = resolvedConfig.root;
1838
+ const outputDir = resolveOutputDir(resolvedConfig);
1839
+ const consumeOptions = normalizeConsumeTypesOptions({
1840
+ context,
1841
+ dtsOptions: normalizedDtsOptions,
1842
+ pluginOptions: dtsModuleFederationConfig
1843
+ });
1844
+ if (consumeOptions != null && (_consumeOptions$host = consumeOptions.host) != null && _consumeOptions$host.typesOnBuild) {
1845
+ try {
1846
+ await consumeTypesAPI(consumeOptions);
1847
+ } catch (error) {
1848
+ logDtsError(error, normalizedDtsOptions);
1849
+ }
1850
+ }
1851
+ const generateOptions = normalizeGenerateTypesOptions({
1852
+ context,
1853
+ outputDir,
1854
+ dtsOptions: normalizedDtsOptions,
1855
+ pluginOptions: dtsModuleFederationConfig
1856
+ });
1857
+ if (!generateOptions) {
1858
+ return;
1859
+ }
1860
+ try {
1861
+ await generateTypesAPI({
1862
+ dtsManagerOptions: generateOptions
1863
+ });
1864
+ } catch (error) {
1865
+ logDtsError(error, normalizedDtsOptions);
1866
+ }
1867
+ }
1868
+ };
1869
+ return [devPlugin, buildPlugin];
1870
+ }
1871
+
1499
1872
  /**
1500
1873
  * example:
1501
1874
  * const store = new PromiseStore<number>();
@@ -1665,7 +2038,7 @@ function federation(mfUserOptions) {
1665
2038
  }
1666
2039
  }, aliasToArrayPlugin, checkAliasConflicts({
1667
2040
  shared
1668
- }), normalizeOptimizeDepsPlugin, ...addEntry({
2041
+ }), normalizeOptimizeDepsPlugin, ...pluginDts(options), ...addEntry({
1669
2042
  entryName: 'remoteEntry',
1670
2043
  entryPath: REMOTE_ENTRY_ID,
1671
2044
  fileName: filename