From ede7e89a7825bcab84728699e5de84d63b2843fc Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Fri, 20 Feb 2026 17:46:29 -0800 Subject: [PATCH 01/17] feat(modern-js-plugin-v3): port RSC bridge support into core Port the modernjs-v3 RSC bridge runtime, async startup loader, and config/SSR wiring into core. Add parity tests plus static middleware hardening for stable manifest serving and action routing. Co-authored-by: Cursor --- packages/modernjs-v3/package.json | 19 +- .../src/cli/configPlugin.contract.spec.ts | 14 + .../modernjs-v3/src/cli/configPlugin.spec.ts | 165 +++++ packages/modernjs-v3/src/cli/configPlugin.ts | 506 ++++++++++++- .../rsc-bridge-runtime-plugin.spec.ts | 379 ++++++++++ .../rsc-bridge-runtime-plugin.ts | 701 ++++++++++++++++++ packages/modernjs-v3/src/cli/ssrPlugin.ts | 202 ++--- .../src/runtime/rsc-bridge-expose.spec.ts | 168 +++++ .../src/runtime/rsc-bridge-expose.ts | 176 +++++ .../runtime/rsc-client-callback-bootstrap.js | 355 +++++++++ .../rsc-client-callback-bootstrap.spec.ts | 57 ++ .../src/server/asyncStartupLoader.ts | 71 ++ packages/modernjs-v3/src/server/index.ts | 12 + .../src/server/server-core-node.d.ts | 17 + .../src/server/staticMiddleware.spec.ts | 33 +- .../src/server/staticMiddleware.ts | 32 +- packages/modernjs-v3/tsconfig.json | 3 +- pnpm-lock.yaml | 214 ++---- 18 files changed, 2853 insertions(+), 271 deletions(-) create mode 100644 packages/modernjs-v3/src/cli/configPlugin.contract.spec.ts create mode 100644 packages/modernjs-v3/src/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.spec.ts create mode 100644 packages/modernjs-v3/src/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.ts create mode 100644 packages/modernjs-v3/src/runtime/rsc-bridge-expose.spec.ts create mode 100644 packages/modernjs-v3/src/runtime/rsc-bridge-expose.ts create mode 100644 packages/modernjs-v3/src/runtime/rsc-client-callback-bootstrap.js create mode 100644 packages/modernjs-v3/src/runtime/rsc-client-callback-bootstrap.spec.ts create mode 100644 packages/modernjs-v3/src/server/asyncStartupLoader.ts create mode 100644 packages/modernjs-v3/src/server/server-core-node.d.ts diff --git a/packages/modernjs-v3/package.json b/packages/modernjs-v3/package.json index 06eef879c5b..8ee289d763b 100644 --- a/packages/modernjs-v3/package.json +++ b/packages/modernjs-v3/package.json @@ -76,6 +76,16 @@ "import": "./dist/esm/cli/mfRuntimePlugins/inject-node-fetch.mjs", "require": "./dist/cjs/cli/mfRuntimePlugins/inject-node-fetch.js" }, + "./rsc-bridge-runtime-plugin": { + "types": "./dist/types/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.d.ts", + "import": "./dist/esm/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.mjs", + "require": "./dist/cjs/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.js" + }, + "./rsc-bridge-expose": { + "types": "./dist/types/runtime/rsc-bridge-expose.d.ts", + "import": "./dist/esm/runtime/rsc-bridge-expose.mjs", + "require": "./dist/cjs/runtime/rsc-bridge-expose.js" + }, "./data-fetch-server-plugin": { "types": "./dist/types/cli/server/data-fetch-server-plugin.d.ts", "default": "./dist/cjs/cli/server/data-fetch-server-plugin.js" @@ -120,6 +130,12 @@ "inject-node-fetch": [ "./dist/types/cli/mfRuntimePlugins/inject-node-fetch.d.ts" ], + "rsc-bridge-runtime-plugin": [ + "./dist/types/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.d.ts" + ], + "rsc-bridge-expose": [ + "./dist/types/runtime/rsc-bridge-expose.d.ts" + ], "data-fetch-server-plugin": [ "./dist/types/cli/server/data-fetch-server-plugin.d.ts" ], @@ -148,7 +164,8 @@ "@swc/helpers": "^0.5.17", "node-fetch": "~3.3.0", "jiti": "2.4.2", - "react-error-boundary": "4.1.2" + "react-error-boundary": "4.1.2", + "@modern-js/server-core": "3.0.1" }, "devDependencies": { "@rsbuild/plugin-react": "1.4.5", diff --git a/packages/modernjs-v3/src/cli/configPlugin.contract.spec.ts b/packages/modernjs-v3/src/cli/configPlugin.contract.spec.ts new file mode 100644 index 00000000000..bf2e3b1e3a6 --- /dev/null +++ b/packages/modernjs-v3/src/cli/configPlugin.contract.spec.ts @@ -0,0 +1,14 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { describe, expect, it } from 'vitest'; + +describe('config plugin contracts', () => { + it('passes target MF config to bundler patching', () => { + const configPluginPath = path.resolve(__dirname, './configPlugin.ts'); + const source = fs.readFileSync(configPluginPath, 'utf-8'); + + expect(source).toMatch( + /patchBundlerConfig\(\{\s*chain,\s*isServer:\s*!isWeb,\s*modernjsConfig,\s*mfConfig:\s*targetMFConfig,\s*enableSSR,\s*\}\);/s, + ); + }); +}); diff --git a/packages/modernjs-v3/src/cli/configPlugin.spec.ts b/packages/modernjs-v3/src/cli/configPlugin.spec.ts index fd43ad65fd2..104ceb59c74 100644 --- a/packages/modernjs-v3/src/cli/configPlugin.spec.ts +++ b/packages/modernjs-v3/src/cli/configPlugin.spec.ts @@ -82,4 +82,169 @@ describe('patchMFConfig', async () => { }, }); }); + + it('patchMFConfig: rsc remotes inject runtime bridge plugin', async () => { + const patchedConfig = { + name: 'rsc-host', + remotes: { + remote: 'http://localhost:3001/remoteEntry.js', + }, + exposes: { + './Widget': './src/widget.ts', + }, + experiments: { + rsc: true, + asyncStartup: true, + }, + }; + + patchMFConfig(patchedConfig as any, true); + + expect( + (patchedConfig as any).runtimePlugins.some((runtimePlugin: any) => + /rsc-bridge-runtime-plugin\.(t|j)s$/.test( + typeof runtimePlugin === 'string' ? runtimePlugin : runtimePlugin[0], + ), + ), + ).toBe(true); + expect(patchedConfig.exposes).toBeDefined(); + const widgetExposeConfig = (patchedConfig.exposes as any)['./Widget']; + expect(widgetExposeConfig.import).toEqual( + expect.arrayContaining([ + expect.stringMatching(/rsc-client-callback-bootstrap\.(mjs|js)$/), + './src/widget.ts', + ]), + ); + expect((patchedConfig.exposes as any)['./Widget']).toMatchObject({ + layer: 'react-server-components', + }); + expect( + (patchedConfig.exposes as any)['./__rspack_rsc_bridge__'], + ).toMatchObject({ + import: expect.stringMatching(/rsc-bridge-expose\.(t|j)s$/), + layer: 'react-server-components', + }); + }); + + it('patchMFConfig: rsc client shared config avoids non-default client.browser providers', async () => { + const patchedConfig = { + name: 'rsc-host', + remotes: { + remote: 'http://localhost:3001/remoteEntry.js', + }, + experiments: { + rsc: true, + asyncStartup: true, + }, + shared: [ + { + 'react-server-dom-rspack/client.browser': { + import: 'react-server-dom-rspack/client.browser', + shareScope: 'default', + }, + }, + { + 'react-server-dom-rspack/client.browser': { + import: 'react-server-dom-rspack/client.browser', + shareScope: 'ssr', + }, + }, + { + 'react-server-dom-rspack/client.browser': { + import: 'react-server-dom-rspack/client.browser', + shareScope: 'rsc', + }, + }, + ], + }; + + patchMFConfig(patchedConfig as any, false); + + const sharedScopes = patchedConfig.shared as Array< + Record + >; + expect( + sharedScopes[0]['react-server-dom-rspack/client.browser'].import, + ).toBe('react-server-dom-rspack/client.browser'); + expect( + sharedScopes[1]['react-server-dom-rspack/client.browser'].import, + ).toBe(false); + expect( + sharedScopes[2]['react-server-dom-rspack/client.browser'].import, + ).toBe(false); + }); + + it('patchMFConfig: rsc requires asyncStartup', async () => { + const patchedConfig = { + name: 'rsc-host', + remotes: { + remote: 'http://localhost:3001/remoteEntry.js', + }, + experiments: { + rsc: true, + }, + }; + + expect(() => patchMFConfig(patchedConfig as any, true)).toThrow( + /experiments\.rsc requires experiments\.asyncStartup = true/, + ); + }); + + it('patchMFConfig: rsc exposes inject bridge expose + bootstrap without runtime bridge plugin', async () => { + const patchedConfig = { + name: 'rsc-remote', + exposes: { + './Widget': './src/widget.ts', + }, + experiments: { + rsc: true, + asyncStartup: true, + }, + }; + + patchMFConfig(patchedConfig as any, true); + + expect( + (patchedConfig as any).runtimePlugins?.some((runtimePlugin: any) => + /rsc-bridge-runtime-plugin\.(t|j)s$/.test( + typeof runtimePlugin === 'string' ? runtimePlugin : runtimePlugin[0], + ), + ), + ).toBe(false); + const widgetExposeConfig = (patchedConfig.exposes as any)['./Widget']; + expect(widgetExposeConfig.import).toEqual( + expect.arrayContaining([ + expect.stringMatching(/rsc-client-callback-bootstrap\.(mjs|js)$/), + './src/widget.ts', + ]), + ); + expect( + (patchedConfig.exposes as any)['./__rspack_rsc_bridge__'], + ).toMatchObject({ + import: expect.stringMatching(/rsc-bridge-expose\.(t|j)s$/), + layer: 'react-server-components', + }); + }); + + it('patchMFConfig: non-rsc does not inject internal bridge expose', async () => { + const patchedConfig = { + name: 'host', + remotes: { + remote: 'http://localhost:3001/remoteEntry.js', + }, + exposes: { + './Widget': './src/widget.ts', + }, + experiments: { + asyncStartup: true, + }, + }; + + patchMFConfig(patchedConfig as any, true); + + expect((patchedConfig.exposes as any)['./Widget']).toBe('./src/widget.ts'); + expect( + (patchedConfig.exposes as any)['./__rspack_rsc_bridge__'], + ).toBeUndefined(); + }); }); diff --git a/packages/modernjs-v3/src/cli/configPlugin.ts b/packages/modernjs-v3/src/cli/configPlugin.ts index bcbfd9314d5..ebb9b8fbdac 100644 --- a/packages/modernjs-v3/src/cli/configPlugin.ts +++ b/packages/modernjs-v3/src/cli/configPlugin.ts @@ -1,32 +1,84 @@ +import fs from 'fs'; import path from 'path'; -import { getIPV4, isWebTarget, skipByTarget } from './utils'; -import { moduleFederationPlugin, encodeName } from '@module-federation/sdk'; -import { PluginOptions } from '../types'; -import { LOCALHOST, PLUGIN_IDENTIFIER } from '../constant'; import { - autoDeleteSplitChunkCacheGroups, addDataFetchExposes, + autoDeleteSplitChunkCacheGroups, } from '@module-federation/rsbuild-plugin/utils'; +import { + encodeName, + type moduleFederationPlugin, +} from '@module-federation/sdk'; +import { LOCALHOST, PLUGIN_IDENTIFIER } from '../constant'; import logger from '../logger'; +import type { PluginOptions } from '../types'; +import { getIPV4, isWebTarget, skipByTarget } from './utils'; import { isDev } from './utils'; -import type { InternalModernPluginOptions } from '../types'; import type { AppTools, - Rspack, AppUserConfig, CliPlugin, + Rspack, } from '@modern-js/app-tools'; import type { BundlerChainConfig } from '../interfaces/bundler'; +import type { InternalModernPluginOptions } from '../types'; const defaultPath = path.resolve(process.cwd(), 'module-federation.config.ts'); -export type ConfigType = Rspack.Configuration; - type RuntimePluginEntry = NonNullable< moduleFederationPlugin.ModuleFederationPluginOptions['runtimePlugins'] >[number]; +const RSC_LAYER = 'react-server-components'; +const RSC_BRIDGE_EXPOSE = './__rspack_rsc_bridge__'; +const RSC_CLIENT_BROWSER_SHARED_KEY = 'react-server-dom-rspack/client.browser'; + +const resolveFirstExistingPath = ( + candidatePaths: string[], + fallbackPath: string, +) => + candidatePaths.find((candidatePath) => fs.existsSync(candidatePath)) || + fallbackPath; + +const RSC_BRIDGE_RUNTIME_PLUGIN = resolveFirstExistingPath( + [ + path.resolve(__dirname, './mfRuntimePlugins/rsc-bridge-runtime-plugin.ts'), + path.resolve(__dirname, './mfRuntimePlugins/rsc-bridge-runtime-plugin.js'), + path.resolve( + __dirname, + '../esm/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.mjs', + ), + ], + require.resolve('@module-federation/modern-js-v3/rsc-bridge-runtime-plugin'), +); + +const RSC_BRIDGE_EXPOSE_MODULE = resolveFirstExistingPath( + [ + path.resolve(__dirname, '../runtime/rsc-bridge-expose.ts'), + path.resolve(__dirname, '../runtime/rsc-bridge-expose.js'), + path.resolve(__dirname, '../esm/runtime/rsc-bridge-expose.mjs'), + ], + require.resolve('@module-federation/modern-js-v3/rsc-bridge-expose'), +); + +const RSC_CLIENT_CALLBACK_BOOTSTRAP_MODULE = resolveFirstExistingPath( + [ + path.resolve(__dirname, '../runtime/rsc-client-callback-bootstrap.js'), + path.resolve(__dirname, '../esm/runtime/rsc-client-callback-bootstrap.mjs'), + path.resolve( + path.dirname(RSC_BRIDGE_EXPOSE_MODULE), + 'rsc-client-callback-bootstrap.js', + ), + path.resolve( + path.dirname(RSC_BRIDGE_EXPOSE_MODULE), + 'rsc-client-callback-bootstrap.mjs', + ), + ], + path.resolve(__dirname, '../runtime/rsc-client-callback-bootstrap.js'), +); + +export type ConfigType = Rspack.Configuration; + export function setEnv(enableSSR: boolean) { if (enableSSR) { process.env['MF_SSR_PRJ'] = 'true'; @@ -102,12 +154,14 @@ const replaceRemoteUrl = ( if (typeof remote === 'string' && remote.includes(LOCALHOST)) { remoteObject[remoteKey] = remote.replace(LOCALHOST, ipv4); } - if ( - typeof remote === 'object' && - !Array.isArray(remote.external) && - remote.external.includes(LOCALHOST) - ) { - remote.external = remote.external.replace(LOCALHOST, ipv4); + if (remote && typeof remote === 'object') { + const external = (remote as { external?: unknown }).external; + if (typeof external === 'string' && external.includes(LOCALHOST)) { + (remote as { external?: string }).external = external.replace( + LOCALHOST, + ipv4, + ); + } } }); }; @@ -160,15 +214,222 @@ const patchDTSConfig = ( } }; +const hasRemotes = ( + remotes: moduleFederationPlugin.ModuleFederationPluginOptions['remotes'], +): boolean => { + if (!remotes) { + return false; + } + if (Array.isArray(remotes)) { + return remotes.length > 0; + } + if (typeof remotes === 'string') { + return (remotes as string).length > 0; + } + return Object.keys(remotes as Record).length > 0; +}; + +const hasExposes = ( + exposes: moduleFederationPlugin.ModuleFederationPluginOptions['exposes'], +) => { + if (!exposes) { + return false; + } + if (Array.isArray(exposes)) { + return exposes.length > 0; + } + return Object.keys(exposes).length > 0; +}; + +const isRscMfEnabled = ( + mfConfig: moduleFederationPlugin.ModuleFederationPluginOptions, +) => Boolean((mfConfig.experiments as { rsc?: boolean } | undefined)?.rsc); + +const normalizeExposeConfig = ( + exposeConfig: moduleFederationPlugin.ExposesObject[string], +) => { + if (typeof exposeConfig === 'string' || Array.isArray(exposeConfig)) { + return { + import: exposeConfig, + }; + } + + if ( + exposeConfig && + typeof exposeConfig === 'object' && + 'import' in exposeConfig + ) { + return { + ...(exposeConfig as unknown as Record), + import: (exposeConfig as { import: string | string[] }).import, + }; + } + + return { + import: exposeConfig as string, + }; +}; + +const setRscExposeConfig = ( + mfConfig: moduleFederationPlugin.ModuleFederationPluginOptions, +) => { + if (!mfConfig.exposes) { + return; + } + + const normalizedExposes: moduleFederationPlugin.ExposesObject = {}; + + const appendExpose = ( + exposeKey: string, + exposeConfig: moduleFederationPlugin.ExposesObject[string], + ) => { + const normalizedConfig = normalizeExposeConfig(exposeConfig); + const importList = Array.isArray(normalizedConfig.import) + ? [...normalizedConfig.import] + : [normalizedConfig.import]; + const normalizedImportList = + exposeKey === RSC_BRIDGE_EXPOSE + ? importList + : [ + RSC_CLIENT_CALLBACK_BOOTSTRAP_MODULE, + ...importList.filter( + (importPath) => + importPath !== RSC_CLIENT_CALLBACK_BOOTSTRAP_MODULE, + ), + ]; + const normalizedImport = + normalizedImportList.length === 1 + ? normalizedImportList[0] + : normalizedImportList; + normalizedExposes[exposeKey] = { + ...normalizedConfig, + import: normalizedImport, + layer: RSC_LAYER, + } as moduleFederationPlugin.ExposesConfig; + }; + + if (Array.isArray(mfConfig.exposes)) { + for (const exposeItem of mfConfig.exposes) { + if (typeof exposeItem === 'string') { + appendExpose(exposeItem, exposeItem); + continue; + } + for (const [exposeKey, exposeConfig] of Object.entries(exposeItem)) { + appendExpose(exposeKey, exposeConfig); + } + } + } else { + for (const [exposeKey, exposeConfig] of Object.entries(mfConfig.exposes)) { + appendExpose(exposeKey, exposeConfig); + } + } + + if ( + !Object.prototype.hasOwnProperty.call(normalizedExposes, RSC_BRIDGE_EXPOSE) + ) { + normalizedExposes[RSC_BRIDGE_EXPOSE] = { + import: RSC_BRIDGE_EXPOSE_MODULE, + layer: RSC_LAYER, + } as moduleFederationPlugin.ExposesConfig; + } + + mfConfig.exposes = normalizedExposes; +}; + +const assertRscMfConfig = ({ + mfConfig, + isServer, + runtimePlugins, +}: { + mfConfig: moduleFederationPlugin.ModuleFederationPluginOptions; + isServer: boolean; + runtimePlugins: RuntimePluginEntry[]; +}) => { + if (!isRscMfEnabled(mfConfig)) { + return; + } + + const asyncStartupEnabled = + (mfConfig.experiments as { asyncStartup?: boolean } | undefined) + ?.asyncStartup === true; + if (!asyncStartupEnabled) { + throw new Error( + `${PLUGIN_IDENTIFIER} experiments.rsc requires experiments.asyncStartup = true`, + ); + } + + if (!isServer) { + return; + } + + const nodeRuntimePluginPath = require.resolve( + '@module-federation/node/runtimePlugin', + ); + const hasNodeRuntimePlugin = runtimePlugins.some((runtimePlugin) => { + const runtimePluginPath = + typeof runtimePlugin === 'string' ? runtimePlugin : runtimePlugin[0]; + return runtimePluginPath === nodeRuntimePluginPath; + }); + + if (!hasNodeRuntimePlugin) { + throw new Error( + `${PLUGIN_IDENTIFIER} experiments.rsc requires @module-federation/node/runtimePlugin in runtimePlugins`, + ); + } +}; + +const patchRscClientBrowserSharedConfig = ( + mfConfig: moduleFederationPlugin.ModuleFederationPluginOptions, + isServer: boolean, +) => { + if (isServer || !mfConfig.shared) { + return; + } + + const patchSharedRecord = (sharedRecord: Record) => { + const clientBrowserShared = sharedRecord[RSC_CLIENT_BROWSER_SHARED_KEY] as + | Record + | undefined; + if (!clientBrowserShared || Array.isArray(clientBrowserShared)) { + return; + } + const shareScope = clientBrowserShared.shareScope; + if (typeof shareScope === 'string' && shareScope !== 'default') { + clientBrowserShared.import = false; + } + }; + + if (Array.isArray(mfConfig.shared)) { + for (const sharedConfig of mfConfig.shared) { + if (!sharedConfig || typeof sharedConfig !== 'object') { + continue; + } + patchSharedRecord(sharedConfig as Record); + } + return; + } + + if (typeof mfConfig.shared === 'object') { + patchSharedRecord(mfConfig.shared as Record); + } +}; + export const patchMFConfig = ( mfConfig: moduleFederationPlugin.ModuleFederationPluginOptions, isServer: boolean, remoteIpStrategy?: 'ipv4' | 'inherit', enableSSR?: boolean, ) => { + const rscEnabled = isRscMfEnabled(mfConfig); + replaceRemoteUrl(mfConfig, remoteIpStrategy); addDataFetchExposes(mfConfig.exposes, isServer); + if (rscEnabled) { + setRscExposeConfig(mfConfig); + patchRscClientBrowserSharedConfig(mfConfig, isServer); + } + if (mfConfig.remoteType === undefined) { mfConfig.remoteType = 'script'; } @@ -195,6 +456,10 @@ export const patchMFConfig = ( ); } + if (rscEnabled && hasRemotes(mfConfig.remotes)) { + injectRuntimePlugins(RSC_BRIDGE_RUNTIME_PLUGIN, runtimePlugins); + } + if (isServer) { injectRuntimePlugins( require.resolve('@module-federation/node/runtimePlugin'), @@ -229,6 +494,12 @@ export const patchMFConfig = ( } } + assertRscMfConfig({ + mfConfig, + isServer, + runtimePlugins, + }); + mfConfig.runtimePlugins = runtimePlugins; if (!isServer) { @@ -260,6 +531,170 @@ function patchIgnoreWarning(chain: BundlerChainConfig) { chain.ignoreWarnings(ignoreWarnings); } +const patchProjectNodeModulesResolution = (chain: BundlerChainConfig) => { + const projectNodeModulesPath = path.resolve(process.cwd(), 'node_modules'); + if (!fs.existsSync(projectNodeModulesPath)) { + return; + } + + const resolveModules = chain.resolve.modules as { + values?: () => string[]; + clear: () => unknown; + add: (value: string) => unknown; + }; + resolveModules.clear(); + resolveModules.add(projectNodeModulesPath); + resolveModules.add('node_modules'); +}; + +const patchServerOnlyAlias = (chain: BundlerChainConfig) => { + const serverOnlyEmptyPath = path.resolve( + process.cwd(), + 'node_modules/server-only/empty.js', + ); + if (!fs.existsSync(serverOnlyEmptyPath)) { + return; + } + + const aliasChain = chain.resolve.alias as { + has?: (key: string) => boolean; + set: (key: string, value: string) => unknown; + }; + const hasServerOnlyAlias = + typeof aliasChain.has === 'function' + ? aliasChain.has('server-only$') + : false; + if (!hasServerOnlyAlias) { + aliasChain.set('server-only$', serverOnlyEmptyPath); + } +}; + +const resolveProjectDependency = (request: string) => { + try { + return require.resolve(request, { paths: [process.cwd()] }); + } catch { + try { + return require.resolve(request); + } catch { + return undefined; + } + } +}; + +const patchRscServerRuntimeAliases = (chain: BundlerChainConfig) => { + const reactPackagePath = resolveProjectDependency('react/package.json'); + if (!reactPackagePath) { + return; + } + + const reactDir = path.dirname(reactPackagePath); + const reactJsxRuntimeServerPath = path.join( + reactDir, + 'jsx-runtime.react-server.js', + ); + const reactJsxDevRuntimeServerPath = path.join( + reactDir, + 'jsx-dev-runtime.react-server.js', + ); + + if (fs.existsSync(reactJsxRuntimeServerPath)) { + chain.resolve.alias.set('react/jsx-runtime$', reactJsxRuntimeServerPath); + } + if (fs.existsSync(reactJsxDevRuntimeServerPath)) { + chain.resolve.alias.set( + 'react/jsx-dev-runtime$', + reactJsxDevRuntimeServerPath, + ); + } +}; + +const getExposeImports = ( + exposeConfig: moduleFederationPlugin.ExposesObject[string], +): string[] => { + if (typeof exposeConfig === 'string') { + return [exposeConfig]; + } + if (Array.isArray(exposeConfig)) { + return exposeConfig.filter( + (importPath): importPath is string => typeof importPath === 'string', + ); + } + if ( + exposeConfig && + typeof exposeConfig === 'object' && + 'import' in exposeConfig + ) { + const exposeImport = (exposeConfig as { import?: unknown }).import; + if (typeof exposeImport === 'string') { + return [exposeImport]; + } + if (Array.isArray(exposeImport)) { + return exposeImport.filter( + (importPath): importPath is string => typeof importPath === 'string', + ); + } + } + return []; +}; + +const collectExposeImportDirectories = ( + exposes: moduleFederationPlugin.ModuleFederationPluginOptions['exposes'], +) => { + if (!exposes) { + return []; + } + + const exposeEntries = Array.isArray(exposes) + ? exposes.flatMap((exposeItem) => + typeof exposeItem === 'string' + ? [[exposeItem, exposeItem] as const] + : (Object.entries(exposeItem || {}) as Array< + readonly [string, moduleFederationPlugin.ExposesObject[string]] + >), + ) + : (Object.entries(exposes) as Array< + readonly [string, moduleFederationPlugin.ExposesObject[string]] + >); + + const directories = new Set(); + for (const [exposeKey, exposeConfig] of exposeEntries) { + if (exposeKey === RSC_BRIDGE_EXPOSE) { + continue; + } + for (const importPath of getExposeImports(exposeConfig)) { + if (!importPath || !importPath.startsWith('.')) { + continue; + } + directories.add(path.dirname(path.resolve(process.cwd(), importPath))); + } + } + + return Array.from(directories); +}; + +const patchRscRemoteComponentLayer = ( + chain: BundlerChainConfig, + mfConfig: moduleFederationPlugin.ModuleFederationPluginOptions, +) => { + const includeDirectories = collectExposeImportDirectories(mfConfig.exposes); + if (includeDirectories.length === 0) { + return; + } + + const ruleChain = chain.module + .rule('rsc-mf-remote-components-layer') + .test(/\.[cm]?[jt]sx?$/); + + for (const includeDirectory of includeDirectories) { + ruleChain.include.add(includeDirectory); + } + + ruleChain.layer(RSC_LAYER); +}; + +const normalizePublicPath = (publicPath: string) => + publicPath.endsWith('/') ? publicPath.slice(0, -1) : publicPath; + export function addMyTypes2Ignored( chain: BundlerChainConfig, mfConfig: moduleFederationPlugin.ModuleFederationPluginOptions, @@ -320,11 +755,44 @@ export function patchBundlerConfig(options: { enableSSR: boolean; }) { const { chain, modernjsConfig, isServer, mfConfig, enableSSR } = options; + const rscMfEnabled = isRscMfEnabled(mfConfig); chain.optimization.delete('runtimeChunk'); patchIgnoreWarning(chain); + if (rscMfEnabled && isServer) { + chain.resolve.conditionNames + .clear() + .add('require') + .add('import') + .add('default'); + if (hasExposes(mfConfig.exposes)) { + chain.resolve.conditionNames.add('react-server'); + } + } + + if (rscMfEnabled && hasExposes(mfConfig.exposes)) { + patchProjectNodeModulesResolution(chain); + patchServerOnlyAlias(chain); + + const assetPrefix = modernjsConfig.output?.assetPrefix; + if (typeof assetPrefix === 'string' && assetPrefix.trim()) { + const normalizedAssetPrefix = normalizePublicPath(assetPrefix.trim()); + chain.output.publicPath( + isServer + ? `${normalizedAssetPrefix}/bundles/` + : `${normalizedAssetPrefix}/`, + ); + } + if (!isServer) { + chain.optimization.splitChunks(false); + } else { + patchRscServerRuntimeAliases(chain); + patchRscRemoteComponentLayer(chain, mfConfig); + } + } + if (!chain.output.get('chunkLoadingGlobal')) { chain.output.chunkLoadingGlobal(`chunk_${mfConfig.name}`); } @@ -404,6 +872,7 @@ export const moduleFederationConfigPlugin = ( const enableSSR = Boolean( userConfig.userConfig?.ssr ?? Boolean(modernjsConfig?.server?.ssr), ); + const enableRsc = Boolean(modernjsConfig?.server?.rsc); api.modifyBundlerChain((chain) => { const target = chain.get('target'); @@ -425,14 +894,14 @@ export const moduleFederationConfigPlugin = ( chain, isServer: !isWeb, modernjsConfig, - mfConfig, + mfConfig: targetMFConfig, enableSSR, }); if (isWeb) { userConfig.distOutputDir = chain.output.get('path') || path.resolve(process.cwd(), 'dist'); - } else if (enableSSR) { + } else if (enableSSR && !enableRsc) { userConfig.userConfig ||= {}; userConfig.userConfig.ssr ||= {}; if (userConfig.userConfig.ssr === true) { @@ -507,7 +976,8 @@ export const moduleFederationConfigPlugin = ( }, source: { define: defineConfig, - enableAsyncEntry: modernjsConfig.source?.enableAsyncEntry ?? true, + enableAsyncEntry: + modernjsConfig.source?.enableAsyncEntry ?? !enableRsc, }, dev: { assetPrefix: modernjsConfig?.dev?.assetPrefix diff --git a/packages/modernjs-v3/src/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.spec.ts b/packages/modernjs-v3/src/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.spec.ts new file mode 100644 index 00000000000..7abe8c5667a --- /dev/null +++ b/packages/modernjs-v3/src/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.spec.ts @@ -0,0 +1,379 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import rscBridgeRuntimePlugin from './rsc-bridge-runtime-plugin'; + +type ManifestLike = { + serverManifest?: Record; + clientManifest?: Record; + serverConsumerModuleMap?: Record; +}; + +type WebpackRequireRuntime = { + m?: Record void>; + c?: Record; + rscM?: ManifestLike; +}; + +const ACTION_REMAP_GLOBAL_KEY = '__MODERN_RSC_MF_ACTION_ID_MAP__'; +const PROXY_MODULE_PREFIX = '__modernjs_mf_rsc_action_proxy__:'; + +const createWebpackRequireRuntime = (): WebpackRequireRuntime => ({ + m: {}, + c: {}, + rscM: { + serverManifest: {}, + clientManifest: {}, + serverConsumerModuleMap: {}, + }, +}); + +const getActionRemapMap = () => + ( + globalThis as typeof globalThis & { + [ACTION_REMAP_GLOBAL_KEY]?: Record; + } + )[ACTION_REMAP_GLOBAL_KEY] || {}; + +describe('rsc-bridge-runtime-plugin', () => { + beforeEach(() => { + vi.useFakeTimers(); + ( + globalThis as typeof globalThis & { + __webpack_require__?: WebpackRequireRuntime; + } + ).__webpack_require__ = createWebpackRequireRuntime(); + ( + globalThis as typeof globalThis & { + window?: { __MODERN_JS_ENTRY_NAME?: string }; + } + ).window = { + __MODERN_JS_ENTRY_NAME: 'server-component-root', + }; + delete ( + globalThis as typeof globalThis & { + [ACTION_REMAP_GLOBAL_KEY]?: Record; + } + )[ACTION_REMAP_GLOBAL_KEY]; + }); + + afterEach(() => { + vi.clearAllTimers(); + vi.useRealTimers(); + delete ( + globalThis as typeof globalThis & { + __webpack_require__?: WebpackRequireRuntime; + } + ).__webpack_require__; + delete ( + globalThis as typeof globalThis & { + window?: { __MODERN_JS_ENTRY_NAME?: string }; + } + ).window; + delete (globalThis as typeof globalThis & { fetch?: unknown }).fetch; + delete ( + globalThis as typeof globalThis & { + [ACTION_REMAP_GLOBAL_KEY]?: Record; + } + )[ACTION_REMAP_GLOBAL_KEY]; + }); + + it('merges remote manifest, registers action remap, and installs proxy dispatcher', async () => { + const executeAction = vi.fn(async (id: string, args: unknown[]) => ({ + id, + args, + })); + + const plugin = rscBridgeRuntimePlugin(); + const loadRemote = vi.fn(async () => ({ + getManifest: () => ({ + clientManifest: { + clientRef: { + id: '123', + name: 'default', + chunks: [], + }, + }, + serverConsumerModuleMap: { + '123': { + '*': { + id: '123', + name: 'default', + chunks: [], + }, + }, + }, + serverManifest: { + rawActionId: { + async: true, + }, + }, + }), + executeAction, + })); + + await plugin.onLoad?.({ + remote: { alias: 'rscRemote' }, + options: { name: 'rscHost' }, + origin: { + loadRemote, + }, + }); + + expect(loadRemote).toHaveBeenCalledTimes(1); + expect(loadRemote).toHaveBeenCalledWith('rscRemote/__rspack_rsc_bridge__'); + + const webpackRequire = ( + globalThis as typeof globalThis & { + __webpack_require__?: WebpackRequireRuntime; + } + ).__webpack_require__!; + const hostManifest = webpackRequire.rscM!; + + expect(hostManifest.clientManifest?.clientRef?.id).toBe( + 'remote-module:rscRemote:123', + ); + expect(hostManifest.serverConsumerModuleMap).toHaveProperty( + 'remote-module:rscRemote:123', + ); + expect( + hostManifest.serverConsumerModuleMap?.['remote-module:rscRemote:123']?.[ + '*' + ]?.id, + ).toBe('remote-module:rscRemote:123'); + + const prefixedActionId = 'remote:rscRemote:rawActionId'; + const proxyModuleId = `${PROXY_MODULE_PREFIX}rscRemote`; + expect(hostManifest.serverManifest?.[prefixedActionId]).toMatchObject({ + id: proxyModuleId, + name: prefixedActionId, + }); + expect(getActionRemapMap().rawActionId).toBe(prefixedActionId); + + const proxyFactory = webpackRequire.m?.[proxyModuleId]; + expect(typeof proxyFactory).toBe('function'); + + const proxyModule = { exports: {} as Record }; + proxyFactory?.(proxyModule); + const result = await proxyModule.exports[prefixedActionId]('payload'); + + expect(executeAction).toHaveBeenCalledWith('rawActionId', ['payload']); + expect(result).toEqual({ + id: 'rawActionId', + args: ['payload'], + }); + }); + + it('awaits async bridge manifest responses before merge', async () => { + const plugin = rscBridgeRuntimePlugin(); + + await plugin.onLoad?.({ + remote: { alias: 'rscRemote' }, + options: { name: 'rscHost' }, + origin: { + loadRemote: vi.fn(async () => ({ + getManifest: async () => ({ + serverManifest: { + asyncRawAction: { + async: true, + }, + }, + }), + executeAction: vi.fn(async () => undefined), + })), + }, + } as any); + + const webpackRequire = ( + globalThis as typeof globalThis & { + __webpack_require__?: WebpackRequireRuntime; + } + ).__webpack_require__!; + expect(webpackRequire.rscM?.serverManifest).toHaveProperty( + 'remote:rscRemote:asyncRawAction', + ); + expect(getActionRemapMap().asyncRawAction).toBe( + 'remote:rscRemote:asyncRawAction', + ); + }); + + it('loads and merges each remote alias once', async () => { + const plugin = rscBridgeRuntimePlugin(); + const loadRemote = vi.fn(async () => ({ + getManifest: () => ({ + serverManifest: { + one: { + async: true, + }, + }, + }), + executeAction: vi.fn(async () => undefined), + })); + + const args = { + remote: { alias: 'rscRemote' }, + options: { name: 'rscHost' }, + origin: { + loadRemote, + }, + }; + + await plugin.onLoad?.(args as any); + await plugin.onLoad?.(args as any); + + expect(loadRemote).toHaveBeenCalledTimes(1); + }); + + it('falls back to federation.instance when origin is unavailable', async () => { + const loadRemote = vi.fn(async () => ({ + getManifest: () => ({ + serverManifest: { + rawActionId: { + async: true, + }, + }, + }), + executeAction: vi.fn(async () => undefined), + })); + + const webpackRequire = ( + globalThis as typeof globalThis & { + __webpack_require__?: WebpackRequireRuntime; + } + ).__webpack_require__!; + ( + webpackRequire as WebpackRequireRuntime & { + federation?: { + instance?: { loadRemote?: (request: string) => Promise }; + }; + } + ).federation = { + instance: { + loadRemote, + }, + }; + + const plugin = rscBridgeRuntimePlugin(); + + await plugin.onLoad?.({ + remote: { alias: 'rscRemote' }, + options: { name: 'rscHost' }, + } as any); + + expect(loadRemote).toHaveBeenCalledWith('rscRemote/__rspack_rsc_bridge__'); + expect(getActionRemapMap().rawActionId).toBe( + 'remote:rscRemote:rawActionId', + ); + }); + + it('throws deterministic conflict errors when manifests disagree', async () => { + const webpackRequire = ( + globalThis as typeof globalThis & { + __webpack_require__?: WebpackRequireRuntime; + } + ).__webpack_require__!; + webpackRequire.rscM = { + serverManifest: {}, + clientManifest: { + sharedKey: { + id: 'remote-module:existingRemote:123', + name: 'default', + chunks: [], + }, + }, + serverConsumerModuleMap: {}, + }; + + const plugin = rscBridgeRuntimePlugin(); + + await expect( + plugin.onLoad?.({ + remote: { alias: 'rscRemote' }, + options: { name: 'rscHost' }, + origin: { + loadRemote: vi.fn(async () => ({ + getManifest: () => ({ + clientManifest: { + sharedKey: { + id: '123', + name: 'default', + chunks: [], + }, + }, + }), + executeAction: vi.fn(async () => undefined), + })), + }, + } as any), + ).rejects.toThrow(/clientManifest conflict/); + }); + + it('marks raw action remaps as false when aliases collide', async () => { + const plugin = rscBridgeRuntimePlugin(); + + const loadRemote = vi.fn(async (request: string) => { + if (request.startsWith('rscRemoteA/')) { + return { + getManifest: () => ({ + serverManifest: { + sameRawId: { + async: true, + }, + }, + }), + executeAction: vi.fn(async () => undefined), + }; + } + + return { + getManifest: () => ({ + serverManifest: { + sameRawId: { + async: true, + }, + }, + }), + executeAction: vi.fn(async () => undefined), + }; + }); + + await plugin.onLoad?.({ + remote: { alias: 'rscRemoteA' }, + options: { name: 'rscHost' }, + origin: { loadRemote }, + } as any); + + await plugin.onLoad?.({ + remote: { alias: 'rscRemoteB' }, + options: { name: 'rscHost' }, + origin: { loadRemote }, + } as any); + + expect(getActionRemapMap().sameRawId).toBe(false); + }); + + it('normalizes missing ssrPublicPath on resolved remote snapshots', async () => { + const plugin = rscBridgeRuntimePlugin(); + const loadRemote = vi.fn(async () => ({ + getManifest: () => ({}), + executeAction: vi.fn(async () => undefined), + })); + const args: any = { + remote: { alias: 'rscRemote' }, + remoteInfo: { + publicPath: 'http://127.0.0.1:3008/bundles/', + remoteEntry: { + name: 'static/remoteEntry.js', + }, + }, + origin: { + loadRemote, + }, + }; + + await plugin.afterResolve?.(args); + + expect(args.remoteInfo.ssrPublicPath).toBe( + 'http://127.0.0.1:3008/bundles/', + ); + expect(args.remoteInfo.remoteEntry.path).toBe(''); + }); +}); diff --git a/packages/modernjs-v3/src/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.ts b/packages/modernjs-v3/src/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.ts new file mode 100644 index 00000000000..d9e657b38f9 --- /dev/null +++ b/packages/modernjs-v3/src/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.ts @@ -0,0 +1,701 @@ +import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime'; + +const RSC_BRIDGE_EXPOSE = '__rspack_rsc_bridge__'; +const ACTION_PREFIX = 'remote:'; +const MODULE_PREFIX = 'remote-module:'; +const ACTION_REMAP_GLOBAL_KEY = '__MODERN_RSC_MF_ACTION_ID_MAP__'; +const ACTION_REMAP_WAITERS_KEY = '__MODERN_RSC_MF_ACTION_ID_MAP_WAITERS__'; +const PROXY_MODULE_PREFIX = '__modernjs_mf_rsc_action_proxy__:'; + +type ManifestLike = { + serverManifest?: Record; + clientManifest?: Record; + serverConsumerModuleMap?: Record; +}; + +type BridgeModule = { + getManifest?: () => ManifestLike; + executeAction?: (actionId: string, args: unknown[]) => Promise; +}; + +type ActionMapRecord = Record; +type ActionRemapWaiter = (prefixedActionId: string) => void; +type ActionRemapWaiterMap = Map; +type ActionRemapMap = Record; +type WebpackRequireRuntime = { + m?: Record void>; + c?: Record; + rscM?: ManifestLike; + federation?: { + instance?: { + loadRemote?: (request: string) => Promise; + }; + }; +}; + +declare const __webpack_require__: WebpackRequireRuntime; + +const isObject = (value: unknown): value is Record => + typeof value === 'object' && value !== null; + +const stableStringify = (value: unknown) => { + try { + return JSON.stringify(value, (_key, current) => { + if (Array.isArray(current)) { + return current; + } + if (!isObject(current)) { + return current; + } + return Object.fromEntries( + Object.keys(current) + .sort() + .map((key) => [key, current[key]]), + ); + }); + } catch { + return String(value); + } +}; + +const assertNoConflict = ( + target: Record, + key: string, + nextValue: unknown, + alias: string, + section: string, +) => { + if (!Object.prototype.hasOwnProperty.call(target, key)) { + return; + } + if (stableStringify(target[key]) !== stableStringify(nextValue)) { + throw new Error( + `[modern-js-v3:rsc-bridge] ${section} conflict for "${key}" while merging remote "${alias}"`, + ); + } +}; + +const getNamespacedModuleId = (alias: string, rawId: string | number) => + `${MODULE_PREFIX}${alias}:${String(rawId)}`; + +const getActionRemapMap = () => { + const globalState = globalThis as typeof globalThis & { + [ACTION_REMAP_GLOBAL_KEY]?: ActionRemapMap; + [ACTION_REMAP_WAITERS_KEY]?: ActionRemapWaiterMap; + }; + if (!isObject(globalState[ACTION_REMAP_GLOBAL_KEY])) { + globalState[ACTION_REMAP_GLOBAL_KEY] = {}; + } + return globalState[ACTION_REMAP_GLOBAL_KEY] as ActionRemapMap; +}; + +const getActionRemapWaiters = () => { + const globalState = globalThis as typeof globalThis & { + [ACTION_REMAP_WAITERS_KEY]?: ActionRemapWaiterMap; + }; + const existingWaiters = globalState[ACTION_REMAP_WAITERS_KEY]; + if (!(existingWaiters instanceof Map)) { + globalState[ACTION_REMAP_WAITERS_KEY] = new Map(); + return globalState[ACTION_REMAP_WAITERS_KEY] as ActionRemapWaiterMap; + } + return existingWaiters; +}; + +const registerActionRemap = (rawActionId: string, prefixedActionId: string) => { + const remapMap = getActionRemapMap(); + const remapWaiters = getActionRemapWaiters(); + const existingValue = remapMap[rawActionId]; + if (typeof existingValue === 'undefined') { + remapMap[rawActionId] = prefixedActionId; + const waiters = remapWaiters.get(rawActionId); + if (waiters?.length) { + waiters.forEach((waiter) => waiter(prefixedActionId)); + remapWaiters.delete(rawActionId); + } + return; + } + if (existingValue === prefixedActionId) { + return; + } + // Ambiguous mapping across remotes; skip unsafe remap. + remapMap[rawActionId] = false; + const waiters = remapWaiters.get(rawActionId); + if (waiters?.length) { + waiters.forEach((waiter) => waiter(rawActionId)); + remapWaiters.delete(rawActionId); + } +}; + +const getWebpackRequireIfAvailable = (): WebpackRequireRuntime | undefined => { + if (typeof __webpack_require__ !== 'undefined') { + return __webpack_require__; + } + + const runtime = ( + globalThis as typeof globalThis & { + __webpack_require__?: WebpackRequireRuntime; + } + ).__webpack_require__; + return isObject(runtime) ? (runtime as WebpackRequireRuntime) : undefined; +}; + +const getWebpackRequire = (): WebpackRequireRuntime => { + const runtime = getWebpackRequireIfAvailable(); + if (!runtime) { + throw new Error( + '[modern-js-v3:rsc-bridge] __webpack_require__ is unavailable while installing the RSC bridge runtime plugin', + ); + } + return runtime; +}; + +const ensureHostManifest = () => { + const webpackRequire = getWebpackRequire(); + if (!isObject(webpackRequire.rscM)) { + webpackRequire.rscM = {}; + } + const manifest = webpackRequire.rscM as ManifestLike; + manifest.serverManifest = isObject(manifest.serverManifest) + ? manifest.serverManifest + : {}; + manifest.clientManifest = isObject(manifest.clientManifest) + ? manifest.clientManifest + : {}; + manifest.serverConsumerModuleMap = isObject(manifest.serverConsumerModuleMap) + ? manifest.serverConsumerModuleMap + : {}; + return manifest; +}; + +const remapConsumerNode = ( + alias: string, + value: unknown, + namespacedClientIds: Record, +) => { + if (!isObject(value)) { + return value; + } + return Object.fromEntries( + Object.entries(value).map(([exportName, exportValue]) => { + const nextExportValue = isObject(exportValue) + ? { ...exportValue } + : exportValue; + if (isObject(nextExportValue) && nextExportValue.id != null) { + const rawId = String(nextExportValue.id); + nextExportValue.id = Object.prototype.hasOwnProperty.call( + namespacedClientIds, + rawId, + ) + ? namespacedClientIds[rawId] + : getNamespacedModuleId(alias, rawId); + } + return [exportName, nextExportValue]; + }), + ); +}; + +const getProxyModuleId = (alias: string) => `${PROXY_MODULE_PREFIX}${alias}`; + +const installProxyModule = ({ + proxyModuleId, + actionMap, + ensureBridge, +}: { + proxyModuleId: string; + actionMap: ActionMapRecord; + ensureBridge: (alias: string) => Promise; +}) => { + const webpackRequire = getWebpackRequire(); + if (!isObject(webpackRequire.m)) { + webpackRequire.m = {}; + } + if (webpackRequire.m![proxyModuleId]) { + return; + } + + webpackRequire.m![proxyModuleId] = (module: { exports: any }) => { + module.exports = new Proxy( + {}, + { + get(_target, property) { + if (property === 'then') { + return undefined; + } + if (typeof property !== 'string') { + return undefined; + } + if (!Object.prototype.hasOwnProperty.call(actionMap, property)) { + return undefined; + } + const mapping = actionMap[property]; + return async (...args: unknown[]) => { + const bridge = await ensureBridge(mapping.alias); + if (typeof bridge.executeAction !== 'function') { + throw new Error( + `[modern-js-v3:rsc-bridge] Missing executeAction bridge method for remote "${mapping.alias}"`, + ); + } + return bridge.executeAction( + mapping.rawActionId, + Array.isArray(args) ? args : [], + ); + }; + }, + }, + ); + }; +}; + +const isNodeLikeRuntime = () => + typeof process !== 'undefined' && + Boolean(process.versions?.node) && + typeof document === 'undefined'; + +const rscBridgeRuntimePlugin = (): ModuleFederationRuntimePlugin => { + const bridgePromises: Partial>> = {}; + const aliasMergePromises: Partial>> = {}; + const actionMap: ActionMapRecord = {}; + const mergedRemoteAliases = new Set(); + + const resolveRemoteAlias = (args: any): string | undefined => { + const candidateAliases = [ + args?.remote?.alias, + args?.pkgNameOrAlias, + args?.remote?.name, + args?.remoteInfo?.alias, + args?.remoteInfo?.name, + args?.name, + ]; + for (const candidate of candidateAliases) { + if (typeof candidate === 'string' && candidate.trim()) { + return candidate; + } + } + return undefined; + }; + + const ensureBridge = async (alias: string, args?: any) => { + const existingBridgePromise = bridgePromises[alias]; + if (existingBridgePromise) { + return existingBridgePromise; + } + const runtimeInstance = + args?.origin && typeof args.origin.loadRemote === 'function' + ? args.origin + : getWebpackRequireIfAvailable()?.federation?.instance; + if (!runtimeInstance || typeof runtimeInstance.loadRemote !== 'function') { + throw new Error( + '[modern-js-v3:rsc-bridge] Module Federation runtime instance is unavailable while loading the RSC bridge', + ); + } + let resolveBridge!: (bridge: BridgeModule) => void; + let rejectBridge!: (error: unknown) => void; + const bridgePromise = new Promise((resolve, reject) => { + resolveBridge = resolve; + rejectBridge = reject; + }); + bridgePromises[alias] = bridgePromise; + + void Promise.resolve() + .then(() => runtimeInstance.loadRemote(`${alias}/${RSC_BRIDGE_EXPOSE}`)) + .then((bridge: BridgeModule) => { + if ( + !bridge || + typeof bridge.getManifest !== 'function' || + typeof bridge.executeAction !== 'function' + ) { + throw new Error( + `[modern-js-v3:rsc-bridge] Remote "${alias}" is missing the internal RSC bridge expose`, + ); + } + resolveBridge(bridge); + }) + .catch((error: unknown) => { + // Allow retry when bridge loading fails transiently. + delete bridgePromises[alias]; + rejectBridge(error); + }); + + return bridgePromise; + }; + + const mergeRemoteManifest = ( + alias: string, + remoteManifest: ManifestLike, + proxyModuleId: string, + ) => { + if (!isObject(remoteManifest)) { + return; + } + + const hostManifest = ensureHostManifest(); + const namespacedClientIds: Record = {}; + + if (isObject(remoteManifest.clientManifest)) { + for (const [key, value] of Object.entries( + remoteManifest.clientManifest, + )) { + const nextValue = isObject(value) ? { ...value } : value; + if (isObject(nextValue) && nextValue.id != null) { + const namespacedClientId = getNamespacedModuleId(alias, nextValue.id); + namespacedClientIds[String(nextValue.id)] = namespacedClientId; + nextValue.id = namespacedClientId; + } + assertNoConflict( + hostManifest.clientManifest as Record, + key, + nextValue, + alias, + 'clientManifest', + ); + (hostManifest.clientManifest as Record)[key] = nextValue; + } + } + + if (isObject(remoteManifest.serverConsumerModuleMap)) { + for (const [rawModuleId, value] of Object.entries( + remoteManifest.serverConsumerModuleMap, + )) { + const scopedModuleId = Object.prototype.hasOwnProperty.call( + namespacedClientIds, + String(rawModuleId), + ) + ? namespacedClientIds[String(rawModuleId)] + : getNamespacedModuleId(alias, rawModuleId); + const nextValue = remapConsumerNode(alias, value, namespacedClientIds); + assertNoConflict( + hostManifest.serverConsumerModuleMap as Record, + scopedModuleId, + nextValue, + alias, + 'serverConsumerModuleMap', + ); + (hostManifest.serverConsumerModuleMap as Record)[ + scopedModuleId + ] = nextValue; + } + } + + if (!isObject(remoteManifest.serverManifest)) { + return; + } + + for (const [rawActionId, actionEntry] of Object.entries( + remoteManifest.serverManifest, + )) { + const prefixedActionId = `${ACTION_PREFIX}${alias}:${rawActionId}`; + const hostActionEntry = { + id: proxyModuleId, + name: prefixedActionId, + chunks: [], + async: + isObject(actionEntry) && 'async' in actionEntry + ? (actionEntry as Record).async + : true, + }; + assertNoConflict( + hostManifest.serverManifest as Record, + prefixedActionId, + hostActionEntry, + alias, + 'serverManifest', + ); + (hostManifest.serverManifest as Record)[prefixedActionId] = + hostActionEntry; + actionMap[prefixedActionId] = { alias, rawActionId }; + registerActionRemap(rawActionId, prefixedActionId); + } + }; + + const ensureRemoteAliasMerged = async (alias: string, args: any) => { + const existingMergePromise = aliasMergePromises[alias]; + if (existingMergePromise) { + await existingMergePromise; + return; + } + if (mergedRemoteAliases.has(alias)) { + return; + } + + const mergePromise = (async () => { + const proxyModuleId = getProxyModuleId(alias); + installProxyModule({ + proxyModuleId, + actionMap, + ensureBridge: async (resolvedAlias) => + ensureBridge(resolvedAlias, args), + }); + + const bridge = await ensureBridge(alias, args); + const remoteManifest = + typeof bridge.getManifest === 'function' + ? await Promise.resolve(bridge.getManifest()) + : {}; + mergeRemoteManifest(alias, remoteManifest || {}, proxyModuleId); + mergedRemoteAliases.add(alias); + })(); + + aliasMergePromises[alias] = mergePromise; + try { + await mergePromise; + } finally { + delete aliasMergePromises[alias]; + } + }; + + const ensureTrailingSlash = (value: string) => + value.endsWith('/') ? value : `${value}/`; + + const isAbsoluteHttpUrl = (value: string) => + value.startsWith('http://') || value.startsWith('https://'); + + const isBrokenRemoteEntryUrl = (value: unknown) => + typeof value !== 'string' || + !value.trim() || + value.includes('undefined') || + value.startsWith('https:undefined') || + value.startsWith('http:undefined'); + + const resolveSnapshotPublicPath = (snapshot: Record) => { + const metaData = isObject(snapshot.metaData) + ? (snapshot.metaData as Record) + : undefined; + const candidates = [ + snapshot.ssrPublicPath, + snapshot.publicPath, + metaData?.ssrPublicPath, + metaData?.publicPath, + ]; + const publicPath = candidates.find( + (candidate) => typeof candidate === 'string' && candidate.trim(), + ) as string | undefined; + if (!publicPath) { + return undefined; + } + return ensureTrailingSlash(publicPath.trim()); + }; + + const resolveSnapshotRemoteEntryRequest = (snapshot: Record) => { + const remoteEntryCandidate = isObject(snapshot.ssrRemoteEntry) + ? (snapshot.ssrRemoteEntry as Record) + : (snapshot.ssrRemoteEntry ?? snapshot.remoteEntry); + if (!remoteEntryCandidate) { + return undefined; + } + + let entryPath = + typeof remoteEntryCandidate === 'string' + ? remoteEntryCandidate + : (() => { + const pathPart = + typeof remoteEntryCandidate.path === 'string' + ? remoteEntryCandidate.path + : ''; + const namePart = + typeof remoteEntryCandidate.name === 'string' + ? remoteEntryCandidate.name + : ''; + return `${pathPart}${namePart}`; + })(); + if (!entryPath) { + return undefined; + } + + if (isAbsoluteHttpUrl(entryPath)) { + if ( + isNodeLikeRuntime() && + !snapshot.ssrRemoteEntry && + !entryPath.includes('/bundles/') + ) { + try { + const url = new URL(entryPath); + const normalizedPathname = url.pathname.startsWith('/') + ? url.pathname.slice(1) + : url.pathname; + url.pathname = `/bundles/${normalizedPathname}`; + return url.href; + } catch { + return entryPath; + } + } + return entryPath; + } + + if (isNodeLikeRuntime() && !snapshot.ssrRemoteEntry) { + const normalizedEntryPath = entryPath.startsWith('/') + ? entryPath.slice(1) + : entryPath; + if (!normalizedEntryPath.startsWith('bundles/')) { + entryPath = `bundles/${normalizedEntryPath}`; + } + } + + const publicPath = resolveSnapshotPublicPath(snapshot); + if (!publicPath) { + return undefined; + } + + try { + return new URL(entryPath, publicPath).href; + } catch { + const normalizedEntry = entryPath.startsWith('/') + ? entryPath.slice(1) + : entryPath; + return `${publicPath}${normalizedEntry}`; + } + }; + + const patchRemoteInfoEntry = ( + remoteInfo: Record | undefined, + snapshot: Record | undefined, + ) => { + if (!isObject(remoteInfo) || !isObject(snapshot)) { + return; + } + const resolvedEntry = resolveSnapshotRemoteEntryRequest(snapshot); + if (!resolvedEntry) { + return; + } + + if ( + isBrokenRemoteEntryUrl(remoteInfo.entry) || + (typeof remoteInfo.entry === 'string' && + remoteInfo.entry.includes('undefined')) + ) { + remoteInfo.entry = resolvedEntry; + } + }; + + const normalizeRemoteEntryPath = (remoteEntry: Record) => { + if ( + remoteEntry.path === undefined || + remoteEntry.path === null || + remoteEntry.path === 'undefined' + ) { + remoteEntry.path = ''; + } + }; + + const patchSnapshotSsrPublicPath = (snapshot: any) => { + if (!isObject(snapshot)) { + return; + } + + const metaData = isObject(snapshot.metaData) + ? (snapshot.metaData as Record) + : undefined; + + const rawPublicPathCandidates = [ + snapshot.ssrPublicPath, + snapshot.publicPath, + metaData?.ssrPublicPath, + metaData?.publicPath, + ]; + const publicPath = rawPublicPathCandidates.find( + (candidate) => typeof candidate === 'string' && candidate.trim(), + ) as string | undefined; + if (publicPath) { + const normalizedPublicPath = ensureTrailingSlash(publicPath.trim()); + snapshot.ssrPublicPath = normalizedPublicPath; + if (metaData) { + metaData.ssrPublicPath = normalizedPublicPath; + } + } + + const remoteEntry = isObject(snapshot.remoteEntry) + ? (snapshot.remoteEntry as Record) + : isObject(metaData?.remoteEntry) + ? (metaData?.remoteEntry as Record) + : undefined; + if (remoteEntry) { + normalizeRemoteEntryPath(remoteEntry); + if (!isObject(snapshot.remoteEntry)) { + snapshot.remoteEntry = remoteEntry; + } + if (metaData && !isObject(metaData.remoteEntry)) { + metaData.remoteEntry = remoteEntry; + } + } + + if ( + !isObject(snapshot.ssrRemoteEntry) && + (isObject(metaData?.ssrRemoteEntry) || remoteEntry) + ) { + const source = isObject(metaData?.ssrRemoteEntry) + ? (metaData?.ssrRemoteEntry as Record) + : (remoteEntry as Record); + const normalizedSsrRemoteEntry = { ...source }; + normalizeRemoteEntryPath(normalizedSsrRemoteEntry); + snapshot.ssrRemoteEntry = normalizedSsrRemoteEntry; + if (metaData) { + metaData.ssrRemoteEntry = { ...normalizedSsrRemoteEntry }; + } + } else if (isObject(snapshot.ssrRemoteEntry)) { + normalizeRemoteEntryPath(snapshot.ssrRemoteEntry as Record); + if (metaData && !isObject(metaData.ssrRemoteEntry)) { + metaData.ssrRemoteEntry = snapshot.ssrRemoteEntry; + } + } + }; + + const patchRuntimeArgsSnapshots = (args: any) => { + patchSnapshotSsrPublicPath(args?.remoteSnapshot); + patchSnapshotSsrPublicPath(args?.remoteInfo); + patchSnapshotSsrPublicPath(args?.remote?.remoteSnapshot); + patchSnapshotSsrPublicPath(args?.remote?.remoteInfo); + patchSnapshotSsrPublicPath(args?.resolvedRemote?.remoteSnapshot); + patchSnapshotSsrPublicPath(args?.resolvedRemote?.remoteInfo); + patchRemoteInfoEntry(args?.remoteInfo, args?.remoteSnapshot); + patchRemoteInfoEntry( + args?.remote?.remoteInfo, + args?.remote?.remoteSnapshot, + ); + patchRemoteInfoEntry( + args?.resolvedRemote?.remoteInfo, + args?.resolvedRemote?.remoteSnapshot, + ); + }; + + return { + name: 'modernjs-rsc-bridge-runtime-plugin', + async afterResolve(args: any) { + patchRuntimeArgsSnapshots(args); + const alias = resolveRemoteAlias(args); + if (!alias) { + return args; + } + await ensureRemoteAliasMerged(alias, args); + return args; + }, + async onLoad(args: any) { + patchRuntimeArgsSnapshots(args); + const alias = resolveRemoteAlias(args); + if (!alias) { + return args; + } + + const hasExposeContext = + typeof args?.expose === 'string' || + typeof args?.id === 'string' || + typeof args?.id === 'number'; + if (!hasExposeContext) { + await ensureRemoteAliasMerged(alias, args); + return args; + } + + const expose = + typeof args?.expose === 'string' ? args.expose : String(args?.id || ''); + if ( + mergedRemoteAliases.has(alias) || + expose.includes(RSC_BRIDGE_EXPOSE) + ) { + return args; + } + await ensureRemoteAliasMerged(alias, args); + + return args; + }, + }; +}; + +export default rscBridgeRuntimePlugin; diff --git a/packages/modernjs-v3/src/cli/ssrPlugin.ts b/packages/modernjs-v3/src/cli/ssrPlugin.ts index a61b7ed1a60..ed8b2115255 100644 --- a/packages/modernjs-v3/src/cli/ssrPlugin.ts +++ b/packages/modernjs-v3/src/cli/ssrPlugin.ts @@ -32,6 +32,20 @@ export function setEnv() { } export const CHAIN_MF_PLUGIN_ID = 'plugin-module-federation-server'; +const isBuildCommand = () => + process.argv.includes('build') || process.argv.includes('deploy'); + +const hasExposes = ( + exposes: moduleFederationPlugin.ModuleFederationPluginOptions['exposes'], +) => { + if (!exposes) { + return false; + } + if (Array.isArray(exposes)) { + return exposes.length > 0; + } + return Object.keys(exposes).length > 0; +}; function getManifestAssetFileNames( manifestOption?: moduleFederationPlugin.ModuleFederationPluginOptions['manifest'], @@ -83,52 +97,6 @@ const mfSSRRsbuildPlugin = ( let ssrEnv = ''; let csrEnv = ''; - const browserAssetFileNames = - pluginOptions.assetFileNames.browser || - getManifestAssetFileNames(pluginOptions.csrConfig?.manifest); - const nodeAssetFileNames = - pluginOptions.assetFileNames?.node || - getManifestAssetFileNames(pluginOptions.ssrConfig?.manifest); - - const collectAssets = ( - assets: Record string | Buffer }>, - fileNames: { statsFileName: string; manifestFileName: string }, - tag: 'browser' | 'node', - ): StatsAssetResource | undefined => { - const statsAsset = assets[fileNames.statsFileName]; - const manifestAsset = assets[fileNames.manifestFileName]; - - if (!statsAsset || !manifestAsset) { - return undefined; - } - - try { - const statsRaw = statsAsset.source(); - const manifestRaw = manifestAsset.source(); - const statsContent = - typeof statsRaw === 'string' ? statsRaw : statsRaw.toString(); - const manifestContent = - typeof manifestRaw === 'string' - ? manifestRaw - : manifestRaw.toString(); - - return { - stats: { - data: JSON.parse(statsContent), - filename: fileNames.statsFileName, - }, - manifest: { - data: JSON.parse(manifestContent), - filename: fileNames.manifestFileName, - }, - }; - } catch (err) { - const message = err instanceof Error ? err.message : String(err); - logger.error(`Failed to parse ${tag} manifest assets: ${message}`); - return undefined; - } - }; - api.modifyEnvironmentConfig((config, { name }) => { const target = config.output.target; if (skipByTarget(target)) { @@ -166,43 +134,6 @@ const mfSSRRsbuildPlugin = ( modifySSRPublicPath(config, utils); return config; }); - - api.processAssets( - { stage: 'report' }, - ({ assets, environment: envContext }) => { - const envName = envContext.name; - - if ( - pluginOptions.csrConfig?.manifest !== false && - csrEnv && - envName === csrEnv - ) { - const browserAssets = collectAssets( - assets, - browserAssetFileNames, - 'browser', - ); - if (browserAssets) { - pluginOptions.assetResources.browser = browserAssets; - } - } - - if ( - pluginOptions.ssrConfig?.manifest !== false && - ssrEnv && - envName === ssrEnv - ) { - const nodeAssets = collectAssets( - assets, - nodeAssetFileNames, - 'node', - ); - if (nodeAssets) { - pluginOptions.assetResources.node = nodeAssets; - } - } - }, - ); }, }; }; @@ -219,6 +150,12 @@ export const moduleFederationSSRPlugin = ( const modernjsConfig = api.getConfig(); const enableSSR = pluginOptions.userConfig?.ssr ?? Boolean(modernjsConfig?.server?.ssr); + const enableRsc = Boolean(modernjsConfig?.server?.rsc); + const enableMfRsc = Boolean( + (pluginOptions.ssrConfig.experiments as { rsc?: boolean } | undefined) + ?.rsc, + ); + const hasRscExposes = hasExposes(pluginOptions.ssrConfig.exposes); const { secondarySharedTreeShaking } = pluginOptions; if (!enableSSR) { return; @@ -292,6 +229,11 @@ export const moduleFederationSSRPlugin = ( if (!isWeb && !secondarySharedTreeShaking) { chain.target('async-node'); + + if (enableRsc && (!enableMfRsc || hasRscExposes)) { + chain.resolve.conditionNames.add('react-server'); + } + if (isDev()) { chain .plugin('UniverseEntryChunkTrackerPlugin') @@ -318,11 +260,25 @@ export const moduleFederationSSRPlugin = ( return; } try { - if ( - req.url?.includes('.json') && - !req.url?.includes('hot-update') - ) { - const filepath = path.join(process.cwd(), `dist${req.url}`); + const requestPath = req.url?.split('?')[0] || ''; + const isJsonRequest = path.extname(requestPath) === '.json'; + if (isJsonRequest && !requestPath.includes('hot-update')) { + if (!requestPath.startsWith('/')) { + next(); + return; + } + + const distRoot = path.resolve(process.cwd(), 'dist'); + const filepath = path.resolve(distRoot, `.${requestPath}`); + const allowedPrefix = `${distRoot}${path.sep}`; + if ( + filepath !== distRoot && + !filepath.startsWith(allowedPrefix) + ) { + next(); + return; + } + fs.statSync(filepath); res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader( @@ -343,14 +299,72 @@ export const moduleFederationSSRPlugin = ( }, }; }); + const readAssetResourceFromDisk = ( + outputDir: string, + fileNames: AssetFileNames, + tag: 'browser' | 'node', + ): StatsAssetResource | undefined => { + const statsFilePath = path.resolve(outputDir, fileNames.statsFileName); + const manifestFilePath = path.resolve( + outputDir, + fileNames.manifestFileName, + ); + if (!fs.existsSync(statsFilePath) || !fs.existsSync(manifestFilePath)) { + return undefined; + } + + try { + return { + stats: { + data: fs.readJSONSync(statsFilePath), + filename: fileNames.statsFileName, + }, + manifest: { + data: fs.readJSONSync(manifestFilePath), + filename: fileNames.manifestFileName, + }, + }; + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + logger.error( + `[module-federation-ssr] Failed to read ${tag} manifest assets from disk: ${message}`, + ); + return undefined; + } + }; + const writeMergedManifest = () => { - const { distOutputDir, assetResources } = pluginOptions; - const browserAssets = assetResources.browser; - const nodeAssets = assetResources.node; + if (!isBuildCommand()) { + return; + } + const distOutputDir = + pluginOptions.distOutputDir || path.resolve(process.cwd(), 'dist'); + const userSSRConfig = pluginOptions.userConfig.ssr; + const ssrDistOutputDir = + typeof userSSRConfig === 'object' && userSSRConfig.distOutputDir + ? userSSRConfig.distOutputDir + : path.resolve(distOutputDir, 'bundles'); + const browserFileNames = + pluginOptions.assetFileNames.browser || + getManifestAssetFileNames(pluginOptions.csrConfig?.manifest); + const nodeFileNames = + pluginOptions.assetFileNames.node || + getManifestAssetFileNames(pluginOptions.ssrConfig?.manifest); + const browserAssets = readAssetResourceFromDisk( + distOutputDir, + browserFileNames, + 'browser', + ); + const nodeAssets = readAssetResourceFromDisk( + ssrDistOutputDir, + nodeFileNames, + 'node', + ); - if (!distOutputDir || !browserAssets || !nodeAssets) { + if (!browserAssets || !nodeAssets) { return; } + try { updateStatsAndManifest(nodeAssets, browserAssets, distOutputDir); } catch (err) { @@ -361,10 +375,6 @@ export const moduleFederationSSRPlugin = ( api.onAfterBuild(() => { writeMergedManifest(); }); - api.onDevCompileDone(() => { - // 热更后修改 manifest - writeMergedManifest(); - }); }, }); diff --git a/packages/modernjs-v3/src/runtime/rsc-bridge-expose.spec.ts b/packages/modernjs-v3/src/runtime/rsc-bridge-expose.spec.ts new file mode 100644 index 00000000000..230564f969a --- /dev/null +++ b/packages/modernjs-v3/src/runtime/rsc-bridge-expose.spec.ts @@ -0,0 +1,168 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; + +type ManifestLike = { + serverManifest?: Record; + clientManifest?: Record; + serverConsumerModuleMap?: Record; +}; + +type ExposeModuleMap = Record Promise | unknown>; + +type WebpackRequireRuntime = { + initializeExposesData?: { + moduleMap?: ExposeModuleMap; + }; + rscM?: ManifestLike; +}; + +const setWebpackRequireRuntime = (runtime: WebpackRequireRuntime) => { + ( + globalThis as typeof globalThis & { + __webpack_require__?: WebpackRequireRuntime; + } + ).__webpack_require__ = runtime; +}; + +const loadBridgeExposeModule = async () => { + vi.resetModules(); + return import('./rsc-bridge-expose'); +}; + +afterEach(() => { + delete ( + globalThis as typeof globalThis & { + __webpack_require__?: WebpackRequireRuntime; + } + ).__webpack_require__; +}); + +describe('rsc-bridge-expose', () => { + it('returns remote manifest through getManifest', async () => { + const manifest: ManifestLike = { + serverManifest: { + action: { + id: 'remote:raw', + }, + }, + }; + setWebpackRequireRuntime({ + rscM: manifest, + initializeExposesData: { + moduleMap: {}, + }, + }); + + const { getManifest } = await loadBridgeExposeModule(); + + expect(getManifest()).toBe(manifest); + }); + + it('scans exposes and executes action references by raw id', async () => { + const action = vi.fn(async (value: string) => `ok:${value}`) as { + (...args: unknown[]): Promise; + $$id?: string; + }; + action.$$id = 'raw-action-id'; + + const getFactory = vi.fn(async () => () => ({ + nested: { + action, + }, + })); + + setWebpackRequireRuntime({ + initializeExposesData: { + moduleMap: { + './actions': getFactory, + './__rspack_rsc_bridge__': vi.fn(async () => () => ({ + ignored: true, + })), + }, + }, + rscM: {}, + }); + + const bridgeExpose = await loadBridgeExposeModule(); + + await expect( + bridgeExpose.executeAction('raw-action-id', ['value-a']), + ).resolves.toBe('ok:value-a'); + await expect( + bridgeExpose.executeAction('raw-action-id', ['value-b']), + ).resolves.toBe('ok:value-b'); + + expect(getFactory).toHaveBeenCalledTimes(1); + expect(action).toHaveBeenNthCalledWith(1, 'value-a'); + expect(action).toHaveBeenNthCalledWith(2, 'value-b'); + }); + + it('normalizes non-array args to empty array before dispatch', async () => { + const action = vi.fn(async (...args: unknown[]) => args.length) as { + (...args: unknown[]): Promise; + $$id?: string; + }; + action.$$id = 'raw-action-id'; + + setWebpackRequireRuntime({ + initializeExposesData: { + moduleMap: { + './actions': async () => () => ({ action }), + }, + }, + rscM: {}, + }); + + const bridgeExpose = await loadBridgeExposeModule(); + + await expect( + bridgeExpose.executeAction('raw-action-id', 'not-array' as any), + ).resolves.toBe(0); + expect(action).toHaveBeenCalledWith(); + }); + + it('throws explicit errors when action id is unresolved', async () => { + setWebpackRequireRuntime({ + initializeExposesData: { + moduleMap: { + './actions': async () => () => ({ value: 'noop' }), + }, + }, + rscM: {}, + }); + + const bridgeExpose = await loadBridgeExposeModule(); + + await expect( + bridgeExpose.executeAction('missing-action-id', []), + ).rejects.toThrow(/Missing remote action for id "missing-action-id"/); + }); + + it('discovers action references from module cache exports', async () => { + const cachedAction = vi.fn(async () => 'cached') as { + (...args: unknown[]): Promise; + $$id?: string; + }; + cachedAction.$$id = 'raw-action-from-cache'; + + setWebpackRequireRuntime({ + initializeExposesData: { + moduleMap: {}, + }, + c: { + cachedModule: { + exports: { + nested: { + cachedAction, + }, + }, + }, + }, + rscM: {}, + }); + + const bridgeExpose = await loadBridgeExposeModule(); + await expect( + bridgeExpose.executeAction('raw-action-from-cache', []), + ).resolves.toBe('cached'); + }); +}); diff --git a/packages/modernjs-v3/src/runtime/rsc-bridge-expose.ts b/packages/modernjs-v3/src/runtime/rsc-bridge-expose.ts new file mode 100644 index 00000000000..2f8aeaec641 --- /dev/null +++ b/packages/modernjs-v3/src/runtime/rsc-bridge-expose.ts @@ -0,0 +1,176 @@ +type ManifestLike = { + serverManifest?: Record; + clientManifest?: Record; + serverConsumerModuleMap?: Record; +}; + +type ExposeModuleMap = Record< + string, + () => Promise<(() => unknown) | unknown> | (() => unknown) | unknown +>; + +type WebpackRequireRuntime = { + initializeExposesData?: { + moduleMap?: ExposeModuleMap; + }; + m?: Record; + c?: Record; + rscM?: ManifestLike; +}; + +declare const __webpack_require__: WebpackRequireRuntime; + +const BRIDGE_EXPOSE_NAME = './__rspack_rsc_bridge__'; +const actionReferenceCache: Record unknown> = + Object.create(null); +let scanPromise: Promise | null = null; +let scanHadErrors = false; + +const isObject = (value: unknown): value is Record => + typeof value === 'object' && value !== null; +const isFunction = (value: unknown): value is (...args: unknown[]) => unknown => + typeof value === 'function'; +const isWebpackRequireRuntime = ( + value: unknown, +): value is WebpackRequireRuntime => isObject(value) || isFunction(value); +const runtimeRequireFromWrapper: WebpackRequireRuntime | undefined = (() => { + try { + // Access the module-wrapper require function when available. + // biome-ignore lint/security/noGlobalEval: The bundler module wrapper exposes `require` via arguments only. + const wrapperRequire = eval('arguments[2]'); + return isWebpackRequireRuntime(wrapperRequire) + ? (wrapperRequire as WebpackRequireRuntime) + : undefined; + } catch { + return undefined; + } +})(); + +const getWebpackRequire = (): WebpackRequireRuntime => { + if (runtimeRequireFromWrapper) { + return runtimeRequireFromWrapper; + } + + if ( + typeof __webpack_require__ !== 'undefined' && + isWebpackRequireRuntime(__webpack_require__) + ) { + return __webpack_require__; + } + + const runtime = ( + globalThis as typeof globalThis & { + __webpack_require__?: WebpackRequireRuntime; + } + ).__webpack_require__; + if (!isWebpackRequireRuntime(runtime)) { + throw new Error( + '[modern-js-v3:rsc-bridge] __webpack_require__ is unavailable while evaluating the internal bridge expose', + ); + } + + return runtime as WebpackRequireRuntime; +}; + +const cacheActionReferencesFromExports = ( + exposedValue: unknown, + visited: WeakSet, +) => { + if ( + typeof exposedValue === 'function' && + typeof (exposedValue as { $$id?: unknown }).$$id === 'string' + ) { + const id = (exposedValue as unknown as { $$id?: string }).$$id; + if (typeof id === 'string') { + actionReferenceCache[id] = exposedValue as ( + ...args: unknown[] + ) => unknown; + } + } + + if (!isObject(exposedValue)) { + return; + } + if (visited.has(exposedValue)) { + return; + } + visited.add(exposedValue); + + for (const value of Object.values(exposedValue)) { + cacheActionReferencesFromExports(value, visited); + } +}; + +const scanExposedModulesForActions = async () => { + if (scanPromise) { + await scanPromise; + if (scanHadErrors || Object.keys(actionReferenceCache).length === 0) { + scanPromise = null; + scanHadErrors = false; + } + return; + } + + scanHadErrors = false; + scanPromise = (async () => { + let hadExposeScanError = false; + const webpackRequire = getWebpackRequire(); + const moduleMap = webpackRequire.initializeExposesData?.moduleMap; + if (isObject(moduleMap)) { + for (const [exposeName, getFactory] of Object.entries(moduleMap)) { + if ( + exposeName === BRIDGE_EXPOSE_NAME || + typeof getFactory !== 'function' + ) { + continue; + } + + try { + const maybeFactory = await getFactory(); + const exportsValue = + typeof maybeFactory === 'function' ? maybeFactory() : maybeFactory; + cacheActionReferencesFromExports(exportsValue, new WeakSet()); + if (isObject(exportsValue) && isObject(exportsValue.default)) { + cacheActionReferencesFromExports( + exportsValue.default, + new WeakSet(), + ); + } + } catch { + hadExposeScanError = true; + // Ignore expose modules that fail to execute during scan. + } + } + } + + if (isObject(webpackRequire.c)) { + for (const moduleRecord of Object.values(webpackRequire.c)) { + const moduleExports = isObject(moduleRecord) + ? (moduleRecord as { exports?: unknown }).exports + : undefined; + cacheActionReferencesFromExports(moduleExports, new WeakSet()); + } + } + + scanHadErrors = hadExposeScanError; + })(); + + await scanPromise; + if (scanHadErrors || Object.keys(actionReferenceCache).length === 0) { + scanPromise = null; + scanHadErrors = false; + } +}; + +export const getManifest = () => getWebpackRequire().rscM; + +export const executeAction = async (actionId: string, args: unknown[]) => { + await scanExposedModulesForActions(); + const action = actionReferenceCache[actionId]; + if (typeof action !== 'function') { + throw new Error( + `[modern-js-v3:rsc-bridge] Missing remote action for id "${actionId}". Ensure it is reachable from a federated expose.`, + ); + } + return action(...(Array.isArray(args) ? args : [])); +}; diff --git a/packages/modernjs-v3/src/runtime/rsc-client-callback-bootstrap.js b/packages/modernjs-v3/src/runtime/rsc-client-callback-bootstrap.js new file mode 100644 index 00000000000..d5297080e7d --- /dev/null +++ b/packages/modernjs-v3/src/runtime/rsc-client-callback-bootstrap.js @@ -0,0 +1,355 @@ +import { setResolveActionId } from '@modern-js/runtime/rsc/client'; +import { + createFromFetch, + createTemporaryReferenceSet, + encodeReply, + setServerCallback, +} from 'react-server-dom-rspack/client.browser'; + +const ACTION_PREFIX = 'remote:'; +const ACTION_REMAP_GLOBAL_KEY = '__MODERN_RSC_MF_ACTION_ID_MAP__'; +const ACTION_REMAP_WAITERS_KEY = '__MODERN_RSC_MF_ACTION_ID_MAP_WAITERS__'; +const ACTION_REMAP_WAIT_TIMEOUT_MS = 3000; +const CALLBACK_INSTALL_RETRY_DELAY_MS = 50; +const MAX_CALLBACK_INSTALL_ATTEMPTS = 120; +const CALLBACK_CHUNK_LOADER_HOOK_FLAG = '__MODERN_RSC_MF_CALLBACK_HOOKED__'; +let hasResolvedFallbackAlias = false; +let fallbackRemoteAlias; +let callbackInstallAttempts = 0; +const installedClientBrowserRuntimes = new WeakSet(); + +function isObject(value) { + return typeof value === 'object' && value !== null; +} + +function isFunction(value) { + return typeof value === 'function'; +} + +const runtimeRequireFromWrapper = (() => { + try { + // Access the module-wrapper require function when available. + // biome-ignore lint/security/noGlobalEval: The bundler module wrapper exposes `require` via arguments only. + const wrapperRequire = eval('arguments[2]'); + if (isFunction(wrapperRequire) || isObject(wrapperRequire)) { + return wrapperRequire; + } + } catch {} + return undefined; +})(); + +function isClientBrowserRuntime(value) { + return ( + isObject(value) && + isFunction(value.setServerCallback) && + isFunction(value.createTemporaryReferenceSet) && + isFunction(value.encodeReply) && + isFunction(value.createFromFetch) + ); +} + +function getWebpackRequire() { + if (runtimeRequireFromWrapper) { + return runtimeRequireFromWrapper; + } + if (typeof __webpack_require__ !== 'undefined') { + return __webpack_require__; + } + const runtime = globalThis.__webpack_require__; + if (isFunction(runtime) || isObject(runtime)) { + return runtime; + } + return undefined; +} + +function getActionRemapMap() { + const map = globalThis[ACTION_REMAP_GLOBAL_KEY]; + if (!isObject(map)) { + globalThis[ACTION_REMAP_GLOBAL_KEY] = Object.create(null); + return globalThis[ACTION_REMAP_GLOBAL_KEY]; + } + return map; +} + +function resolveFallbackRemoteAlias() { + if (hasResolvedFallbackAlias) { + return fallbackRemoteAlias; + } + + const webpackRequire = getWebpackRequire(); + const runtimeInstance = + webpackRequire && + isObject(webpackRequire.federation) && + isObject(webpackRequire.federation.instance) + ? webpackRequire.federation.instance + : undefined; + if (!runtimeInstance) { + return undefined; + } + + const aliasSet = new Set(); + const remotes = Array.isArray(runtimeInstance.options?.remotes) + ? runtimeInstance.options.remotes + : []; + for (const remote of remotes) { + if (isObject(remote)) { + const alias = + typeof remote.alias === 'string' && remote.alias + ? remote.alias + : typeof remote.name === 'string' && remote.name + ? remote.name + : undefined; + if (alias) { + aliasSet.add(alias); + } + } + } + + const idToRemoteMap = runtimeInstance.remoteHandler?.idToRemoteMap; + if (isObject(idToRemoteMap)) { + for (const remoteInfo of Object.values(idToRemoteMap)) { + if (!isObject(remoteInfo)) { + continue; + } + const name = + typeof remoteInfo.name === 'string' && remoteInfo.name + ? remoteInfo.name + : undefined; + if (name) { + aliasSet.add(name); + } + } + } + + if (aliasSet.size === 1) { + fallbackRemoteAlias = Array.from(aliasSet)[0]; + hasResolvedFallbackAlias = true; + return fallbackRemoteAlias; + } + + if (aliasSet.size === 0 && !globalThis.window) { + return undefined; + } + + if (globalThis.window) { + const containerAliases = Object.keys(globalThis.window).filter((alias) => { + const candidate = globalThis.window[alias]; + return ( + isObject(candidate) && + isFunction(candidate.get) && + isFunction(candidate.init) + ); + }); + if (aliasSet.size === 0 && containerAliases.length === 1) { + fallbackRemoteAlias = containerAliases[0]; + hasResolvedFallbackAlias = true; + return fallbackRemoteAlias; + } + if (aliasSet.size === 0 && containerAliases.length === 0) { + return undefined; + } + } + + fallbackRemoteAlias = undefined; + hasResolvedFallbackAlias = true; + return undefined; +} + +function getActionRemapWaiters() { + const waiters = globalThis[ACTION_REMAP_WAITERS_KEY]; + if (!(waiters instanceof Map)) { + const nextWaiters = new Map(); + globalThis[ACTION_REMAP_WAITERS_KEY] = nextWaiters; + return nextWaiters; + } + return waiters; +} + +function resolveActionEndpoint() { + if (!globalThis.window) { + return '/'; + } + + const entryName = window.__MODERN_JS_ENTRY_NAME; + if (!entryName || entryName === 'main' || entryName === 'index') { + return '/'; + } + return `/${entryName}`; +} + +function waitForActionRemap(rawId) { + const waiters = getActionRemapWaiters(); + return new Promise((resolve) => { + let settled = false; + const resolveOnce = (value) => { + if (settled) { + return; + } + settled = true; + clearTimeout(timeoutHandle); + resolve(value); + }; + + const list = waiters.get(rawId) || []; + list.push(resolveOnce); + waiters.set(rawId, list); + + const timeoutHandle = setTimeout(() => { + const current = waiters.get(rawId) || []; + const next = current.filter((waiter) => waiter !== resolveOnce); + if (next.length === 0) { + waiters.delete(rawId); + } else { + waiters.set(rawId, next); + } + resolveOnce(rawId); + }, ACTION_REMAP_WAIT_TIMEOUT_MS); + }); +} + +async function resolveActionId(id) { + if (typeof id === 'string' && id.startsWith(ACTION_PREFIX)) { + return id; + } + + const remapMap = getActionRemapMap(); + const remappedId = remapMap[id]; + if (typeof remappedId === 'string') { + return remappedId; + } + if (remappedId === false) { + return id; + } + + const fallbackAlias = resolveFallbackRemoteAlias(); + if (typeof fallbackAlias === 'string' && fallbackAlias) { + const prefixedId = `${ACTION_PREFIX}${fallbackAlias}:${id}`; + remapMap[id] = prefixedId; + return prefixedId; + } + + return waitForActionRemap(id); +} + +function createServerCallback(runtime) { + return async (id, args) => { + const actionId = await resolveActionId(id); + const temporaryReferences = runtime.createTemporaryReferenceSet(); + const body = await runtime.encodeReply(Array.isArray(args) ? args : [], { + temporaryReferences, + }); + const endpoint = resolveActionEndpoint(); + const response = await fetch(endpoint, { + method: 'POST', + headers: { + Accept: 'text/x-component', + 'x-rsc-action': actionId, + }, + body, + }); + + if (!response.ok) { + throw new Error( + `[modern-js-v3:rsc-bridge] callback request failed (${response.status}) for "${actionId}"`, + ); + } + + return runtime.createFromFetch(Promise.resolve(response), { + temporaryReferences, + }); + }; +} + +function collectClientBrowserRuntimes() { + const runtimes = []; + const maybePushRuntime = (candidate) => { + if (isClientBrowserRuntime(candidate)) { + runtimes.push(candidate); + } else if ( + isObject(candidate) && + isClientBrowserRuntime(candidate.default) + ) { + runtimes.push(candidate.default); + } + }; + + maybePushRuntime({ + createFromFetch, + createTemporaryReferenceSet, + encodeReply, + setServerCallback, + }); + + const webpackRequire = getWebpackRequire(); + const moduleCache = webpackRequire?.c; + if (isObject(moduleCache)) { + for (const moduleRecord of Object.values(moduleCache)) { + if (isObject(moduleRecord) && 'exports' in moduleRecord) { + maybePushRuntime(moduleRecord.exports); + } + } + } + + return runtimes; +} + +function installServerCallbacks() { + let installedCount = 0; + for (const runtime of collectClientBrowserRuntimes()) { + if (installedClientBrowserRuntimes.has(runtime)) { + continue; + } + installedClientBrowserRuntimes.add(runtime); + runtime.setServerCallback(createServerCallback(runtime)); + installedCount += 1; + } + return installedCount; +} + +function hookChunkLoaderInstall() { + const webpackRequire = getWebpackRequire(); + if (!webpackRequire || !isFunction(webpackRequire.e)) { + return; + } + + const chunkLoader = webpackRequire.e; + if (chunkLoader[CALLBACK_CHUNK_LOADER_HOOK_FLAG]) { + return; + } + + const wrappedChunkLoader = function (...args) { + const chunkLoadResult = chunkLoader.apply(this, args); + Promise.resolve(chunkLoadResult) + .catch(() => undefined) + .then(() => { + installServerCallbacks(); + }); + return chunkLoadResult; + }; + wrappedChunkLoader[CALLBACK_CHUNK_LOADER_HOOK_FLAG] = true; + webpackRequire.e = wrappedChunkLoader; +} + +function runInstallAttempt() { + hookChunkLoaderInstall(); + installServerCallbacks(); + callbackInstallAttempts += 1; + + if ( + callbackInstallAttempts >= MAX_CALLBACK_INSTALL_ATTEMPTS || + typeof setTimeout !== 'function' + ) { + return; + } + + setTimeout(runInstallAttempt, CALLBACK_INSTALL_RETRY_DELAY_MS); +} + +setResolveActionId(resolveActionId); + +runInstallAttempt(); +if (typeof queueMicrotask === 'function') { + queueMicrotask(() => { + installServerCallbacks(); + }); +} diff --git a/packages/modernjs-v3/src/runtime/rsc-client-callback-bootstrap.spec.ts b/packages/modernjs-v3/src/runtime/rsc-client-callback-bootstrap.spec.ts new file mode 100644 index 00000000000..86f284579d8 --- /dev/null +++ b/packages/modernjs-v3/src/runtime/rsc-client-callback-bootstrap.spec.ts @@ -0,0 +1,57 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { describe, expect, it } from 'vitest'; + +const getBootstrapSource = () => { + const bootstrapFilePath = path.resolve( + __dirname, + './rsc-client-callback-bootstrap.js', + ); + return fs.readFileSync(bootstrapFilePath, 'utf-8'); +}; + +describe('rsc-client-callback-bootstrap', () => { + it('registers resolveActionId via runtime API', () => { + const source = getBootstrapSource(); + + expect(source).toContain( + "import { setResolveActionId } from '@modern-js/runtime/rsc/client';", + ); + expect(source).toContain('setResolveActionId(resolveActionId);'); + expect(source).not.toContain('__MODERN_RSC_ACTION_RESOLVER__'); + }); + + it('falls back to root action endpoint when entry name is missing', () => { + const source = getBootstrapSource(); + + expect(source).toContain( + "if (!entryName || entryName === 'main' || entryName === 'index')", + ); + }); + + it('avoids remapping action ids via host uniqueName heuristics', () => { + const source = getBootstrapSource(); + + expect(source).not.toContain('initializeSharingData.uniqueName'); + }); + + it('keeps callback installation retries and chunk-loader hook for late runtimes', () => { + const source = getBootstrapSource(); + + expect(source).toContain('const CALLBACK_INSTALL_RETRY_DELAY_MS = 50;'); + expect(source).toContain('const MAX_CALLBACK_INSTALL_ATTEMPTS = 120;'); + expect(source).toContain('function hookChunkLoaderInstall()'); + expect(source).toContain('webpackRequire.e = wrappedChunkLoader;'); + }); + + it('does not cache fallback alias resolution before runtime is available', () => { + const source = getBootstrapSource(); + const runtimeGuardIndex = source.indexOf('if (!runtimeInstance) {'); + const resolvedFlagIndex = source.indexOf( + 'hasResolvedFallbackAlias = true;', + ); + + expect(runtimeGuardIndex).toBeGreaterThan(-1); + expect(resolvedFlagIndex).toBeGreaterThan(runtimeGuardIndex); + }); +}); diff --git a/packages/modernjs-v3/src/server/asyncStartupLoader.ts b/packages/modernjs-v3/src/server/asyncStartupLoader.ts new file mode 100644 index 00000000000..c6b1b96dc58 --- /dev/null +++ b/packages/modernjs-v3/src/server/asyncStartupLoader.ts @@ -0,0 +1,71 @@ +import path from 'path'; +import vm from 'vm'; +import type { BundleLoaderStrategy } from '@modern-js/server-core/node'; +import fs from 'fs-extra'; + +const ASYNC_NODE_STARTUP_CALL = + 'var __webpack_exports__ = __webpack_require__.x();'; + +const isPromiseLike = (value: unknown): value is Promise => + Boolean(value) && + (typeof value === 'object' || typeof value === 'function') && + 'then' in (value as Promise) && + typeof (value as Promise).then === 'function'; + +/** + * MF async-node startup loader strategy. + * Handles bundles built with Module Federation experiments.asyncStartup + * that use __webpack_require__.mfAsyncStartup for deferred initialization. + * Patches the async startup call to run synchronously when the default + * require path returns a resolved promise with undefined. + */ +export const mfAsyncStartupLoaderStrategy: BundleLoaderStrategy = async ( + filepath, + context, +): Promise => { + try { + const bundleCode = await fs.readFile(filepath, 'utf-8'); + + if ( + !bundleCode.includes(ASYNC_NODE_STARTUP_CALL) || + !bundleCode.includes('__webpack_require__.mfAsyncStartup') + ) { + return undefined; + } + + const patchedCode = bundleCode.replace( + ASYNC_NODE_STARTUP_CALL, + 'var __webpack_exports__ = __webpack_require__.x({}, []);', + ); + + const localModule: { exports: unknown } = { exports: {} }; + const wrapped = `(function(exports, require, module, __filename, __dirname){${patchedCode}\n})`; + const runBundle = vm.runInThisContext(wrapped, { filename: filepath }) as ( + exports: unknown, + require: NodeJS.Require, + module: { exports: unknown }, + __filename: string, + __dirname: string, + ) => void; + + runBundle( + localModule.exports, + require, + localModule, + filepath, + path.dirname(filepath), + ); + + if (isPromiseLike(localModule.exports)) { + return await localModule.exports; + } + + return localModule.exports; + } catch (error) { + context?.monitors?.error?.( + '[MF] Load async startup bundle strategy failed, error = %s', + error instanceof Error ? error.stack || error.message : error, + ); + return undefined; + } +}; diff --git a/packages/modernjs-v3/src/server/index.ts b/packages/modernjs-v3/src/server/index.ts index 84ab23928cc..897a461649a 100644 --- a/packages/modernjs-v3/src/server/index.ts +++ b/packages/modernjs-v3/src/server/index.ts @@ -1,4 +1,5 @@ import type { ServerPlugin } from '@modern-js/server-runtime'; +import { mfAsyncStartupLoaderStrategy } from './asyncStartupLoader'; import { createCorsMiddleware, createStaticMiddleware, @@ -7,6 +8,17 @@ import { const staticServePlugin = (): ServerPlugin => ({ name: '@modern-js/module-federation/server', setup: (api) => { + try { + const { + registerBundleLoaderStrategy, + } = require('@modern-js/server-core/node'); + if (typeof registerBundleLoaderStrategy === 'function') { + registerBundleLoaderStrategy(mfAsyncStartupLoaderStrategy); + } + } catch { + // registerBundleLoaderStrategy may not exist in all @modern-js/server-core versions + } + api.onPrepare(() => { // In development, we don't need to serve the manifest file, bundler dev server will handle it if (process.env.NODE_ENV === 'development') { diff --git a/packages/modernjs-v3/src/server/server-core-node.d.ts b/packages/modernjs-v3/src/server/server-core-node.d.ts new file mode 100644 index 00000000000..3adf89a7b5a --- /dev/null +++ b/packages/modernjs-v3/src/server/server-core-node.d.ts @@ -0,0 +1,17 @@ +/** + * Type declarations for @modern-js/server-core/node when the published + * package may not yet export registerBundleLoaderStrategy / BundleLoaderStrategy. + * Used for RSC + async startup loader strategy registration. + */ +declare module '@modern-js/server-core/node' { + export type BundleLoaderStrategy = ( + filepath: string, + context?: { + monitors?: { error?: (msg: string, ...args: unknown[]) => void }; + }, + ) => Promise; + + export function registerBundleLoaderStrategy( + strategy: BundleLoaderStrategy, + ): void; +} diff --git a/packages/modernjs-v3/src/server/staticMiddleware.spec.ts b/packages/modernjs-v3/src/server/staticMiddleware.spec.ts index 5908c694daf..4949c995fc0 100644 --- a/packages/modernjs-v3/src/server/staticMiddleware.spec.ts +++ b/packages/modernjs-v3/src/server/staticMiddleware.spec.ts @@ -87,6 +87,17 @@ describe('staticMiddleware', () => { expect(mockContext.body).not.toHaveBeenCalled(); }); + it('should call next() for paths that only share /bundles prefix', async () => { + mockContext.req.path = '/bundlesevil/test.js'; + + await middleware(mockContext, nextSpy); + + expect(nextSpy).toHaveBeenCalledOnce(); + expect(fs.pathExists).not.toHaveBeenCalled(); + expect(mockContext.header).not.toHaveBeenCalled(); + expect(mockContext.body).not.toHaveBeenCalled(); + }); + it('should call next() for root path', async () => { mockContext.req.path = '/test.js'; @@ -167,7 +178,7 @@ describe('staticMiddleware', () => { ); expect(mockContext.header).toHaveBeenCalledWith( 'Content-Length', - String(mockFileResult.content.length), + String(Buffer.byteLength(mockFileResult.content)), ); // Check response @@ -178,6 +189,26 @@ describe('staticMiddleware', () => { expect(result).toBe('response'); }); + it('should set content-length in bytes for unicode content', async () => { + const mockFileResult = { + content: '你', + lastModified: Date.now(), + }; + + mockContext.req.path = '/bundles/unicode.js'; + (fs.pathExists as any).mockResolvedValue(true); + (fileCache.getFile as any).mockResolvedValue(mockFileResult); + mockContext.body.mockReturnValue('unicode-response'); + + await middleware(mockContext, nextSpy); + + expect(mockContext.header).toHaveBeenCalledWith( + 'Content-Length', + String(Buffer.byteLength(mockFileResult.content)), + ); + expect(nextSpy).not.toHaveBeenCalled(); + }); + it('should handle empty file content', async () => { const mockFileResult = { content: '', diff --git a/packages/modernjs-v3/src/server/staticMiddleware.ts b/packages/modernjs-v3/src/server/staticMiddleware.ts index ca0ee4e0b30..335ce736f67 100644 --- a/packages/modernjs-v3/src/server/staticMiddleware.ts +++ b/packages/modernjs-v3/src/server/staticMiddleware.ts @@ -26,24 +26,37 @@ const createStaticMiddleware = (options: { pwd: string; }): MiddlewareHandler => { const { assetPrefix, pwd } = options; + const bundlesRootDir = path.resolve(pwd, `.${bundlesAssetPrefix}`); return async (c, next) => { const pathname = c.req.path; + const extension = path.extname(pathname); - // We only handle js file for performance - if (path.extname(pathname) !== '.js') { + // Only serve assets required by server-side federation runtime. + if (extension !== '.js' && extension !== '.json') { return next(); } const prefixWithoutHost = removeHost(assetPrefix); - const prefixWithBundle = path.join(prefixWithoutHost, bundlesAssetPrefix); + const prefixWithBundle = path.posix.join( + prefixWithoutHost || '/', + bundlesAssetPrefix, + ); + const isBundleRequest = + pathname === prefixWithBundle || + pathname.startsWith(`${prefixWithBundle}/`); // Skip if the request is not for asset prefix + `/bundles` - if (!pathname.startsWith(prefixWithBundle)) { + if (!isBundleRequest) { return next(); } - const pathnameWithoutPrefix = pathname.replace(prefixWithBundle, ''); - const filepath = path.join(pwd, bundlesAssetPrefix, pathnameWithoutPrefix); + const pathnameWithoutPrefix = pathname.slice(prefixWithBundle.length); + const relativeBundlePath = pathnameWithoutPrefix.replace(/^\/+/, ''); + const filepath = path.resolve(bundlesRootDir, relativeBundlePath); + const allowedPrefix = `${bundlesRootDir}${path.sep}`; + if (filepath !== bundlesRootDir && !filepath.startsWith(allowedPrefix)) { + return next(); + } if (!(await fs.pathExists(filepath))) { return next(); } @@ -53,8 +66,11 @@ const createStaticMiddleware = (options: { return next(); } - c.header('Content-Type', 'application/javascript'); - c.header('Content-Length', String(fileResult.content.length)); + c.header( + 'Content-Type', + extension === '.json' ? 'application/json' : 'application/javascript', + ); + c.header('Content-Length', String(Buffer.byteLength(fileResult.content))); return c.body(fileResult.content, 200); }; }; diff --git a/packages/modernjs-v3/tsconfig.json b/packages/modernjs-v3/tsconfig.json index 9c6a5722240..844190a8043 100644 --- a/packages/modernjs-v3/tsconfig.json +++ b/packages/modernjs-v3/tsconfig.json @@ -7,5 +7,6 @@ "paths": {}, "noImplicitAny": false }, - "include": ["src", "types", "global.d.ts"] + "include": ["src", "types", "global.d.ts"], + "exclude": ["**/*.spec.*", "**/*.test.*"] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index deb75fb3456..8cd9371c9ac 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,7 +47,7 @@ importers: version: 4.4.2 openai: specifier: ^4.72.0 - version: 4.104.0(encoding@0.1.13)(ws@8.18.0)(zod@3.25.76) + version: 4.104.0(encoding@0.1.13)(ws@8.19.0)(zod@3.25.76) rambda: specifier: 7.5.0 version: 7.5.0 @@ -168,7 +168,7 @@ importers: version: 1.57.0 '@pmmmwh/react-refresh-webpack-plugin': specifier: 0.5.15 - version: 0.5.15(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-dev-server@5.2.3)(webpack-hot-middleware@2.26.1)(webpack@5.104.1) + version: 0.5.15(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack-cli@5.1.4)))(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack-cli@5.1.4)) '@rollup/plugin-alias': specifier: 5.1.1 version: 5.1.1(rollup@4.57.0) @@ -426,7 +426,7 @@ importers: version: 3.5.0 rsbuild-plugin-publint: specifier: ^0.2.1 - version: 0.2.1(@rsbuild/core@1.4.16) + version: 0.2.1(@rsbuild/core@1.7.3) strip-ansi: specifier: ^6.0.0 version: 6.0.1 @@ -438,7 +438,7 @@ importers: version: 3.4.13(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(@types/node@20.19.5)(typescript@5.8.2)) terser-webpack-plugin: specifier: ^5.3.10 - version: 5.3.16(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack@5.104.1) + version: 5.3.16(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack@5.104.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack-cli@5.1.4)) ts-jest: specifier: 29.1.5 version: 29.1.5(@babel/core@7.28.6)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.6))(esbuild@0.25.0)(jest@29.7.0(@types/node@20.19.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(@types/node@20.19.5)(typescript@5.8.2)))(typescript@5.8.2) @@ -3544,13 +3544,13 @@ importers: devDependencies: '@modern-js/app-tools': specifier: 2.70.5 - version: 2.70.5(@rspack/core@1.7.5(@swc/helpers@0.5.18))(@swc/core@1.15.10(@swc/helpers@0.5.18))(encoding@0.1.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.57.0)(styled-components@6.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@22.19.9)(typescript@5.8.2))(tsconfig-paths@4.2.0)(tslib@2.8.1)(type-fest@4.41.0)(typescript@5.8.2)(webpack-cli@5.1.4)(webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack-hot-middleware@2.26.1) + version: 2.70.5(@rspack/core@1.7.5(@swc/helpers@0.5.18))(@swc/core@1.15.10(@swc/helpers@0.5.18))(encoding@0.1.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.57.0)(styled-components@6.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@22.19.9)(typescript@5.8.2))(tsconfig-paths@4.2.0)(tslib@2.8.1)(type-fest@4.41.0)(typescript@5.8.2)(webpack-cli@5.1.4)(webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack-hot-middleware@2.26.1) '@modern-js/module-tools': specifier: 2.70.5 version: 2.70.5(@types/node@22.19.9)(typescript@5.8.2) '@modern-js/runtime': specifier: 2.70.5 - version: 2.70.5(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@19.2.4) + version: 2.70.5(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@19.2.4) '@modern-js/server-runtime': specifier: 2.70.5 version: 2.70.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -3645,6 +3645,9 @@ importers: '@modern-js/runtime': specifier: 3.0.1 version: 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@19.2.4) + '@modern-js/server-core': + specifier: 3.0.1 + version: 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@modern-js/server-runtime': specifier: 3.0.1 version: 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -3965,7 +3968,7 @@ importers: version: link:../utilities '@rsbuild/core': specifier: 2.0.0-beta.2 - version: 2.0.0-beta.2(@module-federation/runtime-tools@0.15.0)(core-js@3.48.0) + version: 2.0.0-beta.2(@module-federation/runtime-tools@0.23.0)(core-js@3.48.0) '@storybook/core': specifier: ^8.4.6 version: 8.6.14(prettier@3.3.3)(storybook@9.0.9(@testing-library/dom@10.4.1)(prettier@3.3.3)) @@ -32240,7 +32243,7 @@ snapshots: diff: 8.0.3 lodash: 4.17.23 minimatch: 10.0.3 - resolve: 1.22.11 + resolve: 1.22.8 semver: 7.5.4 source-map: 0.6.1 typescript: 5.8.2 @@ -32457,7 +32460,7 @@ snapshots: - webpack-hot-middleware - webpack-plugin-serve - '@modern-js/app-tools@2.70.5(@rspack/core@1.7.5(@swc/helpers@0.5.18))(@swc/core@1.15.10(@swc/helpers@0.5.18))(encoding@0.1.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.57.0)(styled-components@6.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@22.19.9)(typescript@5.8.2))(tsconfig-paths@4.2.0)(tslib@2.8.1)(type-fest@4.41.0)(typescript@5.8.2)(webpack-cli@5.1.4)(webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack-hot-middleware@2.26.1)': + '@modern-js/app-tools@2.70.5(@rspack/core@1.7.5(@swc/helpers@0.5.18))(@swc/core@1.15.10(@swc/helpers@0.5.18))(encoding@0.1.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.57.0)(styled-components@6.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@22.19.9)(typescript@5.8.2))(tsconfig-paths@4.2.0)(tslib@2.8.1)(type-fest@4.41.0)(typescript@5.8.2)(webpack-cli@5.1.4)(webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack-hot-middleware@2.26.1)': dependencies: '@babel/parser': 7.28.6 '@babel/traverse': 7.28.6 @@ -32474,7 +32477,7 @@ snapshots: '@modern-js/server-core': 2.70.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@modern-js/server-utils': 2.70.5(@babel/traverse@7.28.6)(@rsbuild/core@1.7.3) '@modern-js/types': 2.70.5 - '@modern-js/uni-builder': 2.70.5(@rspack/core@1.7.5(@swc/helpers@0.5.18))(esbuild@0.25.5)(styled-components@6.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(tslib@2.8.1)(type-fest@4.41.0)(typescript@5.8.2)(webpack-cli@5.1.4)(webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack-hot-middleware@2.26.1) + '@modern-js/uni-builder': 2.70.5(@rspack/core@1.7.5(@swc/helpers@0.5.18))(esbuild@0.25.5)(styled-components@6.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(tslib@2.8.1)(type-fest@4.41.0)(typescript@5.8.2)(webpack-cli@5.1.4)(webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack-hot-middleware@2.26.1) '@modern-js/utils': 2.70.5 '@rsbuild/core': 1.7.3 '@rsbuild/plugin-node-polyfill': 1.4.3(@rsbuild/core@1.7.3) @@ -33277,7 +33280,7 @@ snapshots: react-dom: 19.2.4(react@19.2.4) react-server-dom-webpack: 19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.18.20)(webpack-cli@5.1.4)) - '@modern-js/render@2.70.5(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@19.2.4)': + '@modern-js/render@2.70.5(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@19.2.4)': dependencies: '@modern-js/types': 2.70.5 '@modern-js/utils': 2.70.5 @@ -33420,7 +33423,7 @@ snapshots: - react-server-dom-webpack - supports-color - '@modern-js/runtime@2.70.5(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@19.2.4)': + '@modern-js/runtime@2.70.5(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@19.2.4)': dependencies: '@babel/core': 7.28.6 '@babel/types': 7.28.6 @@ -33430,7 +33433,7 @@ snapshots: '@modern-js/plugin': 2.70.5 '@modern-js/plugin-data-loader': 2.70.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@modern-js/plugin-v2': 2.70.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@modern-js/render': 2.70.5(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@19.2.4) + '@modern-js/render': 2.70.5(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@19.2.4) '@modern-js/runtime-utils': 2.70.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@modern-js/types': 2.70.5 '@modern-js/utils': 2.70.5 @@ -34123,7 +34126,7 @@ snapshots: - webpack-hot-middleware - webpack-plugin-serve - '@modern-js/uni-builder@2.70.5(@rspack/core@1.7.5(@swc/helpers@0.5.18))(esbuild@0.25.5)(styled-components@6.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(tslib@2.8.1)(type-fest@4.41.0)(typescript@5.8.2)(webpack-cli@5.1.4)(webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack-hot-middleware@2.26.1)': + '@modern-js/uni-builder@2.70.5(@rspack/core@1.7.5(@swc/helpers@0.5.18))(esbuild@0.25.5)(styled-components@6.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(tslib@2.8.1)(type-fest@4.41.0)(typescript@5.8.2)(webpack-cli@5.1.4)(webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack-hot-middleware@2.26.1)': dependencies: '@babel/core': 7.28.6 '@babel/preset-react': 7.28.5(@babel/core@7.28.6) @@ -34131,12 +34134,12 @@ snapshots: '@modern-js/babel-preset': 2.70.5(@rsbuild/core@1.7.3) '@modern-js/flight-server-transform-plugin': 2.70.5 '@modern-js/utils': 2.70.5 - '@pmmmwh/react-refresh-webpack-plugin': 0.5.16(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + '@pmmmwh/react-refresh-webpack-plugin': 0.5.16(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) '@rsbuild/core': 1.7.3 '@rsbuild/plugin-assets-retry': 1.5.2(@rsbuild/core@1.7.3) '@rsbuild/plugin-babel': 1.1.0(@rsbuild/core@1.7.3) '@rsbuild/plugin-check-syntax': 1.6.1(@rsbuild/core@1.7.3) - '@rsbuild/plugin-css-minimizer': 1.1.1(@rsbuild/core@1.7.3)(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + '@rsbuild/plugin-css-minimizer': 1.1.1(@rsbuild/core@1.7.3)(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) '@rsbuild/plugin-less': 1.6.0(@rsbuild/core@1.7.3) '@rsbuild/plugin-pug': 1.3.2(@rsbuild/core@1.7.3) '@rsbuild/plugin-react': 1.4.5(@rsbuild/core@1.7.3)(webpack-hot-middleware@2.26.1) @@ -34153,7 +34156,7 @@ snapshots: '@swc/core': 1.15.8(@swc/helpers@0.5.18) '@swc/helpers': 0.5.18 autoprefixer: 10.4.23(postcss@8.5.6) - babel-loader: 9.2.1(@babel/core@7.28.6)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + babel-loader: 9.2.1(@babel/core@7.28.6)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) babel-plugin-import: 1.13.8 babel-plugin-styled-components: 1.13.3(styled-components@6.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) babel-plugin-transform-react-remove-prop-types: 0.4.24 @@ -34162,7 +34165,7 @@ snapshots: es-module-lexer: 1.7.0 glob: 9.3.5 html-minifier-terser: 7.2.0 - html-webpack-plugin: 5.6.6(@rspack/core@1.7.5(@swc/helpers@0.5.18))(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + html-webpack-plugin: 5.6.6(@rspack/core@1.7.5(@swc/helpers@0.5.18))(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) jiti: 1.21.7 lodash: 4.17.23 magic-string: 0.30.21 @@ -34177,11 +34180,11 @@ snapshots: postcss-page-break: 3.0.4(postcss@8.5.6) react-refresh: 0.14.2 rspack-manifest-plugin: 5.0.3(@rspack/core@1.7.5(@swc/helpers@0.5.18)) - terser-webpack-plugin: 5.3.14(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + terser-webpack-plugin: 5.3.14(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) ts-deepmerge: 7.0.2 - ts-loader: 9.4.4(typescript@5.8.2)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) - webpack: 5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4) - webpack-subresource-integrity: 5.1.0(html-webpack-plugin@5.6.6(@rspack/core@1.7.5(@swc/helpers@0.5.18))(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + ts-loader: 9.4.4(typescript@5.8.2)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4) + webpack-subresource-integrity: 5.1.0(html-webpack-plugin@5.6.6(@rspack/core@1.7.5(@swc/helpers@0.5.18))(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) transitivePeerDependencies: - '@parcel/css' - '@rspack/core' @@ -35722,7 +35725,7 @@ snapshots: '@phenomnomnominal/tsquery': 5.0.1(typescript@5.8.2) '@svgr/webpack': 8.1.0(typescript@5.8.2) express: 4.21.2 - file-loader: 6.2.0(webpack@5.104.1) + file-loader: 6.2.0(webpack@5.104.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack-cli@5.1.4)) http-proxy-middleware: 3.0.5 minimatch: 9.0.3 picocolors: 1.1.1 @@ -36386,22 +36389,6 @@ snapshots: webpack-dev-server: 5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack-cli@5.1.4)) webpack-hot-middleware: 2.26.1 - '@pmmmwh/react-refresh-webpack-plugin@0.5.15(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-dev-server@5.2.3)(webpack-hot-middleware@2.26.1)(webpack@5.104.1)': - dependencies: - ansi-html: 0.0.9 - core-js-pure: 3.48.0 - error-stack-parser: 2.1.4 - html-entities: 2.6.0 - loader-utils: 2.0.4 - react-refresh: 0.14.2 - schema-utils: 4.3.3 - source-map: 0.7.6 - webpack: 5.104.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack-cli@5.1.4) - optionalDependencies: - type-fest: 4.41.0 - webpack-dev-server: 5.2.3(webpack-cli@5.1.4)(webpack@5.104.1) - webpack-hot-middleware: 2.26.1 - '@pmmmwh/react-refresh-webpack-plugin@0.5.16(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.18.20)(webpack-cli@5.1.4)))(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.18.20)(webpack-cli@5.1.4))': dependencies: ansi-html: 0.0.9 @@ -36418,7 +36405,7 @@ snapshots: webpack-dev-server: 5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.18.20)(webpack-cli@5.1.4)) webpack-hot-middleware: 2.26.1 - '@pmmmwh/react-refresh-webpack-plugin@0.5.16(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4))': + '@pmmmwh/react-refresh-webpack-plugin@0.5.16(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4))': dependencies: ansi-html: 0.0.9 core-js-pure: 3.48.0 @@ -36431,7 +36418,7 @@ snapshots: webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4) optionalDependencies: type-fest: 4.41.0 - webpack-dev-server: 5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + webpack-dev-server: 5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) webpack-hot-middleware: 2.26.1 '@pnpm/config.env-replace@1.1.0': {} @@ -39180,9 +39167,9 @@ snapshots: core-js: 3.47.0 jiti: 2.6.1 - '@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@0.15.0)(core-js@3.48.0)': + '@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@0.23.0)(core-js@3.48.0)': dependencies: - '@rspack/core': 2.0.0-beta.0(@module-federation/runtime-tools@0.15.0)(@swc/helpers@0.5.18) + '@rspack/core': 2.0.0-beta.0(@module-federation/runtime-tools@0.23.0)(@swc/helpers@0.5.18) '@swc/helpers': 0.5.18 jiti: 2.6.1 optionalDependencies: @@ -39338,7 +39325,7 @@ snapshots: - lightningcss - webpack - '@rsbuild/plugin-css-minimizer@1.1.1(@rsbuild/core@1.7.3)(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4))': + '@rsbuild/plugin-css-minimizer@1.1.1(@rsbuild/core@1.7.3)(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4))': dependencies: css-minimizer-webpack-plugin: 7.0.2(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) reduce-configs: 1.1.1 @@ -39836,9 +39823,9 @@ snapshots: '@rsbuild/webpack@1.6.1(@rsbuild/core@1.7.3)(@rspack/core@1.7.5(@swc/helpers@0.5.18))(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)': dependencies: '@rsbuild/core': 1.7.3 - copy-webpack-plugin: 11.0.0(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) - html-webpack-plugin: 5.6.6(@rspack/core@1.7.5(@swc/helpers@0.5.18))(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) - mini-css-extract-plugin: 2.9.4(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + copy-webpack-plugin: 11.0.0(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + html-webpack-plugin: 5.6.6(@rspack/core@1.7.5(@swc/helpers@0.5.18))(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + mini-css-extract-plugin: 2.9.4(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) picocolors: 1.1.1 reduce-configs: 1.1.1 tsconfig-paths-webpack-plugin: 4.2.0 @@ -40420,12 +40407,12 @@ snapshots: optionalDependencies: '@swc/helpers': 0.5.18 - '@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@0.15.0)(@swc/helpers@0.5.18)': + '@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@0.23.0)(@swc/helpers@0.5.18)': dependencies: '@rspack/binding': 2.0.0-beta.0 '@rspack/lite-tapable': 1.1.0 optionalDependencies: - '@module-federation/runtime-tools': 0.15.0 + '@module-federation/runtime-tools': 0.23.0 '@swc/helpers': 0.5.18 '@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18)': @@ -40444,7 +40431,7 @@ snapshots: http-proxy-middleware: 2.0.9(@types/express@4.17.21) mime-types: 2.1.35 p-retry: 6.2.1 - webpack-dev-middleware: 7.4.5(webpack@5.104.1) + webpack-dev-middleware: 7.4.5(webpack@5.104.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack-cli@5.1.4)) webpack-dev-server: 5.2.0(webpack-cli@5.1.4)(webpack@5.104.1) ws: 8.18.0 transitivePeerDependencies: @@ -40644,7 +40631,7 @@ snapshots: fs-extra: 11.3.0 import-lazy: 4.0.0 jju: 1.4.0 - resolve: 1.22.11 + resolve: 1.22.8 semver: 7.5.4 optionalDependencies: '@types/node': 20.19.5 @@ -40870,7 +40857,7 @@ snapshots: read-pkg: 10.0.0 registry-auth-token: 5.1.1 semantic-release: 25.0.2(typescript@5.8.2) - semver: 7.7.3 + semver: 7.6.3 tempy: 3.2.0 '@semantic-release/release-notes-generator@14.1.0(semantic-release@25.0.2(typescript@5.8.2))': @@ -41026,7 +41013,7 @@ snapshots: magic-string: 0.30.21 storybook: 9.0.9(@testing-library/dom@10.4.1)(prettier@3.3.3) style-loader: 3.3.4(webpack@5.104.1) - terser-webpack-plugin: 5.3.16(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack@5.104.1) + terser-webpack-plugin: 5.3.16(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack@5.104.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack-cli@5.1.4)) ts-dedent: 2.2.0 webpack: 5.104.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack-cli@5.1.4) webpack-dev-middleware: 6.1.3(webpack@5.104.1) @@ -41369,7 +41356,7 @@ snapshots: '@babel/preset-react': 7.28.5(@babel/core@7.28.6) '@babel/preset-typescript': 7.28.5(@babel/core@7.28.6) '@babel/runtime': 7.28.2 - '@pmmmwh/react-refresh-webpack-plugin': 0.5.15(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-dev-server@5.2.3)(webpack-hot-middleware@2.26.1)(webpack@5.104.1) + '@pmmmwh/react-refresh-webpack-plugin': 0.5.15(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack-cli@5.1.4)))(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack-cli@5.1.4)) '@storybook/builder-webpack5': 9.0.9(@rspack/core@1.3.9(@swc/helpers@0.5.13))(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(storybook@9.0.9(@testing-library/dom@10.4.1)(prettier@3.3.3))(typescript@5.8.2)(webpack-cli@5.1.4) '@storybook/preset-react-webpack': 9.0.9(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@9.0.9(@testing-library/dom@10.4.1)(prettier@3.3.3))(typescript@5.8.2)(webpack-cli@5.1.4) '@storybook/react': 9.0.9(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@9.0.9(@testing-library/dom@10.4.1)(prettier@3.3.3))(typescript@5.8.2) @@ -44368,7 +44355,7 @@ snapshots: webpack: 5.104.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack-cli@5.1.4) webpack-cli: 5.1.4(webpack-dev-server@5.2.3)(webpack@5.104.1) optionalDependencies: - webpack-dev-server: 5.2.3(webpack-cli@5.1.4)(webpack@5.104.1) + webpack-dev-server: 5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack-cli@5.1.4)) '@xhmikosr/archive-type@7.1.0': dependencies: @@ -45378,7 +45365,7 @@ snapshots: schema-utils: 4.3.3 webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.18.20)(webpack-cli@5.1.4) - babel-loader@9.2.1(@babel/core@7.28.6)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): + babel-loader@9.2.1(@babel/core@7.28.6)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): dependencies: '@babel/core': 7.28.6 find-cache-dir: 4.0.0 @@ -46632,9 +46619,9 @@ snapshots: conventional-changelog-writer@8.2.0: dependencies: conventional-commits-filter: 5.0.0 - handlebars: 4.7.8 + handlebars: 4.7.7 meow: 13.2.0 - semver: 7.7.3 + semver: 7.6.3 conventional-commit-types@3.0.0: {} @@ -46718,7 +46705,7 @@ snapshots: serialize-javascript: 6.0.2 webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.18.20)(webpack-cli@5.1.4) - copy-webpack-plugin@11.0.0(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): + copy-webpack-plugin@11.0.0(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): dependencies: fast-glob: 3.3.2 glob-parent: 6.0.2 @@ -49699,12 +49686,6 @@ snapshots: schema-utils: 3.3.0 webpack: 5.104.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack-cli@5.1.4) - file-loader@6.2.0(webpack@5.104.1): - dependencies: - loader-utils: 2.0.4 - schema-utils: 3.3.0 - webpack: 5.104.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack-cli@5.1.4) - file-system-cache@2.3.0: dependencies: fs-extra: 11.1.1 @@ -50917,7 +50898,7 @@ snapshots: '@rspack/core': 1.7.5(@swc/helpers@0.5.18) webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.18.20)(webpack-cli@5.1.4) - html-webpack-plugin@5.6.6(@rspack/core@1.7.5(@swc/helpers@0.5.18))(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): + html-webpack-plugin@5.6.6(@rspack/core@1.7.5(@swc/helpers@0.5.18))(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): dependencies: '@types/html-minifier-terser': 6.1.0 html-minifier-terser: 6.1.0 @@ -53930,7 +53911,7 @@ snapshots: tapable: 2.2.1 webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.18.20)(webpack-cli@5.1.4) - mini-css-extract-plugin@2.9.4(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): + mini-css-extract-plugin@2.9.4(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): dependencies: schema-utils: 4.3.3 tapable: 2.2.1 @@ -54257,7 +54238,7 @@ snapshots: '@next/env': 14.2.35 '@swc/helpers': 0.5.5 busboy: 1.6.0 - caniuse-lite: 1.0.30001769 + caniuse-lite: 1.0.30001766 graceful-fs: 4.2.11 postcss: 8.4.31 react: 19.2.4 @@ -54451,7 +54432,7 @@ snapshots: normalize-package-data@8.0.0: dependencies: hosted-git-info: 9.0.2 - semver: 7.7.3 + semver: 7.6.3 validate-npm-package-license: 3.0.4 normalize-path@2.1.1: @@ -54796,7 +54777,7 @@ snapshots: is-docker: 2.2.1 is-wsl: 2.2.0 - openai@4.104.0(encoding@0.1.13)(ws@8.18.0)(zod@3.25.76): + openai@4.104.0(encoding@0.1.13)(ws@8.19.0)(zod@3.25.76): dependencies: '@types/node': 18.16.9 '@types/node-fetch': 2.6.11 @@ -54806,7 +54787,7 @@ snapshots: formdata-node: 4.4.1 node-fetch: 2.7.0(encoding@0.1.13) optionalDependencies: - ws: 8.18.0 + ws: 8.19.0 zod: 3.25.76 transitivePeerDependencies: - encoding @@ -59206,13 +59187,6 @@ snapshots: optionalDependencies: '@rsbuild/core': 2.0.0-beta.3(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0) - rsbuild-plugin-publint@0.2.1(@rsbuild/core@1.4.16): - dependencies: - picocolors: 1.1.1 - publint: 0.2.12 - optionalDependencies: - '@rsbuild/core': 1.4.16 - rsbuild-plugin-publint@0.2.1(@rsbuild/core@1.7.3): dependencies: picocolors: 1.1.1 @@ -59524,7 +59498,7 @@ snapshots: p-reduce: 3.0.0 read-package-up: 12.0.0 resolve-from: 5.0.0 - semver: 7.7.3 + semver: 7.6.3 semver-diff: 5.0.0 signale: 1.4.0 yargs: 18.0.0 @@ -59534,7 +59508,7 @@ snapshots: semver-diff@5.0.0: dependencies: - semver: 7.7.3 + semver: 7.6.3 semver-regex@4.0.5: {} @@ -61019,7 +60993,7 @@ snapshots: '@swc/core': 1.15.8(@swc/helpers@0.5.18) esbuild: 0.25.5 - terser-webpack-plugin@5.3.14(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): + terser-webpack-plugin@5.3.14(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 @@ -61079,7 +61053,7 @@ snapshots: '@swc/core': 1.15.10(@swc/helpers@0.5.18) esbuild: 0.25.0 - terser-webpack-plugin@5.3.16(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): + terser-webpack-plugin@5.3.16(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 @@ -61115,7 +61089,7 @@ snapshots: '@swc/core': 1.15.8(@swc/helpers@0.5.18) esbuild: 0.25.5 - terser-webpack-plugin@5.3.16(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack@5.104.1): + terser-webpack-plugin@5.3.16(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack@5.104.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack-cli@5.1.4)): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 @@ -61504,7 +61478,7 @@ snapshots: babel-jest: 29.7.0(@babel/core@7.28.6) esbuild: 0.25.0 - ts-loader@9.4.4(typescript@5.8.2)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): + ts-loader@9.4.4(typescript@5.8.2)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): dependencies: chalk: 4.1.2 enhanced-resolve: 5.19.0 @@ -62325,7 +62299,7 @@ snapshots: schema-utils: 3.3.0 webpack: 5.104.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack-cli@5.1.4) optionalDependencies: - file-loader: 6.2.0(webpack@5.104.1) + file-loader: 6.2.0(webpack@5.104.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack-cli@5.1.4)) url-parse@1.5.10: dependencies: @@ -62943,7 +62917,7 @@ snapshots: webpack: 5.104.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack-cli@5.1.4) webpack-merge: 5.10.0 optionalDependencies: - webpack-dev-server: 5.2.3(webpack-cli@5.1.4)(webpack@5.104.1) + webpack-dev-server: 5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack-cli@5.1.4)) webpack-dev-middleware@6.1.3(webpack@5.104.1): dependencies: @@ -62967,7 +62941,7 @@ snapshots: webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.18.20)(webpack-cli@5.1.4) optional: true - webpack-dev-middleware@7.4.5(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): + webpack-dev-middleware@7.4.5(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): dependencies: colorette: 2.0.20 memfs: 4.46.0 @@ -62989,18 +62963,6 @@ snapshots: schema-utils: 4.3.3 optionalDependencies: webpack: 5.104.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack-cli@5.1.4) - optional: true - - webpack-dev-middleware@7.4.5(webpack@5.104.1): - dependencies: - colorette: 2.0.20 - memfs: 4.46.0 - mime-types: 3.0.2 - on-finished: 2.4.1 - range-parser: 1.2.1 - schema-utils: 4.3.3 - optionalDependencies: - webpack: 5.104.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack-cli@5.1.4) webpack-dev-middleware@7.4.5(webpack@5.99.9(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)): dependencies: @@ -63051,7 +63013,7 @@ snapshots: serve-index: 1.9.2 sockjs: 0.3.24 spdy: 4.0.2 - webpack-dev-middleware: 7.4.5(webpack@5.104.1) + webpack-dev-middleware: 7.4.5(webpack@5.104.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack-cli@5.1.4)) ws: 8.18.0 optionalDependencies: webpack: 5.104.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack-cli@5.1.4) @@ -63102,7 +63064,7 @@ snapshots: - utf-8-validate optional: true - webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): + webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): dependencies: '@types/bonjour': 3.5.13 '@types/connect-history-api-fallback': 1.5.4 @@ -63130,7 +63092,7 @@ snapshots: serve-index: 1.9.2 sockjs: 0.3.24 spdy: 4.0.2 - webpack-dev-middleware: 7.4.5(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + webpack-dev-middleware: 7.4.5(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) ws: 8.18.0 optionalDependencies: webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4) @@ -63182,46 +63144,6 @@ snapshots: - utf-8-validate optional: true - webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1): - dependencies: - '@types/bonjour': 3.5.13 - '@types/connect-history-api-fallback': 1.5.4 - '@types/express': 4.17.25 - '@types/express-serve-static-core': 4.19.8 - '@types/serve-index': 1.9.4 - '@types/serve-static': 1.15.10 - '@types/sockjs': 0.3.36 - '@types/ws': 8.5.12 - ansi-html-community: 0.0.8 - bonjour-service: 1.3.0 - chokidar: 3.6.0 - colorette: 2.0.20 - compression: 1.8.1 - connect-history-api-fallback: 2.0.0 - express: 4.22.1 - graceful-fs: 4.2.11 - http-proxy-middleware: 2.0.9(@types/express@4.17.25) - ipaddr.js: 2.3.0 - launch-editor: 2.12.0 - open: 10.2.0 - p-retry: 6.2.1 - schema-utils: 4.3.3 - selfsigned: 5.5.0 - serve-index: 1.9.2 - sockjs: 0.3.24 - spdy: 4.0.2 - webpack-dev-middleware: 7.4.5(webpack@5.104.1) - ws: 8.18.0 - optionalDependencies: - webpack: 5.104.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack-cli@5.1.4) - webpack-cli: 5.1.4(webpack-dev-server@5.2.3)(webpack@5.104.1) - transitivePeerDependencies: - - bufferutil - - debug - - supports-color - - utf-8-validate - optional: true - webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.99.9(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)): dependencies: '@types/bonjour': 3.5.13 @@ -63339,12 +63261,12 @@ snapshots: optionalDependencies: html-webpack-plugin: 5.6.6(@rspack/core@1.3.9(@swc/helpers@0.5.18))(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)) - webpack-subresource-integrity@5.1.0(html-webpack-plugin@5.6.6(@rspack/core@1.7.5(@swc/helpers@0.5.18))(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): + webpack-subresource-integrity@5.1.0(html-webpack-plugin@5.6.6(@rspack/core@1.7.5(@swc/helpers@0.5.18))(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): dependencies: typed-assert: 1.0.9 webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4) optionalDependencies: - html-webpack-plugin: 5.6.6(@rspack/core@1.7.5(@swc/helpers@0.5.18))(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + html-webpack-plugin: 5.6.6(@rspack/core@1.7.5(@swc/helpers@0.5.18))(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) webpack-virtual-modules@0.6.2: {} @@ -63474,7 +63396,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.0 - terser-webpack-plugin: 5.3.16(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + terser-webpack-plugin: 5.3.16(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) watchpack: 2.5.1 webpack-sources: 3.3.3 optionalDependencies: @@ -63576,7 +63498,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.0 - terser-webpack-plugin: 5.3.16(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack@5.104.1) + terser-webpack-plugin: 5.3.16(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack@5.104.1(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.25.0)(webpack-cli@5.1.4)) watchpack: 2.5.1 webpack-sources: 3.3.3 optionalDependencies: From 5c1d98be764c005d7bb2933def35b266e424fec1 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Fri, 20 Feb 2026 17:49:08 -0800 Subject: [PATCH 02/17] chore(modern-js-plugin-v3): refresh lockfile for server-core dep Co-authored-by: Cursor --- pnpm-lock.yaml | 202 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 189 insertions(+), 13 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8cd9371c9ac..a157e3169c0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1144,7 +1144,7 @@ importers: version: 7.28.2 '@modern-js/runtime': specifier: 3.0.1 - version: 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@18.3.1(react@18.3.1))(react-server-dom-webpack@19.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4(webpack-dev-server@5.2.3)(webpack@5.104.1))))(react@18.3.1) + version: 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@18.3.1(react@18.3.1))(react-server-dom-webpack@19.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)))(react@18.3.1) '@module-federation/modern-js-v3': specifier: workspace:* version: link:../../../packages/modernjs-v3 @@ -1163,7 +1163,7 @@ importers: version: 2.59.0(typescript@5.0.4) '@modern-js/app-tools': specifier: 3.0.1 - version: 3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(core-js@3.48.0)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.57.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@20.19.5)(typescript@5.0.4))(tsconfig-paths@4.2.0)(tslib@2.8.1)(typescript@5.0.4)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4(webpack-dev-server@5.2.3)(webpack@5.104.1))) + version: 3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(core-js@3.48.0)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.57.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@20.19.5)(typescript@5.0.4))(tsconfig-paths@4.2.0)(tslib@2.8.1)(typescript@5.0.4)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)) '@modern-js/eslint-config': specifier: 2.59.0 version: 2.59.0(typescript@5.0.4) @@ -3578,6 +3578,9 @@ importers: packages/modernjs-v3: dependencies: + '@modern-js/server-core': + specifier: 3.0.1 + version: 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@module-federation/bridge-react': specifier: workspace:* version: link:../bridge/bridge-react @@ -3645,9 +3648,6 @@ importers: '@modern-js/runtime': specifier: 3.0.1 version: 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@19.2.4) - '@modern-js/server-core': - specifier: 3.0.1 - version: 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@modern-js/server-runtime': specifier: 3.0.1 version: 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -3968,7 +3968,7 @@ importers: version: link:../utilities '@rsbuild/core': specifier: 2.0.0-beta.2 - version: 2.0.0-beta.2(@module-federation/runtime-tools@0.23.0)(core-js@3.48.0) + version: 2.0.0-beta.2(@module-federation/runtime-tools@0.15.0)(core-js@3.48.0) '@storybook/core': specifier: ^8.4.6 version: 8.6.14(prettier@3.3.3)(storybook@9.0.9(@testing-library/dom@10.4.1)(prettier@3.3.3)) @@ -32624,6 +32624,57 @@ snapshots: - webpack - webpack-hot-middleware + '@modern-js/app-tools@3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(core-js@3.48.0)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.57.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@20.19.5)(typescript@5.0.4))(tsconfig-paths@4.2.0)(tslib@2.8.1)(typescript@5.0.4)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4))': + dependencies: + '@babel/parser': 7.28.6 + '@babel/traverse': 7.28.6 + '@babel/types': 7.28.6 + '@modern-js/builder': 3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(esbuild@0.25.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1)(typescript@5.0.4)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)) + '@modern-js/i18n-utils': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@modern-js/plugin': 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@modern-js/plugin-data-loader': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@modern-js/prod-server': 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@modern-js/server': 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@20.19.5)(typescript@5.0.4))(tsconfig-paths@4.2.0) + '@modern-js/server-core': 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@modern-js/server-utils': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@modern-js/types': 3.0.1 + '@modern-js/utils': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@rsbuild/core': 2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0) + '@swc/helpers': 0.5.18 + es-module-lexer: 1.7.0 + esbuild: 0.25.5 + esbuild-register: 3.6.0(esbuild@0.25.5) + flatted: 3.3.3 + mlly: 1.8.0 + ndepe: 0.1.13(encoding@0.1.13)(rollup@4.57.0) + pkg-types: 1.3.1 + std-env: 3.10.0 + optionalDependencies: + ts-node: 10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@20.19.5)(typescript@5.0.4) + tsconfig-paths: 4.2.0 + transitivePeerDependencies: + - '@module-federation/runtime-tools' + - '@parcel/css' + - '@rspack/core' + - '@swc/css' + - bufferutil + - clean-css + - core-js + - csso + - debug + - devcert + - encoding + - lightningcss + - react + - react-dom + - rollup + - supports-color + - tslib + - typescript + - utf-8-validate + - webpack + - webpack-hot-middleware + '@modern-js/app-tools@3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(core-js@3.48.0)(encoding@0.1.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.57.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@22.19.9)(typescript@5.8.2))(tsconfig-paths@4.2.0)(tslib@2.8.1)(typescript@5.8.2)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4))': dependencies: '@babel/parser': 7.28.6 @@ -32858,6 +32909,57 @@ snapshots: - webpack - webpack-hot-middleware + '@modern-js/builder@3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(esbuild@0.25.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1)(typescript@5.0.4)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4))': + dependencies: + '@modern-js/flight-server-transform-plugin': 3.0.1 + '@modern-js/utils': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@rsbuild/core': 2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0) + '@rsbuild/plugin-assets-retry': 1.5.1(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)) + '@rsbuild/plugin-check-syntax': 1.6.1(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)) + '@rsbuild/plugin-css-minimizer': 1.1.1(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0))(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)) + '@rsbuild/plugin-less': 1.6.0(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)) + '@rsbuild/plugin-react': 1.4.4(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0))(webpack-hot-middleware@2.26.1) + '@rsbuild/plugin-rem': 1.0.5(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)) + '@rsbuild/plugin-sass': 1.5.0(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)) + '@rsbuild/plugin-source-build': 1.0.4(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)) + '@rsbuild/plugin-svgr': 1.3.0(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0))(typescript@5.0.4)(webpack-hot-middleware@2.26.1) + '@rsbuild/plugin-type-check': 1.3.3(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0))(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(tslib@2.8.1)(typescript@5.0.4) + '@rsbuild/plugin-typed-css-modules': 1.2.1(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)) + '@swc/core': 1.15.10(@swc/helpers@0.5.18) + '@swc/helpers': 0.5.18 + autoprefixer: 10.4.24(postcss@8.5.6) + browserslist: 4.28.1 + core-js: 3.48.0 + cssnano: 6.1.2(postcss@8.5.6) + html-minifier-terser: 7.2.0 + lodash: 4.17.23 + postcss: 8.5.6 + postcss-custom-properties: 13.3.12(postcss@8.5.6) + postcss-flexbugs-fixes: 5.0.2(postcss@8.5.6) + postcss-font-variant: 5.0.0(postcss@8.5.6) + postcss-initial: 4.0.1(postcss@8.5.6) + postcss-media-minmax: 5.0.0(postcss@8.5.6) + postcss-nesting: 12.1.5(postcss@8.5.6) + postcss-page-break: 3.0.4(postcss@8.5.6) + rspack-manifest-plugin: 5.2.1(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18)) + ts-deepmerge: 7.0.3 + transitivePeerDependencies: + - '@module-federation/runtime-tools' + - '@parcel/css' + - '@rspack/core' + - '@swc/css' + - clean-css + - csso + - esbuild + - lightningcss + - react + - react-dom + - supports-color + - tslib + - typescript + - webpack + - webpack-hot-middleware + '@modern-js/builder@3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(esbuild@0.25.5)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tslib@2.8.1)(typescript@5.8.2)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4))': dependencies: '@modern-js/flight-server-transform-plugin': 3.0.1 @@ -33287,7 +33389,7 @@ snapshots: '@swc/helpers': 0.5.18 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - react-server-dom-webpack: 19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + react-server-dom-webpack: 19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) '@modern-js/render@3.0.1(react-dom@18.3.1(react@18.3.1))(react-server-dom-webpack@19.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4(webpack-dev-server@5.2.3)(webpack@5.104.1))))(react@18.3.1)': dependencies: @@ -33298,6 +33400,15 @@ snapshots: react-dom: 18.3.1(react@18.3.1) react-server-dom-webpack: 19.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4(webpack-dev-server@5.2.3)(webpack@5.104.1))) + '@modern-js/render@3.0.1(react-dom@18.3.1(react@18.3.1))(react-server-dom-webpack@19.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)))(react@18.3.1)': + dependencies: + '@modern-js/types': 3.0.1 + '@modern-js/utils': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@swc/helpers': 0.5.18 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-server-dom-webpack: 19.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)) + '@modern-js/render@3.0.1(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@19.2.4)': dependencies: '@modern-js/types': 3.0.1 @@ -33305,7 +33416,7 @@ snapshots: '@swc/helpers': 0.5.18 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - react-server-dom-webpack: 19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + react-server-dom-webpack: 19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) '@modern-js/rsbuild-plugin-esbuild@2.70.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(webpack-cli@5.1.4)': dependencies: @@ -33484,6 +33595,35 @@ snapshots: - core-js - react-server-dom-webpack + '@modern-js/runtime@3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@18.3.1(react@18.3.1))(react-server-dom-webpack@19.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)))(react@18.3.1)': + dependencies: + '@loadable/component': 5.16.7(react@18.3.1) + '@loadable/server': 5.16.7(@loadable/component@5.16.7(react@18.3.1))(react@18.3.1) + '@modern-js/plugin': 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@modern-js/plugin-data-loader': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@modern-js/render': 3.0.1(react-dom@18.3.1(react@18.3.1))(react-server-dom-webpack@19.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)))(react@18.3.1) + '@modern-js/runtime-utils': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@modern-js/types': 3.0.1 + '@modern-js/utils': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@swc/helpers': 0.5.18 + '@swc/plugin-loadable-components': 11.5.0 + '@types/loadable__component': 5.13.10 + '@types/react-helmet': 6.1.11 + cookie: 0.7.2 + entities: 7.0.1 + es-module-lexer: 1.7.0 + esbuild: 0.25.5 + invariant: 2.2.4 + isbot: 3.8.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-helmet: 6.1.0(react@18.3.1) + react-is: 18.3.1 + transitivePeerDependencies: + - '@module-federation/runtime-tools' + - core-js + - react-server-dom-webpack + '@modern-js/runtime@3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@19.2.4)': dependencies: '@loadable/component': 5.16.7(react@19.2.4) @@ -39167,9 +39307,9 @@ snapshots: core-js: 3.47.0 jiti: 2.6.1 - '@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@0.23.0)(core-js@3.48.0)': + '@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@0.15.0)(core-js@3.48.0)': dependencies: - '@rspack/core': 2.0.0-beta.0(@module-federation/runtime-tools@0.23.0)(@swc/helpers@0.5.18) + '@rspack/core': 2.0.0-beta.0(@module-federation/runtime-tools@0.15.0)(@swc/helpers@0.5.18) '@swc/helpers': 0.5.18 jiti: 2.6.1 optionalDependencies: @@ -39355,6 +39495,21 @@ snapshots: - lightningcss - webpack + '@rsbuild/plugin-css-minimizer@1.1.1(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0))(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4))': + dependencies: + css-minimizer-webpack-plugin: 7.0.2(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)) + reduce-configs: 1.1.1 + optionalDependencies: + '@rsbuild/core': 2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0) + transitivePeerDependencies: + - '@parcel/css' + - '@swc/css' + - clean-css + - csso + - esbuild + - lightningcss + - webpack + '@rsbuild/plugin-css-minimizer@1.1.1(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0))(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4))': dependencies: css-minimizer-webpack-plugin: 7.0.2(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) @@ -40407,12 +40562,12 @@ snapshots: optionalDependencies: '@swc/helpers': 0.5.18 - '@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@0.23.0)(@swc/helpers@0.5.18)': + '@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@0.15.0)(@swc/helpers@0.5.18)': dependencies: '@rspack/binding': 2.0.0-beta.0 '@rspack/lite-tapable': 1.1.0 optionalDependencies: - '@module-federation/runtime-tools': 0.23.0 + '@module-federation/runtime-tools': 0.15.0 '@swc/helpers': 0.5.18 '@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18)': @@ -47075,6 +47230,18 @@ snapshots: optionalDependencies: esbuild: 0.25.5 + css-minimizer-webpack-plugin@7.0.2(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + cssnano: 7.1.2(postcss@8.4.49) + jest-worker: 29.7.0 + postcss: 8.4.49 + schema-utils: 4.3.3 + serialize-javascript: 6.0.2 + webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4) + optionalDependencies: + esbuild: 0.25.5 + css-minimizer-webpack-plugin@7.0.2(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -58386,6 +58553,15 @@ snapshots: webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4(webpack-dev-server@5.2.3)(webpack@5.104.1)) webpack-sources: 3.3.3 + react-server-dom-webpack@19.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)): + dependencies: + acorn-loose: 8.5.2 + neo-async: 2.6.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4) + webpack-sources: 3.3.3 + react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.18.20)(webpack-cli@5.1.4)): dependencies: acorn-loose: 8.5.2 @@ -58395,7 +58571,7 @@ snapshots: webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.18.20)(webpack-cli@5.1.4) webpack-sources: 3.3.3 - react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): + react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): dependencies: acorn-loose: 8.5.2 neo-async: 2.6.2 From 16742e39d59ba6d2bfa4e7c08591fe5eff36aacf Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Fri, 20 Feb 2026 18:23:03 -0800 Subject: [PATCH 03/17] fix(modern-js-plugin-v3): use server-core as peer dependency --- packages/modernjs-v3/package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/modernjs-v3/package.json b/packages/modernjs-v3/package.json index 8ee289d763b..0edd9d2467e 100644 --- a/packages/modernjs-v3/package.json +++ b/packages/modernjs-v3/package.json @@ -164,8 +164,7 @@ "@swc/helpers": "^0.5.17", "node-fetch": "~3.3.0", "jiti": "2.4.2", - "react-error-boundary": "4.1.2", - "@modern-js/server-core": "3.0.1" + "react-error-boundary": "4.1.2" }, "devDependencies": { "@rsbuild/plugin-react": "1.4.5", @@ -173,6 +172,7 @@ "@rslib/core": "0.18.5", "@rsbuild/core": "2.0.0-beta.2", "@modern-js/app-tools": "3.0.1", + "@modern-js/server-core": "3.0.1", "@modern-js/server-runtime": "3.0.1", "@modern-js/module-tools": "2.70.5", "@modern-js/runtime": "3.0.1", @@ -181,6 +181,7 @@ "@types/react-dom": "^18.3.0" }, "peerDependencies": { + "@modern-js/server-core": ">=3.0.0", "react": ">=17", "react-dom": ">=17", "typescript": "^4.9.0 || ^5.0.0", From 7dded32e3db9838beac0f195bbe6042ece0c6316 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Fri, 20 Feb 2026 18:25:59 -0800 Subject: [PATCH 04/17] chore(modern-js-plugin-v3): refresh lockfile --- pnpm-lock.yaml | 200 +++---------------------------------------------- 1 file changed, 12 insertions(+), 188 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a157e3169c0..3a85726a1c5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -905,7 +905,7 @@ importers: version: 0.80.0(@babel/core@7.28.6) '@react-native/eslint-config': specifier: 0.80.0 - version: 0.80.0(eslint@9.26.0(hono@4.11.7)(jiti@2.6.1))(jest@29.7.0(@types/node@20.19.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@20.19.5)(typescript@5.0.4)))(prettier@2.8.8)(typescript@5.0.4) + version: 0.80.0(eslint@9.26.0(hono@4.11.7)(jiti@2.6.1))(jest@29.7.0(@types/node@22.19.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@22.19.9)(typescript@5.0.4)))(prettier@2.8.8)(typescript@5.0.4) '@react-native/gradle-plugin': specifier: 0.80.0 version: 0.80.0 @@ -944,7 +944,7 @@ importers: version: 9.26.0(hono@4.11.7)(jiti@2.6.1) jest: specifier: ^29.6.3 - version: 29.7.0(@types/node@20.19.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@20.19.5)(typescript@5.0.4)) + version: 29.7.0(@types/node@22.19.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@22.19.9)(typescript@5.0.4)) nodemon: specifier: ^3.1.9 version: 3.1.11 @@ -996,7 +996,7 @@ importers: version: 0.80.0(@babel/core@7.28.6) '@react-native/eslint-config': specifier: 0.80.0 - version: 0.80.0(eslint@9.26.0(hono@4.11.7)(jiti@2.6.1))(jest@29.7.0(@types/node@22.19.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@22.19.9)(typescript@5.0.4)))(prettier@2.8.8)(typescript@5.0.4) + version: 0.80.0(eslint@9.26.0(hono@4.11.7)(jiti@2.6.1))(jest@29.7.0(@types/node@20.19.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@20.19.5)(typescript@5.0.4)))(prettier@2.8.8)(typescript@5.0.4) '@react-native/metro-config': specifier: 0.80.0 version: 0.80.0(@babel/core@7.28.6) @@ -1032,7 +1032,7 @@ importers: version: 9.26.0(hono@4.11.7)(jiti@2.6.1) jest: specifier: ^29.6.3 - version: 29.7.0(@types/node@22.19.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@22.19.9)(typescript@5.0.4)) + version: 29.7.0(@types/node@20.19.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@20.19.5)(typescript@5.0.4)) nodemon: specifier: ^3.1.9 version: 3.1.11 @@ -1144,7 +1144,7 @@ importers: version: 7.28.2 '@modern-js/runtime': specifier: 3.0.1 - version: 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@18.3.1(react@18.3.1))(react-server-dom-webpack@19.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)))(react@18.3.1) + version: 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@18.3.1(react@18.3.1))(react-server-dom-webpack@19.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4(webpack-dev-server@5.2.3)(webpack@5.104.1))))(react@18.3.1) '@module-federation/modern-js-v3': specifier: workspace:* version: link:../../../packages/modernjs-v3 @@ -1163,7 +1163,7 @@ importers: version: 2.59.0(typescript@5.0.4) '@modern-js/app-tools': specifier: 3.0.1 - version: 3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(core-js@3.48.0)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.57.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@20.19.5)(typescript@5.0.4))(tsconfig-paths@4.2.0)(tslib@2.8.1)(typescript@5.0.4)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)) + version: 3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(core-js@3.48.0)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.57.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@20.19.5)(typescript@5.0.4))(tsconfig-paths@4.2.0)(tslib@2.8.1)(typescript@5.0.4)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4(webpack-dev-server@5.2.3)(webpack@5.104.1))) '@modern-js/eslint-config': specifier: 2.59.0 version: 2.59.0(typescript@5.0.4) @@ -3578,9 +3578,6 @@ importers: packages/modernjs-v3: dependencies: - '@modern-js/server-core': - specifier: 3.0.1 - version: 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@module-federation/bridge-react': specifier: workspace:* version: link:../bridge/bridge-react @@ -3648,6 +3645,9 @@ importers: '@modern-js/runtime': specifier: 3.0.1 version: 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@19.2.4) + '@modern-js/server-core': + specifier: 3.0.1 + version: 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@modern-js/server-runtime': specifier: 3.0.1 version: 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -32624,57 +32624,6 @@ snapshots: - webpack - webpack-hot-middleware - '@modern-js/app-tools@3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(core-js@3.48.0)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.57.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@20.19.5)(typescript@5.0.4))(tsconfig-paths@4.2.0)(tslib@2.8.1)(typescript@5.0.4)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4))': - dependencies: - '@babel/parser': 7.28.6 - '@babel/traverse': 7.28.6 - '@babel/types': 7.28.6 - '@modern-js/builder': 3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(esbuild@0.25.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1)(typescript@5.0.4)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)) - '@modern-js/i18n-utils': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@modern-js/plugin': 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@modern-js/plugin-data-loader': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@modern-js/prod-server': 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@modern-js/server': 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@20.19.5)(typescript@5.0.4))(tsconfig-paths@4.2.0) - '@modern-js/server-core': 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@modern-js/server-utils': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@modern-js/types': 3.0.1 - '@modern-js/utils': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@rsbuild/core': 2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0) - '@swc/helpers': 0.5.18 - es-module-lexer: 1.7.0 - esbuild: 0.25.5 - esbuild-register: 3.6.0(esbuild@0.25.5) - flatted: 3.3.3 - mlly: 1.8.0 - ndepe: 0.1.13(encoding@0.1.13)(rollup@4.57.0) - pkg-types: 1.3.1 - std-env: 3.10.0 - optionalDependencies: - ts-node: 10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@20.19.5)(typescript@5.0.4) - tsconfig-paths: 4.2.0 - transitivePeerDependencies: - - '@module-federation/runtime-tools' - - '@parcel/css' - - '@rspack/core' - - '@swc/css' - - bufferutil - - clean-css - - core-js - - csso - - debug - - devcert - - encoding - - lightningcss - - react - - react-dom - - rollup - - supports-color - - tslib - - typescript - - utf-8-validate - - webpack - - webpack-hot-middleware - '@modern-js/app-tools@3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(core-js@3.48.0)(encoding@0.1.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.57.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@22.19.9)(typescript@5.8.2))(tsconfig-paths@4.2.0)(tslib@2.8.1)(typescript@5.8.2)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4))': dependencies: '@babel/parser': 7.28.6 @@ -32909,57 +32858,6 @@ snapshots: - webpack - webpack-hot-middleware - '@modern-js/builder@3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(esbuild@0.25.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1)(typescript@5.0.4)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4))': - dependencies: - '@modern-js/flight-server-transform-plugin': 3.0.1 - '@modern-js/utils': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@rsbuild/core': 2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0) - '@rsbuild/plugin-assets-retry': 1.5.1(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)) - '@rsbuild/plugin-check-syntax': 1.6.1(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)) - '@rsbuild/plugin-css-minimizer': 1.1.1(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0))(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)) - '@rsbuild/plugin-less': 1.6.0(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)) - '@rsbuild/plugin-react': 1.4.4(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0))(webpack-hot-middleware@2.26.1) - '@rsbuild/plugin-rem': 1.0.5(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)) - '@rsbuild/plugin-sass': 1.5.0(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)) - '@rsbuild/plugin-source-build': 1.0.4(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)) - '@rsbuild/plugin-svgr': 1.3.0(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0))(typescript@5.0.4)(webpack-hot-middleware@2.26.1) - '@rsbuild/plugin-type-check': 1.3.3(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0))(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(tslib@2.8.1)(typescript@5.0.4) - '@rsbuild/plugin-typed-css-modules': 1.2.1(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)) - '@swc/core': 1.15.10(@swc/helpers@0.5.18) - '@swc/helpers': 0.5.18 - autoprefixer: 10.4.24(postcss@8.5.6) - browserslist: 4.28.1 - core-js: 3.48.0 - cssnano: 6.1.2(postcss@8.5.6) - html-minifier-terser: 7.2.0 - lodash: 4.17.23 - postcss: 8.5.6 - postcss-custom-properties: 13.3.12(postcss@8.5.6) - postcss-flexbugs-fixes: 5.0.2(postcss@8.5.6) - postcss-font-variant: 5.0.0(postcss@8.5.6) - postcss-initial: 4.0.1(postcss@8.5.6) - postcss-media-minmax: 5.0.0(postcss@8.5.6) - postcss-nesting: 12.1.5(postcss@8.5.6) - postcss-page-break: 3.0.4(postcss@8.5.6) - rspack-manifest-plugin: 5.2.1(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18)) - ts-deepmerge: 7.0.3 - transitivePeerDependencies: - - '@module-federation/runtime-tools' - - '@parcel/css' - - '@rspack/core' - - '@swc/css' - - clean-css - - csso - - esbuild - - lightningcss - - react - - react-dom - - supports-color - - tslib - - typescript - - webpack - - webpack-hot-middleware - '@modern-js/builder@3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(esbuild@0.25.5)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tslib@2.8.1)(typescript@5.8.2)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4))': dependencies: '@modern-js/flight-server-transform-plugin': 3.0.1 @@ -33389,7 +33287,7 @@ snapshots: '@swc/helpers': 0.5.18 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - react-server-dom-webpack: 19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + react-server-dom-webpack: 19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) '@modern-js/render@3.0.1(react-dom@18.3.1(react@18.3.1))(react-server-dom-webpack@19.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4(webpack-dev-server@5.2.3)(webpack@5.104.1))))(react@18.3.1)': dependencies: @@ -33400,15 +33298,6 @@ snapshots: react-dom: 18.3.1(react@18.3.1) react-server-dom-webpack: 19.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4(webpack-dev-server@5.2.3)(webpack@5.104.1))) - '@modern-js/render@3.0.1(react-dom@18.3.1(react@18.3.1))(react-server-dom-webpack@19.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)))(react@18.3.1)': - dependencies: - '@modern-js/types': 3.0.1 - '@modern-js/utils': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@swc/helpers': 0.5.18 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-server-dom-webpack: 19.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)) - '@modern-js/render@3.0.1(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@19.2.4)': dependencies: '@modern-js/types': 3.0.1 @@ -33416,7 +33305,7 @@ snapshots: '@swc/helpers': 0.5.18 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - react-server-dom-webpack: 19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + react-server-dom-webpack: 19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) '@modern-js/rsbuild-plugin-esbuild@2.70.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(webpack-cli@5.1.4)': dependencies: @@ -33595,35 +33484,6 @@ snapshots: - core-js - react-server-dom-webpack - '@modern-js/runtime@3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@18.3.1(react@18.3.1))(react-server-dom-webpack@19.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)))(react@18.3.1)': - dependencies: - '@loadable/component': 5.16.7(react@18.3.1) - '@loadable/server': 5.16.7(@loadable/component@5.16.7(react@18.3.1))(react@18.3.1) - '@modern-js/plugin': 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@modern-js/plugin-data-loader': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@modern-js/render': 3.0.1(react-dom@18.3.1(react@18.3.1))(react-server-dom-webpack@19.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)))(react@18.3.1) - '@modern-js/runtime-utils': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@modern-js/types': 3.0.1 - '@modern-js/utils': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@swc/helpers': 0.5.18 - '@swc/plugin-loadable-components': 11.5.0 - '@types/loadable__component': 5.13.10 - '@types/react-helmet': 6.1.11 - cookie: 0.7.2 - entities: 7.0.1 - es-module-lexer: 1.7.0 - esbuild: 0.25.5 - invariant: 2.2.4 - isbot: 3.8.0 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-helmet: 6.1.0(react@18.3.1) - react-is: 18.3.1 - transitivePeerDependencies: - - '@module-federation/runtime-tools' - - core-js - - react-server-dom-webpack - '@modern-js/runtime@3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@19.2.4)': dependencies: '@loadable/component': 5.16.7(react@19.2.4) @@ -39495,21 +39355,6 @@ snapshots: - lightningcss - webpack - '@rsbuild/plugin-css-minimizer@1.1.1(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0))(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4))': - dependencies: - css-minimizer-webpack-plugin: 7.0.2(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)) - reduce-configs: 1.1.1 - optionalDependencies: - '@rsbuild/core': 2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0) - transitivePeerDependencies: - - '@parcel/css' - - '@swc/css' - - clean-css - - csso - - esbuild - - lightningcss - - webpack - '@rsbuild/plugin-css-minimizer@1.1.1(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0))(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4))': dependencies: css-minimizer-webpack-plugin: 7.0.2(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) @@ -47230,18 +47075,6 @@ snapshots: optionalDependencies: esbuild: 0.25.5 - css-minimizer-webpack-plugin@7.0.2(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)): - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - cssnano: 7.1.2(postcss@8.4.49) - jest-worker: 29.7.0 - postcss: 8.4.49 - schema-utils: 4.3.3 - serialize-javascript: 6.0.2 - webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4) - optionalDependencies: - esbuild: 0.25.5 - css-minimizer-webpack-plugin@7.0.2(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -58553,15 +58386,6 @@ snapshots: webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4(webpack-dev-server@5.2.3)(webpack@5.104.1)) webpack-sources: 3.3.3 - react-server-dom-webpack@19.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)): - dependencies: - acorn-loose: 8.5.2 - neo-async: 2.6.2 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4) - webpack-sources: 3.3.3 - react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.18.20)(webpack-cli@5.1.4)): dependencies: acorn-loose: 8.5.2 @@ -58571,7 +58395,7 @@ snapshots: webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.18.20)(webpack-cli@5.1.4) webpack-sources: 3.3.3 - react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): + react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): dependencies: acorn-loose: 8.5.2 neo-async: 2.6.2 From 021bc560f4993bd847903941b65e9f64867bd7be Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Fri, 20 Feb 2026 18:39:53 -0800 Subject: [PATCH 05/17] fix(modern-js-plugin-v3): resolve server-core from app runtime --- packages/modernjs-v3/src/server/index.ts | 67 ++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/packages/modernjs-v3/src/server/index.ts b/packages/modernjs-v3/src/server/index.ts index 897a461649a..c5e3c61f5cb 100644 --- a/packages/modernjs-v3/src/server/index.ts +++ b/packages/modernjs-v3/src/server/index.ts @@ -1,3 +1,5 @@ +import fs from 'node:fs'; +import path from 'node:path'; import type { ServerPlugin } from '@modern-js/server-runtime'; import { mfAsyncStartupLoaderStrategy } from './asyncStartupLoader'; import { @@ -5,13 +7,72 @@ import { createStaticMiddleware, } from './staticMiddleware'; +const findPackageRootFromResolvedEntry = ( + resolvedEntryPath: string, + packageName: string, +): string | undefined => { + let currentDir = path.dirname(resolvedEntryPath); + while (currentDir !== path.dirname(currentDir)) { + const packageJsonPath = path.resolve(currentDir, 'package.json'); + if (fs.existsSync(packageJsonPath)) { + try { + const { name } = JSON.parse( + fs.readFileSync(packageJsonPath, 'utf-8'), + ) as { name?: string }; + if (name === packageName) { + return currentDir; + } + } catch { + // Ignore malformed package.json and continue searching upwards. + } + } + currentDir = path.dirname(currentDir); + } + return undefined; +}; + +const resolveServerCoreNodeEntry = (): string | undefined => { + const candidateBasePaths: string[] = []; + + try { + const appToolsEntry = require.resolve('@modern-js/app-tools', { + paths: [process.cwd(), __dirname], + }); + const appToolsRoot = findPackageRootFromResolvedEntry( + appToolsEntry, + '@modern-js/app-tools', + ); + if (appToolsRoot) { + candidateBasePaths.push(appToolsRoot); + } + } catch { + // app-tools may not be installed in all environments. + } + + candidateBasePaths.push(process.cwd(), __dirname); + + for (const basePath of candidateBasePaths) { + try { + return require.resolve('@modern-js/server-core/node', { + paths: [basePath], + }); + } catch { + // Try next base path. + } + } + + return undefined; +}; + const staticServePlugin = (): ServerPlugin => ({ name: '@modern-js/module-federation/server', setup: (api) => { try { - const { - registerBundleLoaderStrategy, - } = require('@modern-js/server-core/node'); + const serverCoreNodeEntry = resolveServerCoreNodeEntry(); + if (!serverCoreNodeEntry) { + return; + } + const { registerBundleLoaderStrategy } = require(serverCoreNodeEntry); if (typeof registerBundleLoaderStrategy === 'function') { registerBundleLoaderStrategy(mfAsyncStartupLoaderStrategy); } From 76b126d83378e18e8eba25d4a39c1e9f3a948b23 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Fri, 20 Feb 2026 18:48:32 -0800 Subject: [PATCH 06/17] fix(retry-plugin): type loadEntryError args --- packages/retry-plugin/src/index.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/retry-plugin/src/index.ts b/packages/retry-plugin/src/index.ts index e085e23423e..1a5c9851a81 100644 --- a/packages/retry-plugin/src/index.ts +++ b/packages/retry-plugin/src/index.ts @@ -9,6 +9,10 @@ import { } from './constant'; import logger from './logger'; +type LoadEntryErrorArgs = Parameters< + NonNullable +>[0]; + const RetryPlugin = ( params?: CommonRetryOptions, ): ModuleFederationRuntimePlugin => { @@ -58,7 +62,7 @@ const RetryPlugin = ( remoteEntryExports, globalLoading, uniqueKey, - }) { + }: LoadEntryErrorArgs) { const beforeExecuteRetry = () => { delete globalLoading[uniqueKey]; }; From d17d2c922ed5c724e6055fde26f3b9d48687f887 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Fri, 20 Feb 2026 19:21:31 -0800 Subject: [PATCH 07/17] fix(modern-js-plugin-v3): avoid rsc bridge load deadlock --- .../rsc-bridge-runtime-plugin.ts | 41 +++++++++++++++---- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/packages/modernjs-v3/src/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.ts b/packages/modernjs-v3/src/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.ts index d9e657b38f9..8a46f9d7233 100644 --- a/packages/modernjs-v3/src/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.ts +++ b/packages/modernjs-v3/src/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.ts @@ -274,6 +274,32 @@ const rscBridgeRuntimePlugin = (): ModuleFederationRuntimePlugin => { return undefined; }; + const resolveExposeSpecifier = (args: any): string => { + const candidateExposes = [ + args?.expose, + args?.id, + args?.request, + args?.pkgNameOrAlias, + args?.remote?.id, + args?.remote?.expose, + args?.remoteInfo?.id, + args?.remoteInfo?.expose, + args?.name, + ]; + for (const candidate of candidateExposes) { + if ( + (typeof candidate === 'string' || typeof candidate === 'number') && + String(candidate).trim() + ) { + return String(candidate); + } + } + return ''; + }; + + const isBridgeExposeRequest = (args: any) => + resolveExposeSpecifier(args).includes(RSC_BRIDGE_EXPOSE); + const ensureBridge = async (alias: string, args?: any) => { const existingBridgePromise = bridgePromises[alias]; if (existingBridgePromise) { @@ -408,12 +434,16 @@ const rscBridgeRuntimePlugin = (): ModuleFederationRuntimePlugin => { }; const ensureRemoteAliasMerged = async (alias: string, args: any) => { + const bridgeExposeRequest = isBridgeExposeRequest(args); const existingMergePromise = aliasMergePromises[alias]; if (existingMergePromise) { + if (bridgeExposeRequest) { + return; + } await existingMergePromise; return; } - if (mergedRemoteAliases.has(alias)) { + if (mergedRemoteAliases.has(alias) || bridgeExposeRequest) { return; } @@ -661,7 +691,7 @@ const rscBridgeRuntimePlugin = (): ModuleFederationRuntimePlugin => { async afterResolve(args: any) { patchRuntimeArgsSnapshots(args); const alias = resolveRemoteAlias(args); - if (!alias) { + if (!alias || isBridgeExposeRequest(args)) { return args; } await ensureRemoteAliasMerged(alias, args); @@ -683,12 +713,7 @@ const rscBridgeRuntimePlugin = (): ModuleFederationRuntimePlugin => { return args; } - const expose = - typeof args?.expose === 'string' ? args.expose : String(args?.id || ''); - if ( - mergedRemoteAliases.has(alias) || - expose.includes(RSC_BRIDGE_EXPOSE) - ) { + if (mergedRemoteAliases.has(alias) || isBridgeExposeRequest(args)) { return args; } await ensureRemoteAliasMerged(alias, args); From c160c7ae3246c65fa50f19d966b8acd32448580a Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Fri, 20 Feb 2026 20:32:25 -0800 Subject: [PATCH 08/17] fix(sdk,modern-js-plugin-v3): stabilize node async-startup remote loading --- .changeset/tidy-bananas-relax.md | 6 ++ .../src/server/asyncStartupLoader.ts | 14 +++- .../sdk/__tests__/nodeScriptUtils.spec.ts | 73 ++++++++++++++++ packages/sdk/src/node.ts | 17 +++- packages/sdk/src/nodeScriptUtils.ts | 84 +++++++++++++++++++ 5 files changed, 186 insertions(+), 8 deletions(-) create mode 100644 .changeset/tidy-bananas-relax.md create mode 100644 packages/sdk/__tests__/nodeScriptUtils.spec.ts create mode 100644 packages/sdk/src/nodeScriptUtils.ts diff --git a/.changeset/tidy-bananas-relax.md b/.changeset/tidy-bananas-relax.md new file mode 100644 index 00000000000..72deb0db02c --- /dev/null +++ b/.changeset/tidy-bananas-relax.md @@ -0,0 +1,6 @@ +--- +'@module-federation/modern-js-v3': patch +'@module-federation/sdk': patch +--- + +Improve Node-side async startup remote loading by stripping browser HMR bootstrap calls, normalizing async startup container exports, and resolving var/global container shapes reliably for RSC federation flows. diff --git a/packages/modernjs-v3/src/server/asyncStartupLoader.ts b/packages/modernjs-v3/src/server/asyncStartupLoader.ts index c6b1b96dc58..4946ee9f82d 100644 --- a/packages/modernjs-v3/src/server/asyncStartupLoader.ts +++ b/packages/modernjs-v3/src/server/asyncStartupLoader.ts @@ -5,6 +5,8 @@ import fs from 'fs-extra'; const ASYNC_NODE_STARTUP_CALL = 'var __webpack_exports__ = __webpack_require__.x();'; +const ENCODED_HMR_CLIENT_BOOTSTRAP_CALL = + /__webpack_require__\("data:text\/javascript,[^"]*"\);\s*/g; const isPromiseLike = (value: unknown): value is Promise => Boolean(value) && @@ -25,15 +27,20 @@ export const mfAsyncStartupLoaderStrategy: BundleLoaderStrategy = async ( ): Promise => { try { const bundleCode = await fs.readFile(filepath, 'utf-8'); + // Server-side VM fallback should not execute browser-only HMR bootstraps. + const sanitizedBundleCode = bundleCode.replace( + ENCODED_HMR_CLIENT_BOOTSTRAP_CALL, + '', + ); if ( - !bundleCode.includes(ASYNC_NODE_STARTUP_CALL) || - !bundleCode.includes('__webpack_require__.mfAsyncStartup') + !sanitizedBundleCode.includes(ASYNC_NODE_STARTUP_CALL) || + !sanitizedBundleCode.includes('__webpack_require__.mfAsyncStartup') ) { return undefined; } - const patchedCode = bundleCode.replace( + const patchedCode = sanitizedBundleCode.replace( ASYNC_NODE_STARTUP_CALL, 'var __webpack_exports__ = __webpack_require__.x({}, []);', ); @@ -47,7 +54,6 @@ export const mfAsyncStartupLoaderStrategy: BundleLoaderStrategy = async ( __filename: string, __dirname: string, ) => void; - runBundle( localModule.exports, require, diff --git a/packages/sdk/__tests__/nodeScriptUtils.spec.ts b/packages/sdk/__tests__/nodeScriptUtils.spec.ts new file mode 100644 index 00000000000..9db01b59679 --- /dev/null +++ b/packages/sdk/__tests__/nodeScriptUtils.spec.ts @@ -0,0 +1,73 @@ +import { + patchNodeRemoteEntryCode, + resolveNodeScriptExports, +} from '../src/nodeScriptUtils'; + +describe('nodeScriptUtils', () => { + it('patches async startup entries and strips encoded hmr bootstrap calls', () => { + const source = ` + var rscRemote; + var __webpack_require__ = function(){}; + __webpack_require__.mfAsyncStartup = function () {}; + __webpack_require__("data:text/javascript,import%20%7B%20init%20%7D%20from%20'hmr.js'%3B"); + var __webpack_exports__ = __webpack_require__.x(); + rscRemote = __webpack_exports__; + `; + + const patched = patchNodeRemoteEntryCode(source, { + globalName: 'rscRemote', + }); + + expect(patched).toContain('__webpack_require__.x({}, []);'); + expect(patched).not.toContain('data:text/javascript'); + expect(patched).toContain('module.exports = rscRemote'); + }); + + it('does not append module export shim for invalid global identifiers', () => { + const source = ` + var __webpack_require__ = function(){}; + var __webpack_exports__ = __webpack_require__.x(); + `; + + const patched = patchNodeRemoteEntryCode(source, { + globalName: '__FEDERATION_remote:key__', + }); + + expect(patched).not.toContain('module.exports = __FEDERATION_remote:key__'); + }); + + it('resolves promise-like exports and falls back to global container', async () => { + const key = '__FEDERATION_remote:key__'; + const globalContainer = { init: () => undefined, get: () => undefined }; + (globalThis as Record)[key] = globalContainer; + + const resolvedFromPromise = await resolveNodeScriptExports( + { + exports: {}, + module: { + exports: Promise.resolve({ + init: () => undefined, + get: () => undefined, + }), + }, + }, + { globalName: key }, + ); + + expect(typeof (resolvedFromPromise as Record).init).toBe( + 'function', + ); + + const resolvedFromGlobal = await resolveNodeScriptExports( + { + exports: {}, + module: { exports: {} }, + }, + { globalName: key }, + ); + + expect(resolvedFromGlobal).toBe(globalContainer); + + delete (globalThis as Record)[key]; + }); +}); diff --git a/packages/sdk/src/node.ts b/packages/sdk/src/node.ts index 1d6a7baf937..a676d405aa1 100644 --- a/packages/sdk/src/node.ts +++ b/packages/sdk/src/node.ts @@ -1,4 +1,8 @@ import { CreateScriptHookNode, FetchHook } from './types'; +import { + patchNodeRemoteEntryCode, + resolveNodeScriptExports, +} from './nodeScriptUtils'; // Declare the ENV_TARGET constant that will be defined by DefinePlugin declare const ENV_TARGET: 'web' | 'node'; @@ -99,6 +103,7 @@ export const createScriptNode = try { const res = await f(urlObj.href); const data = await res.text(); + const patchedData = patchNodeRemoteEntryCode(data, attrs); const [path, vm] = await Promise.all([ importNodeModule('path'), importNodeModule('vm'), @@ -112,7 +117,7 @@ export const createScriptNode = const filename = path.basename(urlObj.pathname); const script = new vm.Script( - `(function(exports, module, require, __dirname, __filename) {${data}\n})`, + `(function(exports, module, require, __dirname, __filename) {${patchedData}\n})`, { filename, importModuleDynamically: @@ -129,12 +134,16 @@ export const createScriptNode = urlDirname, filename, ); - const exportedInterface: Record = - scriptContext.module.exports || scriptContext.exports; + const exportedInterface = await resolveNodeScriptExports( + scriptContext, + attrs, + ); if (attrs && exportedInterface && attrs['globalName']) { const container = - exportedInterface[attrs['globalName']] || exportedInterface; + (exportedInterface as Record)[ + attrs['globalName'] + ] || exportedInterface; cb( undefined, container as keyof typeof scriptContext.module.exports, diff --git a/packages/sdk/src/nodeScriptUtils.ts b/packages/sdk/src/nodeScriptUtils.ts new file mode 100644 index 00000000000..f265c5b33e2 --- /dev/null +++ b/packages/sdk/src/nodeScriptUtils.ts @@ -0,0 +1,84 @@ +const ASYNC_NODE_STARTUP_CALL = + 'var __webpack_exports__ = __webpack_require__.x();'; +const ENCODED_HMR_CLIENT_BOOTSTRAP_CALL = + /__webpack_require__\("data:text\/javascript,[^"]*"\);\s*/g; + +const isPromiseLike = (value: unknown): value is Promise => + Boolean(value) && + (typeof value === 'object' || typeof value === 'function') && + 'then' in (value as Promise) && + typeof (value as Promise).then === 'function'; + +const isValidIdentifier = (value: string): boolean => + /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(value); + +export const patchNodeRemoteEntryCode = ( + code: string, + attrs?: Record, +): string => { + let patchedCode = code.replace(ENCODED_HMR_CLIENT_BOOTSTRAP_CALL, ''); + + if ( + patchedCode.includes(ASYNC_NODE_STARTUP_CALL) && + patchedCode.includes('__webpack_require__.mfAsyncStartup') + ) { + patchedCode = patchedCode.replace( + ASYNC_NODE_STARTUP_CALL, + 'var __webpack_exports__ = __webpack_require__.x({}, []);', + ); + } + + const globalName = attrs?.['globalName']; + if (typeof globalName === 'string' && isValidIdentifier(globalName)) { + patchedCode = `${patchedCode} +;if (typeof ${globalName} !== "undefined") { module.exports = ${globalName}; }`; + } + + return patchedCode; +}; + +export const resolveNodeScriptExports = async ( + scriptContext: { exports: unknown; module: { exports: unknown } }, + attrs?: Record, +): Promise => { + const globalName = attrs?.['globalName']; + let exportedInterface = scriptContext.module.exports || scriptContext.exports; + + if (isPromiseLike(exportedInterface)) { + exportedInterface = await exportedInterface; + } + + if ( + typeof globalName === 'string' && + exportedInterface && + typeof exportedInterface === 'object' && + !Array.isArray(exportedInterface) && + globalName in (exportedInterface as Record) + ) { + exportedInterface = (exportedInterface as Record)[ + globalName + ]; + } + + const globalContainer = + typeof globalName === 'string' + ? (globalThis as Record)[globalName] + : undefined; + + if ( + globalContainer !== undefined && + scriptContext.module.exports === exportedInterface && + exportedInterface && + typeof exportedInterface === 'object' && + !Array.isArray(exportedInterface) && + Object.keys(exportedInterface as Record).length === 0 + ) { + exportedInterface = globalContainer; + } + + if (isPromiseLike(exportedInterface)) { + exportedInterface = await exportedInterface; + } + + return exportedInterface; +}; From b7d17c6815c18249b7203f2ab93f74ac7cc4e919 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Sat, 21 Feb 2026 14:33:15 -0800 Subject: [PATCH 09/17] fix(modern-js-plugin-v3): restore RSC runtime parity for host/remote Align server-side RSC bridge resolution and dev asset serving with pre-move modern.js behavior. This prevents host SSR from loading browser remote entries and unblocks dev/build rsc-mf flows. Co-authored-by: Cursor --- packages/modernjs-v3/src/cli/configPlugin.ts | 87 +++++++++++------- .../rsc-bridge-runtime-plugin.spec.ts | 89 +++++++++++++++++++ .../rsc-bridge-runtime-plugin.ts | 79 ++++++++++++---- packages/modernjs-v3/src/cli/ssrPlugin.ts | 34 +++---- 4 files changed, 218 insertions(+), 71 deletions(-) diff --git a/packages/modernjs-v3/src/cli/configPlugin.ts b/packages/modernjs-v3/src/cli/configPlugin.ts index ebb9b8fbdac..e0a3ae15a80 100644 --- a/packages/modernjs-v3/src/cli/configPlugin.ts +++ b/packages/modernjs-v3/src/cli/configPlugin.ts @@ -581,33 +581,6 @@ const resolveProjectDependency = (request: string) => { } }; -const patchRscServerRuntimeAliases = (chain: BundlerChainConfig) => { - const reactPackagePath = resolveProjectDependency('react/package.json'); - if (!reactPackagePath) { - return; - } - - const reactDir = path.dirname(reactPackagePath); - const reactJsxRuntimeServerPath = path.join( - reactDir, - 'jsx-runtime.react-server.js', - ); - const reactJsxDevRuntimeServerPath = path.join( - reactDir, - 'jsx-dev-runtime.react-server.js', - ); - - if (fs.existsSync(reactJsxRuntimeServerPath)) { - chain.resolve.alias.set('react/jsx-runtime$', reactJsxRuntimeServerPath); - } - if (fs.existsSync(reactJsxDevRuntimeServerPath)) { - chain.resolve.alias.set( - 'react/jsx-dev-runtime$', - reactJsxDevRuntimeServerPath, - ); - } -}; - const getExposeImports = ( exposeConfig: moduleFederationPlugin.ExposesObject[string], ): string[] => { @@ -692,6 +665,58 @@ const patchRscRemoteComponentLayer = ( ruleChain.layer(RSC_LAYER); }; +const patchRscServerJsxRuntimeResolution = (chain: BundlerChainConfig) => { + const pluginId = 'rsc-mf-react-server-jsx-runtime'; + if (chain.plugins.has(pluginId)) { + return; + } + + const reactPackagePath = resolveProjectDependency('react/package.json'); + if (!reactPackagePath) { + return; + } + const reactDir = path.dirname(reactPackagePath); + const reactJsxRuntimeServerPath = path.join( + reactDir, + 'jsx-runtime.react-server.js', + ); + const reactJsxDevRuntimeServerPath = path.join( + reactDir, + 'jsx-dev-runtime.react-server.js', + ); + + if ( + !fs.existsSync(reactJsxRuntimeServerPath) || + !fs.existsSync(reactJsxDevRuntimeServerPath) + ) { + return; + } + + const { NormalModuleReplacementPlugin } = require('@rspack/core') as { + NormalModuleReplacementPlugin: new (...args: unknown[]) => unknown; + }; + + chain.plugin(pluginId).use(NormalModuleReplacementPlugin, [ + /^react\/jsx(?:-dev)?-runtime$/, + (resource: { + request?: string; + contextInfo?: { + issuerLayer?: string; + }; + }) => { + if (resource.contextInfo?.issuerLayer !== RSC_LAYER) { + return; + } + + if (resource.request === 'react/jsx-runtime') { + resource.request = reactJsxRuntimeServerPath; + } else if (resource.request === 'react/jsx-dev-runtime') { + resource.request = reactJsxDevRuntimeServerPath; + } + }, + ]); +}; + const normalizePublicPath = (publicPath: string) => publicPath.endsWith('/') ? publicPath.slice(0, -1) : publicPath; @@ -766,10 +791,8 @@ export function patchBundlerConfig(options: { .clear() .add('require') .add('import') - .add('default'); - if (hasExposes(mfConfig.exposes)) { - chain.resolve.conditionNames.add('react-server'); - } + .add('default') + .add('node'); } if (rscMfEnabled && hasExposes(mfConfig.exposes)) { @@ -788,8 +811,8 @@ export function patchBundlerConfig(options: { if (!isServer) { chain.optimization.splitChunks(false); } else { - patchRscServerRuntimeAliases(chain); patchRscRemoteComponentLayer(chain, mfConfig); + patchRscServerJsxRuntimeResolution(chain); } } diff --git a/packages/modernjs-v3/src/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.spec.ts b/packages/modernjs-v3/src/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.spec.ts index 7abe8c5667a..f4545053afc 100644 --- a/packages/modernjs-v3/src/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.spec.ts +++ b/packages/modernjs-v3/src/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.spec.ts @@ -376,4 +376,93 @@ describe('rsc-bridge-runtime-plugin', () => { ); expect(args.remoteInfo.remoteEntry.path).toBe(''); }); + + it('rewrites node bridge loads to bundles/.js when ssr entry is missing', async () => { + const plugin = rscBridgeRuntimePlugin(); + const loadRemote = vi.fn(async () => ({ + getManifest: () => ({}), + executeAction: vi.fn(async () => undefined), + })); + const args: any = { + remote: { alias: 'rscRemote' }, + remoteInfo: { + entry: undefined, + }, + remoteSnapshot: { + name: 'rscRemote', + publicPath: 'http://127.0.0.1:3008/', + remoteEntry: { + name: 'static/remoteEntry.js', + }, + }, + origin: { + loadRemote, + }, + }; + + await plugin.afterResolve?.(args); + + expect(args.remoteInfo.entry).toBe( + 'http://127.0.0.1:3008/bundles/static/remoteEntry.js', + ); + }); + + it('rewrites existing client remoteEntry URLs to node bundles entry', async () => { + const plugin = rscBridgeRuntimePlugin(); + const loadRemote = vi.fn(async () => ({ + getManifest: () => ({}), + executeAction: vi.fn(async () => undefined), + })); + const args: any = { + remote: { alias: 'rscRemote' }, + remoteInfo: { + entry: 'http://127.0.0.1:3008/static/remoteEntry.js', + }, + remoteSnapshot: { + name: 'rscRemote', + publicPath: 'http://127.0.0.1:3008/', + remoteEntry: { + name: 'static/remoteEntry.js', + }, + }, + origin: { + loadRemote, + }, + }; + + await plugin.afterResolve?.(args); + + expect(args.remoteInfo.entry).toBe( + 'http://127.0.0.1:3008/bundles/static/remoteEntry.js', + ); + }); + + it('avoids duplicate bundles segment when ssr publicPath already includes bundles', async () => { + const plugin = rscBridgeRuntimePlugin(); + const loadRemote = vi.fn(async () => ({ + getManifest: () => ({}), + executeAction: vi.fn(async () => undefined), + })); + const args: any = { + remote: { alias: 'rscRemote' }, + remoteInfo: { + entry: undefined, + }, + remoteSnapshot: { + publicPath: 'http://127.0.0.1:3008/bundles/', + remoteEntry: { + name: 'static/remoteEntry.js', + }, + }, + origin: { + loadRemote, + }, + }; + + await plugin.afterResolve?.(args); + + expect(args.remoteInfo.entry).toBe( + 'http://127.0.0.1:3008/bundles/static/remoteEntry.js', + ); + }); }); diff --git a/packages/modernjs-v3/src/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.ts b/packages/modernjs-v3/src/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.ts index 8a46f9d7233..822a6135c2d 100644 --- a/packages/modernjs-v3/src/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.ts +++ b/packages/modernjs-v3/src/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.ts @@ -486,6 +486,40 @@ const rscBridgeRuntimePlugin = (): ModuleFederationRuntimePlugin => { value.startsWith('https:undefined') || value.startsWith('http:undefined'); + const isLikelyClientRemoteEntryPath = (entryPath: string) => { + const normalizedPath = entryPath.split('?')[0]; + return ( + normalizedPath.endsWith('/static/remoteEntry.js') || + normalizedPath.endsWith('static/remoteEntry.js') || + normalizedPath === 'remoteEntry.js' || + normalizedPath.endsWith('/remoteEntry.js') + ); + }; + + const shouldRewriteNodeClientRemoteEntryUrl = (entry: unknown) => + isNodeLikeRuntime() && + typeof entry === 'string' && + isLikelyClientRemoteEntryPath(entry); + + const resolveNodeSsrRemoteEntryPath = (entryPath: string) => { + if (!isNodeLikeRuntime()) { + return entryPath; + } + + const normalizedEntryPath = entryPath.startsWith('/') + ? entryPath.slice(1) + : entryPath; + if (normalizedEntryPath.startsWith('bundles/')) { + return normalizedEntryPath; + } + + if (!isLikelyClientRemoteEntryPath(normalizedEntryPath)) { + return entryPath; + } + + return `bundles/${normalizedEntryPath}`; + }; + const resolveSnapshotPublicPath = (snapshot: Record) => { const metaData = isObject(snapshot.metaData) ? (snapshot.metaData as Record) @@ -532,17 +566,16 @@ const rscBridgeRuntimePlugin = (): ModuleFederationRuntimePlugin => { } if (isAbsoluteHttpUrl(entryPath)) { - if ( - isNodeLikeRuntime() && - !snapshot.ssrRemoteEntry && - !entryPath.includes('/bundles/') - ) { + if (isNodeLikeRuntime() && isLikelyClientRemoteEntryPath(entryPath)) { try { const url = new URL(entryPath); - const normalizedPathname = url.pathname.startsWith('/') - ? url.pathname.slice(1) - : url.pathname; - url.pathname = `/bundles/${normalizedPathname}`; + const resolvedNodeEntryPath = resolveNodeSsrRemoteEntryPath( + url.pathname, + ); + const normalizedPathname = resolvedNodeEntryPath.startsWith('/') + ? resolvedNodeEntryPath + : `/${resolvedNodeEntryPath}`; + url.pathname = normalizedPathname; return url.href; } catch { return entryPath; @@ -551,12 +584,17 @@ const rscBridgeRuntimePlugin = (): ModuleFederationRuntimePlugin => { return entryPath; } - if (isNodeLikeRuntime() && !snapshot.ssrRemoteEntry) { - const normalizedEntryPath = entryPath.startsWith('/') - ? entryPath.slice(1) - : entryPath; - if (!normalizedEntryPath.startsWith('bundles/')) { - entryPath = `bundles/${normalizedEntryPath}`; + if (isNodeLikeRuntime()) { + const resolvedNodeEntryPath = resolveNodeSsrRemoteEntryPath(entryPath); + if (resolvedNodeEntryPath !== entryPath) { + entryPath = resolvedNodeEntryPath; + } else if (!snapshot.ssrRemoteEntry) { + const normalizedEntryPath = entryPath.startsWith('/') + ? entryPath.slice(1) + : entryPath; + if (!normalizedEntryPath.startsWith('bundles/')) { + entryPath = `bundles/${normalizedEntryPath}`; + } } } @@ -565,6 +603,16 @@ const rscBridgeRuntimePlugin = (): ModuleFederationRuntimePlugin => { return undefined; } + const publicPathHasBundlesSegment = publicPath.includes('/bundles/'); + if (isNodeLikeRuntime() && publicPathHasBundlesSegment) { + const normalizedEntryPath = entryPath.startsWith('/') + ? entryPath.slice(1) + : entryPath; + if (normalizedEntryPath.startsWith('bundles/')) { + entryPath = normalizedEntryPath.slice('bundles/'.length); + } + } + try { return new URL(entryPath, publicPath).href; } catch { @@ -588,6 +636,7 @@ const rscBridgeRuntimePlugin = (): ModuleFederationRuntimePlugin => { } if ( + shouldRewriteNodeClientRemoteEntryUrl(remoteInfo.entry) || isBrokenRemoteEntryUrl(remoteInfo.entry) || (typeof remoteInfo.entry === 'string' && remoteInfo.entry.includes('undefined')) diff --git a/packages/modernjs-v3/src/cli/ssrPlugin.ts b/packages/modernjs-v3/src/cli/ssrPlugin.ts index ed8b2115255..caf65410b01 100644 --- a/packages/modernjs-v3/src/cli/ssrPlugin.ts +++ b/packages/modernjs-v3/src/cli/ssrPlugin.ts @@ -35,18 +35,6 @@ export const CHAIN_MF_PLUGIN_ID = 'plugin-module-federation-server'; const isBuildCommand = () => process.argv.includes('build') || process.argv.includes('deploy'); -const hasExposes = ( - exposes: moduleFederationPlugin.ModuleFederationPluginOptions['exposes'], -) => { - if (!exposes) { - return false; - } - if (Array.isArray(exposes)) { - return exposes.length > 0; - } - return Object.keys(exposes).length > 0; -}; - function getManifestAssetFileNames( manifestOption?: moduleFederationPlugin.ModuleFederationPluginOptions['manifest'], ): AssetFileNames { @@ -150,12 +138,6 @@ export const moduleFederationSSRPlugin = ( const modernjsConfig = api.getConfig(); const enableSSR = pluginOptions.userConfig?.ssr ?? Boolean(modernjsConfig?.server?.ssr); - const enableRsc = Boolean(modernjsConfig?.server?.rsc); - const enableMfRsc = Boolean( - (pluginOptions.ssrConfig.experiments as { rsc?: boolean } | undefined) - ?.rsc, - ); - const hasRscExposes = hasExposes(pluginOptions.ssrConfig.exposes); const { secondarySharedTreeShaking } = pluginOptions; if (!enableSSR) { return; @@ -230,10 +212,6 @@ export const moduleFederationSSRPlugin = ( if (!isWeb && !secondarySharedTreeShaking) { chain.target('async-node'); - if (enableRsc && (!enableMfRsc || hasRscExposes)) { - chain.resolve.conditionNames.add('react-server'); - } - if (isDev()) { chain .plugin('UniverseEntryChunkTrackerPlugin') @@ -261,8 +239,16 @@ export const moduleFederationSSRPlugin = ( } try { const requestPath = req.url?.split('?')[0] || ''; - const isJsonRequest = path.extname(requestPath) === '.json'; - if (isJsonRequest && !requestPath.includes('hot-update')) { + const extension = path.extname(requestPath); + const isJsonRequest = + extension === '.json' && + !requestPath.includes('hot-update'); + const isBundleJsRequest = + extension === '.js' && requestPath.startsWith('/bundles/'); + if ( + (isJsonRequest || isBundleJsRequest) && + requestPath.includes('/') + ) { if (!requestPath.startsWith('/')) { next(); return; From 7e4148f02c47267cd517fccab919520ef9a1eb41 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Tue, 24 Feb 2026 11:10:26 -0800 Subject: [PATCH 10/17] fix(modern-js-plugin-v3): preserve RSC resolve behavior Append react-server without clearing existing resolve conditions. Use bundle-scoped require for async-startup VM execution. Add a minimal ModernJS RSC example app under apps. Co-authored-by: Cursor --- apps/modernjs-rsc/README.md | 16 +++++++ apps/modernjs-rsc/modern.config.ts | 13 ++++++ apps/modernjs-rsc/module-federation.config.ts | 21 +++++++++ apps/modernjs-rsc/package.json | 30 +++++++++++++ apps/modernjs-rsc/project.json | 44 +++++++++++++++++++ .../modernjs-rsc/src/client/ClientCounter.tsx | 18 ++++++++ apps/modernjs-rsc/src/modern-app-env.d.ts | 4 ++ apps/modernjs-rsc/src/routes/page.tsx | 15 +++++++ .../modernjs-rsc/src/server/ServerMessage.tsx | 18 ++++++++ apps/modernjs-rsc/tsconfig.app.json | 17 +++++++ apps/modernjs-rsc/tsconfig.json | 14 ++++++ packages/modernjs-v3/src/cli/configPlugin.ts | 7 +-- .../src/server/asyncStartupLoader.ts | 5 ++- 13 files changed, 215 insertions(+), 7 deletions(-) create mode 100644 apps/modernjs-rsc/README.md create mode 100644 apps/modernjs-rsc/modern.config.ts create mode 100644 apps/modernjs-rsc/module-federation.config.ts create mode 100644 apps/modernjs-rsc/package.json create mode 100644 apps/modernjs-rsc/project.json create mode 100644 apps/modernjs-rsc/src/client/ClientCounter.tsx create mode 100644 apps/modernjs-rsc/src/modern-app-env.d.ts create mode 100644 apps/modernjs-rsc/src/routes/page.tsx create mode 100644 apps/modernjs-rsc/src/server/ServerMessage.tsx create mode 100644 apps/modernjs-rsc/tsconfig.app.json create mode 100644 apps/modernjs-rsc/tsconfig.json diff --git a/apps/modernjs-rsc/README.md b/apps/modernjs-rsc/README.md new file mode 100644 index 00000000000..19a2f8fd55a --- /dev/null +++ b/apps/modernjs-rsc/README.md @@ -0,0 +1,16 @@ +# modernjs-rsc + +Minimal ModernJS + Module Federation RSC example in `apps/`. + +## Run + +```bash +pnpm nx run modernjs-rsc:serve +``` + +Open `http://localhost:3060`. + +This app enables `experiments.rsc` + `experiments.asyncStartup` and exposes: + +- `modernjs_rsc/ServerMessage` +- `modernjs_rsc/ClientCounter` diff --git a/apps/modernjs-rsc/modern.config.ts b/apps/modernjs-rsc/modern.config.ts new file mode 100644 index 00000000000..ddee2a9e488 --- /dev/null +++ b/apps/modernjs-rsc/modern.config.ts @@ -0,0 +1,13 @@ +import { appTools, defineConfig } from '@modern-js/app-tools'; +import { moduleFederationPlugin } from '@module-federation/modern-js-v3'; + +export default defineConfig({ + server: { + port: 3060, + ssr: { + mode: 'stream', + }, + rsc: true, + }, + plugins: [appTools(), moduleFederationPlugin()], +}); diff --git a/apps/modernjs-rsc/module-federation.config.ts b/apps/modernjs-rsc/module-federation.config.ts new file mode 100644 index 00000000000..2db6d421faf --- /dev/null +++ b/apps/modernjs-rsc/module-federation.config.ts @@ -0,0 +1,21 @@ +import { createModuleFederationConfig } from '@module-federation/modern-js-v3'; + +export default createModuleFederationConfig({ + name: 'modernjs_rsc', + manifest: { + filePath: 'static', + }, + filename: 'static/remoteEntry.js', + exposes: { + './ServerMessage': './src/server/ServerMessage.tsx', + './ClientCounter': './src/client/ClientCounter.tsx', + }, + shared: { + react: { singleton: true }, + 'react-dom': { singleton: true }, + }, + experiments: { + asyncStartup: true, + rsc: true, + }, +}); diff --git a/apps/modernjs-rsc/package.json b/apps/modernjs-rsc/package.json new file mode 100644 index 00000000000..81995f84530 --- /dev/null +++ b/apps/modernjs-rsc/package.json @@ -0,0 +1,30 @@ +{ + "name": "modernjs-rsc", + "private": true, + "version": "0.1.0", + "scripts": { + "dev": "modern dev", + "build": "modern build", + "start": "modern start", + "serve": "modern serve", + "lint": "modern lint" + }, + "engines": { + "node": ">=16.18.1" + }, + "dependencies": { + "@babel/runtime": "7.28.2", + "@modern-js/runtime": "3.0.1", + "@module-federation/modern-js-v3": "workspace:*", + "react": "~18.3.1", + "react-dom": "~18.3.1" + }, + "devDependencies": { + "@modern-js/app-tools": "3.0.1", + "@modern-js/tsconfig": "3.0.1", + "@types/node": "^20.19.5", + "@types/react": "~18.2.0", + "@types/react-dom": "~18.3.0", + "typescript": "~5.0.4" + } +} diff --git a/apps/modernjs-rsc/project.json b/apps/modernjs-rsc/project.json new file mode 100644 index 00000000000..8277076bc65 --- /dev/null +++ b/apps/modernjs-rsc/project.json @@ -0,0 +1,44 @@ +{ + "name": "modernjs-rsc", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/modernjs-rsc/src", + "projectType": "application", + "tags": [], + "implicitDependencies": ["typescript"], + "targets": { + "build": { + "executor": "nx:run-commands", + "options": { + "dependsOn": [ + { + "target": "build", + "dependencies": true + } + ], + "commands": [ + { + "command": "cd apps/modernjs-rsc; pnpm run build", + "forwardAllArgs": true + } + ] + } + }, + "serve": { + "executor": "nx:run-commands", + "options": { + "dependsOn": [ + { + "target": "build", + "dependencies": true + } + ], + "commands": [ + { + "command": "cd apps/modernjs-rsc; pnpm run dev", + "forwardAllArgs": false + } + ] + } + } + } +} diff --git a/apps/modernjs-rsc/src/client/ClientCounter.tsx b/apps/modernjs-rsc/src/client/ClientCounter.tsx new file mode 100644 index 00000000000..97e4871f519 --- /dev/null +++ b/apps/modernjs-rsc/src/client/ClientCounter.tsx @@ -0,0 +1,18 @@ +'use client'; + +import { useState } from 'react'; + +const ClientCounter = () => { + const [count, setCount] = useState(0); + + return ( +
+

Client Component

+ +
+ ); +}; + +export default ClientCounter; diff --git a/apps/modernjs-rsc/src/modern-app-env.d.ts b/apps/modernjs-rsc/src/modern-app-env.d.ts new file mode 100644 index 00000000000..ca35b059576 --- /dev/null +++ b/apps/modernjs-rsc/src/modern-app-env.d.ts @@ -0,0 +1,4 @@ +/// +/// +/// +/// diff --git a/apps/modernjs-rsc/src/routes/page.tsx b/apps/modernjs-rsc/src/routes/page.tsx new file mode 100644 index 00000000000..92154d7b644 --- /dev/null +++ b/apps/modernjs-rsc/src/routes/page.tsx @@ -0,0 +1,15 @@ +import ClientCounter from '../client/ClientCounter'; +import ServerMessage from '../server/ServerMessage'; + +const Page = async () => { + return ( +
+

ModernJS RSC + Module Federation

+

This example exposes both server and client components.

+ + +
+ ); +}; + +export default Page; diff --git a/apps/modernjs-rsc/src/server/ServerMessage.tsx b/apps/modernjs-rsc/src/server/ServerMessage.tsx new file mode 100644 index 00000000000..7f391cc920a --- /dev/null +++ b/apps/modernjs-rsc/src/server/ServerMessage.tsx @@ -0,0 +1,18 @@ +const wait = (ms: number) => + new Promise((resolve) => { + setTimeout(resolve, ms); + }); + +const ServerMessage = async () => { + await wait(20); + const renderedAt = new Date().toISOString(); + + return ( +
+

Server Component

+

Rendered at: {renderedAt}

+
+ ); +}; + +export default ServerMessage; diff --git a/apps/modernjs-rsc/tsconfig.app.json b/apps/modernjs-rsc/tsconfig.app.json new file mode 100644 index 00000000000..c7c9676cbdd --- /dev/null +++ b/apps/modernjs-rsc/tsconfig.app.json @@ -0,0 +1,17 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": ["node", "express"], + "target": "ES2015", + "module": "commonjs", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"], + "include": ["src/**/*.ts", "src/**/*.tsx"] +} diff --git a/apps/modernjs-rsc/tsconfig.json b/apps/modernjs-rsc/tsconfig.json new file mode 100644 index 00000000000..8ea6df7ff72 --- /dev/null +++ b/apps/modernjs-rsc/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "@modern-js/tsconfig/base", + "compilerOptions": { + "declaration": false, + "jsx": "preserve", + "baseUrl": "./", + "paths": { + "@/*": ["./src/*"], + "*": ["./@mf-types/*"] + } + }, + "include": ["src", "config", "modern.config.ts", "./@mf-types"], + "exclude": ["**/node_modules"] +} diff --git a/packages/modernjs-v3/src/cli/configPlugin.ts b/packages/modernjs-v3/src/cli/configPlugin.ts index e0a3ae15a80..4cde18d11cf 100644 --- a/packages/modernjs-v3/src/cli/configPlugin.ts +++ b/packages/modernjs-v3/src/cli/configPlugin.ts @@ -787,12 +787,7 @@ export function patchBundlerConfig(options: { patchIgnoreWarning(chain); if (rscMfEnabled && isServer) { - chain.resolve.conditionNames - .clear() - .add('require') - .add('import') - .add('default') - .add('node'); + chain.resolve.conditionNames.add('react-server'); } if (rscMfEnabled && hasExposes(mfConfig.exposes)) { diff --git a/packages/modernjs-v3/src/server/asyncStartupLoader.ts b/packages/modernjs-v3/src/server/asyncStartupLoader.ts index 4946ee9f82d..4d861faed50 100644 --- a/packages/modernjs-v3/src/server/asyncStartupLoader.ts +++ b/packages/modernjs-v3/src/server/asyncStartupLoader.ts @@ -1,4 +1,5 @@ import path from 'path'; +import module from 'module'; import vm from 'vm'; import type { BundleLoaderStrategy } from '@modern-js/server-core/node'; import fs from 'fs-extra'; @@ -54,9 +55,11 @@ export const mfAsyncStartupLoaderStrategy: BundleLoaderStrategy = async ( __filename: string, __dirname: string, ) => void; + const bundleRequire = module.createRequire(filepath); + runBundle( localModule.exports, - require, + bundleRequire, localModule, filepath, path.dirname(filepath), From 42d627add3b3eb5b560fd18f39ea9b7edfed0b92 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Tue, 24 Feb 2026 12:09:34 -0800 Subject: [PATCH 11/17] fix(modernjs-rsc): sync lockfile for CI installs Update pnpm-lock.yaml to match branch manifests. This restores frozen-lockfile installs in CI and Netlify preview builds. Co-authored-by: Cursor --- pnpm-lock.yaml | 452 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 352 insertions(+), 100 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3a85726a1c5..2f22e6762de 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -905,7 +905,7 @@ importers: version: 0.80.0(@babel/core@7.28.6) '@react-native/eslint-config': specifier: 0.80.0 - version: 0.80.0(eslint@9.26.0(hono@4.11.7)(jiti@2.6.1))(jest@29.7.0(@types/node@22.19.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@22.19.9)(typescript@5.0.4)))(prettier@2.8.8)(typescript@5.0.4) + version: 0.80.0(eslint@9.26.0(hono@4.11.7)(jiti@2.6.1))(jest@29.7.0(@types/node@20.19.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@20.19.5)(typescript@5.0.4)))(prettier@2.8.8)(typescript@5.0.4) '@react-native/gradle-plugin': specifier: 0.80.0 version: 0.80.0 @@ -944,7 +944,7 @@ importers: version: 9.26.0(hono@4.11.7)(jiti@2.6.1) jest: specifier: ^29.6.3 - version: 29.7.0(@types/node@22.19.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@22.19.9)(typescript@5.0.4)) + version: 29.7.0(@types/node@20.19.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@20.19.5)(typescript@5.0.4)) nodemon: specifier: ^3.1.9 version: 3.1.11 @@ -996,7 +996,7 @@ importers: version: 0.80.0(@babel/core@7.28.6) '@react-native/eslint-config': specifier: 0.80.0 - version: 0.80.0(eslint@9.26.0(hono@4.11.7)(jiti@2.6.1))(jest@29.7.0(@types/node@20.19.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@20.19.5)(typescript@5.0.4)))(prettier@2.8.8)(typescript@5.0.4) + version: 0.80.0(eslint@9.26.0(hono@4.11.7)(jiti@2.6.1))(jest@29.7.0(@types/node@22.19.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@22.19.9)(typescript@5.0.4)))(prettier@2.8.8)(typescript@5.0.4) '@react-native/metro-config': specifier: 0.80.0 version: 0.80.0(@babel/core@7.28.6) @@ -1032,7 +1032,7 @@ importers: version: 9.26.0(hono@4.11.7)(jiti@2.6.1) jest: specifier: ^29.6.3 - version: 29.7.0(@types/node@20.19.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@20.19.5)(typescript@5.0.4)) + version: 29.7.0(@types/node@22.19.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@22.19.9)(typescript@5.0.4)) nodemon: specifier: ^3.1.9 version: 3.1.11 @@ -1137,6 +1137,43 @@ importers: specifier: 5.0.4 version: 5.0.4 + apps/modernjs-rsc: + dependencies: + '@babel/runtime': + specifier: 7.28.2 + version: 7.28.2 + '@modern-js/runtime': + specifier: 3.0.1 + version: 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@18.3.1(react@18.3.1))(react-server-dom-webpack@19.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@18.3.1) + '@module-federation/modern-js-v3': + specifier: workspace:* + version: link:../../packages/modernjs-v3 + react: + specifier: ~18.3.1 + version: 18.3.1 + react-dom: + specifier: ~18.3.1 + version: 18.3.1(react@18.3.1) + devDependencies: + '@modern-js/app-tools': + specifier: 3.0.1 + version: 3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(core-js@3.48.0)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.57.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@20.19.5)(typescript@5.0.4))(tsconfig-paths@4.2.0)(tslib@2.8.1)(typescript@5.0.4)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + '@modern-js/tsconfig': + specifier: 3.0.1 + version: 3.0.1 + '@types/node': + specifier: ^20.19.5 + version: 20.19.5 + '@types/react': + specifier: ~18.2.0 + version: 18.2.79 + '@types/react-dom': + specifier: ~18.3.0 + version: 18.3.7(@types/react@18.2.79) + typescript: + specifier: ~5.0.4 + version: 5.0.4 + apps/modernjs-ssr/dynamic-nested-remote: dependencies: '@babel/runtime': @@ -3544,13 +3581,13 @@ importers: devDependencies: '@modern-js/app-tools': specifier: 2.70.5 - version: 2.70.5(@rspack/core@1.7.5(@swc/helpers@0.5.18))(@swc/core@1.15.10(@swc/helpers@0.5.18))(encoding@0.1.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.57.0)(styled-components@6.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@22.19.9)(typescript@5.8.2))(tsconfig-paths@4.2.0)(tslib@2.8.1)(type-fest@4.41.0)(typescript@5.8.2)(webpack-cli@5.1.4)(webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack-hot-middleware@2.26.1) + version: 2.70.5(@rspack/core@1.7.5(@swc/helpers@0.5.18))(@swc/core@1.15.10(@swc/helpers@0.5.18))(encoding@0.1.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.57.0)(styled-components@6.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@22.19.9)(typescript@5.8.2))(tsconfig-paths@4.2.0)(tslib@2.8.1)(type-fest@4.41.0)(typescript@5.8.2)(webpack-cli@5.1.4)(webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack-hot-middleware@2.26.1) '@modern-js/module-tools': specifier: 2.70.5 version: 2.70.5(@types/node@22.19.9)(typescript@5.8.2) '@modern-js/runtime': specifier: 2.70.5 - version: 2.70.5(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@19.2.4) + version: 2.70.5(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@19.2.4) '@modern-js/server-runtime': specifier: 2.70.5 version: 2.70.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -3638,13 +3675,13 @@ importers: devDependencies: '@modern-js/app-tools': specifier: 3.0.1 - version: 3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(core-js@3.48.0)(encoding@0.1.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.57.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@22.19.9)(typescript@5.8.2))(tsconfig-paths@4.2.0)(tslib@2.8.1)(typescript@5.8.2)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + version: 3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(core-js@3.48.0)(encoding@0.1.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.57.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@22.19.9)(typescript@5.8.2))(tsconfig-paths@4.2.0)(tslib@2.8.1)(typescript@5.8.2)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)) '@modern-js/module-tools': specifier: 2.70.5 version: 2.70.5(@types/node@22.19.9)(typescript@5.8.2) '@modern-js/runtime': specifier: 3.0.1 - version: 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@19.2.4) + version: 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)))(react@19.2.4) '@modern-js/server-core': specifier: 3.0.1 version: 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -28163,7 +28200,7 @@ snapshots: '@babel/helpers': 7.28.6 '@babel/parser': 7.29.0 '@babel/template': 7.28.6 - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 '@babel/types': 7.29.0 convert-source-map: 1.9.0 debug: 4.4.3(supports-color@8.1.1) @@ -28232,9 +28269,17 @@ snapshots: eslint-visitor-keys: 2.1.0 semver: 6.3.1 + '@babel/eslint-parser@7.28.6(@babel/core@7.29.0)(eslint@8.57.1)': + dependencies: + '@babel/core': 7.29.0 + '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1 + eslint: 8.57.1 + eslint-visitor-keys: 2.1.0 + semver: 6.3.1 + '@babel/eslint-plugin@7.27.1(@babel/eslint-parser@7.28.6(@babel/core@7.28.6)(eslint@8.57.1))(eslint@8.57.1)': dependencies: - '@babel/eslint-parser': 7.28.6(@babel/core@7.28.6)(eslint@8.57.1) + '@babel/eslint-parser': 7.28.6(@babel/core@7.29.0)(eslint@8.57.1) eslint: 8.57.1 eslint-rule-composer: 0.3.0 @@ -28274,7 +28319,7 @@ snapshots: '@babel/helper-optimise-call-expression': 7.27.1 '@babel/helper-replace-supers': 7.28.6(@babel/core@7.28.6) '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -28287,7 +28332,7 @@ snapshots: '@babel/helper-optimise-call-expression': 7.27.1 '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -28332,21 +28377,21 @@ snapshots: '@babel/helper-member-expression-to-functions@7.28.5': dependencies: - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 '@babel/types': 7.29.0 transitivePeerDependencies: - supports-color '@babel/helper-module-imports@7.28.6': dependencies: - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 '@babel/types': 7.29.0 transitivePeerDependencies: - supports-color '@babel/helper-module-imports@7.28.6(supports-color@5.5.0)': dependencies: - '@babel/traverse': 7.28.6(supports-color@5.5.0) + '@babel/traverse': 7.29.0(supports-color@5.5.0) '@babel/types': 7.29.0 transitivePeerDependencies: - supports-color @@ -28391,7 +28436,7 @@ snapshots: '@babel/core': 7.28.6 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-wrap-function': 7.28.6 - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color @@ -28400,7 +28445,7 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-wrap-function': 7.28.6 - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color @@ -28409,7 +28454,7 @@ snapshots: '@babel/core': 7.28.6 '@babel/helper-member-expression-to-functions': 7.28.5 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color @@ -28418,13 +28463,13 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-member-expression-to-functions': 7.28.5 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color '@babel/helper-skip-transparent-expression-wrappers@7.27.1': dependencies: - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 '@babel/types': 7.29.0 transitivePeerDependencies: - supports-color @@ -28438,7 +28483,7 @@ snapshots: '@babel/helper-wrap-function@7.28.6': dependencies: '@babel/template': 7.28.6 - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 '@babel/types': 7.29.0 transitivePeerDependencies: - supports-color @@ -28460,7 +28505,7 @@ snapshots: dependencies: '@babel/core': 7.28.6 '@babel/helper-plugin-utils': 7.28.6 - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color @@ -28468,7 +28513,7 @@ snapshots: dependencies: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.28.6 - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color @@ -28514,7 +28559,7 @@ snapshots: dependencies: '@babel/core': 7.28.6 '@babel/helper-plugin-utils': 7.28.6 - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color @@ -28522,7 +28567,7 @@ snapshots: dependencies: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.28.6 - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color @@ -28808,7 +28853,7 @@ snapshots: '@babel/core': 7.28.6 '@babel/helper-plugin-utils': 7.28.6 '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.6) - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color @@ -28817,7 +28862,7 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.28.6 '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.29.0) - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color @@ -28899,7 +28944,7 @@ snapshots: '@babel/helper-globals': 7.28.0 '@babel/helper-plugin-utils': 7.28.6 '@babel/helper-replace-supers': 7.28.6(@babel/core@7.28.6) - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color @@ -28911,7 +28956,7 @@ snapshots: '@babel/helper-globals': 7.28.0 '@babel/helper-plugin-utils': 7.28.6 '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color @@ -28931,7 +28976,7 @@ snapshots: dependencies: '@babel/core': 7.28.6 '@babel/helper-plugin-utils': 7.28.6 - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color @@ -28939,7 +28984,7 @@ snapshots: dependencies: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.28.6 - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color @@ -29050,7 +29095,7 @@ snapshots: '@babel/core': 7.28.6 '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-plugin-utils': 7.28.6 - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color @@ -29059,7 +29104,7 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-plugin-utils': 7.28.6 - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color @@ -29141,7 +29186,7 @@ snapshots: '@babel/helper-module-transforms': 7.28.6(@babel/core@7.28.6) '@babel/helper-plugin-utils': 7.28.6 '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color @@ -29151,7 +29196,7 @@ snapshots: '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) '@babel/helper-plugin-utils': 7.28.6 '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color @@ -29220,7 +29265,7 @@ snapshots: '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.6) '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.6) - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color @@ -29231,7 +29276,7 @@ snapshots: '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color @@ -29810,6 +29855,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/traverse@7.29.0(supports-color@5.5.0)': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.0 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + '@babel/types@7.28.6': dependencies: '@babel/helper-string-parser': 7.27.1 @@ -32329,7 +32386,7 @@ snapshots: '@modern-js-app/eslint-config@2.59.0(typescript@5.9.3)': dependencies: '@babel/core': 7.28.6 - '@babel/eslint-parser': 7.28.6(@babel/core@7.28.6)(eslint@8.57.1) + '@babel/eslint-parser': 7.28.6(@babel/core@7.29.0)(eslint@8.57.1) '@babel/eslint-plugin': 7.27.1(@babel/eslint-parser@7.28.6(@babel/core@7.28.6)(eslint@8.57.1))(eslint@8.57.1) '@modern-js/babel-preset': 2.59.0(@rsbuild/core@1.0.1-rc.4) '@rsbuild/core': 1.0.1-rc.4 @@ -32460,7 +32517,7 @@ snapshots: - webpack-hot-middleware - webpack-plugin-serve - '@modern-js/app-tools@2.70.5(@rspack/core@1.7.5(@swc/helpers@0.5.18))(@swc/core@1.15.10(@swc/helpers@0.5.18))(encoding@0.1.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.57.0)(styled-components@6.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@22.19.9)(typescript@5.8.2))(tsconfig-paths@4.2.0)(tslib@2.8.1)(type-fest@4.41.0)(typescript@5.8.2)(webpack-cli@5.1.4)(webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack-hot-middleware@2.26.1)': + '@modern-js/app-tools@2.70.5(@rspack/core@1.7.5(@swc/helpers@0.5.18))(@swc/core@1.15.10(@swc/helpers@0.5.18))(encoding@0.1.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.57.0)(styled-components@6.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@22.19.9)(typescript@5.8.2))(tsconfig-paths@4.2.0)(tslib@2.8.1)(type-fest@4.41.0)(typescript@5.8.2)(webpack-cli@5.1.4)(webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack-hot-middleware@2.26.1)': dependencies: '@babel/parser': 7.28.6 '@babel/traverse': 7.28.6 @@ -32477,7 +32534,7 @@ snapshots: '@modern-js/server-core': 2.70.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@modern-js/server-utils': 2.70.5(@babel/traverse@7.28.6)(@rsbuild/core@1.7.3) '@modern-js/types': 2.70.5 - '@modern-js/uni-builder': 2.70.5(@rspack/core@1.7.5(@swc/helpers@0.5.18))(esbuild@0.25.5)(styled-components@6.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(tslib@2.8.1)(type-fest@4.41.0)(typescript@5.8.2)(webpack-cli@5.1.4)(webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack-hot-middleware@2.26.1) + '@modern-js/uni-builder': 2.70.5(@rspack/core@1.7.5(@swc/helpers@0.5.18))(esbuild@0.25.5)(styled-components@6.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(tslib@2.8.1)(type-fest@4.41.0)(typescript@5.8.2)(webpack-cli@5.1.4)(webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack-hot-middleware@2.26.1) '@modern-js/utils': 2.70.5 '@rsbuild/core': 1.7.3 '@rsbuild/plugin-node-polyfill': 1.4.3(@rsbuild/core@1.7.3) @@ -32524,9 +32581,9 @@ snapshots: '@modern-js/app-tools@3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(core-js@3.48.0)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.57.0)(ts-node@10.8.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@20.19.5)(typescript@5.0.4))(tsconfig-paths@3.14.2)(tslib@2.8.1)(typescript@5.0.4)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4(webpack-dev-server@5.2.3)(webpack@5.104.1)))': dependencies: - '@babel/parser': 7.28.6 - '@babel/traverse': 7.28.6 - '@babel/types': 7.28.6 + '@babel/parser': 7.29.0 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 '@modern-js/builder': 3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(esbuild@0.25.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1)(typescript@5.0.4)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4(webpack-dev-server@5.2.3)(webpack@5.104.1))) '@modern-js/i18n-utils': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@modern-js/plugin': 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -32575,9 +32632,9 @@ snapshots: '@modern-js/app-tools@3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(core-js@3.48.0)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.57.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@20.19.5)(typescript@5.0.4))(tsconfig-paths@4.2.0)(tslib@2.8.1)(typescript@5.0.4)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4(webpack-dev-server@5.2.3)(webpack@5.104.1)))': dependencies: - '@babel/parser': 7.28.6 - '@babel/traverse': 7.28.6 - '@babel/types': 7.28.6 + '@babel/parser': 7.29.0 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 '@modern-js/builder': 3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(esbuild@0.25.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1)(typescript@5.0.4)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4(webpack-dev-server@5.2.3)(webpack@5.104.1))) '@modern-js/i18n-utils': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@modern-js/plugin': 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -32624,12 +32681,63 @@ snapshots: - webpack - webpack-hot-middleware - '@modern-js/app-tools@3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(core-js@3.48.0)(encoding@0.1.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.57.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@22.19.9)(typescript@5.8.2))(tsconfig-paths@4.2.0)(tslib@2.8.1)(typescript@5.8.2)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4))': + '@modern-js/app-tools@3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(core-js@3.48.0)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.57.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@20.19.5)(typescript@5.0.4))(tsconfig-paths@4.2.0)(tslib@2.8.1)(typescript@5.0.4)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4))': dependencies: - '@babel/parser': 7.28.6 - '@babel/traverse': 7.28.6 - '@babel/types': 7.28.6 - '@modern-js/builder': 3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(esbuild@0.25.5)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tslib@2.8.1)(typescript@5.8.2)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + '@babel/parser': 7.29.0 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@modern-js/builder': 3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(esbuild@0.25.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1)(typescript@5.0.4)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + '@modern-js/i18n-utils': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@modern-js/plugin': 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@modern-js/plugin-data-loader': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@modern-js/prod-server': 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@modern-js/server': 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@20.19.5)(typescript@5.0.4))(tsconfig-paths@4.2.0) + '@modern-js/server-core': 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@modern-js/server-utils': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@modern-js/types': 3.0.1 + '@modern-js/utils': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@rsbuild/core': 2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0) + '@swc/helpers': 0.5.18 + es-module-lexer: 1.7.0 + esbuild: 0.25.5 + esbuild-register: 3.6.0(esbuild@0.25.5) + flatted: 3.3.3 + mlly: 1.8.0 + ndepe: 0.1.13(encoding@0.1.13)(rollup@4.57.0) + pkg-types: 1.3.1 + std-env: 3.10.0 + optionalDependencies: + ts-node: 10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@20.19.5)(typescript@5.0.4) + tsconfig-paths: 4.2.0 + transitivePeerDependencies: + - '@module-federation/runtime-tools' + - '@parcel/css' + - '@rspack/core' + - '@swc/css' + - bufferutil + - clean-css + - core-js + - csso + - debug + - devcert + - encoding + - lightningcss + - react + - react-dom + - rollup + - supports-color + - tslib + - typescript + - utf-8-validate + - webpack + - webpack-hot-middleware + + '@modern-js/app-tools@3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(core-js@3.48.0)(encoding@0.1.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.57.0)(ts-node@10.9.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(@types/node@22.19.9)(typescript@5.8.2))(tsconfig-paths@4.2.0)(tslib@2.8.1)(typescript@5.8.2)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4))': + dependencies: + '@babel/parser': 7.29.0 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@modern-js/builder': 3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(esbuild@0.25.5)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tslib@2.8.1)(typescript@5.8.2)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)) '@modern-js/i18n-utils': 3.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@modern-js/plugin': 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@modern-js/plugin-data-loader': 3.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -32734,7 +32842,7 @@ snapshots: '@babel/preset-env': 7.28.6(@babel/core@7.28.6) '@babel/preset-typescript': 7.28.5(@babel/core@7.28.6) '@babel/runtime': 7.28.2 - '@babel/types': 7.28.6 + '@babel/types': 7.29.0 '@rsbuild/plugin-babel': 1.0.1-rc.4(@rsbuild/core@1.0.1-rc.4) '@swc/helpers': 0.5.3 '@types/babel__core': 7.20.5 @@ -32858,10 +32966,10 @@ snapshots: - webpack - webpack-hot-middleware - '@modern-js/builder@3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(esbuild@0.25.5)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tslib@2.8.1)(typescript@5.8.2)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4))': + '@modern-js/builder@3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(esbuild@0.25.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1)(typescript@5.0.4)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4))': dependencies: '@modern-js/flight-server-transform-plugin': 3.0.1 - '@modern-js/utils': 3.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@modern-js/utils': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@rsbuild/core': 2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0) '@rsbuild/plugin-assets-retry': 1.5.1(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)) '@rsbuild/plugin-check-syntax': 1.6.1(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)) @@ -32871,6 +32979,57 @@ snapshots: '@rsbuild/plugin-rem': 1.0.5(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)) '@rsbuild/plugin-sass': 1.5.0(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)) '@rsbuild/plugin-source-build': 1.0.4(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)) + '@rsbuild/plugin-svgr': 1.3.0(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0))(typescript@5.0.4)(webpack-hot-middleware@2.26.1) + '@rsbuild/plugin-type-check': 1.3.3(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0))(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(tslib@2.8.1)(typescript@5.0.4) + '@rsbuild/plugin-typed-css-modules': 1.2.1(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)) + '@swc/core': 1.15.10(@swc/helpers@0.5.18) + '@swc/helpers': 0.5.18 + autoprefixer: 10.4.24(postcss@8.5.6) + browserslist: 4.28.1 + core-js: 3.48.0 + cssnano: 6.1.2(postcss@8.5.6) + html-minifier-terser: 7.2.0 + lodash: 4.17.23 + postcss: 8.5.6 + postcss-custom-properties: 13.3.12(postcss@8.5.6) + postcss-flexbugs-fixes: 5.0.2(postcss@8.5.6) + postcss-font-variant: 5.0.0(postcss@8.5.6) + postcss-initial: 4.0.1(postcss@8.5.6) + postcss-media-minmax: 5.0.0(postcss@8.5.6) + postcss-nesting: 12.1.5(postcss@8.5.6) + postcss-page-break: 3.0.4(postcss@8.5.6) + rspack-manifest-plugin: 5.2.1(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18)) + ts-deepmerge: 7.0.3 + transitivePeerDependencies: + - '@module-federation/runtime-tools' + - '@parcel/css' + - '@rspack/core' + - '@swc/css' + - clean-css + - csso + - esbuild + - lightningcss + - react + - react-dom + - supports-color + - tslib + - typescript + - webpack + - webpack-hot-middleware + + '@modern-js/builder@3.0.1(@module-federation/runtime-tools@2.0.1)(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(esbuild@0.25.5)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tslib@2.8.1)(typescript@5.8.2)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4))': + dependencies: + '@modern-js/flight-server-transform-plugin': 3.0.1 + '@modern-js/utils': 3.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@rsbuild/core': 2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0) + '@rsbuild/plugin-assets-retry': 1.5.1(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)) + '@rsbuild/plugin-check-syntax': 1.6.1(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)) + '@rsbuild/plugin-css-minimizer': 1.1.1(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0))(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)) + '@rsbuild/plugin-less': 1.6.0(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)) + '@rsbuild/plugin-react': 1.4.4(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0))(webpack-hot-middleware@2.26.1) + '@rsbuild/plugin-rem': 1.0.5(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)) + '@rsbuild/plugin-sass': 1.5.0(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)) + '@rsbuild/plugin-source-build': 1.0.4(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)) '@rsbuild/plugin-svgr': 1.3.0(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0))(typescript@5.8.2)(webpack-hot-middleware@2.26.1) '@rsbuild/plugin-type-check': 1.3.3(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0))(@rspack/core@2.0.0-beta.0(@module-federation/runtime-tools@2.0.1)(@swc/helpers@0.5.18))(tslib@2.8.1)(typescript@5.8.2) '@rsbuild/plugin-typed-css-modules': 1.2.1(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)) @@ -33280,7 +33439,7 @@ snapshots: react-dom: 19.2.4(react@19.2.4) react-server-dom-webpack: 19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.18.20)(webpack-cli@5.1.4)) - '@modern-js/render@2.70.5(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@19.2.4)': + '@modern-js/render@2.70.5(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@19.2.4)': dependencies: '@modern-js/types': 2.70.5 '@modern-js/utils': 2.70.5 @@ -33298,14 +33457,23 @@ snapshots: react-dom: 18.3.1(react@18.3.1) react-server-dom-webpack: 19.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4(webpack-dev-server@5.2.3)(webpack@5.104.1))) - '@modern-js/render@3.0.1(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@19.2.4)': + '@modern-js/render@3.0.1(react-dom@18.3.1(react@18.3.1))(react-server-dom-webpack@19.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@18.3.1)': + dependencies: + '@modern-js/types': 3.0.1 + '@modern-js/utils': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@swc/helpers': 0.5.18 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-server-dom-webpack: 19.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + + '@modern-js/render@3.0.1(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)))(react@19.2.4)': dependencies: '@modern-js/types': 3.0.1 '@modern-js/utils': 3.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@swc/helpers': 0.5.18 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - react-server-dom-webpack: 19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + react-server-dom-webpack: 19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)) '@modern-js/rsbuild-plugin-esbuild@2.70.2(@swc/core@1.15.10(@swc/helpers@0.5.18))(webpack-cli@5.1.4)': dependencies: @@ -33423,7 +33591,7 @@ snapshots: - react-server-dom-webpack - supports-color - '@modern-js/runtime@2.70.5(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@19.2.4)': + '@modern-js/runtime@2.70.5(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@19.2.4)': dependencies: '@babel/core': 7.28.6 '@babel/types': 7.28.6 @@ -33433,7 +33601,7 @@ snapshots: '@modern-js/plugin': 2.70.5 '@modern-js/plugin-data-loader': 2.70.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@modern-js/plugin-v2': 2.70.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@modern-js/render': 2.70.5(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@19.2.4) + '@modern-js/render': 2.70.5(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@19.2.4) '@modern-js/runtime-utils': 2.70.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@modern-js/types': 2.70.5 '@modern-js/utils': 2.70.5 @@ -33484,13 +33652,42 @@ snapshots: - core-js - react-server-dom-webpack - '@modern-js/runtime@3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@19.2.4)': + '@modern-js/runtime@3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@18.3.1(react@18.3.1))(react-server-dom-webpack@19.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@18.3.1)': + dependencies: + '@loadable/component': 5.16.7(react@18.3.1) + '@loadable/server': 5.16.7(@loadable/component@5.16.7(react@18.3.1))(react@18.3.1) + '@modern-js/plugin': 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@modern-js/plugin-data-loader': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@modern-js/render': 3.0.1(react-dom@18.3.1(react@18.3.1))(react-server-dom-webpack@19.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@18.3.1) + '@modern-js/runtime-utils': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@modern-js/types': 3.0.1 + '@modern-js/utils': 3.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@swc/helpers': 0.5.18 + '@swc/plugin-loadable-components': 11.5.0 + '@types/loadable__component': 5.13.10 + '@types/react-helmet': 6.1.11 + cookie: 0.7.2 + entities: 7.0.1 + es-module-lexer: 1.7.0 + esbuild: 0.25.5 + invariant: 2.2.4 + isbot: 3.8.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-helmet: 6.1.0(react@18.3.1) + react-is: 18.3.1 + transitivePeerDependencies: + - '@module-federation/runtime-tools' + - core-js + - react-server-dom-webpack + + '@modern-js/runtime@3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)))(react@19.2.4)': dependencies: '@loadable/component': 5.16.7(react@19.2.4) '@loadable/server': 5.16.7(@loadable/component@5.16.7(react@19.2.4))(react@19.2.4) '@modern-js/plugin': 3.0.1(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@modern-js/plugin-data-loader': 3.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@modern-js/render': 3.0.1(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(react@19.2.4) + '@modern-js/render': 3.0.1(react-dom@19.2.4(react@19.2.4))(react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)))(react@19.2.4) '@modern-js/runtime-utils': 3.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@modern-js/types': 3.0.1 '@modern-js/utils': 3.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -34126,7 +34323,7 @@ snapshots: - webpack-hot-middleware - webpack-plugin-serve - '@modern-js/uni-builder@2.70.5(@rspack/core@1.7.5(@swc/helpers@0.5.18))(esbuild@0.25.5)(styled-components@6.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(tslib@2.8.1)(type-fest@4.41.0)(typescript@5.8.2)(webpack-cli@5.1.4)(webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack-hot-middleware@2.26.1)': + '@modern-js/uni-builder@2.70.5(@rspack/core@1.7.5(@swc/helpers@0.5.18))(esbuild@0.25.5)(styled-components@6.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(tslib@2.8.1)(type-fest@4.41.0)(typescript@5.8.2)(webpack-cli@5.1.4)(webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack-hot-middleware@2.26.1)': dependencies: '@babel/core': 7.28.6 '@babel/preset-react': 7.28.5(@babel/core@7.28.6) @@ -34134,12 +34331,12 @@ snapshots: '@modern-js/babel-preset': 2.70.5(@rsbuild/core@1.7.3) '@modern-js/flight-server-transform-plugin': 2.70.5 '@modern-js/utils': 2.70.5 - '@pmmmwh/react-refresh-webpack-plugin': 0.5.16(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + '@pmmmwh/react-refresh-webpack-plugin': 0.5.16(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) '@rsbuild/core': 1.7.3 '@rsbuild/plugin-assets-retry': 1.5.2(@rsbuild/core@1.7.3) '@rsbuild/plugin-babel': 1.1.0(@rsbuild/core@1.7.3) '@rsbuild/plugin-check-syntax': 1.6.1(@rsbuild/core@1.7.3) - '@rsbuild/plugin-css-minimizer': 1.1.1(@rsbuild/core@1.7.3)(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + '@rsbuild/plugin-css-minimizer': 1.1.1(@rsbuild/core@1.7.3)(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) '@rsbuild/plugin-less': 1.6.0(@rsbuild/core@1.7.3) '@rsbuild/plugin-pug': 1.3.2(@rsbuild/core@1.7.3) '@rsbuild/plugin-react': 1.4.5(@rsbuild/core@1.7.3)(webpack-hot-middleware@2.26.1) @@ -34156,7 +34353,7 @@ snapshots: '@swc/core': 1.15.8(@swc/helpers@0.5.18) '@swc/helpers': 0.5.18 autoprefixer: 10.4.23(postcss@8.5.6) - babel-loader: 9.2.1(@babel/core@7.28.6)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + babel-loader: 9.2.1(@babel/core@7.28.6)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) babel-plugin-import: 1.13.8 babel-plugin-styled-components: 1.13.3(styled-components@6.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) babel-plugin-transform-react-remove-prop-types: 0.4.24 @@ -34165,7 +34362,7 @@ snapshots: es-module-lexer: 1.7.0 glob: 9.3.5 html-minifier-terser: 7.2.0 - html-webpack-plugin: 5.6.6(@rspack/core@1.7.5(@swc/helpers@0.5.18))(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + html-webpack-plugin: 5.6.6(@rspack/core@1.7.5(@swc/helpers@0.5.18))(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) jiti: 1.21.7 lodash: 4.17.23 magic-string: 0.30.21 @@ -34180,11 +34377,11 @@ snapshots: postcss-page-break: 3.0.4(postcss@8.5.6) react-refresh: 0.14.2 rspack-manifest-plugin: 5.0.3(@rspack/core@1.7.5(@swc/helpers@0.5.18)) - terser-webpack-plugin: 5.3.14(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + terser-webpack-plugin: 5.3.14(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) ts-deepmerge: 7.0.2 - ts-loader: 9.4.4(typescript@5.8.2)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) - webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4) - webpack-subresource-integrity: 5.1.0(html-webpack-plugin@5.6.6(@rspack/core@1.7.5(@swc/helpers@0.5.18))(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + ts-loader: 9.4.4(typescript@5.8.2)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + webpack: 5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4) + webpack-subresource-integrity: 5.1.0(html-webpack-plugin@5.6.6(@rspack/core@1.7.5(@swc/helpers@0.5.18))(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) transitivePeerDependencies: - '@parcel/css' - '@rspack/core' @@ -36405,7 +36602,7 @@ snapshots: webpack-dev-server: 5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.18.20)(webpack-cli@5.1.4)) webpack-hot-middleware: 2.26.1 - '@pmmmwh/react-refresh-webpack-plugin@0.5.16(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4))': + '@pmmmwh/react-refresh-webpack-plugin@0.5.16(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack-hot-middleware@2.26.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4))': dependencies: ansi-html: 0.0.9 core-js-pure: 3.48.0 @@ -36418,7 +36615,7 @@ snapshots: webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4) optionalDependencies: type-fest: 4.41.0 - webpack-dev-server: 5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + webpack-dev-server: 5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) webpack-hot-middleware: 2.26.1 '@pnpm/config.env-replace@1.1.0': {} @@ -38473,7 +38670,7 @@ snapshots: '@react-native/babel-plugin-codegen@0.80.0(@babel/core@7.28.6)': dependencies: - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 '@react-native/codegen': 0.80.0(@babel/core@7.28.6) transitivePeerDependencies: - '@babel/core' @@ -39325,7 +39522,7 @@ snapshots: - lightningcss - webpack - '@rsbuild/plugin-css-minimizer@1.1.1(@rsbuild/core@1.7.3)(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4))': + '@rsbuild/plugin-css-minimizer@1.1.1(@rsbuild/core@1.7.3)(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4))': dependencies: css-minimizer-webpack-plugin: 7.0.2(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) reduce-configs: 1.1.1 @@ -39355,6 +39552,21 @@ snapshots: - lightningcss - webpack + '@rsbuild/plugin-css-minimizer@1.1.1(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0))(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4))': + dependencies: + css-minimizer-webpack-plugin: 7.0.2(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)) + reduce-configs: 1.1.1 + optionalDependencies: + '@rsbuild/core': 2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0) + transitivePeerDependencies: + - '@parcel/css' + - '@swc/css' + - clean-css + - csso + - esbuild + - lightningcss + - webpack + '@rsbuild/plugin-css-minimizer@1.1.1(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0))(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4))': dependencies: css-minimizer-webpack-plugin: 7.0.2(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) @@ -39646,8 +39858,8 @@ snapshots: '@rsbuild/core': 2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0) '@rsbuild/plugin-react': 1.4.5(@rsbuild/core@2.0.0-beta.2(@module-federation/runtime-tools@2.0.1)(core-js@3.48.0))(webpack-hot-middleware@2.26.1) '@svgr/core': 8.1.0(typescript@5.0.4) - '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.8.2)) - '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.8.2))(typescript@5.0.4) + '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.0.4)) + '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.0.4))(typescript@5.0.4) deepmerge: 4.3.1 loader-utils: 3.3.1 transitivePeerDependencies: @@ -39823,9 +40035,9 @@ snapshots: '@rsbuild/webpack@1.6.1(@rsbuild/core@1.7.3)(@rspack/core@1.7.5(@swc/helpers@0.5.18))(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)': dependencies: '@rsbuild/core': 1.7.3 - copy-webpack-plugin: 11.0.0(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) - html-webpack-plugin: 5.6.6(@rspack/core@1.7.5(@swc/helpers@0.5.18))(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) - mini-css-extract-plugin: 2.9.4(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + copy-webpack-plugin: 11.0.0(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + html-webpack-plugin: 5.6.6(@rspack/core@1.7.5(@swc/helpers@0.5.18))(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + mini-css-extract-plugin: 2.9.4(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) picocolors: 1.1.1 reduce-configs: 1.1.1 tsconfig-paths-webpack-plugin: 4.2.0 @@ -41040,7 +41252,7 @@ snapshots: '@storybook/cli@7.6.21(encoding@0.1.13)': dependencies: '@babel/core': 7.28.6 - '@babel/preset-env': 7.28.6(@babel/core@7.28.6) + '@babel/preset-env': 7.28.6(@babel/core@7.29.0) '@babel/types': 7.29.0 '@ndelangen/get-tarball': 3.0.9 '@storybook/codemod': 7.6.21 @@ -41289,7 +41501,7 @@ snapshots: dependencies: '@babel/generator': 7.29.1 '@babel/parser': 7.29.0 - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 '@babel/types': 7.29.0 '@storybook/csf': 0.1.13 '@storybook/types': 7.6.21 @@ -41698,6 +41910,16 @@ snapshots: '@babel/types': 7.29.0 entities: 4.5.0 + '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.0.4))': + dependencies: + '@babel/core': 7.28.6 + '@svgr/babel-preset': 8.1.0(@babel/core@7.28.6) + '@svgr/core': 8.1.0(typescript@5.0.4) + '@svgr/hast-util-to-babel-ast': 8.0.0 + svg-parser: 2.0.4 + transitivePeerDependencies: + - supports-color + '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.8.2))': dependencies: '@babel/core': 7.28.6 @@ -41718,9 +41940,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@svgr/plugin-svgo@8.1.0(@svgr/core@8.1.0(typescript@5.8.2))(typescript@5.0.4)': + '@svgr/plugin-svgo@8.1.0(@svgr/core@8.1.0(typescript@5.0.4))(typescript@5.0.4)': dependencies: - '@svgr/core': 8.1.0(typescript@5.8.2) + '@svgr/core': 8.1.0(typescript@5.0.4) cosmiconfig: 8.3.6(typescript@5.0.4) deepmerge: 4.3.1 svgo: 3.3.2 @@ -42060,7 +42282,7 @@ snapshots: '@testing-library/dom@10.4.1': dependencies: '@babel/code-frame': 7.29.0 - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.28.2 '@types/aria-query': 5.0.4 aria-query: 5.3.0 dom-accessibility-api: 0.5.16 @@ -45365,7 +45587,7 @@ snapshots: schema-utils: 4.3.3 webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.18.20)(webpack-cli@5.1.4) - babel-loader@9.2.1(@babel/core@7.28.6)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): + babel-loader@9.2.1(@babel/core@7.28.6)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): dependencies: '@babel/core': 7.28.6 find-cache-dir: 4.0.0 @@ -45404,7 +45626,7 @@ snapshots: '@babel/core': 7.28.6 '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.28.6) - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color @@ -46705,7 +46927,7 @@ snapshots: serialize-javascript: 6.0.2 webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.18.20)(webpack-cli@5.1.4) - copy-webpack-plugin@11.0.0(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): + copy-webpack-plugin@11.0.0(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): dependencies: fast-glob: 3.3.2 glob-parent: 6.0.2 @@ -47075,6 +47297,18 @@ snapshots: optionalDependencies: esbuild: 0.25.5 + css-minimizer-webpack-plugin@7.0.2(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + cssnano: 7.1.2(postcss@8.4.49) + jest-worker: 29.7.0 + postcss: 8.4.49 + schema-utils: 4.3.3 + serialize-javascript: 6.0.2 + webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4) + optionalDependencies: + esbuild: 0.25.5 + css-minimizer-webpack-plugin@7.0.2(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -49189,7 +49423,7 @@ snapshots: estree-to-babel@3.2.1: dependencies: - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.29.0 '@babel/types': 7.29.0 c8: 7.14.0 transitivePeerDependencies: @@ -50898,7 +51132,7 @@ snapshots: '@rspack/core': 1.7.5(@swc/helpers@0.5.18) webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.18.20)(webpack-cli@5.1.4) - html-webpack-plugin@5.6.6(@rspack/core@1.7.5(@swc/helpers@0.5.18))(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): + html-webpack-plugin@5.6.6(@rspack/core@1.7.5(@swc/helpers@0.5.18))(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): dependencies: '@types/html-minifier-terser': 6.1.0 html-minifier-terser: 6.1.0 @@ -52395,7 +52629,7 @@ snapshots: temp: 0.8.4 write-file-atomic: 2.4.3 optionalDependencies: - '@babel/preset-env': 7.28.6(@babel/core@7.28.6) + '@babel/preset-env': 7.28.6(@babel/core@7.29.0) transitivePeerDependencies: - supports-color @@ -53911,7 +54145,7 @@ snapshots: tapable: 2.2.1 webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.18.20)(webpack-cli@5.1.4) - mini-css-extract-plugin@2.9.4(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): + mini-css-extract-plugin@2.9.4(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): dependencies: schema-utils: 4.3.3 tapable: 2.2.1 @@ -58386,6 +58620,15 @@ snapshots: webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4(webpack-dev-server@5.2.3)(webpack@5.104.1)) webpack-sources: 3.3.3 + react-server-dom-webpack@19.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): + dependencies: + acorn-loose: 8.5.2 + neo-async: 2.6.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4) + webpack-sources: 3.3.3 + react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.18.20)(webpack-cli@5.1.4)): dependencies: acorn-loose: 8.5.2 @@ -58395,6 +58638,15 @@ snapshots: webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.18.20)(webpack-cli@5.1.4) webpack-sources: 3.3.3 + react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)): + dependencies: + acorn-loose: 8.5.2 + neo-async: 2.6.2 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4) + webpack-sources: 3.3.3 + react-server-dom-webpack@19.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): dependencies: acorn-loose: 8.5.2 @@ -60993,7 +61245,7 @@ snapshots: '@swc/core': 1.15.8(@swc/helpers@0.5.18) esbuild: 0.25.5 - terser-webpack-plugin@5.3.14(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): + terser-webpack-plugin@5.3.14(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 @@ -61053,7 +61305,7 @@ snapshots: '@swc/core': 1.15.10(@swc/helpers@0.5.18) esbuild: 0.25.0 - terser-webpack-plugin@5.3.16(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): + terser-webpack-plugin@5.3.16(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 @@ -61478,7 +61730,7 @@ snapshots: babel-jest: 29.7.0(@babel/core@7.28.6) esbuild: 0.25.0 - ts-loader@9.4.4(typescript@5.8.2)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): + ts-loader@9.4.4(typescript@5.8.2)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): dependencies: chalk: 4.1.2 enhanced-resolve: 5.19.0 @@ -62941,7 +63193,7 @@ snapshots: webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.18.20)(webpack-cli@5.1.4) optional: true - webpack-dev-middleware@7.4.5(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): + webpack-dev-middleware@7.4.5(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): dependencies: colorette: 2.0.20 memfs: 4.46.0 @@ -63064,7 +63316,7 @@ snapshots: - utf-8-validate optional: true - webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): + webpack-dev-server@5.2.3(webpack-cli@5.1.4)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): dependencies: '@types/bonjour': 3.5.13 '@types/connect-history-api-fallback': 1.5.4 @@ -63092,7 +63344,7 @@ snapshots: serve-index: 1.9.2 sockjs: 0.3.24 spdy: 4.0.2 - webpack-dev-middleware: 7.4.5(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + webpack-dev-middleware: 7.4.5(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) ws: 8.18.0 optionalDependencies: webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4) @@ -63261,12 +63513,12 @@ snapshots: optionalDependencies: html-webpack-plugin: 5.6.6(@rspack/core@1.3.9(@swc/helpers@0.5.18))(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.0)(webpack-cli@5.1.4)) - webpack-subresource-integrity@5.1.0(html-webpack-plugin@5.6.6(@rspack/core@1.7.5(@swc/helpers@0.5.18))(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): + webpack-subresource-integrity@5.1.0(html-webpack-plugin@5.6.6(@rspack/core@1.7.5(@swc/helpers@0.5.18))(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)))(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)): dependencies: typed-assert: 1.0.9 webpack: 5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4) optionalDependencies: - html-webpack-plugin: 5.6.6(@rspack/core@1.7.5(@swc/helpers@0.5.18))(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + html-webpack-plugin: 5.6.6(@rspack/core@1.7.5(@swc/helpers@0.5.18))(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) webpack-virtual-modules@0.6.2: {} @@ -63396,7 +63648,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.0 - terser-webpack-plugin: 5.3.16(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.8(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) + terser-webpack-plugin: 5.3.16(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack@5.104.1(@swc/core@1.15.10(@swc/helpers@0.5.18))(esbuild@0.25.5)(webpack-cli@5.1.4)) watchpack: 2.5.1 webpack-sources: 3.3.3 optionalDependencies: From e18f07b8e06c00fa397bc4454f7ce20df6b7a483 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Tue, 24 Feb 2026 15:11:44 -0800 Subject: [PATCH 12/17] fix(dts-plugin): align workspace entrypoints and RawSource typing Resolve the dts-plugin TYPE-001 failure by correcting package entry paths for workspace dependencies and updating RawSource usage for webpack typings. Co-authored-by: Cursor --- .../dts-plugin/src/plugins/GenerateTypesPlugin.ts | 6 ++---- packages/error-codes/package.json | 8 ++++---- packages/sdk/package.json | 12 ++++++------ 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/packages/dts-plugin/src/plugins/GenerateTypesPlugin.ts b/packages/dts-plugin/src/plugins/GenerateTypesPlugin.ts index a8b4aaeed5f..d103436ff60 100644 --- a/packages/dts-plugin/src/plugins/GenerateTypesPlugin.ts +++ b/packages/dts-plugin/src/plugins/GenerateTypesPlugin.ts @@ -172,8 +172,7 @@ export class GenerateTypesPlugin implements WebpackPluginInstance { compilation.emitAsset( zipName, new compiler.webpack.sources.RawSource( - fs.readFileSync(zipTypesPath), - false, + fs.readFileSync(zipTypesPath) as unknown as string, ), ); } @@ -186,8 +185,7 @@ export class GenerateTypesPlugin implements WebpackPluginInstance { compilation.emitAsset( apiFileName, new compiler.webpack.sources.RawSource( - fs.readFileSync(apiTypesPath), - false, + fs.readFileSync(apiTypesPath) as unknown as string, ), ); } diff --git a/packages/error-codes/package.json b/packages/error-codes/package.json index 126b794b07c..352010e14a9 100644 --- a/packages/error-codes/package.json +++ b/packages/error-codes/package.json @@ -25,14 +25,14 @@ "browser": { "url": false }, - "main": "./dist/index.cjs.js", - "module": "./dist/index.esm.mjs", + "main": "./dist/index.cjs", + "module": "./dist/index.mjs", "types": "./dist/index.d.ts", "exports": { ".": { "types": "./dist/index.d.ts", - "import": "./dist/index.esm.mjs", - "require": "./dist/index.cjs.js" + "import": "./dist/index.mjs", + "require": "./dist/index.cjs" } }, "typesVersions": { diff --git a/packages/sdk/package.json b/packages/sdk/package.json index cc3f5bde8d4..85026bc4549 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -23,8 +23,8 @@ }, "author": "zhanghang ", "sideEffects": false, - "main": "./dist/index.cjs.cjs", - "module": "./dist/index.esm.js", + "main": "./dist/index.cjs", + "module": "./dist/index.js", "types": "./dist/index.d.ts", "browser": { "url": false @@ -33,21 +33,21 @@ ".": { "import": { "types": "./dist/index.d.ts", - "default": "./dist/index.esm.js" + "default": "./dist/index.js" }, "require": { "types": "./dist/index.d.ts", - "default": "./dist/index.cjs.cjs" + "default": "./dist/index.cjs" } }, "./normalize-webpack-path": { "import": { "types": "./dist/normalize-webpack-path.d.ts", - "default": "./dist/normalize-webpack-path.esm.js" + "default": "./dist/normalize-webpack-path.js" }, "require": { "types": "./dist/normalize-webpack-path.d.ts", - "default": "./dist/normalize-webpack-path.cjs.cjs" + "default": "./dist/normalize-webpack-path.cjs" } } }, From d2c332a245c4290e1da06e31e55c6f6eff836cbb Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Tue, 24 Feb 2026 19:15:58 -0800 Subject: [PATCH 13/17] fix(sdk): align package entrypoints with emitted artifacts Restore sdk and error-codes export paths to the filenames emitted by the current build so CI package resolution no longer fails on these branches. Co-authored-by: Cursor --- packages/error-codes/package.json | 8 ++++---- packages/sdk/package.json | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/error-codes/package.json b/packages/error-codes/package.json index 352010e14a9..126b794b07c 100644 --- a/packages/error-codes/package.json +++ b/packages/error-codes/package.json @@ -25,14 +25,14 @@ "browser": { "url": false }, - "main": "./dist/index.cjs", - "module": "./dist/index.mjs", + "main": "./dist/index.cjs.js", + "module": "./dist/index.esm.mjs", "types": "./dist/index.d.ts", "exports": { ".": { "types": "./dist/index.d.ts", - "import": "./dist/index.mjs", - "require": "./dist/index.cjs" + "import": "./dist/index.esm.mjs", + "require": "./dist/index.cjs.js" } }, "typesVersions": { diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 85026bc4549..cc3f5bde8d4 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -23,8 +23,8 @@ }, "author": "zhanghang ", "sideEffects": false, - "main": "./dist/index.cjs", - "module": "./dist/index.js", + "main": "./dist/index.cjs.cjs", + "module": "./dist/index.esm.js", "types": "./dist/index.d.ts", "browser": { "url": false @@ -33,21 +33,21 @@ ".": { "import": { "types": "./dist/index.d.ts", - "default": "./dist/index.js" + "default": "./dist/index.esm.js" }, "require": { "types": "./dist/index.d.ts", - "default": "./dist/index.cjs" + "default": "./dist/index.cjs.cjs" } }, "./normalize-webpack-path": { "import": { "types": "./dist/normalize-webpack-path.d.ts", - "default": "./dist/normalize-webpack-path.js" + "default": "./dist/normalize-webpack-path.esm.js" }, "require": { "types": "./dist/normalize-webpack-path.d.ts", - "default": "./dist/normalize-webpack-path.cjs" + "default": "./dist/normalize-webpack-path.cjs.cjs" } } }, From 0ef17542bd497882b07b8ff11ed2da3e909190e4 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Wed, 25 Feb 2026 15:43:22 -0800 Subject: [PATCH 14/17] fix(module-federation): tolerate metro serve shutdown exit in e2e script --- tools/scripts/run-metro-e2e.mjs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/scripts/run-metro-e2e.mjs b/tools/scripts/run-metro-e2e.mjs index 38094e61b2e..cafa4625821 100644 --- a/tools/scripts/run-metro-e2e.mjs +++ b/tools/scripts/run-metro-e2e.mjs @@ -126,8 +126,10 @@ async function main() { } if (!isExpectedServeExit(serveExitInfo)) { - throw new Error( - `Metro serve command exited unexpectedly with ${formatExit(serveExitInfo)}`, + console.warn( + `[metro-e2e] Metro serve command exited with ${formatExit( + serveExitInfo, + )} during shutdown; treating as non-fatal.`, ); } From 44d5372507f20c9e31df8dbc7a41a0634efee8ce Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Wed, 25 Feb 2026 16:01:57 -0800 Subject: [PATCH 15/17] revert(modernjs-rsc): drop unrelated metro e2e shutdown behavior change --- tools/scripts/run-metro-e2e.mjs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tools/scripts/run-metro-e2e.mjs b/tools/scripts/run-metro-e2e.mjs index cafa4625821..38094e61b2e 100644 --- a/tools/scripts/run-metro-e2e.mjs +++ b/tools/scripts/run-metro-e2e.mjs @@ -126,10 +126,8 @@ async function main() { } if (!isExpectedServeExit(serveExitInfo)) { - console.warn( - `[metro-e2e] Metro serve command exited with ${formatExit( - serveExitInfo, - )} during shutdown; treating as non-fatal.`, + throw new Error( + `Metro serve command exited unexpectedly with ${formatExit(serveExitInfo)}`, ); } From 3d9f8a8f1c3abe919e23c85d3ec95dc24ddeeec7 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Fri, 27 Feb 2026 19:34:52 -0800 Subject: [PATCH 16/17] chore(modern-js-plugin-v3): remove global webpack require fallback --- .../modernjs-v3/src/runtime/rsc-client-callback-bootstrap.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/modernjs-v3/src/runtime/rsc-client-callback-bootstrap.js b/packages/modernjs-v3/src/runtime/rsc-client-callback-bootstrap.js index d5297080e7d..b9f98fbdaf3 100644 --- a/packages/modernjs-v3/src/runtime/rsc-client-callback-bootstrap.js +++ b/packages/modernjs-v3/src/runtime/rsc-client-callback-bootstrap.js @@ -55,10 +55,6 @@ function getWebpackRequire() { if (typeof __webpack_require__ !== 'undefined') { return __webpack_require__; } - const runtime = globalThis.__webpack_require__; - if (isFunction(runtime) || isObject(runtime)) { - return runtime; - } return undefined; } From a41e7abdcde180e2d28c28a3277f114f88a492bd Mon Sep 17 00:00:00 2001 From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com> Date: Sun, 8 Mar 2026 16:53:03 -0700 Subject: [PATCH 17/17] fix(modern-js-plugin-v3): harden RSC bridge runtime merge (#4484) Co-authored-by: zhouxiao.shaw --- packages/bridge/bridge-react/package.json | 12 +- .../src/core/lib/typeScriptCompiler.ts | 3 +- .../container/ContainerPlugin.check.ts | 259 ++- .../schemas/container/ContainerPlugin.json | 5 + .../src/schemas/container/ContainerPlugin.ts | 5 + .../container/ModuleFederationPlugin.check.ts | 17 +- .../container/ModuleFederationPlugin.json | 5 + .../container/ModuleFederationPlugin.ts | 5 + packages/error-codes/package.json | 6 +- packages/managers/src/ContainerManager.ts | 8 +- .../rsc-bridge-runtime-plugin.spec.ts | 144 +- .../rsc-bridge-runtime-plugin.ts | 70 +- .../src/runtime/rsc-bridge-expose.spec.ts | 12 +- .../src/runtime/rsc-bridge-expose.ts | 16 +- .../runtime/rsc-client-callback-bootstrap.js | 291 ++- .../rsc-client-callback-bootstrap.spec.ts | 396 +++- .../src/server/asyncStartupLoader.ts | 8 +- packages/modernjs-v3/src/server/index.ts | 11 +- packages/rspack/src/ModuleFederationPlugin.ts | 134 +- packages/runtime-core/package.json | 12 +- .../src/plugins/snapshot/index.ts | 17 +- packages/runtime/package.json | 24 +- .../sdk/__tests__/nodeScriptUtils.spec.ts | 4 +- packages/sdk/package.json | 12 +- packages/sdk/src/nodeScriptUtils.ts | 10 +- .../types/plugins/ModuleFederationPlugin.ts | 4 + .../__tests__/resolveRemoteModuleId.spec.ts | 127 ++ packages/webpack-bundler-runtime/package.json | 38 +- packages/webpack-bundler-runtime/src/index.ts | 3 + .../src/initContainerEntry.ts | 62 +- .../webpack-bundler-runtime/src/remotes.ts | 9 +- .../src/resolveRemoteModuleId.ts | 77 + .../src/rscBridgeExpose.ts | 877 ++++++++ .../src/rscBridgeRuntimePlugin.ts | 1970 +++++++++++++++++ .../src/rscManifest.ts | 130 ++ packages/webpack-bundler-runtime/src/types.ts | 9 + .../webpack-bundler-runtime/tsdown.config.ts | 2 + 37 files changed, 4366 insertions(+), 428 deletions(-) create mode 100644 packages/webpack-bundler-runtime/__tests__/resolveRemoteModuleId.spec.ts create mode 100644 packages/webpack-bundler-runtime/src/resolveRemoteModuleId.ts create mode 100644 packages/webpack-bundler-runtime/src/rscBridgeExpose.ts create mode 100644 packages/webpack-bundler-runtime/src/rscBridgeRuntimePlugin.ts create mode 100644 packages/webpack-bundler-runtime/src/rscManifest.ts diff --git a/packages/bridge/bridge-react/package.json b/packages/bridge/bridge-react/package.json index 818b30b81d6..f0e5935e602 100644 --- a/packages/bridge/bridge-react/package.json +++ b/packages/bridge/bridge-react/package.json @@ -19,7 +19,8 @@ ".": { "types": "./dist/index.d.ts", "import": "./dist/index.es.js", - "require": "./dist/index.cjs.js" + "require": "./dist/index.cjs.js", + "default": "./dist/index.es.js" }, "./base": { "types": "./dist/base.d.ts", @@ -64,17 +65,20 @@ "./lazy-utils": { "types": "./dist/lazy-utils.d.ts", "import": "./dist/lazy-utils.es.js", - "require": "./dist/lazy-utils.cjs.js" + "require": "./dist/lazy-utils.cjs.js", + "default": "./dist/lazy-utils.es.js" }, "./data-fetch-utils": { "types": "./dist/data-fetch-utils.d.ts", "import": "./dist/data-fetch-utils.es.js", - "require": "./dist/data-fetch-utils.cjs.js" + "require": "./dist/data-fetch-utils.cjs.js", + "default": "./dist/data-fetch-utils.es.js" }, "./data-fetch-server-middleware": { "types": "./dist/data-fetch-server-middleware.d.ts", "import": "./dist/data-fetch-server-middleware.es.js", - "require": "./dist/data-fetch-server-middleware.cjs.js" + "require": "./dist/data-fetch-server-middleware.cjs.js", + "default": "./dist/data-fetch-server-middleware.es.js" }, "./lazy-load-component-plugin": { "types": "./dist/lazy-load-component-plugin.d.ts", diff --git a/packages/dts-plugin/src/core/lib/typeScriptCompiler.ts b/packages/dts-plugin/src/core/lib/typeScriptCompiler.ts index 57208228480..c9ff57436b0 100644 --- a/packages/dts-plugin/src/core/lib/typeScriptCompiler.ts +++ b/packages/dts-plugin/src/core/lib/typeScriptCompiler.ts @@ -1,4 +1,4 @@ -import { ensureDirSync, writeFileSync, existsSync } from 'fs-extra'; +import fsExtra from 'fs-extra'; import crypto from 'crypto'; import { stat, readdir, writeFile, rm, readFile } from 'fs/promises'; import { @@ -26,6 +26,7 @@ import { TsConfigJson } from '../interfaces/TsConfigJson'; import { logger } from '../../server'; const STARTS_WITH_SLASH = /^\//; +const { ensureDirSync, writeFileSync, existsSync } = fsExtra; const DEFINITION_FILE_EXTENSION = '.d.ts'; diff --git a/packages/enhanced/src/schemas/container/ContainerPlugin.check.ts b/packages/enhanced/src/schemas/container/ContainerPlugin.check.ts index f41e37b89f0..0dfc1f6225e 100644 --- a/packages/enhanced/src/schemas/container/ContainerPlugin.check.ts +++ b/packages/enhanced/src/schemas/container/ContainerPlugin.check.ts @@ -44,6 +44,7 @@ const e = { ], }, name: { type: 'string' }, + layer: { type: 'string', minLength: 1 }, }, required: ['import'], }, @@ -233,7 +234,7 @@ function a( { const r = l; for (const e in t) - if ('import' !== e && 'name' !== e) + if ('import' !== e && 'name' !== e && 'layer' !== e) return (a.errors = [{ params: { additionalProperty: e } }]), !1; if (r === l) { if (void 0 !== t.import) { @@ -241,7 +242,7 @@ function a( const n = l, m = l; let u = !1; - const c = l; + const y = l; if (l == l) if ('string' == typeof r) { if (r.length < 1) { @@ -252,7 +253,7 @@ function a( const t = { params: { type: 'string' } }; null === i ? (i = [t]) : i.push(t), l++; } - var p = c === l; + var p = y === l; if (((u = u || p), !u)) { const n = l; s(r, { @@ -275,13 +276,25 @@ function a( (l = m), null !== i && (m ? (i.length = m) : (i = null)); var f = n === l; } else f = !0; - if (f) + if (f) { if (void 0 !== t.name) { const e = l; if ('string' != typeof t.name) return (a.errors = [{ params: { type: 'string' } }]), !1; f = e === l; } else f = !0; + if (f) + if (void 0 !== t.layer) { + let e = t.layer; + const r = l; + if (l === r) { + if ('string' != typeof e) + return (a.errors = [{ params: { type: 'string' } }]), !1; + if (e.length < 1) return (a.errors = [{ params: {} }]), !1; + } + f = r === l; + } else f = !0; + } } } } @@ -306,16 +319,16 @@ function o( let n = t[r]; const m = p, u = p; - let c = !1; - const y = p; + let y = !1; + const c = p; a(n, { instancePath: e + '/' + r.replace(/~/g, '~0').replace(/\//g, '~1'), parentData: t, parentDataProperty: r, rootData: i, }) || ((l = null === l ? a.errors : l.concat(a.errors)), (p = l.length)); - var f = y === p; - if (((c = c || f), !c)) { + var f = c === p; + if (((y = y || f), !y)) { const a = p; if (p == p) if ('string' == typeof n) { @@ -327,7 +340,7 @@ function o( const t = { params: { type: 'string' } }; null === l ? (l = [t]) : l.push(t), p++; } - if (((f = a === p), (c = c || f), !c)) { + if (((f = a === p), (y = y || f), !y)) { const a = p; s(n, { instancePath: e + '/' + r.replace(/~/g, '~0').replace(/\//g, '~1'), @@ -337,10 +350,10 @@ function o( }) || ((l = null === l ? s.errors : l.concat(s.errors)), (p = l.length)), (f = a === p), - (c = c || f); + (y = y || f); } } - if (!c) { + if (!y) { const t = { params: {} }; return null === l ? (l = [t]) : l.push(t), p++, (o.errors = l), !1; } @@ -407,8 +420,8 @@ function i( const t = { params: { type: 'array' } }; null === a ? (a = [t]) : a.push(t), l++; } - var c = m === l; - if (((f = f || c), !f)) { + var y = m === l; + if (((f = f || y), !f)) { const i = l; o(t, { instancePath: e, @@ -416,8 +429,8 @@ function i( parentDataProperty: n, rootData: s, }) || ((a = null === a ? o.errors : a.concat(o.errors)), (l = a.length)), - (c = i === l), - (f = f || c); + (y = i === l), + (f = f || y); } if (!f) { const t = { params: {} }; @@ -674,8 +687,8 @@ function f( const t = { params: { type: 'array' } }; null === a ? (a = [t]) : a.push(t), o++; } - var c = i === o; - if (((s = s || c), !s)) { + var y = i === o; + if (((s = s || y), !s)) { const t = o; if (o === t) if ('string' == typeof e) { @@ -687,7 +700,7 @@ function f( const t = { params: { type: 'string' } }; null === a ? (a = [t]) : a.push(t), o++; } - (c = t === o), (s = s || c); + (y = t === o), (s = s || y); } if (s) (o = n), null !== a && (n ? (a.length = n) : (a = null)); @@ -868,14 +881,14 @@ function m( const t = { params: { allowedValues: l.anyOf[0].enum } }; null === a ? (a = [t]) : a.push(t), o++; } - var c = p === o; - if (((s = s || c), !s)) { + var y = p === o; + if (((s = s || y), !s)) { const t = o; if ('string' != typeof e) { const t = { params: { type: 'string' } }; null === a ? (a = [t]) : a.push(t), o++; } - (c = t === o), (s = s || c); + (y = t === o), (s = s || y); } if (!s) { const t = { params: {} }; @@ -919,8 +932,8 @@ function u( } = {}, ) { let f = null, - c = 0; - if (0 === c) { + y = 0; + if (0 === y) { if (!s || 'object' != typeof s || Array.isArray(s)) return (u.errors = [{ params: { type: 'object' } }]), !1; { @@ -931,13 +944,13 @@ function u( ) return (u.errors = [{ params: { missingProperty: o } }]), !1; { - const o = c; + const o = y; for (const t in s) if (!n.call(e.properties, t)) return (u.errors = [{ params: { additionalProperty: t } }]), !1; - if (o === c) { + if (o === y) { if (void 0 !== s.exposes) { - const t = c; + const t = y; i(s.exposes, { instancePath: a + '/exposes', parentData: s, @@ -945,25 +958,25 @@ function u( rootData: p, }) || ((f = null === f ? i.errors : f.concat(i.errors)), - (c = f.length)); - var y = t === c; - } else y = !0; - if (y) { + (y = f.length)); + var c = t === y; + } else c = !0; + if (c) { if (void 0 !== s.filename) { let e = s.filename; - const r = c; - if (c === r) { + const r = y; + if (y === r) { if ('string' != typeof e) return (u.errors = [{ params: { type: 'string' } }]), !1; if (e.length < 1) return (u.errors = [{ params: {} }]), !1; if (e.includes('!') || !1 !== t.test(e)) return (u.errors = [{ params: {} }]), !1; } - y = r === c; - } else y = !0; - if (y) { + c = r === y; + } else c = !0; + if (c) { if (void 0 !== s.library) { - const t = c; + const t = y; m(s.library, { instancePath: a + '/library', parentData: s, @@ -971,127 +984,127 @@ function u( rootData: p, }) || ((f = null === f ? m.errors : f.concat(m.errors)), - (c = f.length)), - (y = t === c); - } else y = !0; - if (y) { + (y = f.length)), + (c = t === y); + } else c = !0; + if (c) { if (void 0 !== s.name) { let t = s.name; - const e = c; - if (c === e) { + const e = y; + if (y === e) { if ('string' != typeof t) return (u.errors = [{ params: { type: 'string' } }]), !1; if (t.length < 1) return (u.errors = [{ params: {} }]), !1; } - y = e === c; - } else y = !0; - if (y) { + c = e === y; + } else c = !0; + if (c) { if (void 0 !== s.runtime) { let t = s.runtime; - const e = c, - n = c; + const e = y, + n = y; let a = !1; - const o = c; + const o = y; if (!1 !== t) { const t = { params: { allowedValues: r.anyOf[0].enum } }; - null === f ? (f = [t]) : f.push(t), c++; + null === f ? (f = [t]) : f.push(t), y++; } - var g = o === c; + var g = o === y; if (((a = a || g), !a)) { - const e = c; - if (c === e) + const e = y; + if (y === e) if ('string' == typeof t) { if (t.length < 1) { const t = { params: {} }; - null === f ? (f = [t]) : f.push(t), c++; + null === f ? (f = [t]) : f.push(t), y++; } } else { const t = { params: { type: 'string' } }; - null === f ? (f = [t]) : f.push(t), c++; + null === f ? (f = [t]) : f.push(t), y++; } - (g = e === c), (a = a || g); + (g = e === y), (a = a || g); } if (!a) { const t = { params: {} }; return ( null === f ? (f = [t]) : f.push(t), - c++, + y++, (u.errors = f), !1 ); } - (c = n), + (y = n), null !== f && (n ? (f.length = n) : (f = null)), - (y = e === c); - } else y = !0; - if (y) { + (c = e === y); + } else c = !0; + if (c) { if (void 0 !== s.shareScope) { let t = s.shareScope; - const e = c, - r = c; + const e = y, + r = y; let n = !1; - const a = c; - if (c === a) + const a = y; + if (y === a) if ('string' == typeof t) { if (t.length < 1) { const t = { params: {} }; - null === f ? (f = [t]) : f.push(t), c++; + null === f ? (f = [t]) : f.push(t), y++; } } else { const t = { params: { type: 'string' } }; - null === f ? (f = [t]) : f.push(t), c++; + null === f ? (f = [t]) : f.push(t), y++; } - var h = a === c; + var h = a === y; if (((n = n || h), !n)) { - const e = c; - if (c === e) + const e = y; + if (y === e) if (Array.isArray(t)) { const e = t.length; for (let r = 0; r < e; r++) { let e = t[r]; - const n = c; - if (c === n) + const n = y; + if (y === n) if ('string' == typeof e) { if (e.length < 1) { const t = { params: {} }; - null === f ? (f = [t]) : f.push(t), c++; + null === f ? (f = [t]) : f.push(t), y++; } } else { const t = { params: { type: 'string' } }; - null === f ? (f = [t]) : f.push(t), c++; + null === f ? (f = [t]) : f.push(t), y++; } - if (n !== c) break; + if (n !== y) break; } } else { const t = { params: { type: 'array' } }; - null === f ? (f = [t]) : f.push(t), c++; + null === f ? (f = [t]) : f.push(t), y++; } - (h = e === c), (n = n || h); + (h = e === y), (n = n || h); } if (!n) { const t = { params: {} }; return ( null === f ? (f = [t]) : f.push(t), - c++, + y++, (u.errors = f), !1 ); } - (c = r), + (y = r), null !== f && (r ? (f.length = r) : (f = null)), - (y = e === c); - } else y = !0; - if (y) { + (c = e === y); + } else c = !0; + if (c) { if (void 0 !== s.experiments) { let t = s.experiments; - const e = c; - if (c === e) { + const e = y; + if (y === e) { if (!t || 'object' != typeof t || Array.isArray(t)) return ( (u.errors = [{ params: { type: 'object' } }]), !1 ); { - const e = c; + const e = y; for (const e in t) if ( 'asyncStartup' !== e && @@ -1104,9 +1117,9 @@ function u( ]), !1 ); - if (e === c) { + if (e === y) { if (void 0 !== t.asyncStartup) { - const e = c; + const e = y; if ('boolean' != typeof t.asyncStartup) return ( (u.errors = [ @@ -1114,11 +1127,11 @@ function u( ]), !1 ); - var d = e === c; + var d = e === y; } else d = !0; if (d) { if (void 0 !== t.externalRuntime) { - const e = c; + const e = y; if ('boolean' != typeof t.externalRuntime) return ( (u.errors = [ @@ -1126,11 +1139,11 @@ function u( ]), !1 ); - d = e === c; + d = e === y; } else d = !0; if (d) if (void 0 !== t.provideExternalRuntime) { - const e = c; + const e = y; if ( 'boolean' != typeof t.provideExternalRuntime @@ -1141,28 +1154,28 @@ function u( ]), !1 ); - d = e === c; + d = e === y; } else d = !0; } } } } - y = e === c; - } else y = !0; - if (y) { + c = e === y; + } else c = !0; + if (c) { if (void 0 !== s.dataPrefetch) { - const t = c; + const t = y; if ('boolean' != typeof s.dataPrefetch) return ( (u.errors = [{ params: { type: 'boolean' } }]), !1 ); - y = t === c; - } else y = !0; - if (y) + c = t === y; + } else c = !0; + if (c) if (void 0 !== s.runtimePlugins) { let t = s.runtimePlugins; - const e = c; - if (c === e) { + const e = y; + if (y === e) { if (!Array.isArray(t)) return ( (u.errors = [{ params: { type: 'array' } }]), @@ -1172,31 +1185,31 @@ function u( const e = t.length; for (let r = 0; r < e; r++) { let e = t[r]; - const n = c, - s = c; + const n = y, + s = y; let a = !1; - const o = c; + const o = y; if ('string' != typeof e) { const t = { params: { type: 'string' } }; - null === f ? (f = [t]) : f.push(t), c++; + null === f ? (f = [t]) : f.push(t), y++; } - var b = o === c; + var b = o === y; if (((a = a || b), !a)) { - const t = c; - if (c === t) + const t = y; + if (y === t) if (Array.isArray(e)) if (e.length > 2) { const t = { params: { limit: 2 } }; null === f ? (f = [t]) : f.push(t), - c++; + y++; } else if (e.length < 2) { const t = { params: { limit: 2 } }; null === f ? (f = [t]) : f.push(t), - c++; + y++; } else { const t = e.length; if (t > 0) { - const t = c; + const t = y; if ('string' != typeof e[0]) { const t = { params: { type: 'string' }, @@ -1204,13 +1217,13 @@ function u( null === f ? (f = [t]) : f.push(t), - c++; + y++; } - var j = t === c; + var j = t === y; } if (j && t > 1) { let t = e[1]; - const r = c; + const r = y; if ( !t || 'object' != typeof t || @@ -1222,38 +1235,38 @@ function u( null === f ? (f = [t]) : f.push(t), - c++; + y++; } - j = r === c; + j = r === y; } } else { const t = { params: { type: 'array' } }; - null === f ? (f = [t]) : f.push(t), c++; + null === f ? (f = [t]) : f.push(t), y++; } - (b = t === c), (a = a || b); + (b = t === y), (a = a || b); } if (!a) { const t = { params: {} }; return ( null === f ? (f = [t]) : f.push(t), - c++, + y++, (u.errors = f), !1 ); } if ( - ((c = s), + ((y = s), null !== f && (s ? (f.length = s) : (f = null)), - n !== c) + n !== y) ) break; } } } - y = e === c; - } else y = !0; + c = e === y; + } else c = !0; } } } @@ -1265,5 +1278,5 @@ function u( } } } - return (u.errors = f), 0 === c; + return (u.errors = f), 0 === y; } diff --git a/packages/enhanced/src/schemas/container/ContainerPlugin.json b/packages/enhanced/src/schemas/container/ContainerPlugin.json index a5bf16d0d7b..855b73ab2e0 100644 --- a/packages/enhanced/src/schemas/container/ContainerPlugin.json +++ b/packages/enhanced/src/schemas/container/ContainerPlugin.json @@ -70,6 +70,11 @@ "name": { "description": "Custom chunk name for the exposed module.", "type": "string" + }, + "layer": { + "description": "Layer in which the exposed module should be placed.", + "type": "string", + "minLength": 1 } }, "required": ["import"] diff --git a/packages/enhanced/src/schemas/container/ContainerPlugin.ts b/packages/enhanced/src/schemas/container/ContainerPlugin.ts index 6f86e60d6e2..98fce2984ec 100644 --- a/packages/enhanced/src/schemas/container/ContainerPlugin.ts +++ b/packages/enhanced/src/schemas/container/ContainerPlugin.ts @@ -83,6 +83,11 @@ export default { description: 'Custom chunk name for the exposed module.', type: 'string', }, + layer: { + description: 'Layer in which the exposed module should be placed.', + type: 'string', + minLength: 1, + }, }, required: ['import'], }, diff --git a/packages/enhanced/src/schemas/container/ModuleFederationPlugin.check.ts b/packages/enhanced/src/schemas/container/ModuleFederationPlugin.check.ts index 47c62bf5dac..6fca149564e 100644 --- a/packages/enhanced/src/schemas/container/ModuleFederationPlugin.check.ts +++ b/packages/enhanced/src/schemas/container/ModuleFederationPlugin.check.ts @@ -44,6 +44,7 @@ const t = { ], }, name: { type: 'string' }, + layer: { type: 'string', minLength: 1 }, }, required: ['import'], }, @@ -604,7 +605,7 @@ function i( { const r = l; for (const t in e) - if ('import' !== t && 'name' !== t) + if ('import' !== t && 'name' !== t && 'layer' !== t) return (i.errors = [{ params: { additionalProperty: t } }]), !1; if (r === l) { if (void 0 !== e.import) { @@ -646,13 +647,25 @@ function i( (l = u), null !== a && (u ? (a.length = u) : (a = null)); var f = n === l; } else f = !0; - if (f) + if (f) { if (void 0 !== e.name) { const t = l; if ('string' != typeof e.name) return (i.errors = [{ params: { type: 'string' } }]), !1; f = t === l; } else f = !0; + if (f) + if (void 0 !== e.layer) { + let t = e.layer; + const r = l; + if (l === r) { + if ('string' != typeof t) + return (i.errors = [{ params: { type: 'string' } }]), !1; + if (t.length < 1) return (i.errors = [{ params: {} }]), !1; + } + f = r === l; + } else f = !0; + } } } } diff --git a/packages/enhanced/src/schemas/container/ModuleFederationPlugin.json b/packages/enhanced/src/schemas/container/ModuleFederationPlugin.json index 4ca4c940283..0bc48dcbebb 100644 --- a/packages/enhanced/src/schemas/container/ModuleFederationPlugin.json +++ b/packages/enhanced/src/schemas/container/ModuleFederationPlugin.json @@ -70,6 +70,11 @@ "name": { "description": "Custom chunk name for the exposed module.", "type": "string" + }, + "layer": { + "description": "Layer in which the exposed module should be placed.", + "type": "string", + "minLength": 1 } }, "required": ["import"] diff --git a/packages/enhanced/src/schemas/container/ModuleFederationPlugin.ts b/packages/enhanced/src/schemas/container/ModuleFederationPlugin.ts index 4cfba3f43aa..6cfc79071e2 100644 --- a/packages/enhanced/src/schemas/container/ModuleFederationPlugin.ts +++ b/packages/enhanced/src/schemas/container/ModuleFederationPlugin.ts @@ -83,6 +83,11 @@ export default { description: 'Custom chunk name for the exposed module.', type: 'string', }, + layer: { + description: 'Layer in which the exposed module should be placed.', + type: 'string', + minLength: 1, + }, }, required: ['import'], }, diff --git a/packages/error-codes/package.json b/packages/error-codes/package.json index e1e5397cf54..de4c710e300 100644 --- a/packages/error-codes/package.json +++ b/packages/error-codes/package.json @@ -30,14 +30,14 @@ "types": "./dist/index.d.ts", "exports": { ".": { + "types": "./dist/index.d.ts", "import": { - "types": "./dist/index.d.mts", "default": "./dist/index.mjs" }, "require": { - "types": "./dist/index.d.ts", "default": "./dist/index.cjs" - } + }, + "default": "./dist/index.mjs" } }, "typesVersions": { diff --git a/packages/managers/src/ContainerManager.ts b/packages/managers/src/ContainerManager.ts index 7226f395ae7..eb91edb9f6d 100644 --- a/packages/managers/src/ContainerManager.ts +++ b/packages/managers/src/ContainerManager.ts @@ -21,7 +21,7 @@ function normalizeExposeModuleName(exposeKey: string): string { class ContainerManager extends BasicPluginOptionsManager { private _manifestModuleInfos?: ManifestModuleInfos; private _parsedOptions?: Array< - [exposeKey: string, { import: string[]; name?: string }] + [exposeKey: string, { import: string[]; name?: string; layer?: string }] >; override get enable(): boolean { @@ -54,10 +54,12 @@ class ContainerManager extends BasicPluginOptionsManager ({ import: Array.isArray(item) ? item : [item], name: generateExposeFilename(key, false), + layer: undefined, }), (item, key) => ({ import: Array.isArray(item.import) ? item.import : [item.import], name: item.name || generateExposeFilename(key, false), + layer: item.layer, }), ); @@ -97,10 +99,12 @@ class ContainerManager extends BasicPluginOptionsManager ({ import: Array.isArray(item) ? item : [item], name: generateExposeFilename(key, false), + layer: undefined, }), (item, key) => ({ import: Array.isArray(item.import) ? item.import : [item.import], name: item.name || generateExposeFilename(key, false), + layer: item.layer, }), ); return parsedOptions.reduce( @@ -185,10 +189,12 @@ class ContainerManager extends BasicPluginOptionsManager ({ import: Array.isArray(item) ? item : [item], name: undefined, + layer: undefined, }), (item) => ({ import: Array.isArray(item.import) ? item.import : [item.import], name: undefined, + layer: item.layer, }), ); diff --git a/packages/modernjs-v3/src/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.spec.ts b/packages/modernjs-v3/src/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.spec.ts index f4545053afc..e4356dec85f 100644 --- a/packages/modernjs-v3/src/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.spec.ts +++ b/packages/modernjs-v3/src/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.spec.ts @@ -15,6 +15,9 @@ type WebpackRequireRuntime = { const ACTION_REMAP_GLOBAL_KEY = '__MODERN_RSC_MF_ACTION_ID_MAP__'; const PROXY_MODULE_PREFIX = '__modernjs_mf_rsc_action_proxy__:'; +type FederationState = { + [ACTION_REMAP_GLOBAL_KEY]?: Record; +}; const createWebpackRequireRuntime = (): WebpackRequireRuntime => ({ m: {}, @@ -25,55 +28,35 @@ const createWebpackRequireRuntime = (): WebpackRequireRuntime => ({ serverConsumerModuleMap: {}, }, }); +let runtimeRequire: WebpackRequireRuntime; + +const getFederationState = () => + global as typeof global & { + __FEDERATION__?: FederationState; + fetch?: unknown; + }; const getActionRemapMap = () => - ( - globalThis as typeof globalThis & { - [ACTION_REMAP_GLOBAL_KEY]?: Record; - } - )[ACTION_REMAP_GLOBAL_KEY] || {}; + getFederationState().__FEDERATION__?.[ACTION_REMAP_GLOBAL_KEY] || {}; describe('rsc-bridge-runtime-plugin', () => { beforeEach(() => { vi.useFakeTimers(); - ( - globalThis as typeof globalThis & { - __webpack_require__?: WebpackRequireRuntime; - } - ).__webpack_require__ = createWebpackRequireRuntime(); - ( - globalThis as typeof globalThis & { - window?: { __MODERN_JS_ENTRY_NAME?: string }; - } - ).window = { + runtimeRequire = createWebpackRequireRuntime(); + vi.stubGlobal('__webpack_require__', runtimeRequire); + vi.stubGlobal('__FEDERATION__', {}); + vi.stubGlobal('window', { __MODERN_JS_ENTRY_NAME: 'server-component-root', - }; - delete ( - globalThis as typeof globalThis & { - [ACTION_REMAP_GLOBAL_KEY]?: Record; - } - )[ACTION_REMAP_GLOBAL_KEY]; + }); + delete getFederationState().__FEDERATION__?.[ACTION_REMAP_GLOBAL_KEY]; }); afterEach(() => { vi.clearAllTimers(); vi.useRealTimers(); - delete ( - globalThis as typeof globalThis & { - __webpack_require__?: WebpackRequireRuntime; - } - ).__webpack_require__; - delete ( - globalThis as typeof globalThis & { - window?: { __MODERN_JS_ENTRY_NAME?: string }; - } - ).window; - delete (globalThis as typeof globalThis & { fetch?: unknown }).fetch; - delete ( - globalThis as typeof globalThis & { - [ACTION_REMAP_GLOBAL_KEY]?: Record; - } - )[ACTION_REMAP_GLOBAL_KEY]; + vi.unstubAllGlobals(); + delete getFederationState().fetch; + delete getFederationState().__FEDERATION__?.[ACTION_REMAP_GLOBAL_KEY]; }); it('merges remote manifest, registers action remap, and installs proxy dispatcher', async () => { @@ -121,16 +104,12 @@ describe('rsc-bridge-runtime-plugin', () => { expect(loadRemote).toHaveBeenCalledTimes(1); expect(loadRemote).toHaveBeenCalledWith('rscRemote/__rspack_rsc_bridge__'); - const webpackRequire = ( - globalThis as typeof globalThis & { - __webpack_require__?: WebpackRequireRuntime; - } - ).__webpack_require__!; + const webpackRequire = runtimeRequire; const hostManifest = webpackRequire.rscM!; - expect(hostManifest.clientManifest?.clientRef?.id).toBe( - 'remote-module:rscRemote:123', - ); + expect( + hostManifest.clientManifest?.['remote-module:rscRemote:clientRef']?.id, + ).toBe('remote-module:rscRemote:123'); expect(hostManifest.serverConsumerModuleMap).toHaveProperty( 'remote-module:rscRemote:123', ); @@ -182,11 +161,7 @@ describe('rsc-bridge-runtime-plugin', () => { }, } as any); - const webpackRequire = ( - globalThis as typeof globalThis & { - __webpack_require__?: WebpackRequireRuntime; - } - ).__webpack_require__!; + const webpackRequire = runtimeRequire; expect(webpackRequire.rscM?.serverManifest).toHaveProperty( 'remote:rscRemote:asyncRawAction', ); @@ -195,6 +170,61 @@ describe('rsc-bridge-runtime-plugin', () => { ); }); + it('namespaces clientManifest keys per alias for same remote export key', async () => { + const plugin = rscBridgeRuntimePlugin(); + + const loadRemote = vi.fn(async (request: string) => { + if (request.startsWith('rscRemoteA/')) { + return { + getManifest: () => ({ + clientManifest: { + sharedClientRef: { + id: '123', + name: 'default', + chunks: [], + }, + }, + }), + executeAction: vi.fn(async () => undefined), + }; + } + + return { + getManifest: () => ({ + clientManifest: { + sharedClientRef: { + id: '123', + name: 'default', + chunks: [], + }, + }, + }), + executeAction: vi.fn(async () => undefined), + }; + }); + + await plugin.onLoad?.({ + remote: { alias: 'rscRemoteA' }, + options: { name: 'rscHost' }, + origin: { loadRemote }, + } as any); + + await plugin.onLoad?.({ + remote: { alias: 'rscRemoteB' }, + options: { name: 'rscHost' }, + origin: { loadRemote }, + } as any); + + const webpackRequire = runtimeRequire; + + expect(webpackRequire.rscM?.clientManifest).toHaveProperty( + 'remote-module:rscRemoteA:sharedClientRef', + ); + expect(webpackRequire.rscM?.clientManifest).toHaveProperty( + 'remote-module:rscRemoteB:sharedClientRef', + ); + }); + it('loads and merges each remote alias once', async () => { const plugin = rscBridgeRuntimePlugin(); const loadRemote = vi.fn(async () => ({ @@ -234,11 +264,7 @@ describe('rsc-bridge-runtime-plugin', () => { executeAction: vi.fn(async () => undefined), })); - const webpackRequire = ( - globalThis as typeof globalThis & { - __webpack_require__?: WebpackRequireRuntime; - } - ).__webpack_require__!; + const webpackRequire = runtimeRequire; ( webpackRequire as WebpackRequireRuntime & { federation?: { @@ -265,15 +291,11 @@ describe('rsc-bridge-runtime-plugin', () => { }); it('throws deterministic conflict errors when manifests disagree', async () => { - const webpackRequire = ( - globalThis as typeof globalThis & { - __webpack_require__?: WebpackRequireRuntime; - } - ).__webpack_require__!; + const webpackRequire = runtimeRequire; webpackRequire.rscM = { serverManifest: {}, clientManifest: { - sharedKey: { + 'remote-module:rscRemote:sharedKey': { id: 'remote-module:existingRemote:123', name: 'default', chunks: [], diff --git a/packages/modernjs-v3/src/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.ts b/packages/modernjs-v3/src/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.ts index 822a6135c2d..c35a3a1e337 100644 --- a/packages/modernjs-v3/src/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.ts +++ b/packages/modernjs-v3/src/cli/mfRuntimePlugins/rsc-bridge-runtime-plugin.ts @@ -4,7 +4,6 @@ const RSC_BRIDGE_EXPOSE = '__rspack_rsc_bridge__'; const ACTION_PREFIX = 'remote:'; const MODULE_PREFIX = 'remote-module:'; const ACTION_REMAP_GLOBAL_KEY = '__MODERN_RSC_MF_ACTION_ID_MAP__'; -const ACTION_REMAP_WAITERS_KEY = '__MODERN_RSC_MF_ACTION_ID_MAP_WAITERS__'; const PROXY_MODULE_PREFIX = '__modernjs_mf_rsc_action_proxy__:'; type ManifestLike = { @@ -19,9 +18,10 @@ type BridgeModule = { }; type ActionMapRecord = Record; -type ActionRemapWaiter = (prefixedActionId: string) => void; -type ActionRemapWaiterMap = Map; type ActionRemapMap = Record; +type FederationState = { + [ACTION_REMAP_GLOBAL_KEY]?: ActionRemapMap; +}; type WebpackRequireRuntime = { m?: Record void>; c?: Record; @@ -34,6 +34,7 @@ type WebpackRequireRuntime = { }; declare const __webpack_require__: WebpackRequireRuntime; +declare const __FEDERATION__: FederationState | undefined; const isObject = (value: unknown): value is Record => typeof value === 'object' && value !== null; @@ -78,40 +79,34 @@ const assertNoConflict = ( const getNamespacedModuleId = (alias: string, rawId: string | number) => `${MODULE_PREFIX}${alias}:${String(rawId)}`; -const getActionRemapMap = () => { - const globalState = globalThis as typeof globalThis & { - [ACTION_REMAP_GLOBAL_KEY]?: ActionRemapMap; - [ACTION_REMAP_WAITERS_KEY]?: ActionRemapWaiterMap; - }; - if (!isObject(globalState[ACTION_REMAP_GLOBAL_KEY])) { - globalState[ACTION_REMAP_GLOBAL_KEY] = {}; +const getNamespacedClientManifestKey = (alias: string, key: string | number) => + `${MODULE_PREFIX}${alias}:${String(key)}`; + +const actionRemapMapFallback: ActionRemapMap = {}; + +const getFederationState = (): FederationState | undefined => { + if (typeof __FEDERATION__ !== 'undefined' && isObject(__FEDERATION__)) { + return __FEDERATION__; } - return globalState[ACTION_REMAP_GLOBAL_KEY] as ActionRemapMap; + return undefined; }; -const getActionRemapWaiters = () => { - const globalState = globalThis as typeof globalThis & { - [ACTION_REMAP_WAITERS_KEY]?: ActionRemapWaiterMap; - }; - const existingWaiters = globalState[ACTION_REMAP_WAITERS_KEY]; - if (!(existingWaiters instanceof Map)) { - globalState[ACTION_REMAP_WAITERS_KEY] = new Map(); - return globalState[ACTION_REMAP_WAITERS_KEY] as ActionRemapWaiterMap; +const getActionRemapMap = () => { + const federationState = getFederationState(); + if (!federationState) { + return actionRemapMapFallback; } - return existingWaiters; + if (!isObject(federationState[ACTION_REMAP_GLOBAL_KEY])) { + federationState[ACTION_REMAP_GLOBAL_KEY] = {}; + } + return federationState[ACTION_REMAP_GLOBAL_KEY] as ActionRemapMap; }; const registerActionRemap = (rawActionId: string, prefixedActionId: string) => { const remapMap = getActionRemapMap(); - const remapWaiters = getActionRemapWaiters(); const existingValue = remapMap[rawActionId]; if (typeof existingValue === 'undefined') { remapMap[rawActionId] = prefixedActionId; - const waiters = remapWaiters.get(rawActionId); - if (waiters?.length) { - waiters.forEach((waiter) => waiter(prefixedActionId)); - remapWaiters.delete(rawActionId); - } return; } if (existingValue === prefixedActionId) { @@ -119,24 +114,13 @@ const registerActionRemap = (rawActionId: string, prefixedActionId: string) => { } // Ambiguous mapping across remotes; skip unsafe remap. remapMap[rawActionId] = false; - const waiters = remapWaiters.get(rawActionId); - if (waiters?.length) { - waiters.forEach((waiter) => waiter(rawActionId)); - remapWaiters.delete(rawActionId); - } }; const getWebpackRequireIfAvailable = (): WebpackRequireRuntime | undefined => { if (typeof __webpack_require__ !== 'undefined') { return __webpack_require__; } - - const runtime = ( - globalThis as typeof globalThis & { - __webpack_require__?: WebpackRequireRuntime; - } - ).__webpack_require__; - return isObject(runtime) ? (runtime as WebpackRequireRuntime) : undefined; + return undefined; }; const getWebpackRequire = (): WebpackRequireRuntime => { @@ -361,6 +345,10 @@ const rscBridgeRuntimePlugin = (): ModuleFederationRuntimePlugin => { for (const [key, value] of Object.entries( remoteManifest.clientManifest, )) { + const scopedClientManifestKey = getNamespacedClientManifestKey( + alias, + key, + ); const nextValue = isObject(value) ? { ...value } : value; if (isObject(nextValue) && nextValue.id != null) { const namespacedClientId = getNamespacedModuleId(alias, nextValue.id); @@ -369,12 +357,14 @@ const rscBridgeRuntimePlugin = (): ModuleFederationRuntimePlugin => { } assertNoConflict( hostManifest.clientManifest as Record, - key, + scopedClientManifestKey, nextValue, alias, 'clientManifest', ); - (hostManifest.clientManifest as Record)[key] = nextValue; + (hostManifest.clientManifest as Record)[ + scopedClientManifestKey + ] = nextValue; } } diff --git a/packages/modernjs-v3/src/runtime/rsc-bridge-expose.spec.ts b/packages/modernjs-v3/src/runtime/rsc-bridge-expose.spec.ts index 230564f969a..c43cabd0be4 100644 --- a/packages/modernjs-v3/src/runtime/rsc-bridge-expose.spec.ts +++ b/packages/modernjs-v3/src/runtime/rsc-bridge-expose.spec.ts @@ -16,11 +16,7 @@ type WebpackRequireRuntime = { }; const setWebpackRequireRuntime = (runtime: WebpackRequireRuntime) => { - ( - globalThis as typeof globalThis & { - __webpack_require__?: WebpackRequireRuntime; - } - ).__webpack_require__ = runtime; + vi.stubGlobal('__webpack_require__', runtime); }; const loadBridgeExposeModule = async () => { @@ -29,11 +25,7 @@ const loadBridgeExposeModule = async () => { }; afterEach(() => { - delete ( - globalThis as typeof globalThis & { - __webpack_require__?: WebpackRequireRuntime; - } - ).__webpack_require__; + vi.unstubAllGlobals(); }); describe('rsc-bridge-expose', () => { diff --git a/packages/modernjs-v3/src/runtime/rsc-bridge-expose.ts b/packages/modernjs-v3/src/runtime/rsc-bridge-expose.ts index 2f8aeaec641..48d155eb95c 100644 --- a/packages/modernjs-v3/src/runtime/rsc-bridge-expose.ts +++ b/packages/modernjs-v3/src/runtime/rsc-bridge-expose.ts @@ -57,19 +57,9 @@ const getWebpackRequire = (): WebpackRequireRuntime => { ) { return __webpack_require__; } - - const runtime = ( - globalThis as typeof globalThis & { - __webpack_require__?: WebpackRequireRuntime; - } - ).__webpack_require__; - if (!isWebpackRequireRuntime(runtime)) { - throw new Error( - '[modern-js-v3:rsc-bridge] __webpack_require__ is unavailable while evaluating the internal bridge expose', - ); - } - - return runtime as WebpackRequireRuntime; + throw new Error( + '[modern-js-v3:rsc-bridge] __webpack_require__ is unavailable while evaluating the internal bridge expose', + ); }; const cacheActionReferencesFromExports = ( diff --git a/packages/modernjs-v3/src/runtime/rsc-client-callback-bootstrap.js b/packages/modernjs-v3/src/runtime/rsc-client-callback-bootstrap.js index b9f98fbdaf3..740cf460d2c 100644 --- a/packages/modernjs-v3/src/runtime/rsc-client-callback-bootstrap.js +++ b/packages/modernjs-v3/src/runtime/rsc-client-callback-bootstrap.js @@ -8,15 +8,23 @@ import { const ACTION_PREFIX = 'remote:'; const ACTION_REMAP_GLOBAL_KEY = '__MODERN_RSC_MF_ACTION_ID_MAP__'; -const ACTION_REMAP_WAITERS_KEY = '__MODERN_RSC_MF_ACTION_ID_MAP_WAITERS__'; -const ACTION_REMAP_WAIT_TIMEOUT_MS = 3000; const CALLBACK_INSTALL_RETRY_DELAY_MS = 50; const MAX_CALLBACK_INSTALL_ATTEMPTS = 120; -const CALLBACK_CHUNK_LOADER_HOOK_FLAG = '__MODERN_RSC_MF_CALLBACK_HOOKED__'; +const CALLBACK_CHUNK_LOADER_HOOK_FLAG = + '__MODERN_RSC_MF_CALLBACK_CHUNK_LOADER_HOOKED__'; +const CALLBACK_CHUNK_LOADER_WRAPPED_FLAG = + '__MODERN_RSC_MF_CALLBACK_CHUNK_LOADER_WRAPPED__'; +const CALLBACK_CHUNK_HANDLER_KEY = '__MODERN_RSC_MF_CALLBACK_CHUNK_HANDLER__'; +const CALLBACK_CHUNK_HANDLER_WRAPPED_FLAG = + '__MODERN_RSC_MF_CALLBACK_CHUNK_HANDLER_WRAPPED__'; let hasResolvedFallbackAlias = false; let fallbackRemoteAlias; let callbackInstallAttempts = 0; +const actionRemapMapFallback = Object.create(null); const installedClientBrowserRuntimes = new WeakSet(); +const wrappedChunkLoaders = new WeakMap(); +const wrappedChunkHandlers = new WeakMap(); +const hookedChunkHandlers = new WeakSet(); function isObject(value) { return typeof value === 'object' && value !== null; @@ -59,14 +67,41 @@ function getWebpackRequire() { } function getActionRemapMap() { - const map = globalThis[ACTION_REMAP_GLOBAL_KEY]; + const federationState = + typeof __FEDERATION__ !== 'undefined' && isObject(__FEDERATION__) + ? __FEDERATION__ + : undefined; + if (!federationState) { + return actionRemapMapFallback; + } + const map = federationState[ACTION_REMAP_GLOBAL_KEY]; if (!isObject(map)) { - globalThis[ACTION_REMAP_GLOBAL_KEY] = Object.create(null); - return globalThis[ACTION_REMAP_GLOBAL_KEY]; + federationState[ACTION_REMAP_GLOBAL_KEY] = Object.create(null); + return federationState[ACTION_REMAP_GLOBAL_KEY]; } return map; } +function getHostServerManifest() { + const webpackRequire = getWebpackRequire(); + if ( + webpackRequire && + isObject(webpackRequire.rscM) && + isObject(webpackRequire.rscM.serverManifest) + ) { + return webpackRequire.rscM.serverManifest; + } + return undefined; +} + +function hasHostServerAction(rawId) { + const serverManifest = getHostServerManifest(); + if (!isObject(serverManifest)) { + return false; + } + return Object.prototype.hasOwnProperty.call(serverManifest, rawId); +} + function resolveFallbackRemoteAlias() { if (hasResolvedFallbackAlias) { return fallbackRemoteAlias; @@ -123,13 +158,13 @@ function resolveFallbackRemoteAlias() { return fallbackRemoteAlias; } - if (aliasSet.size === 0 && !globalThis.window) { + if (aliasSet.size === 0 && typeof window === 'undefined') { return undefined; } - if (globalThis.window) { - const containerAliases = Object.keys(globalThis.window).filter((alias) => { - const candidate = globalThis.window[alias]; + if (typeof window !== 'undefined') { + const containerAliases = Object.keys(window).filter((alias) => { + const candidate = window[alias]; return ( isObject(candidate) && isFunction(candidate.get) && @@ -151,18 +186,8 @@ function resolveFallbackRemoteAlias() { return undefined; } -function getActionRemapWaiters() { - const waiters = globalThis[ACTION_REMAP_WAITERS_KEY]; - if (!(waiters instanceof Map)) { - const nextWaiters = new Map(); - globalThis[ACTION_REMAP_WAITERS_KEY] = nextWaiters; - return nextWaiters; - } - return waiters; -} - function resolveActionEndpoint() { - if (!globalThis.window) { + if (typeof window === 'undefined') { return '/'; } @@ -173,58 +198,36 @@ function resolveActionEndpoint() { return `/${entryName}`; } -function waitForActionRemap(rawId) { - const waiters = getActionRemapWaiters(); - return new Promise((resolve) => { - let settled = false; - const resolveOnce = (value) => { - if (settled) { - return; - } - settled = true; - clearTimeout(timeoutHandle); - resolve(value); - }; - - const list = waiters.get(rawId) || []; - list.push(resolveOnce); - waiters.set(rawId, list); - - const timeoutHandle = setTimeout(() => { - const current = waiters.get(rawId) || []; - const next = current.filter((waiter) => waiter !== resolveOnce); - if (next.length === 0) { - waiters.delete(rawId); - } else { - waiters.set(rawId, next); - } - resolveOnce(rawId); - }, ACTION_REMAP_WAIT_TIMEOUT_MS); - }); -} - async function resolveActionId(id) { - if (typeof id === 'string' && id.startsWith(ACTION_PREFIX)) { - return id; + const rawId = String(id); + if (rawId.startsWith(ACTION_PREFIX)) { + return rawId; } const remapMap = getActionRemapMap(); - const remappedId = remapMap[id]; + const remappedId = remapMap[rawId]; if (typeof remappedId === 'string') { return remappedId; } + if (hasHostServerAction(rawId)) { + return rawId; + } if (remappedId === false) { - return id; + throw new Error( + `[modern-js-v3:rsc-bridge] Ambiguous remote action id "${rawId}" cannot be resolved safely.`, + ); } const fallbackAlias = resolveFallbackRemoteAlias(); if (typeof fallbackAlias === 'string' && fallbackAlias) { - const prefixedId = `${ACTION_PREFIX}${fallbackAlias}:${id}`; - remapMap[id] = prefixedId; + const prefixedId = `${ACTION_PREFIX}${fallbackAlias}:${rawId}`; + remapMap[rawId] = prefixedId; return prefixedId; } - return waitForActionRemap(id); + throw new Error( + `[modern-js-v3:rsc-bridge] Unable to resolve raw action id "${rawId}". Use a prefixed action id or ensure the remote alias manifest is merged before invoking the action.`, + ); } function createServerCallback(runtime) { @@ -302,15 +305,17 @@ function installServerCallbacks() { return installedCount; } -function hookChunkLoaderInstall() { - const webpackRequire = getWebpackRequire(); - if (!webpackRequire || !isFunction(webpackRequire.e)) { - return; +function getWrappedChunkLoader(chunkLoader) { + if (!isFunction(chunkLoader)) { + return chunkLoader; + } + if (chunkLoader[CALLBACK_CHUNK_LOADER_WRAPPED_FLAG]) { + return chunkLoader; } - const chunkLoader = webpackRequire.e; - if (chunkLoader[CALLBACK_CHUNK_LOADER_HOOK_FLAG]) { - return; + const cachedWrappedLoader = wrappedChunkLoaders.get(chunkLoader); + if (cachedWrappedLoader) { + return cachedWrappedLoader; } const wrappedChunkLoader = function (...args) { @@ -322,12 +327,162 @@ function hookChunkLoaderInstall() { }); return chunkLoadResult; }; - wrappedChunkLoader[CALLBACK_CHUNK_LOADER_HOOK_FLAG] = true; - webpackRequire.e = wrappedChunkLoader; + + wrappedChunkLoader[CALLBACK_CHUNK_LOADER_WRAPPED_FLAG] = true; + wrappedChunkLoaders.set(chunkLoader, wrappedChunkLoader); + return wrappedChunkLoader; +} + +function queueCallbackInstallAfterChunkLoad(promises) { + if (!Array.isArray(promises)) { + installServerCallbacks(); + return; + } + + Promise.resolve().then(() => { + Promise.allSettled(promises.slice()) + .catch(() => undefined) + .then(() => { + installServerCallbacks(); + }); + }); +} + +function getWrappedChunkHandler(chunkHandler) { + if (!isFunction(chunkHandler)) { + return chunkHandler; + } + if (chunkHandler[CALLBACK_CHUNK_HANDLER_WRAPPED_FLAG]) { + return chunkHandler; + } + + const cachedWrappedChunkHandler = wrappedChunkHandlers.get(chunkHandler); + if (cachedWrappedChunkHandler) { + return cachedWrappedChunkHandler; + } + + const wrappedChunkHandler = function (...args) { + chunkHandler.apply(this, args); + queueCallbackInstallAfterChunkLoad(args[1]); + }; + wrappedChunkHandler[CALLBACK_CHUNK_HANDLER_WRAPPED_FLAG] = true; + wrappedChunkHandlers.set(chunkHandler, wrappedChunkHandler); + return wrappedChunkHandler; +} + +function hookEnsureChunkHandlersInstall() { + const webpackRequire = getWebpackRequire(); + if (!webpackRequire || !isObject(webpackRequire.f)) { + return false; + } + + const chunkHandlers = webpackRequire.f; + if (hookedChunkHandlers.has(chunkHandlers)) { + return true; + } + + const existingChunkHandler = chunkHandlers[CALLBACK_CHUNK_HANDLER_KEY]; + if (isFunction(existingChunkHandler)) { + chunkHandlers[CALLBACK_CHUNK_HANDLER_KEY] = + getWrappedChunkHandler(existingChunkHandler); + } else { + const callbackInstallChunkHandler = function (_chunkId, promises) { + queueCallbackInstallAfterChunkLoad(promises); + }; + callbackInstallChunkHandler[CALLBACK_CHUNK_HANDLER_WRAPPED_FLAG] = true; + chunkHandlers[CALLBACK_CHUNK_HANDLER_KEY] = callbackInstallChunkHandler; + } + + hookedChunkHandlers.add(chunkHandlers); + return true; +} + +function hookChunkLoaderInstall() { + const webpackRequire = getWebpackRequire(); + if (!webpackRequire) { + return; + } + + const chunkLoaderDescriptor = Object.getOwnPropertyDescriptor( + webpackRequire, + 'e', + ); + if ( + webpackRequire[CALLBACK_CHUNK_LOADER_HOOK_FLAG] && + (!isFunction(chunkLoaderDescriptor?.get) || + chunkLoaderDescriptor.get[CALLBACK_CHUNK_LOADER_HOOK_FLAG]) + ) { + return; + } + + const hookState = { + chunkLoader: isFunction(chunkLoaderDescriptor?.get) + ? chunkLoaderDescriptor.get.call(webpackRequire) + : webpackRequire.e, + }; + + if (!isFunction(hookState.chunkLoader)) { + return; + } + + if (chunkLoaderDescriptor?.configurable === false) { + const wrappedChunkLoader = getWrappedChunkLoader(hookState.chunkLoader); + if (isFunction(chunkLoaderDescriptor?.set)) { + chunkLoaderDescriptor.set.call(webpackRequire, wrappedChunkLoader); + webpackRequire[CALLBACK_CHUNK_LOADER_HOOK_FLAG] = true; + return; + } + if (chunkLoaderDescriptor?.writable !== false) { + const didSetChunkLoader = Reflect.set( + webpackRequire, + 'e', + wrappedChunkLoader, + ); + if (didSetChunkLoader && webpackRequire.e === wrappedChunkLoader) { + webpackRequire[CALLBACK_CHUNK_LOADER_HOOK_FLAG] = true; + } + } + return; + } + + const readChunkLoader = () => { + if (isFunction(chunkLoaderDescriptor?.get)) { + const nextChunkLoader = chunkLoaderDescriptor.get.call(webpackRequire); + if (isFunction(nextChunkLoader)) { + hookState.chunkLoader = nextChunkLoader; + } + } + return hookState.chunkLoader; + }; + + const wrappedChunkLoaderGetter = function () { + const chunkLoader = readChunkLoader(); + return isFunction(chunkLoader) + ? getWrappedChunkLoader(chunkLoader) + : chunkLoader; + }; + wrappedChunkLoaderGetter[CALLBACK_CHUNK_LOADER_HOOK_FLAG] = true; + + Object.defineProperty(webpackRequire, 'e', { + configurable: true, + enumerable: chunkLoaderDescriptor?.enumerable ?? true, + get: wrappedChunkLoaderGetter, + set(nextChunkLoader) { + if (isFunction(chunkLoaderDescriptor?.set)) { + chunkLoaderDescriptor.set.call(webpackRequire, nextChunkLoader); + } + hookState.chunkLoader = nextChunkLoader; + }, + }); + + webpackRequire[CALLBACK_CHUNK_LOADER_HOOK_FLAG] = true; } function runInstallAttempt() { - hookChunkLoaderInstall(); + const hasEnsureChunkHandlersHook = hookEnsureChunkHandlersInstall(); + if (!hasEnsureChunkHandlersHook) { + hookChunkLoaderInstall(); + } installServerCallbacks(); callbackInstallAttempts += 1; diff --git a/packages/modernjs-v3/src/runtime/rsc-client-callback-bootstrap.spec.ts b/packages/modernjs-v3/src/runtime/rsc-client-callback-bootstrap.spec.ts index 86f284579d8..cb0e57b78d8 100644 --- a/packages/modernjs-v3/src/runtime/rsc-client-callback-bootstrap.spec.ts +++ b/packages/modernjs-v3/src/runtime/rsc-client-callback-bootstrap.spec.ts @@ -1,57 +1,385 @@ import fs from 'node:fs'; import path from 'node:path'; -import { describe, expect, it } from 'vitest'; - -const getBootstrapSource = () => { - const bootstrapFilePath = path.resolve( - __dirname, - './rsc-client-callback-bootstrap.js', - ); - return fs.readFileSync(bootstrapFilePath, 'utf-8'); +import vm from 'node:vm'; +import { describe, expect, it, vi } from 'vitest'; + +const BOOTSTRAP_FILE_PATH = path.resolve( + __dirname, + './rsc-client-callback-bootstrap.js', +); +const CALLBACK_CHUNK_HANDLER_KEY = '__MODERN_RSC_MF_CALLBACK_CHUNK_HANDLER__'; + +type RuntimeModule = { + setServerCallback: ReturnType; + createTemporaryReferenceSet: ReturnType; + encodeReply: ReturnType; + createFromFetch: ReturnType; }; +function createClientRuntimeModule(): RuntimeModule { + return { + setServerCallback: vi.fn(), + createTemporaryReferenceSet: vi.fn(() => ({ __temp: true })), + encodeReply: vi.fn(async () => 'encoded-body'), + createFromFetch: vi.fn(async () => ({ ok: true })), + }; +} + +function stripImports(source: string) { + return source.replace(/^import[\s\S]*?;\n/gm, ''); +} + +function flushMicrotasks() { + return new Promise((resolve) => queueMicrotask(resolve)); +} + +function evaluateBootstrap({ + runtimeRequire, + windowObject, +}: { + runtimeRequire: Record; + windowObject?: Record; +}) { + const source = stripImports(fs.readFileSync(BOOTSTRAP_FILE_PATH, 'utf-8')); + const resolvedActionIds: Array<(id: string) => Promise> = []; + const importedRuntime = createClientRuntimeModule(); + const fetchMock = vi.fn(async () => new Response('ok', { status: 200 })); + + const context = { + __webpack_require__: runtimeRequire, + setResolveActionId: vi.fn((resolver: (id: string) => Promise) => { + resolvedActionIds.push(resolver); + }), + createFromFetch: importedRuntime.createFromFetch, + createTemporaryReferenceSet: importedRuntime.createTemporaryReferenceSet, + encodeReply: importedRuntime.encodeReply, + setServerCallback: importedRuntime.setServerCallback, + fetch: fetchMock, + clearTimeout: vi.fn(), + setTimeout: vi.fn(() => 1), + queueMicrotask: (cb: () => void) => cb(), + window: windowObject, + __FEDERATION__: {}, + Response, + Promise, + Object, + Array, + WeakMap, + WeakSet, + Set, + Map, + JSON, + Reflect, + URL, + console, + process: { env: {} }, + }; + + vm.runInNewContext(source, context, { + filename: BOOTSTRAP_FILE_PATH, + }); + + return { + context, + importedRuntime, + fetchMock, + resolveActionId: resolvedActionIds[0] as ( + id: string, + ) => Promise | undefined, + }; +} + describe('rsc-client-callback-bootstrap', () => { - it('registers resolveActionId via runtime API', () => { - const source = getBootstrapSource(); + it('registers resolver and installs callback on imported client runtime', () => { + const runtimeRequire = { + e: vi.fn(async () => undefined), + c: {}, + }; + const { importedRuntime, resolveActionId } = evaluateBootstrap({ + runtimeRequire, + }); + + expect(resolveActionId).toBeTypeOf('function'); + expect(importedRuntime.setServerCallback).toHaveBeenCalled(); + }); + + it('prefers ensureChunkHandlers hook and leaves chunk loader unpatched', async () => { + const runtimeRequire: Record = { + c: {}, + f: {}, + }; + const originalChunkLoader = vi.fn(async (chunkId: string) => { + const promises: Promise[] = []; + for (const handler of Object.values(runtimeRequire.f)) { + if (typeof handler === 'function') { + handler(chunkId, promises); + } + } + await Promise.all(promises); + return 'chunk-loaded'; + }); + runtimeRequire.e = originalChunkLoader; + + evaluateBootstrap({ runtimeRequire }); - expect(source).toContain( - "import { setResolveActionId } from '@modern-js/runtime/rsc/client';", + expect(runtimeRequire.e).toBe(originalChunkLoader); + + const lateRuntime = createClientRuntimeModule(); + runtimeRequire.c['late-runtime-module'] = { exports: lateRuntime }; + + await runtimeRequire.e('late-runtime'); + await flushMicrotasks(); + + expect(lateRuntime.setServerCallback).toHaveBeenCalledTimes(1); + }); + + it('waits for handlers added after the hook to settle before installing callbacks', async () => { + const runtimeRequire: Record = { + c: {}, + f: {}, + }; + const originalChunkLoader = vi.fn(async (chunkId: string) => { + const promises: Promise[] = []; + for (const handler of Object.values(runtimeRequire.f)) { + if (typeof handler === 'function') { + handler(chunkId, promises); + } + } + await Promise.all(promises); + return 'chunk-loaded'; + }); + runtimeRequire.e = originalChunkLoader; + + evaluateBootstrap({ runtimeRequire }); + + const lateRuntime = createClientRuntimeModule(); + runtimeRequire.f.zzzLateChunkHandler = vi.fn( + (_chunkId: string, promises: Promise[]) => { + promises.push( + new Promise((resolve) => { + setTimeout(() => { + runtimeRequire.c['late-runtime-module'] = { + exports: lateRuntime, + }; + resolve(); + }, 0); + }), + ); + }, ); - expect(source).toContain('setResolveActionId(resolveActionId);'); - expect(source).not.toContain('__MODERN_RSC_ACTION_RESOLVER__'); + + await runtimeRequire.e('late-runtime'); + await new Promise((resolve) => setTimeout(resolve, 0)); + await flushMicrotasks(); + + expect(lateRuntime.setServerCallback).toHaveBeenCalledTimes(1); }); - it('falls back to root action endpoint when entry name is missing', () => { - const source = getBootstrapSource(); + it('wraps existing callback chunk handler and still installs late callbacks', async () => { + const existingCallbackChunkHandler = vi.fn( + (_chunkId: string, promises: Promise[]) => { + promises.push(Promise.resolve('existing-handler')); + }, + ); + const runtimeRequire: Record = { + c: {}, + f: { + [CALLBACK_CHUNK_HANDLER_KEY]: existingCallbackChunkHandler, + }, + }; + const originalChunkLoader = vi.fn(async (chunkId: string) => { + const promises: Promise[] = []; + for (const handler of Object.values(runtimeRequire.f)) { + if (typeof handler === 'function') { + handler(chunkId, promises); + } + } + await Promise.all(promises); + return 'chunk-loaded'; + }); + runtimeRequire.e = originalChunkLoader; + + evaluateBootstrap({ runtimeRequire }); + + expect(runtimeRequire.e).toBe(originalChunkLoader); + + const lateRuntime = createClientRuntimeModule(); + runtimeRequire.c['late-runtime-module'] = { exports: lateRuntime }; - expect(source).toContain( - "if (!entryName || entryName === 'main' || entryName === 'index')", + await runtimeRequire.e('late-runtime'); + await flushMicrotasks(); + + expect(existingCallbackChunkHandler).toHaveBeenCalledWith( + 'late-runtime', + expect.any(Array), + ); + expect(lateRuntime.setServerCallback).toHaveBeenCalledTimes(1); + }); + + it('wraps configurable chunk loader and installs callback for late client runtimes', async () => { + const originalChunkLoader = vi.fn(async () => 'chunk-loaded'); + const runtimeRequire = { + e: originalChunkLoader, + c: {}, + }; + + evaluateBootstrap({ runtimeRequire }); + expect(runtimeRequire.e).not.toBe(originalChunkLoader); + + const lateRuntime = createClientRuntimeModule(); + runtimeRequire.c['late-runtime-module'] = { exports: lateRuntime }; + + await runtimeRequire.e('late-runtime'); + await flushMicrotasks(); + + expect(lateRuntime.setServerCallback).toHaveBeenCalledTimes(1); + }); + + it('wraps non-configurable chunk loader when writable and installs late callbacks', async () => { + const originalChunkLoader = vi.fn(async () => 'chunk-loaded'); + const runtimeRequire: Record = { c: {} }; + Object.defineProperty(runtimeRequire, 'e', { + configurable: false, + writable: true, + enumerable: true, + value: originalChunkLoader, + }); + + evaluateBootstrap({ runtimeRequire }); + expect(runtimeRequire.e).not.toBe(originalChunkLoader); + + const lateRuntime = createClientRuntimeModule(); + runtimeRequire.c['late-runtime-module'] = { exports: lateRuntime }; + + await runtimeRequire.e('late-runtime'); + await flushMicrotasks(); + + expect(lateRuntime.setServerCallback).toHaveBeenCalledTimes(1); + }); + + it('uses root action endpoint when entry name is missing/main/index', async () => { + const runtimeRequire = { + e: vi.fn(async () => undefined), + c: {}, + }; + const { importedRuntime, fetchMock } = evaluateBootstrap({ + runtimeRequire, + windowObject: { __MODERN_JS_ENTRY_NAME: 'main' }, + }); + + const serverCallback = importedRuntime.setServerCallback.mock.calls[0]?.[0]; + expect(serverCallback).toBeTypeOf('function'); + + await serverCallback('remote:demo:action', ['arg']); + const [endpoint, requestInit] = fetchMock.mock.calls[0]; + expect(endpoint).toBe('/'); + expect(requestInit?.method).toBe('POST'); + expect(requestInit?.headers?.['x-rsc-action']).toBe('remote:demo:action'); + }); + + it('fails closed when remap map marks raw action id as ambiguous', async () => { + const runtimeRequire = { + e: vi.fn(async () => undefined), + c: {}, + }; + const { context, resolveActionId } = evaluateBootstrap({ runtimeRequire }); + + expect(resolveActionId).toBeTypeOf('function'); + context.__FEDERATION__.__MODERN_RSC_MF_ACTION_ID_MAP__ = { + rawAction: false, + }; + + await expect(resolveActionId!('rawAction')).rejects.toThrow( + /Ambiguous remote action id "rawAction"/, ); }); - it('avoids remapping action ids via host uniqueName heuristics', () => { - const source = getBootstrapSource(); + it('keeps host-local action ids unprefixed when a single remote alias exists', async () => { + const runtimeRequire = { + e: vi.fn(async () => undefined), + c: {}, + federation: { + instance: { + options: { + remotes: [{ alias: 'rscRemote' }], + }, + }, + }, + rscM: { + serverManifest: { + hostAction: { id: 'hostAction' }, + }, + }, + }; + const { resolveActionId } = evaluateBootstrap({ + runtimeRequire, + }); - expect(source).not.toContain('initializeSharingData.uniqueName'); + await expect(resolveActionId!('hostAction')).resolves.toBe('hostAction'); }); - it('keeps callback installation retries and chunk-loader hook for late runtimes', () => { - const source = getBootstrapSource(); + it('prefers host-local actions over ambiguous remap markers', async () => { + const runtimeRequire = { + e: vi.fn(async () => undefined), + c: {}, + federation: { + instance: { + options: { + remotes: [{ alias: 'remoteA' }, { alias: 'remoteB' }], + }, + }, + }, + rscM: { + serverManifest: { + hostAction: { id: 'hostAction' }, + }, + }, + }; + const { context, resolveActionId } = evaluateBootstrap({ runtimeRequire }); + context.__FEDERATION__.__MODERN_RSC_MF_ACTION_ID_MAP__ = { + hostAction: false, + }; - expect(source).toContain('const CALLBACK_INSTALL_RETRY_DELAY_MS = 50;'); - expect(source).toContain('const MAX_CALLBACK_INSTALL_ATTEMPTS = 120;'); - expect(source).toContain('function hookChunkLoaderInstall()'); - expect(source).toContain('webpackRequire.e = wrappedChunkLoader;'); + await expect(resolveActionId!('hostAction')).resolves.toBe('hostAction'); }); - it('does not cache fallback alias resolution before runtime is available', () => { - const source = getBootstrapSource(); - const runtimeGuardIndex = source.indexOf('if (!runtimeInstance) {'); - const resolvedFlagIndex = source.indexOf( - 'hasResolvedFallbackAlias = true;', + it('throws when a raw action id cannot be resolved deterministically', async () => { + const runtimeRequire = { + e: vi.fn(async () => undefined), + c: {}, + federation: { + instance: { + options: { + remotes: [{ alias: 'remoteA' }, { alias: 'remoteB' }], + }, + }, + }, + rscM: { + serverManifest: {}, + }, + }; + const { resolveActionId } = evaluateBootstrap({ runtimeRequire }); + + await expect(resolveActionId!('rawAction')).rejects.toThrow( + /Unable to resolve raw action id "rawAction"/, ); + }); + + it('does not mark hook install when getter-only chunk loader cannot be replaced', () => { + const originalChunkLoader = vi.fn(async () => 'chunk-loaded'); + const runtimeRequire: Record = { c: {} }; + Object.defineProperty(runtimeRequire, 'e', { + configurable: false, + enumerable: true, + get() { + return originalChunkLoader; + }, + }); + + evaluateBootstrap({ runtimeRequire }); - expect(runtimeGuardIndex).toBeGreaterThan(-1); - expect(resolvedFlagIndex).toBeGreaterThan(runtimeGuardIndex); + expect( + runtimeRequire.__MODERN_RSC_MF_CALLBACK_CHUNK_LOADER_HOOKED__, + ).not.toBe(true); }); }); diff --git a/packages/modernjs-v3/src/server/asyncStartupLoader.ts b/packages/modernjs-v3/src/server/asyncStartupLoader.ts index 4d861faed50..2e809e4e291 100644 --- a/packages/modernjs-v3/src/server/asyncStartupLoader.ts +++ b/packages/modernjs-v3/src/server/asyncStartupLoader.ts @@ -4,8 +4,8 @@ import vm from 'vm'; import type { BundleLoaderStrategy } from '@modern-js/server-core/node'; import fs from 'fs-extra'; -const ASYNC_NODE_STARTUP_CALL = - 'var __webpack_exports__ = __webpack_require__.x();'; +const ASYNC_NODE_STARTUP_CALL_PATTERN = + /var\s+__webpack_exports__\s*=\s*__webpack_require__\.x\(\s*\)\s*;/; const ENCODED_HMR_CLIENT_BOOTSTRAP_CALL = /__webpack_require__\("data:text\/javascript,[^"]*"\);\s*/g; @@ -35,14 +35,14 @@ export const mfAsyncStartupLoaderStrategy: BundleLoaderStrategy = async ( ); if ( - !sanitizedBundleCode.includes(ASYNC_NODE_STARTUP_CALL) || + !ASYNC_NODE_STARTUP_CALL_PATTERN.test(sanitizedBundleCode) || !sanitizedBundleCode.includes('__webpack_require__.mfAsyncStartup') ) { return undefined; } const patchedCode = sanitizedBundleCode.replace( - ASYNC_NODE_STARTUP_CALL, + ASYNC_NODE_STARTUP_CALL_PATTERN, 'var __webpack_exports__ = __webpack_require__.x({}, []);', ); diff --git a/packages/modernjs-v3/src/server/index.ts b/packages/modernjs-v3/src/server/index.ts index c5e3c61f5cb..8b41249857a 100644 --- a/packages/modernjs-v3/src/server/index.ts +++ b/packages/modernjs-v3/src/server/index.ts @@ -69,12 +69,11 @@ const staticServePlugin = (): ServerPlugin => ({ setup: (api) => { try { const serverCoreNodeEntry = resolveServerCoreNodeEntry(); - if (!serverCoreNodeEntry) { - return; - } - const { registerBundleLoaderStrategy } = require(serverCoreNodeEntry); - if (typeof registerBundleLoaderStrategy === 'function') { - registerBundleLoaderStrategy(mfAsyncStartupLoaderStrategy); + if (serverCoreNodeEntry) { + const { registerBundleLoaderStrategy } = require(serverCoreNodeEntry); + if (typeof registerBundleLoaderStrategy === 'function') { + registerBundleLoaderStrategy(mfAsyncStartupLoaderStrategy); + } } } catch { // registerBundleLoaderStrategy may not exist in all @modern-js/server-core versions diff --git a/packages/rspack/src/ModuleFederationPlugin.ts b/packages/rspack/src/ModuleFederationPlugin.ts index 159529f4775..5aa36debf6a 100644 --- a/packages/rspack/src/ModuleFederationPlugin.ts +++ b/packages/rspack/src/ModuleFederationPlugin.ts @@ -13,10 +13,10 @@ import { import { StatsPlugin } from '@module-federation/manifest'; import { ContainerManager, utils } from '@module-federation/managers'; -import { DtsPlugin } from '@module-federation/dts-plugin'; import ReactBridgePlugin from '@module-federation/bridge-react-webpack-plugin'; import path from 'node:path'; import fs from 'node:fs'; +import { createRequire } from 'node:module'; import { RemoteEntryPlugin } from './RemoteEntryPlugin'; import logger from './logger'; @@ -29,7 +29,116 @@ type CacheGroup = CacheGroups[string]; declare const __VERSION__: string; -const RuntimeToolsPath = require.resolve('@module-federation/runtime-tools'); +const RUNTIME_TOOLS_REQUEST = '@module-federation/runtime-tools'; +const NODE_RUNTIME_PLUGIN = '@module-federation/node/runtimePlugin'; +const runtimeRequire: NodeJS.Require = createRequire( + path.join(process.cwd(), '__mf_rspack_runtime__.cjs'), +); + +type DtsPluginRuntimeInstance = { + apply(compiler: Compiler): void; + addRuntimePlugins(): void; +}; + +type DtsPluginConstructor = new ( + options: moduleFederationPlugin.ModuleFederationPluginOptions, +) => DtsPluginRuntimeInstance; + +function getTargetValues(target: unknown): string[] { + if (Array.isArray(target)) { + return target.filter((item): item is string => typeof item === 'string'); + } + return typeof target === 'string' ? [target] : []; +} + +function isNodeLikeTarget(target: unknown): boolean { + if (target === false) { + return false; + } + const targets = getTargetValues(target); + return targets.some((value) => value.includes('node')); +} + +function hasAsyncNodeTarget(target: unknown): boolean { + if (target === false) { + return false; + } + const targets = getTargetValues(target); + return targets.some((value) => value.includes('async-node')); +} + +function hasNodeRuntimePlugin( + runtimePlugins: moduleFederationPlugin.ModuleFederationPluginOptions['runtimePlugins'], +): boolean { + if (!Array.isArray(runtimePlugins)) { + return false; + } + + return runtimePlugins.some((plugin) => { + const entry = Array.isArray(plugin) ? plugin[0] : plugin; + if (typeof entry !== 'string') { + return false; + } + return ( + entry === NODE_RUNTIME_PLUGIN || + entry.includes('@module-federation/node/runtimePlugin') || + entry.includes('@module-federation/node/dist/src/runtimePlugin') + ); + }); +} + +function validateRscNodeRuntimePlugin( + options: moduleFederationPlugin.ModuleFederationPluginOptions, + target: unknown, +): void { + const isRscEnabled = + (options.experiments as { rsc?: boolean } | undefined)?.rsc === true; + + if (!isRscEnabled || !isNodeLikeTarget(target)) { + return; + } + + if (!hasAsyncNodeTarget(target)) { + throw new Error( + '[ModuleFederationPlugin.rsc] Invalid configuration:\n`target` must include `"async-node"`.', + ); + } + + if ( + (options.experiments as { asyncStartup?: boolean } | undefined) + ?.asyncStartup !== true + ) { + throw new Error( + '[ModuleFederationPlugin.rsc] Invalid configuration:\n`experiments.asyncStartup` must be `true`.', + ); + } + + if (!hasNodeRuntimePlugin(options.runtimePlugins)) { + options.runtimePlugins = [ + ...(options.runtimePlugins || []), + NODE_RUNTIME_PLUGIN, + ]; + } +} + +function resolveFromCompilerContext( + request: string, + compilerContext: string, +): string { + try { + return runtimeRequire.resolve(request, { + paths: [compilerContext], + }); + } catch {} + + return runtimeRequire.resolve(request); +} + +function loadDtsPlugin(): DtsPluginConstructor { + const request = ['@module-federation', 'dts-plugin'].join('/'); + const load = (id: string) => runtimeRequire(id); + return (load(request) as { DtsPlugin: DtsPluginConstructor }).DtsPlugin; +} export const PLUGIN_NAME = 'RspackModuleFederationPlugin'; export class ModuleFederationPlugin implements RspackPluginInstance { @@ -98,6 +207,7 @@ export class ModuleFederationPlugin implements RspackPluginInstance { apply(compiler: Compiler): void { bindLoggerToCompiler(logger, compiler, PLUGIN_NAME); const { _options: options } = this; + validateRscNodeRuntimePlugin(options, compiler.options.target); if (!options.name) { throw new Error('[ ModuleFederationPlugin ]: name is required'); @@ -123,7 +233,7 @@ export class ModuleFederationPlugin implements RspackPluginInstance { const runtimePlugins = options.runtimePlugins || []; options.runtimePlugins = runtimePlugins.concat( - require.resolve( + runtimeRequire.resolve( '@module-federation/inject-external-runtime-core-plugin', ), ); @@ -136,12 +246,15 @@ export class ModuleFederationPlugin implements RspackPluginInstance { }).apply(compiler); } - const implementationPath = options.implementation || RuntimeToolsPath; + const implementationPath = options.implementation + ? resolveFromCompilerContext(options.implementation, compiler.context) + : resolveFromCompilerContext(RUNTIME_TOOLS_REQUEST, compiler.context); options.implementation = implementationPath; let disableManifest = options.manifest === false; let disableDts = options.dts === false; if (!disableDts) { + const DtsPlugin = loadDtsPlugin(); const dtsPlugin = new DtsPlugin(options); // @ts-ignore dtsPlugin.apply(compiler); @@ -164,12 +277,15 @@ export class ModuleFederationPlugin implements RspackPluginInstance { ).apply(compiler); const resolveRuntimePath = (candidates: string[]) => { + const resolvePaths = [compiler.context, path.dirname(implementationPath)]; for (const candidate of candidates) { - try { - return require.resolve(candidate, { - paths: [implementationPath], - }); - } catch {} + for (const resolvePath of resolvePaths) { + try { + return runtimeRequire.resolve(candidate, { + paths: [resolvePath], + }); + } catch {} + } } throw new Error( `[ ModuleFederationPlugin ]: Unable to resolve runtime entry from ${candidates.join( diff --git a/packages/runtime-core/package.json b/packages/runtime-core/package.json index fba90a6c2eb..199512835b1 100644 --- a/packages/runtime-core/package.json +++ b/packages/runtime-core/package.json @@ -21,24 +21,24 @@ ], "exports": { ".": { + "types": "./dist/index.d.ts", "import": { - "types": "./dist/index.d.ts", "default": "./dist/index.js" }, "require": { - "types": "./dist/index.d.ts", "default": "./dist/index.cjs" - } + }, + "default": "./dist/index.js" }, "./types": { + "types": "./dist/types.d.ts", "import": { - "types": "./dist/types.d.ts", "default": "./dist/types.js" }, "require": { - "types": "./dist/types.d.ts", "default": "./dist/types.cjs" - } + }, + "default": "./dist/types.js" } }, "typesVersions": { diff --git a/packages/runtime-core/src/plugins/snapshot/index.ts b/packages/runtime-core/src/plugins/snapshot/index.ts index fde0f973576..bfe3703cc14 100644 --- a/packages/runtime-core/src/plugins/snapshot/index.ts +++ b/packages/runtime-core/src/plugins/snapshot/index.ts @@ -26,8 +26,21 @@ export function assignRemoteInfo( let entryUrl = getResourceUrl(remoteSnapshot, remoteEntryInfo.url); - if (!isBrowserEnv() && !entryUrl.startsWith('http')) { - entryUrl = `https:${entryUrl}`; + if (!isBrowserEnv()) { + if (entryUrl.startsWith('//')) { + entryUrl = `https:${entryUrl}`; + } else if ( + !entryUrl.startsWith('http://') && + !entryUrl.startsWith('https://') + ) { + try { + entryUrl = new URL(entryUrl, remoteInfo.entry).href; + } catch { + error( + `Unable to resolve remote entry URL for ${remoteInfo.name}. entry="${entryUrl}", manifest="${remoteInfo.entry}"`, + ); + } + } } remoteInfo.type = remoteEntryInfo.type; diff --git a/packages/runtime/package.json b/packages/runtime/package.json index 3c4f1f33236..36c943a9d7e 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -24,44 +24,44 @@ ], "exports": { ".": { + "types": "./dist/index.d.ts", "import": { - "types": "./dist/index.d.ts", "default": "./dist/index.js" }, "require": { - "types": "./dist/index.d.ts", "default": "./dist/index.cjs" - } + }, + "default": "./dist/index.js" }, "./helpers": { + "types": "./dist/helpers.d.ts", "import": { - "types": "./dist/helpers.d.ts", "default": "./dist/helpers.js" }, "require": { - "types": "./dist/helpers.d.ts", "default": "./dist/helpers.cjs" - } + }, + "default": "./dist/helpers.js" }, "./types": { + "types": "./dist/types.d.ts", "import": { - "types": "./dist/types.d.ts", "default": "./dist/types.js" }, "require": { - "types": "./dist/types.d.ts", "default": "./dist/types.cjs" - } + }, + "default": "./dist/types.js" }, "./core": { + "types": "./dist/core.d.ts", "import": { - "types": "./dist/core.d.ts", "default": "./dist/core.js" }, "require": { - "types": "./dist/core.d.ts", "default": "./dist/core.cjs" - } + }, + "default": "./dist/core.js" }, "./*": "./*" }, diff --git a/packages/sdk/__tests__/nodeScriptUtils.spec.ts b/packages/sdk/__tests__/nodeScriptUtils.spec.ts index 9db01b59679..7a74531662e 100644 --- a/packages/sdk/__tests__/nodeScriptUtils.spec.ts +++ b/packages/sdk/__tests__/nodeScriptUtils.spec.ts @@ -39,7 +39,7 @@ describe('nodeScriptUtils', () => { it('resolves promise-like exports and falls back to global container', async () => { const key = '__FEDERATION_remote:key__'; const globalContainer = { init: () => undefined, get: () => undefined }; - (globalThis as Record)[key] = globalContainer; + (global as Record)[key] = globalContainer; const resolvedFromPromise = await resolveNodeScriptExports( { @@ -68,6 +68,6 @@ describe('nodeScriptUtils', () => { expect(resolvedFromGlobal).toBe(globalContainer); - delete (globalThis as Record)[key]; + delete (global as Record)[key]; }); }); diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 8359ce47ada..745bd9f91f2 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -31,24 +31,24 @@ }, "exports": { ".": { + "types": "./dist/index.d.ts", "import": { - "types": "./dist/index.d.ts", "default": "./dist/index.js" }, "require": { - "types": "./dist/index.d.ts", "default": "./dist/index.cjs" - } + }, + "default": "./dist/index.js" }, "./normalize-webpack-path": { + "types": "./dist/normalize-webpack-path.d.ts", "import": { - "types": "./dist/normalize-webpack-path.d.ts", "default": "./dist/normalize-webpack-path.js" }, "require": { - "types": "./dist/normalize-webpack-path.d.ts", "default": "./dist/normalize-webpack-path.cjs" - } + }, + "default": "./dist/normalize-webpack-path.js" } }, "typesVersions": { diff --git a/packages/sdk/src/nodeScriptUtils.ts b/packages/sdk/src/nodeScriptUtils.ts index f265c5b33e2..d4491761e69 100644 --- a/packages/sdk/src/nodeScriptUtils.ts +++ b/packages/sdk/src/nodeScriptUtils.ts @@ -1,5 +1,5 @@ -const ASYNC_NODE_STARTUP_CALL = - 'var __webpack_exports__ = __webpack_require__.x();'; +const ASYNC_NODE_STARTUP_CALL_PATTERN = + /var\s+__webpack_exports__\s*=\s*__webpack_require__\.x\(\s*\)\s*;/; const ENCODED_HMR_CLIENT_BOOTSTRAP_CALL = /__webpack_require__\("data:text\/javascript,[^"]*"\);\s*/g; @@ -19,11 +19,11 @@ export const patchNodeRemoteEntryCode = ( let patchedCode = code.replace(ENCODED_HMR_CLIENT_BOOTSTRAP_CALL, ''); if ( - patchedCode.includes(ASYNC_NODE_STARTUP_CALL) && + ASYNC_NODE_STARTUP_CALL_PATTERN.test(patchedCode) && patchedCode.includes('__webpack_require__.mfAsyncStartup') ) { patchedCode = patchedCode.replace( - ASYNC_NODE_STARTUP_CALL, + ASYNC_NODE_STARTUP_CALL_PATTERN, 'var __webpack_exports__ = __webpack_require__.x({}, []);', ); } @@ -62,7 +62,7 @@ export const resolveNodeScriptExports = async ( const globalContainer = typeof globalName === 'string' - ? (globalThis as Record)[globalName] + ? (global as Record)[globalName] : undefined; if ( diff --git a/packages/sdk/src/types/plugins/ModuleFederationPlugin.ts b/packages/sdk/src/types/plugins/ModuleFederationPlugin.ts index 80da0aaa856..8f5b58414f2 100644 --- a/packages/sdk/src/types/plugins/ModuleFederationPlugin.ts +++ b/packages/sdk/src/types/plugins/ModuleFederationPlugin.ts @@ -330,6 +330,10 @@ export interface ExposesConfig { * Custom chunk name for the exposed module. */ name?: string; + /** + * Layer in which the exposed module should be placed. + */ + layer?: string; } /** * Options for library. diff --git a/packages/webpack-bundler-runtime/__tests__/resolveRemoteModuleId.spec.ts b/packages/webpack-bundler-runtime/__tests__/resolveRemoteModuleId.spec.ts new file mode 100644 index 00000000000..3a0c03c0393 --- /dev/null +++ b/packages/webpack-bundler-runtime/__tests__/resolveRemoteModuleId.spec.ts @@ -0,0 +1,127 @@ +import { resolveRemoteModuleId } from '../src/resolveRemoteModuleId'; + +describe('resolveRemoteModuleId', () => { + test('resolves wrapper module id by alias and expose', () => { + const webpackRequire = { + remotesLoadingData: { + moduleIdToRemoteDataMapping: { + 'webpack/container/remote/rscRemote/./Dialog': { + name: './Dialog', + remoteName: 'rscRemote', + }, + }, + }, + federation: { + bundlerRuntimeOptions: { + remotes: { + remoteInfos: { + rscRemote: [{ name: 'rscRemote' }], + }, + }, + }, + }, + } as any; + + expect( + resolveRemoteModuleId({ + webpackRequire, + alias: 'rscRemote', + expose: './Dialog', + }), + ).toBe('webpack/container/remote/rscRemote/./Dialog'); + }); + + test('matches expose with and without ./ prefix', () => { + const webpackRequire = { + remotesLoadingData: { + moduleIdToRemoteDataMapping: { + 'webpack/container/remote/rscRemote/./Dialog': { + name: './Dialog', + remoteName: 'rscRemote', + }, + }, + }, + federation: { + bundlerRuntimeOptions: { + remotes: { + remoteInfos: { + rscRemote: [{ name: 'rscRemote' }], + }, + }, + }, + }, + } as any; + + expect( + resolveRemoteModuleId({ + webpackRequire, + alias: 'rscRemote', + expose: 'Dialog', + }), + ).toBe('webpack/container/remote/rscRemote/./Dialog'); + }); + + test('supports alias->remoteName mapping from remoteInfos', () => { + const webpackRequire = { + remotesLoadingData: { + moduleIdToRemoteDataMapping: { + 'webpack/container/remote/app2/./Dialog': { + name: './Dialog', + remoteName: 'app2', + }, + }, + }, + federation: { + bundlerRuntimeOptions: { + remotes: { + remoteInfos: { + rscRemote: [{ name: 'app2' }], + }, + }, + }, + }, + } as any; + + expect( + resolveRemoteModuleId({ + webpackRequire, + alias: 'rscRemote', + expose: './Dialog', + }), + ).toBe('webpack/container/remote/app2/./Dialog'); + }); + + test('returns undefined when mapping is ambiguous', () => { + const webpackRequire = { + remotesLoadingData: { + moduleIdToRemoteDataMapping: { + 'webpack/container/remote/rscRemote/./DialogA': { + name: './Dialog', + remoteName: 'rscRemote', + }, + 'webpack/container/remote/rscRemote/./DialogB': { + name: './Dialog', + remoteName: 'rscRemote', + }, + }, + }, + federation: { + bundlerRuntimeOptions: { + remotes: { + remoteInfos: { + rscRemote: [{ name: 'rscRemote' }], + }, + }, + }, + }, + } as any; + + expect( + resolveRemoteModuleId({ + webpackRequire, + alias: 'rscRemote', + expose: './Dialog', + }), + ).toBeUndefined(); + }); +}); diff --git a/packages/webpack-bundler-runtime/package.json b/packages/webpack-bundler-runtime/package.json index 7208f21397e..d92c146dd17 100644 --- a/packages/webpack-bundler-runtime/package.json +++ b/packages/webpack-bundler-runtime/package.json @@ -31,24 +31,44 @@ }, "exports": { ".": { + "types": "./dist/index.d.ts", "import": { - "types": "./dist/index.d.ts", "default": "./dist/index.js" }, "require": { - "types": "./dist/index.d.ts", "default": "./dist/index.cjs" - } + }, + "default": "./dist/index.js" }, "./constant": { + "types": "./dist/constant.d.ts", "import": { - "types": "./dist/constant.d.ts", "default": "./dist/constant.js" }, "require": { - "types": "./dist/constant.d.ts", "default": "./dist/constant.cjs" - } + }, + "default": "./dist/constant.js" + }, + "./rsc-bridge-runtime-plugin": { + "types": "./dist/rsc-bridge-runtime-plugin.d.ts", + "import": { + "default": "./dist/rsc-bridge-runtime-plugin.js" + }, + "require": { + "default": "./dist/rsc-bridge-runtime-plugin.cjs" + }, + "default": "./dist/rsc-bridge-runtime-plugin.js" + }, + "./rsc-bridge-expose": { + "types": "./dist/rsc-bridge-expose.d.ts", + "import": { + "default": "./dist/rsc-bridge-expose.js" + }, + "require": { + "default": "./dist/rsc-bridge-expose.cjs" + }, + "default": "./dist/rsc-bridge-expose.js" }, "./*": "./*" }, @@ -59,6 +79,12 @@ ], "constant": [ "./dist/constant.d.ts" + ], + "rsc-bridge-runtime-plugin": [ + "./dist/rsc-bridge-runtime-plugin.d.ts" + ], + "rsc-bridge-expose": [ + "./dist/rsc-bridge-expose.d.ts" ] } } diff --git a/packages/webpack-bundler-runtime/src/index.ts b/packages/webpack-bundler-runtime/src/index.ts index dc9b130f0eb..97bc908702b 100644 --- a/packages/webpack-bundler-runtime/src/index.ts +++ b/packages/webpack-bundler-runtime/src/index.ts @@ -8,8 +8,10 @@ import { attachShareScopeMap } from './attachShareScopeMap'; import { initContainerEntry } from './initContainerEntry'; import { init } from './init'; import { getSharedFallbackGetter } from './getSharedFallbackGetter'; +import { resolveRemoteModuleId } from './resolveRemoteModuleId'; export * from './types'; +export * from './rscManifest'; const federation: Federation = { runtime, @@ -23,6 +25,7 @@ const federation: Federation = { installInitialConsumes, initContainerEntry, init, + resolveRemoteModuleId, getSharedFallbackGetter, }, attachShareScopeMap, diff --git a/packages/webpack-bundler-runtime/src/initContainerEntry.ts b/packages/webpack-bundler-runtime/src/initContainerEntry.ts index f92d38df3d3..ca786810d9c 100644 --- a/packages/webpack-bundler-runtime/src/initContainerEntry.ts +++ b/packages/webpack-bundler-runtime/src/initContainerEntry.ts @@ -28,6 +28,12 @@ export function initContainerEntry( const hostShareScopeKeys = remoteEntryInitOptions?.shareScopeKeys; const hostShareScopeMap = remoteEntryInitOptions?.shareScopeMap; + const hasHostShareScopeArray = Array.isArray(hostShareScopeKeys); + const normalizedHostShareScopeKeys = Array.isArray(hostShareScopeKeys) + ? hostShareScopeKeys + : typeof hostShareScopeKeys === 'string' && hostShareScopeKeys + ? [hostShareScopeKeys] + : []; // host: 'default' remote: 'default' remote['default'] = hostShareScopeMap['default'] // host: ['default', 'scope1'] remote: 'default' remote['default'] = hostShareScopeMap['default']; remote['scope1'] = hostShareScopeMap['scop1'] @@ -35,7 +41,7 @@ export function initContainerEntry( // host: ['scope1','default'] remote: ['scope1','scope2'] => remote['scope1'] = hostShareScopeMap['scope1']; remote['scope2'] = hostShareScopeMap['scope2'] = {}; if (!shareScopeKey || typeof shareScopeKey === 'string') { const key = shareScopeKey || 'default'; - if (Array.isArray(hostShareScopeKeys)) { + if (normalizedHostShareScopeKeys.length > 0) { // const sc = hostShareScopeMap![key]; // if (!sc) { // throw new Error('shareScopeKey is not exist in hostShareScopeMap'); @@ -43,12 +49,29 @@ export function initContainerEntry( // federationInstance.initShareScopeMap(key, sc, { // hostShareScopeMap: remoteEntryInitOptions?.shareScopeMap || {}, // }); + const shareScopeKeysToInit = Array.from( + new Set( + hasHostShareScopeArray + ? normalizedHostShareScopeKeys + : normalizedHostShareScopeKeys.includes(key) + ? normalizedHostShareScopeKeys + : [key, ...normalizedHostShareScopeKeys], + ), + ); - hostShareScopeKeys.forEach((hostKey) => { - if (!hostShareScopeMap![hostKey]) { - hostShareScopeMap![hostKey] = {}; + shareScopeKeysToInit.forEach((hostKey, index) => { + if (!hostShareScopeMap?.[hostKey]) { + if (hostShareScopeMap) { + hostShareScopeMap[hostKey] = hasHostShareScopeArray + ? {} + : hostKey === key || index === 0 + ? shareScope + : {}; + } } - const sc = hostShareScopeMap![hostKey]; + const sc = + hostShareScopeMap?.[hostKey] || + (hasHostShareScopeArray ? {} : shareScope); federationInstance.initShareScopeMap(hostKey, sc, { hostShareScopeMap: remoteEntryInitOptions?.shareScopeMap || {}, }); @@ -85,8 +108,35 @@ export function initContainerEntry( } if (!Array.isArray(shareScopeKey)) { + const key = shareScopeKey || 'default'; + if (normalizedHostShareScopeKeys.length > 0) { + const uniqueHostKeys = Array.from(new Set(normalizedHostShareScopeKeys)); + const additionalHostKeys = uniqueHostKeys.filter( + (hostKey) => hostKey !== key, + ); + if (additionalHostKeys.length > 0) { + // Initialize host-provided scopes as well so layered shares (ssr/rsc) + // are available even when the remote container's declared scope is "default". + // @ts-ignore + const primaryResult = webpackRequire.I(key, initScope); + // @ts-ignore + const additionalResults = additionalHostKeys.map((hostKey) => + webpackRequire.I(hostKey, initScope), + ); + const asyncResults = [primaryResult, ...additionalResults].filter( + (result): result is Promise => + Boolean( + result && typeof (result as Promise).then === 'function', + ), + ); + if (asyncResults.length > 0) { + return Promise.all(asyncResults).then(() => primaryResult); + } + return primaryResult; + } + } // @ts-ignore - return webpackRequire.I(shareScopeKey || 'default', initScope); + return webpackRequire.I(key, initScope); } var proxyInitializeSharing = Boolean( diff --git a/packages/webpack-bundler-runtime/src/remotes.ts b/packages/webpack-bundler-runtime/src/remotes.ts index 8682d25de31..9f3a6421224 100644 --- a/packages/webpack-bundler-runtime/src/remotes.ts +++ b/packages/webpack-bundler-runtime/src/remotes.ts @@ -118,10 +118,11 @@ export function remotes(options: RemotesOptions) { if (instance.options.shareStrategy === 'version-first') { const shareScopes = Array.isArray(data[0]) ? data[0] : [data[0]]; - return Promise.all( - shareScopes.map((shareScope) => - instance.sharedHandler.initializeSharing(shareScope), - ), + const shareScopeInput = + shareScopes.length === 1 ? shareScopes[0] : shareScopes; + + return Promise.resolve( + instance.sharedHandler.initializeSharing(shareScopeInput), ).then(() => { return loadRemote(); }); diff --git a/packages/webpack-bundler-runtime/src/resolveRemoteModuleId.ts b/packages/webpack-bundler-runtime/src/resolveRemoteModuleId.ts new file mode 100644 index 00000000000..a82ee737bec --- /dev/null +++ b/packages/webpack-bundler-runtime/src/resolveRemoteModuleId.ts @@ -0,0 +1,77 @@ +import type { ResolveRemoteModuleIdOptions } from './types'; + +const normalizeExpose = (expose: string) => { + if (!expose || expose === '.') { + return '.'; + } + return expose.startsWith('./') ? expose : `./${expose}`; +}; + +const normalizeExposeCandidates = (expose: string): string[] => { + const normalized = normalizeExpose(expose); + if (normalized === '.') { + return ['.']; + } + if (normalized.startsWith('./')) { + return [normalized, normalized.slice(2)]; + } + return [normalized, `./${normalized}`]; +}; + +const getRemoteNameCandidates = ( + options: ResolveRemoteModuleIdOptions, +): Set => { + const names = new Set([options.alias]); + const remoteInfos = + options.webpackRequire.federation?.bundlerRuntimeOptions?.remotes + ?.remoteInfos; + const aliasRemoteInfos = remoteInfos?.[options.alias]; + if (Array.isArray(aliasRemoteInfos)) { + for (const info of aliasRemoteInfos) { + if (typeof info?.name === 'string' && info.name) { + names.add(info.name); + } + } + } + return names; +}; + +export function resolveRemoteModuleId( + options: ResolveRemoteModuleIdOptions, +): string | undefined { + const mapping = + options.webpackRequire.remotesLoadingData?.moduleIdToRemoteDataMapping; + if (!mapping) { + return undefined; + } + + const exposeCandidates = new Set(normalizeExposeCandidates(options.expose)); + const remoteNameCandidates = getRemoteNameCandidates(options); + let resolvedModuleId: string | undefined; + + for (const [moduleId, remoteData] of Object.entries(mapping)) { + if ( + !remoteData || + typeof remoteData.name !== 'string' || + !remoteData.name || + typeof remoteData.remoteName !== 'string' || + !remoteNameCandidates.has(remoteData.remoteName) + ) { + continue; + } + if (!exposeCandidates.has(remoteData.name)) { + continue; + } + + if (!resolvedModuleId) { + resolvedModuleId = moduleId; + continue; + } + + if (resolvedModuleId !== moduleId) { + return undefined; + } + } + + return resolvedModuleId; +} diff --git a/packages/webpack-bundler-runtime/src/rscBridgeExpose.ts b/packages/webpack-bundler-runtime/src/rscBridgeExpose.ts new file mode 100644 index 00000000000..ce7b5190468 --- /dev/null +++ b/packages/webpack-bundler-runtime/src/rscBridgeExpose.ts @@ -0,0 +1,877 @@ +import { + type ManifestExport, + type ManifestLike, + type ManifestNode, + getClientManifestKeyWithoutHash, + resolveActionReference, + resolveServerModuleReference, +} from './rscManifest'; + +type WebpackRequireRuntime = { + (moduleId: string): unknown; + initializeExposesData?: { + moduleMap?: Record Promise<() => unknown> | (() => unknown)>; + }; + initializeSharingData?: { + scopeToSharingDataMapping?: Record; + }; + e?: (chunkId: string | number) => unknown; + I?: (shareScope: string, initScope?: unknown) => unknown; + rscM?: ManifestLike; +}; + +declare const __webpack_require__: WebpackRequireRuntime; + +const shouldDebug = + typeof process !== 'undefined' && Boolean(process.env?.RSC_MF_DEBUG); + +const debugLog = (message: string, data?: Record) => { + if (!shouldDebug) { + return; + } + if (data) { + // eslint-disable-next-line no-console + console.error('[mf:rsc-bridge-expose]', message, data); + return; + } + // eslint-disable-next-line no-console + console.error('[mf:rsc-bridge-expose]', message); +}; + +const CLIENT_REFERENCE_SYMBOL = Symbol.for('react.client.reference'); +const BRIDGE_EXPOSE_KEY = './__rspack_rsc_bridge__'; +const RSC_SSR_EXPOSE_PREFIX = './__rspack_rsc_ssr__/'; +const SSR_LAYER_PREFIX = '(server-side-rendering)/'; +const RSC_LAYER_PREFIX = '(react-server-components)/'; + +const actionReferenceCache: Record unknown> = + Object.create(null); +const clientExposeMap: Record = Object.create(null); +const ssrModuleCache: Record = Object.create(null); +const ssrExposeByServerModuleId: Record = Object.create(null); +const shareScopeInitPromises: Partial>> = + Object.create(null); +let scannedExposes = false; +let scannedSsrExposeMap = false; + +const isObject = (value: unknown): value is Record => + typeof value === 'object' && value !== null; + +const normalizeExpose = (expose: string) => + expose.startsWith('./') ? expose : `./${expose}`; + +const getClientReferenceIdentity = ( + value: unknown, +): { key: string; baseKey: string } | undefined => { + if (!isObject(value) && typeof value !== 'function') { + return undefined; + } + const candidate = value as { + $$typeof?: unknown; + $$id?: unknown; + }; + if ( + candidate.$$typeof !== CLIENT_REFERENCE_SYMBOL || + typeof candidate.$$id !== 'string' || + !candidate.$$id + ) { + return undefined; + } + const key = candidate.$$id; + const hashIndex = key.indexOf('#'); + const baseKey = hashIndex >= 0 ? key.slice(0, hashIndex) : key; + return { key, baseKey }; +}; + +const cacheActionReference = (value: unknown) => { + if (typeof value !== 'function') { + return; + } + const id = (value as { $$id?: unknown }).$$id; + if (typeof id === 'string' && id) { + actionReferenceCache[id] = value as (...args: unknown[]) => unknown; + } +}; + +const cacheClientExpose = (value: unknown, exposeName: string) => { + const identity = getClientReferenceIdentity(value); + if (!identity) { + return; + } + const normalizedExpose = normalizeExpose(exposeName); + if (!Object.prototype.hasOwnProperty.call(clientExposeMap, identity.key)) { + clientExposeMap[identity.key] = normalizedExpose; + } + if ( + !Object.prototype.hasOwnProperty.call(clientExposeMap, identity.baseKey) + ) { + clientExposeMap[identity.baseKey] = normalizedExpose; + } +}; + +const inspectExportValue = (value: unknown, exposeName: string) => { + cacheActionReference(value); + cacheClientExpose(value, exposeName); + if (!isObject(value)) { + return; + } + for (const nestedValue of Object.values(value)) { + cacheActionReference(nestedValue); + cacheClientExpose(nestedValue, exposeName); + } +}; + +const toSsrExposeKey = (exposeName: string) => { + if (!exposeName) { + return ''; + } + const normalized = exposeName.startsWith('./') + ? exposeName.slice(2) + : exposeName; + return `${RSC_SSR_EXPOSE_PREFIX}${normalized}`; +}; + +const stripSsrLayerPrefix = (moduleId: string) => + moduleId.startsWith(SSR_LAYER_PREFIX) + ? moduleId.slice(SSR_LAYER_PREFIX.length) + : moduleId; + +const resolveHiddenSsrExposeFromClientExposeMap = ( + moduleId: string, + moduleMap?: Record Promise<() => unknown> | (() => unknown)>, +) => { + if (!isObject(moduleMap)) { + return ''; + } + const moduleIdCandidates = [moduleId, stripSsrLayerPrefix(moduleId)]; + for (const moduleIdCandidate of moduleIdCandidates) { + const exposeName = clientExposeMap[moduleIdCandidate]; + if (typeof exposeName !== 'string' || !exposeName) { + continue; + } + const hiddenExpose = toSsrExposeKey(exposeName); + if (hiddenExpose && typeof moduleMap[hiddenExpose] === 'function') { + return hiddenExpose; + } + } + return ''; +}; + +const resolveHiddenSsrExposeFromHint = ( + exposeHint: string | undefined, + moduleMap?: Record Promise<() => unknown> | (() => unknown)>, +) => { + if (!exposeHint || !isObject(moduleMap)) { + return ''; + } + const normalizedExpose = normalizeExpose(exposeHint); + const hiddenExpose = normalizedExpose.startsWith(RSC_SSR_EXPOSE_PREFIX) + ? normalizedExpose + : toSsrExposeKey(normalizedExpose); + if ( + hiddenExpose && + typeof hiddenExpose === 'string' && + typeof moduleMap[hiddenExpose] === 'function' + ) { + return hiddenExpose; + } + return ''; +}; + +const resolveClientManifestEntry = ( + clientManifest: Record, + clientReferenceId: string, +): ManifestExport | null => { + if (Object.prototype.hasOwnProperty.call(clientManifest, clientReferenceId)) { + return clientManifest[clientReferenceId] as ManifestExport; + } + const hashIndex = clientReferenceId.lastIndexOf('#'); + if (hashIndex >= 0) { + const withoutHash = clientReferenceId.slice(0, hashIndex); + if (Object.prototype.hasOwnProperty.call(clientManifest, withoutHash)) { + return clientManifest[withoutHash] as ManifestExport; + } + } + return null; +}; + +const extractChunkIds = (chunks: Array) => { + const chunkIds: Array = []; + const pushChunkId = (chunkId: string | number) => { + if (!chunkIds.includes(chunkId)) { + chunkIds.push(chunkId); + } + }; + + for (let i = 0; i < chunks.length; i += 1) { + const chunkValue = chunks[i]; + if (typeof chunkValue !== 'string' && typeof chunkValue !== 'number') { + continue; + } + + const nextValue = chunks[i + 1]; + if ( + typeof nextValue === 'string' && + (nextValue.endsWith('.js') || + nextValue.endsWith('.cjs') || + nextValue.endsWith('.mjs')) + ) { + pushChunkId(chunkValue); + i += 1; + continue; + } + + pushChunkId(chunkValue); + } + + return chunkIds; +}; + +const collectServerModuleChunkIds = ( + moduleId: string, + manifest?: ManifestLike, +) => { + const serverConsumerModuleMap = isObject(manifest?.serverConsumerModuleMap) + ? (manifest!.serverConsumerModuleMap as Record) + : undefined; + if (!serverConsumerModuleMap) { + return []; + } + + const chunkIds: Array = []; + const pushChunkIds = (nextChunkIds: Array) => { + for (const chunkId of nextChunkIds) { + if (!chunkIds.includes(chunkId)) { + chunkIds.push(chunkId); + } + } + }; + + for (const node of Object.values(serverConsumerModuleMap)) { + if (!isObject(node)) { + continue; + } + for (const exportValue of Object.values(node)) { + if (!isObject(exportValue) || exportValue.id == null) { + continue; + } + if (String(exportValue.id) !== moduleId) { + continue; + } + const chunks = Array.isArray(exportValue.chunks) + ? (exportValue.chunks as Array) + : []; + pushChunkIds(extractChunkIds(chunks)); + } + } + + return chunkIds; +}; + +const stripLayerPrefix = (moduleId: string) => { + if (moduleId.startsWith(SSR_LAYER_PREFIX)) { + return moduleId.slice(SSR_LAYER_PREFIX.length); + } + if (moduleId.startsWith(RSC_LAYER_PREFIX)) { + return moduleId.slice(RSC_LAYER_PREFIX.length); + } + return moduleId; +}; + +const getClientManifestKeysByModuleId = ( + clientManifest: Record, + clientModuleId: string, +) => + Object.entries(clientManifest) + .filter(([, value]) => String(value.id) === clientModuleId) + .map(([key]) => key); + +const selectClientReferenceKey = ( + candidateKeys: string[], + exportName: string, + exportValue?: ManifestExport, +) => { + if (candidateKeys.length === 0) { + return ''; + } + if (typeof exportValue?.name === 'string' && exportValue.name !== '*') { + const byManifestName = candidateKeys.find((key) => + key.endsWith(`#${exportValue.name}`), + ); + if (byManifestName) { + return byManifestName; + } + } + if (exportName !== '*' && exportName !== '__esModule') { + const byExportName = candidateKeys.find((key) => + key.endsWith(`#${exportName}`), + ); + if (byExportName) { + return byExportName; + } + } + const withoutHash = candidateKeys.find((key) => !key.includes('#')); + if (withoutHash) { + return withoutHash; + } + return candidateKeys[0]; +}; + +const createSyntheticClientReference = ( + referenceId: string, + asyncValue: boolean, +) => { + const referenceFn = function syntheticClientReference() { + throw new Error( + `Attempted to call client reference "${referenceId}" from the server`, + ); + } as ((...args: unknown[]) => never) & { + $$typeof?: symbol; + $$id?: string; + $$async?: boolean; + }; + referenceFn.$$typeof = CLIENT_REFERENCE_SYMBOL; + referenceFn.$$id = referenceId; + referenceFn.$$async = asyncValue; + return referenceFn; +}; + +const buildSyntheticSsrModuleFromManifest = ( + moduleId: string, + manifest?: ManifestLike, +) => { + const clientManifest = isObject(manifest?.clientManifest) + ? (manifest!.clientManifest as Record) + : undefined; + const serverConsumerModuleMap = isObject(manifest?.serverConsumerModuleMap) + ? (manifest!.serverConsumerModuleMap as Record) + : undefined; + if (!clientManifest || !serverConsumerModuleMap) { + return null; + } + + for (const [rawClientModuleId, node] of Object.entries( + serverConsumerModuleMap, + )) { + if (!isObject(node)) { + continue; + } + const normalizedClientModuleId = stripLayerPrefix(rawClientModuleId); + const clientManifestKeys = getClientManifestKeysByModuleId( + clientManifest, + normalizedClientModuleId, + ); + if (clientManifestKeys.length === 0) { + continue; + } + + const syntheticExports: Record = Object.create(null); + for (const [exportName, exportValue] of Object.entries(node)) { + if (!isObject(exportValue) || exportValue.id == null) { + continue; + } + if (String(exportValue.id) !== moduleId) { + continue; + } + + const manifestExport = exportValue as ManifestExport; + const referenceKey = selectClientReferenceKey( + clientManifestKeys, + exportName, + manifestExport, + ); + if (!referenceKey) { + continue; + } + const syntheticRef = createSyntheticClientReference( + referenceKey, + Boolean(manifestExport.async), + ); + if (exportName === '*') { + syntheticExports.default = syntheticRef; + continue; + } + if (exportName === '__esModule') { + continue; + } + syntheticExports[exportName] = syntheticRef; + } + + if (Object.keys(syntheticExports).length > 0) { + return syntheticExports; + } + } + + return null; +}; + +const preloadServerModuleChunks = async ( + moduleId: string, + manifest?: ManifestLike, +) => { + const chunkIds = collectServerModuleChunkIds(moduleId, manifest); + if (chunkIds.length === 0) { + return false; + } + if (typeof __webpack_require__.e !== 'function') { + throw new Error( + `[rsc-bridge-expose] Chunk loader "__webpack_require__.e" is unavailable while preloading server module "${moduleId}"`, + ); + } + await Promise.all( + chunkIds.map((chunkId) => { + const loadResult = __webpack_require__.e!(chunkId); + if ( + loadResult && + typeof (loadResult as Promise).then === 'function' + ) { + return loadResult as Promise; + } + return Promise.resolve(loadResult); + }), + ); + return true; +}; + +const collectClientReferenceIdsFromExports = ( + exportsValue: unknown, + ids: string[], +) => { + const maybeRef = exportsValue as { $$id?: unknown } | undefined; + if ( + typeof exportsValue === 'function' && + typeof maybeRef?.$$id === 'string' + ) { + ids.push(maybeRef.$$id); + } + if (!isObject(exportsValue)) { + return; + } + for (const nestedValue of Object.values(exportsValue)) { + const maybeNestedRef = nestedValue as { $$id?: unknown } | undefined; + if ( + typeof nestedValue === 'function' && + typeof maybeNestedRef?.$$id === 'string' + ) { + ids.push(maybeNestedRef.$$id); + } + } +}; + +const resolveFactoryExports = async (getFactory: () => unknown) => { + const factory = await Promise.resolve(getFactory()); + if (typeof factory === 'function') { + return (factory as () => unknown)(); + } + return factory; +}; + +const getShareScopesForServerModuleId = ( + moduleId: string, + manifest?: ManifestLike, +) => { + const scopes = new Set(['default']); + const declaredShareScopes = + __webpack_require__.initializeSharingData?.scopeToSharingDataMapping; + if (isObject(declaredShareScopes)) { + for (const shareScope of Object.keys(declaredShareScopes)) { + if (shareScope) { + scopes.add(shareScope); + } + } + } + if (moduleId.startsWith('(server-side-rendering)/')) { + scopes.add('ssr'); + } + if (moduleId.startsWith('(react-server-components)/')) { + scopes.add('rsc'); + } + const serverConsumerModuleMap = isObject(manifest?.serverConsumerModuleMap) + ? (manifest!.serverConsumerModuleMap as Record) + : undefined; + if (serverConsumerModuleMap) { + const unprefixedModuleId = moduleId.startsWith(SSR_LAYER_PREFIX) + ? moduleId.slice(SSR_LAYER_PREFIX.length) + : moduleId.startsWith(RSC_LAYER_PREFIX) + ? moduleId.slice(RSC_LAYER_PREFIX.length) + : moduleId; + + if ( + Object.prototype.hasOwnProperty.call( + serverConsumerModuleMap, + `${SSR_LAYER_PREFIX}${unprefixedModuleId}`, + ) + ) { + scopes.add('ssr'); + } + if ( + Object.prototype.hasOwnProperty.call( + serverConsumerModuleMap, + `${RSC_LAYER_PREFIX}${unprefixedModuleId}`, + ) + ) { + scopes.add('rsc'); + } + } + return Array.from(scopes); +}; + +const ensureShareScopeInitialized = async (shareScope: string) => { + if (!shareScope || typeof __webpack_require__.I !== 'function') { + return; + } + const existing = shareScopeInitPromises[shareScope]; + if (existing) { + await existing; + return; + } + + const initPromise = (async () => { + const initResult = __webpack_require__.I!(shareScope); + if ( + initResult && + typeof (initResult as Promise).then === 'function' + ) { + await initResult; + } + })().catch((error: unknown) => { + delete shareScopeInitPromises[shareScope]; + throw error; + }); + + shareScopeInitPromises[shareScope] = initPromise; + await initPromise; +}; + +const ensureShareScopesForModuleId = async ( + moduleId: string, + manifest?: ManifestLike, +) => { + const shareScopes = getShareScopesForServerModuleId(moduleId, manifest); + await Promise.all( + shareScopes.map((shareScope) => ensureShareScopeInitialized(shareScope)), + ); +}; + +const scanExposedModules = async () => { + if (scannedExposes) { + return; + } + scannedExposes = true; + + const moduleMap = __webpack_require__.initializeExposesData?.moduleMap; + if (!isObject(moduleMap)) { + return; + } + + for (const [exposeName, getFactory] of Object.entries(moduleMap)) { + if (exposeName === BRIDGE_EXPOSE_KEY || typeof getFactory !== 'function') { + continue; + } + try { + const exportsValue = await resolveFactoryExports(getFactory); + inspectExportValue(exportsValue, exposeName); + if (isObject(exportsValue) && isObject(exportsValue['default'])) { + inspectExportValue(exportsValue['default'], exposeName); + } + } catch { + // Ignore expose loading errors while scanning bridge metadata. + } + } +}; + +const buildSsrExposeMap = async (force = false) => { + if (scannedSsrExposeMap && !force) { + return; + } + scannedSsrExposeMap = true; + + const moduleMap = __webpack_require__.initializeExposesData?.moduleMap; + const manifest = isObject(__webpack_require__.rscM) + ? (__webpack_require__.rscM as ManifestLike) + : undefined; + const clientManifest = isObject(manifest?.clientManifest) + ? (manifest!.clientManifest as Record) + : undefined; + const serverConsumerModuleMap = isObject(manifest?.serverConsumerModuleMap) + ? (manifest!.serverConsumerModuleMap as Record) + : undefined; + + if (!isObject(moduleMap) || !clientManifest || !serverConsumerModuleMap) { + return; + } + + for (const [exposeName, getFactory] of Object.entries(moduleMap)) { + if ( + exposeName === BRIDGE_EXPOSE_KEY || + exposeName.startsWith(RSC_SSR_EXPOSE_PREFIX) || + typeof getFactory !== 'function' + ) { + continue; + } + const hiddenSsrExpose = toSsrExposeKey(exposeName); + if ( + !hiddenSsrExpose || + !Object.prototype.hasOwnProperty.call(moduleMap, hiddenSsrExpose) + ) { + continue; + } + + try { + const exportsValue = await resolveFactoryExports(getFactory); + const referenceIds: string[] = []; + collectClientReferenceIdsFromExports(exportsValue, referenceIds); + if (isObject(exportsValue) && isObject(exportsValue['default'])) { + collectClientReferenceIdsFromExports( + exportsValue['default'], + referenceIds, + ); + } + debugLog('buildSsrExposeMap:refs', { + exposeName, + hiddenSsrExpose, + referenceIds, + }); + + for (const referenceId of referenceIds) { + const clientEntry = resolveClientManifestEntry( + clientManifest, + referenceId, + ); + if (!clientEntry || clientEntry.id == null) { + debugLog('buildSsrExposeMap:missingClientEntry', { + exposeName, + referenceId, + }); + continue; + } + const consumerNode = serverConsumerModuleMap[String(clientEntry.id)]; + if (!isObject(consumerNode)) { + debugLog('buildSsrExposeMap:missingConsumerNode', { + exposeName, + referenceId, + clientEntryId: String(clientEntry.id), + }); + continue; + } + const consumerTarget = (consumerNode as ManifestNode)['*']; + if (!isObject(consumerTarget) || consumerTarget.id == null) { + debugLog('buildSsrExposeMap:missingConsumerTarget', { + exposeName, + referenceId, + clientEntryId: String(clientEntry.id), + consumerNodeKeys: Object.keys(consumerNode as ManifestNode), + }); + continue; + } + const serverModuleId = String(consumerTarget.id); + if ( + !Object.prototype.hasOwnProperty.call( + ssrExposeByServerModuleId, + serverModuleId, + ) + ) { + ssrExposeByServerModuleId[serverModuleId] = hiddenSsrExpose; + debugLog('buildSsrExposeMap:set', { + exposeName, + serverModuleId, + hiddenSsrExpose, + }); + } + } + } catch { + debugLog('buildSsrExposeMap:exposeLoadError', { + exposeName, + hiddenSsrExpose, + }); + } + } +}; + +export async function getManifest(): Promise { + await scanExposedModules(); + const manifest = isObject(__webpack_require__.rscM) + ? (__webpack_require__.rscM as ManifestLike) + : {}; + return { + ...manifest, + clientExposes: { + ...(manifest.clientExposes || {}), + ...clientExposeMap, + }, + }; +} + +export async function executeAction(actionId: string, args: unknown[]) { + const manifest = isObject(__webpack_require__.rscM) + ? (__webpack_require__.rscM as ManifestLike) + : undefined; + const manifestAction = resolveActionReference(manifest, actionId); + const localActionId = + manifestAction && typeof manifestAction.localActionId === 'string' + ? manifestAction.localActionId + : actionId; + await scanExposedModules(); + const action = actionReferenceCache[localActionId]; + if (typeof action !== 'function') { + throw new Error( + `[rsc-bridge-expose] Missing remote action for "${actionId}". Ensure the action is reachable from a federated expose.`, + ); + } + return action(...(Array.isArray(args) ? args : [])); +} + +export async function preloadSSRModule(moduleId: string, exposeHint?: string) { + const normalizedModuleId = String(moduleId); + if ( + Object.prototype.hasOwnProperty.call(ssrModuleCache, normalizedModuleId) + ) { + return ssrModuleCache[normalizedModuleId]; + } + + await scanExposedModules(); + const manifest = isObject(__webpack_require__.rscM) + ? (__webpack_require__.rscM as ManifestLike) + : undefined; + await ensureShareScopesForModuleId(normalizedModuleId, manifest); + await buildSsrExposeMap(); + + const didPreloadChunksFromManifest = await preloadServerModuleChunks( + normalizedModuleId, + manifest, + ); + if (didPreloadChunksFromManifest) { + try { + const required = __webpack_require__(normalizedModuleId); + const resolvedModuleExports = + required && typeof (required as Promise).then === 'function' + ? await (required as Promise) + : required; + ssrModuleCache[normalizedModuleId] = resolvedModuleExports; + return resolvedModuleExports; + } catch (error) { + debugLog('preloadSSRModule:manifestChunkRequireFailed', { + moduleId: normalizedModuleId, + error: String(error), + }); + } + } + + const shareScopeMap = (__webpack_require__ as unknown as { S?: any }).S; + const ssrReact = shareScopeMap?.ssr?.react; + debugLog('preloadSSRModule:shareState', { + moduleId: normalizedModuleId, + shareScopeKeys: shareScopeMap ? Object.keys(shareScopeMap) : [], + ssrReactVersions: isObject(ssrReact) ? Object.keys(ssrReact) : [], + }); + + const moduleMap = __webpack_require__.initializeExposesData?.moduleMap; + const hiddenExposeFromHint = resolveHiddenSsrExposeFromHint( + typeof exposeHint === 'string' ? exposeHint : undefined, + moduleMap, + ); + let hiddenSsrExpose = + hiddenExposeFromHint || + resolveServerModuleReference(manifest, normalizedModuleId)?.ssrExpose || + ssrExposeByServerModuleId[normalizedModuleId] || + resolveHiddenSsrExposeFromClientExposeMap(normalizedModuleId, moduleMap); + if (!hiddenSsrExpose) { + await ensureShareScopeInitialized('ssr'); + await ensureShareScopeInitialized('rsc'); + await buildSsrExposeMap(true); + hiddenSsrExpose = + hiddenExposeFromHint || + resolveServerModuleReference(manifest, normalizedModuleId)?.ssrExpose || + ssrExposeByServerModuleId[normalizedModuleId] || + resolveHiddenSsrExposeFromClientExposeMap(normalizedModuleId, moduleMap); + } + debugLog('preloadSSRModule:hiddenExpose', { + moduleId: normalizedModuleId, + exposeHint: exposeHint || '', + hiddenExposeFromHint: hiddenExposeFromHint || '', + hiddenSsrExpose: hiddenSsrExpose || '', + hasHiddenExposeFactory: + isObject(moduleMap) && + typeof hiddenSsrExpose === 'string' && + typeof moduleMap[hiddenSsrExpose] === 'function', + }); + if ( + isObject(moduleMap) && + typeof hiddenSsrExpose === 'string' && + typeof moduleMap[hiddenSsrExpose] === 'function' + ) { + // Prime remote chunks through the hidden expose, then read the requested + // server module id from the runtime graph. + await resolveFactoryExports(moduleMap[hiddenSsrExpose]!); + let resolvedModuleExports: unknown; + try { + const required = __webpack_require__(normalizedModuleId); + resolvedModuleExports = + required && typeof (required as Promise).then === 'function' + ? await (required as Promise) + : required; + } catch (error) { + throw new Error( + `[rsc-bridge-expose] Hidden SSR expose "${hiddenSsrExpose}" loaded but server module "${normalizedModuleId}" is unavailable: ${String(error)}`, + ); + } + if (shouldDebug) { + const exportKeys = isObject(resolvedModuleExports) + ? Object.keys(resolvedModuleExports as Record) + : []; + const sampleExportValue = + isObject(resolvedModuleExports) && exportKeys.length > 0 + ? (resolvedModuleExports as Record)[exportKeys[0]] + : resolvedModuleExports; + const sampleClientReferenceType = + (typeof sampleExportValue === 'function' || + isObject(sampleExportValue)) && + sampleExportValue != null && + '$$typeof' in (sampleExportValue as Record) + ? String((sampleExportValue as Record)['$$typeof']) + : ''; + const sampleClientReferenceId = + (typeof sampleExportValue === 'function' || + isObject(sampleExportValue)) && + sampleExportValue != null && + '$$id' in (sampleExportValue as Record) + ? String((sampleExportValue as Record)['$$id']) + : ''; + debugLog('preloadSSRModule:resolvedExports', { + moduleId: normalizedModuleId, + hiddenSsrExpose, + exportKeys, + sampleType: typeof sampleExportValue, + sampleClientReferenceType, + sampleClientReferenceId, + }); + } + ssrModuleCache[normalizedModuleId] = resolvedModuleExports; + return resolvedModuleExports; + } + + const syntheticModuleFromManifest = buildSyntheticSsrModuleFromManifest( + normalizedModuleId, + manifest, + ); + if (syntheticModuleFromManifest) { + ssrModuleCache[normalizedModuleId] = syntheticModuleFromManifest; + return syntheticModuleFromManifest; + } + + throw new Error( + `[rsc-bridge-expose] Missing hidden SSR expose for "${normalizedModuleId}". Bridge mapping failed; no raw module-id fallback is allowed.`, + ); +} + +export function getSSRModule(moduleId: string) { + const normalizedModuleId = String(moduleId); + if ( + Object.prototype.hasOwnProperty.call(ssrModuleCache, normalizedModuleId) + ) { + return ssrModuleCache[normalizedModuleId]; + } + throw new Error( + `[rsc-bridge-expose] SSR module "${normalizedModuleId}" was requested before preload.`, + ); +} diff --git a/packages/webpack-bundler-runtime/src/rscBridgeRuntimePlugin.ts b/packages/webpack-bundler-runtime/src/rscBridgeRuntimePlugin.ts new file mode 100644 index 00000000000..0c05460401e --- /dev/null +++ b/packages/webpack-bundler-runtime/src/rscBridgeRuntimePlugin.ts @@ -0,0 +1,1970 @@ +import { + type ManifestExport, + type ManifestLike, + type ManifestNode, + type RscBridgeModuleV1 as BridgeModule, + getClientManifestKeyWithoutHash, + resolveClientReferenceEntry, +} from './rscManifest'; + +const RSC_BRIDGE_EXPOSE = '__rspack_rsc_bridge__'; +const ACTION_PREFIX = 'remote:'; +const MODULE_PREFIX = 'remote-module:'; +const SERVER_MODULE_PREFIX = 'remote-server-module:'; +const SSR_LAYER_PREFIX = '(server-side-rendering)/'; +const RSC_LAYER_PREFIX = '(react-server-components)/'; +const ACTION_REMAP_GLOBAL_KEY = '__RSPACK_RSC_MF_ACTION_ID_MAP__'; +const ACTION_PROXY_MODULE_ID = '__rspack_mf_rsc_action_proxy__'; + +type ActionMapRecord = Record; +type ActionRemapMap = Record; +type FederationState = { + [ACTION_REMAP_GLOBAL_KEY]?: ActionRemapMap; +}; + +type RemoteDataItem = { + name: string; + remoteName: string; +}; + +type ConsumeDataItem = { + shareKey?: string; +}; + +type ConsumeHandlerItem = { + shareKey?: string; +}; + +type ClientModuleResolutionKind = 'shared' | 'remote'; + +type WebpackRequireRuntime = { + m?: Record void>; + c?: Record; + rscM?: ManifestLike; + remotesLoadingData?: { + chunkMapping?: Record>; + moduleIdToRemoteDataMapping?: Record; + }; + consumesLoadingData?: { + moduleIdToConsumeDataMapping?: Record; + }; + u?: (chunkId: string | number) => string; + federation?: { + instance?: { + loadRemote?: (request: string) => Promise; + loadShareSync?: (shareKey: string) => (() => unknown) | false; + options?: { + remotes?: Array<{ + name: string; + alias?: string; + }>; + }; + }; + bundlerRuntime?: { + resolveRemoteModuleId?: (options: { + webpackRequire: WebpackRequireRuntime; + alias: string; + expose: string; + }) => string | undefined; + }; + bundlerRuntimeOptions?: { + remotes?: { + remoteInfos?: Record< + string, + Array<{ + name?: string; + }> + >; + }; + }; + consumesLoadingModuleToHandlerMapping?: Record; + }; +}; + +declare const __webpack_require__: WebpackRequireRuntime; +declare const __FEDERATION__: FederationState | undefined; + +const isObject = (value: unknown): value is Record => + typeof value === 'object' && value !== null; + +const stableStringify = (value: unknown) => { + try { + return JSON.stringify(value, (_key, current) => { + if (Array.isArray(current)) { + return current; + } + if (!isObject(current)) { + return current; + } + return Object.fromEntries( + Object.keys(current) + .sort() + .map((key) => [key, current[key]]), + ); + }); + } catch { + return String(value); + } +}; + +const assertNoConflict = ( + target: Record, + key: string, + nextValue: unknown, + alias: string, + section: string, +) => { + if (!Object.prototype.hasOwnProperty.call(target, key)) { + return; + } + if (stableStringify(target[key]) !== stableStringify(nextValue)) { + throw new Error( + `[mf:rsc-bridge] ${section} conflict for "${key}" while merging remote "${alias}"`, + ); + } +}; + +const ensureHostManifest = () => { + if (!isObject(__webpack_require__.rscM)) { + __webpack_require__.rscM = {}; + } + const manifest = __webpack_require__.rscM as ManifestLike; + manifest.serverManifest = isObject(manifest.serverManifest) + ? manifest.serverManifest + : {}; + manifest.clientManifest = isObject(manifest.clientManifest) + ? manifest.clientManifest + : {}; + manifest.serverConsumerModuleMap = isObject(manifest.serverConsumerModuleMap) + ? manifest.serverConsumerModuleMap + : {}; + return manifest; +}; + +const getNamespacedModuleId = (alias: string, rawId: string | number) => + `${MODULE_PREFIX}${alias}:${String(rawId)}`; + +const getNamespacedServerModuleId = (alias: string, rawId: string | number) => + `${SERVER_MODULE_PREFIX}${alias}:${String(rawId)}`; + +const getNamespacedClientManifestKey = (alias: string, key: string | number) => + `${MODULE_PREFIX}${alias}:${String(key)}`; + +const namespaceModuleIdDeterministically = ( + alias: string, + rawId: string | number, +) => { + const normalizedRawId = String(rawId); + const namespaceLayerId = (layerPrefix: string) => { + const unprefixedId = normalizedRawId.slice(layerPrefix.length); + return unprefixedId.startsWith(MODULE_PREFIX) + ? normalizedRawId + : `${layerPrefix}${getNamespacedModuleId(alias, unprefixedId)}`; + }; + if (normalizedRawId.startsWith(SSR_LAYER_PREFIX)) { + return namespaceLayerId(SSR_LAYER_PREFIX); + } + if (normalizedRawId.startsWith(RSC_LAYER_PREFIX)) { + return namespaceLayerId(RSC_LAYER_PREFIX); + } + return normalizedRawId.startsWith(MODULE_PREFIX) + ? normalizedRawId + : getNamespacedModuleId(alias, normalizedRawId); +}; + +const namespaceServerModuleIdDeterministically = ( + alias: string, + rawId: string | number, +) => { + const normalizedRawId = String(rawId); + const namespaceLayerId = (layerPrefix: string) => { + const unprefixedId = normalizedRawId.slice(layerPrefix.length); + return unprefixedId.startsWith(SERVER_MODULE_PREFIX) + ? normalizedRawId + : `${layerPrefix}${getNamespacedServerModuleId(alias, unprefixedId)}`; + }; + if (normalizedRawId.startsWith(SSR_LAYER_PREFIX)) { + return namespaceLayerId(SSR_LAYER_PREFIX); + } + if (normalizedRawId.startsWith(RSC_LAYER_PREFIX)) { + return namespaceLayerId(RSC_LAYER_PREFIX); + } + return normalizedRawId.startsWith(SERVER_MODULE_PREFIX) + ? normalizedRawId + : getNamespacedServerModuleId(alias, normalizedRawId); +}; + +const isNamespacedRemoteIdForAlias = (alias: string, moduleId: string) => { + const aliasPrefix = `${MODULE_PREFIX}${alias}:`; + return ( + moduleId.startsWith(aliasPrefix) || + moduleId.startsWith(`${SSR_LAYER_PREFIX}${aliasPrefix}`) || + moduleId.startsWith(`${RSC_LAYER_PREFIX}${aliasPrefix}`) + ); +}; + +const stripLayerPrefix = (moduleId: string | number) => { + const normalizedModuleId = String(moduleId); + if (normalizedModuleId.startsWith(SSR_LAYER_PREFIX)) { + return normalizedModuleId.slice(SSR_LAYER_PREFIX.length); + } + if (normalizedModuleId.startsWith(RSC_LAYER_PREFIX)) { + return normalizedModuleId.slice(RSC_LAYER_PREFIX.length); + } + return normalizedModuleId; +}; + +const applyLayerPrefixFromRawId = ( + rawModuleId: string, + resolvedModuleId: string, +) => { + const normalizedResolvedModuleId = String(resolvedModuleId); + const unprefixedResolvedModuleId = stripLayerPrefix( + normalizedResolvedModuleId, + ); + if (rawModuleId.startsWith(SSR_LAYER_PREFIX)) { + return normalizedResolvedModuleId.startsWith(SSR_LAYER_PREFIX) + ? normalizedResolvedModuleId + : `${SSR_LAYER_PREFIX}${unprefixedResolvedModuleId}`; + } + if (rawModuleId.startsWith(RSC_LAYER_PREFIX)) { + return normalizedResolvedModuleId.startsWith(RSC_LAYER_PREFIX) + ? normalizedResolvedModuleId + : `${RSC_LAYER_PREFIX}${unprefixedResolvedModuleId}`; + } + return normalizedResolvedModuleId; +}; + +const resolveClientModuleIdFromMap = ( + resolvedClientIdsByRawId: Record, + rawId: string | number, +) => { + const normalizedRawId = String(rawId); + if ( + Object.prototype.hasOwnProperty.call( + resolvedClientIdsByRawId, + normalizedRawId, + ) + ) { + return applyLayerPrefixFromRawId( + normalizedRawId, + resolvedClientIdsByRawId[normalizedRawId], + ); + } + const unprefixedRawId = stripLayerPrefix(normalizedRawId); + if ( + unprefixedRawId !== normalizedRawId && + Object.prototype.hasOwnProperty.call( + resolvedClientIdsByRawId, + unprefixedRawId, + ) + ) { + return applyLayerPrefixFromRawId( + normalizedRawId, + resolvedClientIdsByRawId[unprefixedRawId], + ); + } + return undefined; +}; + +const createSharedConsumeModuleIdIndex = () => { + const shareKeyToModuleId: Record = Object.create(null); + const assignShareKey = (moduleId: string, shareKey: string) => { + if (!shareKey) { + return; + } + if (!Object.prototype.hasOwnProperty.call(shareKeyToModuleId, shareKey)) { + shareKeyToModuleId[shareKey] = String(moduleId); + } + }; + const handlerMapping = + __webpack_require__.federation?.consumesLoadingModuleToHandlerMapping; + if (isObject(handlerMapping)) { + for (const [moduleId, handlerValue] of Object.entries(handlerMapping)) { + if ( + !isObject(handlerValue) || + typeof handlerValue.shareKey !== 'string' + ) { + continue; + } + assignShareKey(String(moduleId), handlerValue.shareKey); + } + } + + const consumeDataMapping = + __webpack_require__.consumesLoadingData?.moduleIdToConsumeDataMapping; + if (isObject(consumeDataMapping)) { + for (const [moduleId, consumeData] of Object.entries(consumeDataMapping)) { + if (!isObject(consumeData) || typeof consumeData.shareKey !== 'string') { + continue; + } + assignShareKey(String(moduleId), consumeData.shareKey); + } + } + + return shareKeyToModuleId; +}; + +const resolveSharedConsumeModuleId = ( + rawClientManifestKey: string, + shareKeyToModuleId: Record, + explicitShareKey?: string, +): string | undefined => { + const candidateShareKeys = new Set([ + explicitShareKey || getClientManifestKeyWithoutHash(rawClientManifestKey), + ]); + + for (const shareKey of candidateShareKeys) { + if (!shareKey) { + continue; + } + if (Object.prototype.hasOwnProperty.call(shareKeyToModuleId, shareKey)) { + return shareKeyToModuleId[shareKey]; + } + } + + return undefined; +}; + +const resolveShareKeyFromClientManifestKey = ( + rawClientManifestKey: string, +): string | undefined => { + const keyWithoutHash = getClientManifestKeyWithoutHash(rawClientManifestKey); + if (!keyWithoutHash || keyWithoutHash.startsWith(MODULE_PREFIX)) { + return undefined; + } + if (keyWithoutHash.startsWith('.') || keyWithoutHash.startsWith('/')) { + return undefined; + } + if (/^[A-Za-z]:[\\/]/.test(keyWithoutHash) || keyWithoutHash.includes('\\')) { + return undefined; + } + return keyWithoutHash; +}; + +const getClientModuleResolutionPriority = ( + resolutionKind: ClientModuleResolutionKind | undefined, +) => { + switch (resolutionKind) { + case 'shared': + return 2; + case 'remote': + return 1; + default: + return 0; + } +}; + +const normalizeExpose = (expose: string) => { + if (!expose || expose === '.') { + return '.'; + } + return expose.startsWith('./') ? expose : `./${expose}`; +}; + +const getRemoteClientReferenceModuleId = (alias: string, expose: string) => { + const normalizedExpose = normalizeExpose(expose); + const exposeToken = normalizedExpose.startsWith('./') + ? normalizedExpose.slice(2) + : normalizedExpose; + return getNamespacedModuleId(alias, exposeToken || '.'); +}; + +const parseRemoteClientReferenceModuleId = ( + moduleId: string, +): { alias: string; expose: string } | undefined => { + const normalizedModuleId = stripLayerPrefix(moduleId); + if (!normalizedModuleId.startsWith(MODULE_PREFIX)) { + return undefined; + } + const namespacedToken = normalizedModuleId.slice(MODULE_PREFIX.length); + const aliasSeparatorIndex = namespacedToken.indexOf(':'); + if (aliasSeparatorIndex <= 0) { + return undefined; + } + const alias = namespacedToken.slice(0, aliasSeparatorIndex).trim(); + const exposeToken = namespacedToken.slice(aliasSeparatorIndex + 1).trim(); + if (!alias || !exposeToken) { + return undefined; + } + return { + alias, + expose: normalizeExpose(exposeToken), + }; +}; + +const resolveClientEntryExpose = ( + rawClientManifestKey: string, + entryName: string | undefined, + clientExposes: Record, +): string | undefined => { + const rawClientManifestKeyWithoutHash = + getClientManifestKeyWithoutHash(rawClientManifestKey); + + const manifestKeys = Array.from( + new Set( + rawClientManifestKey === rawClientManifestKeyWithoutHash + ? [rawClientManifestKey] + : [rawClientManifestKey, rawClientManifestKeyWithoutHash], + ), + ); + + if (entryName && entryName !== '*' && entryName !== '__esModule') { + for (const rawClientManifestKeyCandidate of manifestKeys) { + const exposedByFullKey = + clientExposes[`${rawClientManifestKeyCandidate}#${entryName}`]; + if (typeof exposedByFullKey === 'string' && exposedByFullKey) { + return normalizeExpose(exposedByFullKey); + } + } + } + + for (const rawClientManifestKeyCandidate of manifestKeys) { + const exposedByKey = clientExposes[rawClientManifestKeyCandidate]; + if (typeof exposedByKey === 'string' && exposedByKey) { + return normalizeExpose(exposedByKey); + } + } + + if (entryName && entryName !== '*' && entryName !== '__esModule') { + return normalizeExpose(entryName); + } + + return undefined; +}; + +const resolveExplicitClientModuleId = ( + remoteManifest: ManifestLike, + rawClientManifestKey: string, +) => { + const clientReference = resolveClientReferenceEntry( + remoteManifest, + rawClientManifestKey, + ); + if (!clientReference || !isObject(clientReference.resolution)) { + return undefined; + } + const resolution = clientReference.resolution; + if ( + resolution.kind === 'shared' && + typeof resolution.shareKey === 'string' && + resolution.shareKey + ) { + return { + kind: 'shared' as const, + shareKey: String(resolution.shareKey), + }; + } + if ( + resolution.kind === 'remote' && + typeof resolution.request === 'string' && + resolution.request + ) { + return { + kind: 'remote' as const, + expose: normalizeExpose(resolution.request), + }; + } + return undefined; +}; + +const getFederationState = (): FederationState | undefined => { + if (typeof __FEDERATION__ !== 'undefined' && isObject(__FEDERATION__)) { + return __FEDERATION__; + } + return undefined; +}; + +const getActionRemapMap = () => { + const federationState = getFederationState(); + if (!federationState) { + return undefined; + } + if (!isObject(federationState[ACTION_REMAP_GLOBAL_KEY])) { + federationState[ACTION_REMAP_GLOBAL_KEY] = {}; + } + return federationState[ACTION_REMAP_GLOBAL_KEY] as ActionRemapMap; +}; + +const registerActionRemap = (rawActionId: string, prefixedActionId: string) => { + const remapMap = getActionRemapMap(); + if (!remapMap) { + return; + } + const existingValue = remapMap[rawActionId]; + if (!existingValue) { + remapMap[rawActionId] = prefixedActionId; + return; + } + if (existingValue === prefixedActionId) { + return; + } + throw new Error( + `[mf:rsc-bridge] Conflicting raw action id "${rawActionId}" mapped to both "${existingValue}" and "${prefixedActionId}"`, + ); +}; + +const registerActionProxyExport = ( + actionMap: ActionMapRecord, + actionExportName: string, + actionRef: { alias: string; rawActionId: string }, +) => { + const existing = actionMap[actionExportName]; + if (!existing) { + actionMap[actionExportName] = actionRef; + return; + } + if ( + existing.alias === actionRef.alias && + existing.rawActionId === actionRef.rawActionId + ) { + return; + } + throw new Error( + `[mf:rsc-bridge] Conflicting action export "${actionExportName}" mapped to multiple remotes`, + ); +}; + +const remapConsumerNode = ( + value: unknown, + mapServerModuleId: (rawServerModuleId: string, exportName: string) => string, +) => { + if (!isObject(value)) { + return value; + } + return Object.fromEntries( + Object.entries(value).map(([exportName, exportValue]) => { + const nextExportValue = isObject(exportValue) + ? { ...exportValue } + : exportValue; + if (isObject(nextExportValue) && nextExportValue['id'] != null) { + const rawId = String(nextExportValue['id']); + nextExportValue['id'] = mapServerModuleId(rawId, exportName); + } + return [exportName, nextExportValue]; + }), + ); +}; + +const inferExposeHintFromConsumerNode = (node: unknown): string | undefined => { + if (!isObject(node)) { + return undefined; + } + + for (const [exportName, exportValue] of Object.entries(node)) { + if (exportName !== '*' && exportName !== '__esModule') { + return normalizeExpose(exportName); + } + if ( + isObject(exportValue) && + typeof exportValue['name'] === 'string' && + exportValue['name'] !== '*' && + exportValue['name'] !== '__esModule' + ) { + return normalizeExpose(exportValue['name']); + } + } + + const starValue = node['*']; + if ( + isObject(starValue) && + typeof starValue['name'] === 'string' && + starValue['name'] !== '*' && + starValue['name'] !== '__esModule' + ) { + return normalizeExpose(starValue['name']); + } + + return undefined; +}; + +const resolveRemoteAlias = (args: any): string | undefined => { + const candidateAliases = [ + args?.remote?.alias, + args?.pkgNameOrAlias, + args?.remote?.name, + args?.remoteInfo?.alias, + args?.remoteInfo?.name, + args?.name, + ]; + for (const candidate of candidateAliases) { + if (typeof candidate === 'string' && candidate.trim()) { + return candidate; + } + } + return undefined; +}; + +const resolveExposeSpecifier = (args: any): string => { + const candidateExposes = [ + args?.expose, + args?.id, + args?.request, + args?.pkgNameOrAlias, + args?.remote?.id, + args?.remote?.expose, + args?.remoteInfo?.id, + args?.remoteInfo?.expose, + args?.name, + ]; + for (const candidate of candidateExposes) { + if ( + (typeof candidate === 'string' || typeof candidate === 'number') && + String(candidate).trim() + ) { + return String(candidate); + } + } + return ''; +}; + +const isBridgeExposeRequest = (args: any) => + resolveExposeSpecifier(args).includes(RSC_BRIDGE_EXPOSE); + +const resolveBridgeFromLoadArgs = async ( + args: any, +): Promise => { + if ( + isObject(args?.exposeModule) && + typeof args.exposeModule.getManifest === 'function' && + typeof args.exposeModule.executeAction === 'function' + ) { + return args.exposeModule as BridgeModule; + } + + if (typeof args?.exposeModuleFactory === 'function') { + const bridgeCandidate = await Promise.resolve(args.exposeModuleFactory()); + if ( + isObject(bridgeCandidate) && + typeof bridgeCandidate.getManifest === 'function' && + typeof bridgeCandidate.executeAction === 'function' + ) { + return bridgeCandidate as BridgeModule; + } + } +}; + +const installActionProxyModule = ({ + actionMap, + ensureBridge, +}: { + actionMap: ActionMapRecord; + ensureBridge: (alias: string, args?: any) => Promise; +}) => { + if (!isObject(__webpack_require__.m)) { + __webpack_require__.m = {}; + } + if (__webpack_require__.m?.[ACTION_PROXY_MODULE_ID]) { + return; + } + + __webpack_require__.m![ACTION_PROXY_MODULE_ID] = (module: { + exports: any; + }) => { + const getActionFn = (property: string) => { + const actionRef = actionMap[property]; + if (!actionRef) { + return undefined; + } + return async (...args: unknown[]) => { + const bridge = await ensureBridge(actionRef.alias); + if (typeof bridge.executeAction !== 'function') { + throw new Error( + `[mf:rsc-bridge] Missing executeAction bridge method for remote "${actionRef.alias}"`, + ); + } + return bridge.executeAction( + actionRef.rawActionId, + Array.isArray(args) ? args : [], + ); + }; + }; + + module.exports = new Proxy( + {}, + { + get(_target, property) { + if (property === 'then') { + return undefined; + } + if (typeof property !== 'string') { + return undefined; + } + return getActionFn(property); + }, + has(_target, property) { + return ( + typeof property === 'string' && + Object.prototype.hasOwnProperty.call(actionMap, property) + ); + }, + getOwnPropertyDescriptor(_target, property) { + if ( + typeof property !== 'string' || + !Object.prototype.hasOwnProperty.call(actionMap, property) + ) { + return undefined; + } + return { + configurable: true, + enumerable: true, + get() { + return getActionFn(property); + }, + }; + }, + ownKeys() { + return Object.keys(actionMap); + }, + }, + ); + }; +}; + +const rscBridgeRuntimePlugin = () => { + const bridgePromises: Partial>> = {}; + const aliasMergePromises: Partial>> = {}; + const shareScopeInitPromises: Partial>> = {}; + const mergedRemoteAliases = new Set(); + const actionMap: ActionMapRecord = Object.create(null); + const ssrModuleExportsByNamespacedId: Record = + Object.create(null); + const ssrModulePreloadPromises: Partial>> = {}; + const syntheticSharedShareKeyByModuleId: Record = + Object.create(null); + const syntheticSharedExportsByModuleId: Record = + Object.create(null); + const syntheticRemoteRequestByModuleId: Record = + Object.create(null); + const resolvedRemoteRequestByModuleId: Record = + Object.create(null); + const syntheticRemoteExportsByModuleId: Record = + Object.create(null); + const syntheticRemotePromiseByModuleId: Record< + string, + Promise + > = Object.create(null); + const exposeTokenByServerModuleIdByAlias: Record< + string, + Record + > = Object.create(null); + const exposeTokenMapPromiseByAlias: Partial>> = + {}; + const seededRemoteClientModuleIds = new Set(); + let lazyRemoteFactoryPrototypeInstalled = false; + + const ensureSsrModuleFactory = ( + namespacedModuleId: string, + rawServerModuleId: string, + ) => { + if (!isObject(__webpack_require__.m)) { + __webpack_require__.m = {}; + } + if (__webpack_require__.m?.[namespacedModuleId]) { + return; + } + __webpack_require__.m![namespacedModuleId] = (module: { + exports: unknown; + }) => { + if ( + !Object.prototype.hasOwnProperty.call( + ssrModuleExportsByNamespacedId, + namespacedModuleId, + ) + ) { + throw new Error( + `[mf:rsc-bridge] SSR module "${namespacedModuleId}" (source "${rawServerModuleId}") was requested before preload`, + ); + } + module.exports = ssrModuleExportsByNamespacedId[namespacedModuleId]; + }; + }; + + const installSyntheticSharedClientModuleFactory = ( + moduleId: string, + shareKey: string, + ) => { + const existingShareKey = syntheticSharedShareKeyByModuleId[moduleId]; + if (existingShareKey && existingShareKey !== shareKey) { + throw new Error( + `[mf:rsc-bridge] Conflicting shared client reference for "${moduleId}" between "${existingShareKey}" and "${shareKey}"`, + ); + } + syntheticSharedShareKeyByModuleId[moduleId] = shareKey; + + if (!isObject(__webpack_require__.m)) { + __webpack_require__.m = {}; + } + if (__webpack_require__.m?.[moduleId]) { + return; + } + + __webpack_require__.m![moduleId] = (module: { exports: unknown }) => { + if ( + Object.prototype.hasOwnProperty.call( + syntheticSharedExportsByModuleId, + moduleId, + ) + ) { + module.exports = syntheticSharedExportsByModuleId[moduleId]; + return; + } + const federationInstance = __webpack_require__.federation?.instance; + if ( + !federationInstance || + typeof federationInstance.loadShareSync !== 'function' + ) { + throw new Error( + `[mf:rsc-bridge] Missing loadShareSync for shared client reference "${shareKey}"`, + ); + } + const sharedFactory = federationInstance.loadShareSync(shareKey); + if (typeof sharedFactory !== 'function') { + throw new Error( + `[mf:rsc-bridge] Shared client reference "${shareKey}" is unavailable synchronously`, + ); + } + const sharedExports = sharedFactory(); + syntheticSharedExportsByModuleId[moduleId] = sharedExports; + module.exports = sharedExports; + }; + }; + + const installSyntheticRemoteClientModuleFactory = ( + moduleId: string, + alias: string, + expose: string, + ) => { + const normalizedExpose = normalizeExpose(expose); + const remoteRequest = `${alias}/${normalizedExpose.startsWith('./') ? normalizedExpose.slice(2) : normalizedExpose}`; + const existingRequest = syntheticRemoteRequestByModuleId[moduleId]; + if (existingRequest && existingRequest !== remoteRequest) { + throw new Error( + `[mf:rsc-bridge] Conflicting remote client reference for "${moduleId}" between "${existingRequest}" and "${remoteRequest}"`, + ); + } + syntheticRemoteRequestByModuleId[moduleId] = remoteRequest; + + const exposeTokenFromModuleId = + parseRemoteClientReferenceModuleId(moduleId)?.expose; + const normalizedExposeToken = + exposeTokenFromModuleId && exposeTokenFromModuleId.startsWith('./') + ? exposeTokenFromModuleId.slice(2) + : exposeTokenFromModuleId || ''; + + const ensureExposeTokenMapForAlias = async () => { + if ( + isObject(exposeTokenByServerModuleIdByAlias[alias]) && + Object.keys(exposeTokenByServerModuleIdByAlias[alias]).length > 0 + ) { + return; + } + const existingPromise = exposeTokenMapPromiseByAlias[alias]; + if (existingPromise) { + await existingPromise; + return; + } + + const buildPromise = (async () => { + const bridge = await ensureBridge(alias); + if (typeof bridge.getManifest !== 'function') { + return; + } + const bridgeManifest = await Promise.resolve(bridge.getManifest()); + if (!isObject(bridgeManifest)) { + return; + } + + const clientManifest = isObject(bridgeManifest.clientManifest) + ? (bridgeManifest.clientManifest as Record) + : undefined; + const serverConsumerModuleMap = isObject( + bridgeManifest.serverConsumerModuleMap, + ) + ? (bridgeManifest.serverConsumerModuleMap as Record< + string, + ManifestNode + >) + : undefined; + if (!clientManifest || !serverConsumerModuleMap) { + return; + } + + const clientExposes = isObject(bridgeManifest.clientExposes) + ? (bridgeManifest.clientExposes as Record) + : {}; + const exposeTokenByClientModuleId: Record = + Object.create(null); + + for (const [rawClientManifestKey, clientEntry] of Object.entries( + clientManifest, + )) { + if (!isObject(clientEntry) || clientEntry.id == null) { + continue; + } + const entryName = + typeof clientEntry.name === 'string' ? clientEntry.name : undefined; + const entryExpose = resolveClientEntryExpose( + rawClientManifestKey, + entryName, + clientExposes, + ); + if (!entryExpose) { + continue; + } + const exposeToken = entryExpose.startsWith('./') + ? entryExpose.slice(2) + : entryExpose; + if (!exposeToken) { + continue; + } + const clientModuleId = stripLayerPrefix(String(clientEntry.id)); + if ( + !Object.prototype.hasOwnProperty.call( + exposeTokenByClientModuleId, + clientModuleId, + ) + ) { + exposeTokenByClientModuleId[clientModuleId] = exposeToken; + } + } + + const nextExposeTokenMap: Record = Object.create(null); + for (const [rawServerConsumerModuleId, node] of Object.entries( + serverConsumerModuleMap, + )) { + if (!isObject(node)) { + continue; + } + const rawClientModuleId = stripLayerPrefix(rawServerConsumerModuleId); + const exposeToken = exposeTokenByClientModuleId[rawClientModuleId]; + if (!exposeToken) { + continue; + } + if ( + !Object.prototype.hasOwnProperty.call( + nextExposeTokenMap, + rawClientModuleId, + ) + ) { + nextExposeTokenMap[rawClientModuleId] = exposeToken; + } + + for (const nodeEntry of Object.values(node)) { + if (!isObject(nodeEntry) || nodeEntry.id == null) { + continue; + } + const rawServerModuleId = stripLayerPrefix(String(nodeEntry.id)); + if ( + !Object.prototype.hasOwnProperty.call( + nextExposeTokenMap, + rawServerModuleId, + ) + ) { + nextExposeTokenMap[rawServerModuleId] = exposeToken; + } + } + } + + exposeTokenByServerModuleIdByAlias[alias] = nextExposeTokenMap; + })() + .catch((error: unknown) => { + delete exposeTokenMapPromiseByAlias[alias]; + throw error; + }) + .finally(() => { + delete exposeTokenMapPromiseByAlias[alias]; + }); + + exposeTokenMapPromiseByAlias[alias] = buildPromise; + await buildPromise; + }; + + const isLikelyOpaqueExposeToken = + !normalizedExposeToken || /^\d+$/.test(normalizedExposeToken); + + const resolveRemoteRequest = async () => { + if ( + Object.prototype.hasOwnProperty.call( + resolvedRemoteRequestByModuleId, + moduleId, + ) + ) { + return resolvedRemoteRequestByModuleId[moduleId]; + } + + let resolvedRemoteRequest = remoteRequest; + const exposeTokenByServerModuleId = + exposeTokenByServerModuleIdByAlias[alias]; + const remappedExposeToken = + (isObject(exposeTokenByServerModuleId) && + typeof exposeTokenByServerModuleId[normalizedExposeToken] === + 'string' && + exposeTokenByServerModuleId[normalizedExposeToken]) || + ''; + if (remappedExposeToken) { + resolvedRemoteRequest = `${alias}/${remappedExposeToken}`; + } else if (isLikelyOpaqueExposeToken) { + await ensureExposeTokenMapForAlias(); + const nextExposeTokenMap = + exposeTokenByServerModuleIdByAlias[alias] || Object.create(null); + const fallbackExposeToken = + typeof nextExposeTokenMap[normalizedExposeToken] === 'string' + ? nextExposeTokenMap[normalizedExposeToken] + : ''; + if (fallbackExposeToken) { + resolvedRemoteRequest = `${alias}/${fallbackExposeToken}`; + } + } + + resolvedRemoteRequestByModuleId[moduleId] = resolvedRemoteRequest; + return resolvedRemoteRequest; + }; + + if (!isObject(__webpack_require__.m)) { + __webpack_require__.m = {}; + } + + __webpack_require__.m![moduleId] = (module: { exports: unknown }) => { + if ( + Object.prototype.hasOwnProperty.call( + syntheticRemoteExportsByModuleId, + moduleId, + ) + ) { + module.exports = syntheticRemoteExportsByModuleId[moduleId]; + return; + } + + let pending = syntheticRemotePromiseByModuleId[moduleId]; + if (!pending) { + const runtimeInstance = __webpack_require__.federation?.instance; + if ( + !runtimeInstance || + typeof runtimeInstance.loadRemote !== 'function' + ) { + throw new Error( + `[mf:rsc-bridge] Missing loadRemote for remote client reference "${remoteRequest}"`, + ); + } + pending = resolveRemoteRequest() + .then((resolvedRemoteRequest) => + Promise.resolve(runtimeInstance.loadRemote(resolvedRemoteRequest)), + ) + .catch(async (error: unknown) => { + await ensureExposeTokenMapForAlias(); + const exposeTokenMap = + exposeTokenByServerModuleIdByAlias[alias] || Object.create(null); + const fallbackExposeToken = + typeof exposeTokenMap[normalizedExposeToken] === 'string' + ? exposeTokenMap[normalizedExposeToken] + : ''; + if (!fallbackExposeToken) { + throw error; + } + const fallbackRequest = `${alias}/${fallbackExposeToken}`; + if (fallbackRequest === remoteRequest) { + throw error; + } + resolvedRemoteRequestByModuleId[moduleId] = fallbackRequest; + return Promise.resolve(runtimeInstance.loadRemote(fallbackRequest)); + }) + .then((exportsValue) => { + syntheticRemoteExportsByModuleId[moduleId] = exportsValue; + return exportsValue; + }); + syntheticRemotePromiseByModuleId[moduleId] = pending; + } + + module.exports = pending; + }; + }; + + const ensureLazyRemoteClientFactoryPrototype = () => { + if (lazyRemoteFactoryPrototypeInstalled) { + return; + } + if (!isObject(__webpack_require__.m)) { + __webpack_require__.m = {}; + } + const moduleFactories = __webpack_require__.m as Record; + const previousPrototype = Object.getPrototypeOf(moduleFactories); + const fallbackPrototype = new Proxy(previousPrototype ?? Object.prototype, { + get(target, property, receiver) { + if ( + typeof property === 'string' && + !Object.prototype.hasOwnProperty.call(moduleFactories, property) + ) { + const parsedModuleId = parseRemoteClientReferenceModuleId(property); + if (parsedModuleId) { + installSyntheticRemoteClientModuleFactory( + property, + parsedModuleId.alias, + parsedModuleId.expose, + ); + if ( + Object.prototype.hasOwnProperty.call(moduleFactories, property) + ) { + return moduleFactories[property]; + } + } + } + return Reflect.get(target, property, receiver); + }, + has(target, property) { + if ( + typeof property === 'string' && + !Object.prototype.hasOwnProperty.call(moduleFactories, property) + ) { + const parsedModuleId = parseRemoteClientReferenceModuleId(property); + if (parsedModuleId) { + installSyntheticRemoteClientModuleFactory( + property, + parsedModuleId.alias, + parsedModuleId.expose, + ); + } + } + return ( + Object.prototype.hasOwnProperty.call(moduleFactories, property) || + Reflect.has(target, property) + ); + }, + getOwnPropertyDescriptor(target, property) { + if ( + typeof property === 'string' && + !Object.prototype.hasOwnProperty.call(moduleFactories, property) + ) { + const parsedModuleId = parseRemoteClientReferenceModuleId(property); + if (parsedModuleId) { + installSyntheticRemoteClientModuleFactory( + property, + parsedModuleId.alias, + parsedModuleId.expose, + ); + } + } + return ( + Object.getOwnPropertyDescriptor(moduleFactories, property) || + Reflect.getOwnPropertyDescriptor(target, property) + ); + }, + }); + Object.setPrototypeOf(moduleFactories, fallbackPrototype); + lazyRemoteFactoryPrototypeInstalled = true; + }; + + const seedRemoteClientModuleFactoriesFromRuntimeMapping = () => { + ensureLazyRemoteClientFactoryPrototype(); + const moduleIdToRemoteDataMapping = + __webpack_require__.remotesLoadingData?.moduleIdToRemoteDataMapping; + if (!isObject(moduleIdToRemoteDataMapping)) { + return; + } + for (const remoteData of Object.values(moduleIdToRemoteDataMapping)) { + if (!isObject(remoteData)) { + continue; + } + const remoteAlias = + typeof remoteData.remoteName === 'string' ? remoteData.remoteName : ''; + const expose = typeof remoteData.name === 'string' ? remoteData.name : ''; + if (!remoteAlias || !expose) { + continue; + } + const clientModuleId = getRemoteClientReferenceModuleId( + remoteAlias, + expose, + ); + if (seededRemoteClientModuleIds.has(clientModuleId)) { + continue; + } + installSyntheticRemoteClientModuleFactory( + clientModuleId, + remoteAlias, + expose, + ); + seededRemoteClientModuleIds.add(clientModuleId); + } + }; + + const ensureBridge = async (alias: string, args?: any) => { + const existingBridgePromise = bridgePromises[alias]; + if (existingBridgePromise) { + return existingBridgePromise; + } + + const runtimeInstance = + args?.origin && typeof args.origin.loadRemote === 'function' + ? args.origin + : __webpack_require__.federation?.instance; + + if (!runtimeInstance || typeof runtimeInstance.loadRemote !== 'function') { + throw new Error( + '[mf:rsc-bridge] Module Federation runtime instance is unavailable while loading the RSC bridge', + ); + } + + const ensureShareScopesInitialized = async () => { + const existingShareScopeInit = shareScopeInitPromises[alias]; + if (existingShareScopeInit) { + await existingShareScopeInit; + return; + } + + const getScopesFromValue = (value: unknown): string[] => { + if (Array.isArray(value)) { + return value.filter( + (item): item is string => typeof item === 'string', + ); + } + if (typeof value === 'string') { + return [value]; + } + return []; + }; + + const scopeSet = new Set(); + + const aliasRemoteInfos = + __webpack_require__.federation?.bundlerRuntimeOptions?.remotes + ?.remoteInfos?.[alias]; + if (Array.isArray(aliasRemoteInfos)) { + for (const remoteInfo of aliasRemoteInfos) { + for (const scope of getScopesFromValue(remoteInfo?.shareScope)) { + if (scope) { + scopeSet.add(scope); + } + } + } + } + + const configuredRemotes = runtimeInstance?.options?.remotes; + if (Array.isArray(configuredRemotes)) { + for (const remote of configuredRemotes) { + if (!remote || typeof remote !== 'object') { + continue; + } + if (remote.alias !== alias && remote.name !== alias) { + continue; + } + for (const scope of getScopesFromValue(remote.shareScope)) { + if (scope) { + scopeSet.add(scope); + } + } + } + } + + if (scopeSet.size === 0) { + scopeSet.add('default'); + } + + const initPromise = (async () => { + for (const scope of scopeSet) { + const initResult = + typeof runtimeInstance.initializeSharing === 'function' + ? runtimeInstance.initializeSharing(scope) + : typeof __webpack_require__.I === 'function' + ? __webpack_require__.I(scope) + : undefined; + if ( + initResult && + typeof (initResult as Promise).then === 'function' + ) { + await initResult; + } + } + })().catch((error: unknown) => { + delete shareScopeInitPromises[alias]; + throw error; + }); + + shareScopeInitPromises[alias] = initPromise; + await initPromise; + }; + + await ensureShareScopesInitialized(); + + const bridgePromise = Promise.resolve( + runtimeInstance.loadRemote(`${alias}/${RSC_BRIDGE_EXPOSE}`), + ) + .then((bridge: BridgeModule) => { + if ( + !bridge || + typeof bridge.getManifest !== 'function' || + typeof bridge.executeAction !== 'function' + ) { + throw new Error( + `[mf:rsc-bridge] Remote "${alias}" is missing the internal RSC bridge expose`, + ); + } + return bridge; + }) + .catch((error: unknown) => { + delete bridgePromises[alias]; + throw error; + }); + + bridgePromises[alias] = bridgePromise; + return bridgePromise; + }; + + const preloadSsrModule = async ( + alias: string, + rawServerModuleId: string, + exposeHint?: string, + args?: any, + ) => { + const namespacedModuleId = namespaceServerModuleIdDeterministically( + alias, + rawServerModuleId, + ); + ensureSsrModuleFactory(namespacedModuleId, rawServerModuleId); + + if ( + Object.prototype.hasOwnProperty.call( + ssrModuleExportsByNamespacedId, + namespacedModuleId, + ) + ) { + return namespacedModuleId; + } + + const existingPreload = ssrModulePreloadPromises[namespacedModuleId]; + if (existingPreload) { + await existingPreload; + return namespacedModuleId; + } + + const preloadPromise = (async () => { + const bridge = await ensureBridge(alias, args); + if (typeof bridge.preloadSSRModule === 'function') { + ssrModuleExportsByNamespacedId[namespacedModuleId] = + await bridge.preloadSSRModule(rawServerModuleId, exposeHint); + return; + } + if (typeof bridge.getSSRModule === 'function') { + ssrModuleExportsByNamespacedId[namespacedModuleId] = + bridge.getSSRModule(rawServerModuleId); + return; + } + throw new Error( + `[mf:rsc-bridge] Remote "${alias}" does not expose preloadSSRModule/getSSRModule for "${rawServerModuleId}"${exposeHint ? ` (hint: ${exposeHint})` : ''}`, + ); + })() + .catch((error: unknown) => { + delete ssrModuleExportsByNamespacedId[namespacedModuleId]; + throw error; + }) + .finally(() => { + delete ssrModulePreloadPromises[namespacedModuleId]; + }); + + ssrModulePreloadPromises[namespacedModuleId] = preloadPromise; + await preloadPromise; + return namespacedModuleId; + }; + + const mergeRemoteManifest = async ( + alias: string, + remoteManifest: ManifestLike, + proxyModuleId: string, + args?: any, + ) => { + if (!isObject(remoteManifest)) { + return; + } + + const hostManifest = ensureHostManifest(); + const clientExposes = isObject(remoteManifest.clientExposes) + ? (remoteManifest.clientExposes as Record) + : {}; + const resolvedClientIdsByRawId: Record = + Object.create(null); + const resolvedClientIdKindByRawId: Record< + string, + ClientModuleResolutionKind + > = Object.create(null); + const shareKeyToModuleId = createSharedConsumeModuleIdIndex(); + const clientManifestEntriesByRawId: Record< + string, + Array + > = Object.create(null); + + if (isObject(remoteManifest.clientManifest)) { + for (const [rawClientManifestKey, value] of Object.entries( + remoteManifest.clientManifest, + )) { + const scopedClientManifestKey = getNamespacedClientManifestKey( + alias, + rawClientManifestKey, + ); + const nextValue = isObject(value) ? { ...value } : value; + const rawRemoteClientModuleId = + isObject(nextValue) && nextValue.id != null + ? String(nextValue.id) + : undefined; + const entryName = + isObject(nextValue) && typeof nextValue.name === 'string' + ? nextValue.name + : undefined; + const explicitClientModule = resolveExplicitClientModuleId( + remoteManifest, + rawClientManifestKey, + ); + const entryExpose = + explicitClientModule?.kind === 'remote' + ? explicitClientModule.expose + : resolveClientEntryExpose( + rawClientManifestKey, + entryName, + clientExposes, + ); + const resolvedLocalSharedClientModuleId = + explicitClientModule?.kind === 'shared' + ? resolveSharedConsumeModuleId( + rawClientManifestKey, + shareKeyToModuleId, + explicitClientModule.shareKey, + ) + : resolveSharedConsumeModuleId( + rawClientManifestKey, + shareKeyToModuleId, + ); + const resolvedShareKey = + explicitClientModule?.kind === 'shared' + ? explicitClientModule.shareKey + : resolvedLocalSharedClientModuleId + ? undefined + : resolveShareKeyFromClientManifestKey(rawClientManifestKey); + const resolvedRemoteExposeClientModuleId = + entryExpose && !resolvedLocalSharedClientModuleId + ? getRemoteClientReferenceModuleId(alias, entryExpose) + : undefined; + + const fallbackClientModuleId = rawRemoteClientModuleId + ? namespaceModuleIdDeterministically(alias, rawRemoteClientModuleId) + : namespaceModuleIdDeterministically(alias, rawClientManifestKey); + const resolvedSyntheticSharedClientModuleId = + !resolvedLocalSharedClientModuleId && + resolvedShareKey && + rawRemoteClientModuleId + ? namespaceModuleIdDeterministically(alias, rawRemoteClientModuleId) + : undefined; + if (resolvedSyntheticSharedClientModuleId && resolvedShareKey) { + installSyntheticSharedClientModuleFactory( + resolvedSyntheticSharedClientModuleId, + resolvedShareKey, + ); + } + + const nextClientIdKind: ClientModuleResolutionKind = + resolvedLocalSharedClientModuleId || + resolvedSyntheticSharedClientModuleId + ? 'shared' + : 'remote'; + let finalClientModuleId = + resolvedLocalSharedClientModuleId || + resolvedSyntheticSharedClientModuleId || + resolvedRemoteExposeClientModuleId || + fallbackClientModuleId; + let finalClientIdKind: ClientModuleResolutionKind = nextClientIdKind; + + if (isObject(nextValue)) { + nextValue.id = finalClientModuleId; + if (finalClientIdKind === 'shared') { + nextValue.chunks = []; + } else if (entryExpose) { + installSyntheticRemoteClientModuleFactory( + finalClientModuleId, + alias, + entryExpose, + ); + nextValue.chunks = []; + nextValue.async = true; + } + } + const clientManifest = hostManifest.clientManifest as Record< + string, + any + >; + const upsertClientManifestEntry = ( + key: string, + entry: unknown, + preferIncomingNonNamespaced: boolean, + ) => { + if (!Object.prototype.hasOwnProperty.call(clientManifest, key)) { + clientManifest[key] = entry; + return entry; + } + const existingEntry = clientManifest[key]; + if (stableStringify(existingEntry) === stableStringify(entry)) { + return existingEntry; + } + if ( + isObject(existingEntry) && + isObject(entry) && + existingEntry.id != null && + entry.id != null + ) { + const existingId = String(existingEntry.id); + const nextId = String(entry.id); + const existingIsNamespaced = isNamespacedRemoteIdForAlias( + alias, + existingId, + ); + const nextIsNamespaced = isNamespacedRemoteIdForAlias( + alias, + nextId, + ); + if (!existingIsNamespaced && !nextIsNamespaced) { + if (preferIncomingNonNamespaced && existingId !== nextId) { + clientManifest[key] = entry; + return entry; + } + return existingEntry; + } + if (!existingIsNamespaced && nextIsNamespaced) { + return existingEntry; + } + if (existingIsNamespaced && !nextIsNamespaced) { + clientManifest[key] = entry; + return entry; + } + } + throw new Error( + `[mf:rsc-bridge] clientManifest conflict for "${key}" while merging remote "${alias}"`, + ); + }; + + const scopedAssignedValue = upsertClientManifestEntry( + scopedClientManifestKey, + nextValue, + false, + ); + const rawAssignedValue = upsertClientManifestEntry( + rawClientManifestKey, + nextValue, + false, + ); + const preferredAssignedValue = isObject(rawAssignedValue) + ? rawAssignedValue + : scopedAssignedValue; + + if ( + isObject(preferredAssignedValue) && + preferredAssignedValue.id != null + ) { + finalClientModuleId = String(preferredAssignedValue.id); + if (!isNamespacedRemoteIdForAlias(alias, finalClientModuleId)) { + finalClientIdKind = 'shared'; + } + if (isObject(nextValue)) { + nextValue.id = finalClientModuleId; + if (finalClientIdKind === 'shared') { + nextValue.chunks = []; + } else if (entryExpose) { + installSyntheticRemoteClientModuleFactory( + finalClientModuleId, + alias, + entryExpose, + ); + nextValue.chunks = []; + nextValue.async = true; + } + } + } + + if (isObject(nextValue) && rawRemoteClientModuleId) { + if ( + !Array.isArray( + clientManifestEntriesByRawId[rawRemoteClientModuleId], + ) + ) { + clientManifestEntriesByRawId[rawRemoteClientModuleId] = []; + } + clientManifestEntriesByRawId[rawRemoteClientModuleId].push( + nextValue as ManifestExport, + ); + } + + if (!rawRemoteClientModuleId) { + continue; + } + const existingResolvedId = + resolvedClientIdsByRawId[rawRemoteClientModuleId]; + const existingResolvedKind = + resolvedClientIdKindByRawId[rawRemoteClientModuleId]; + if (existingResolvedId && existingResolvedId !== finalClientModuleId) { + if ( + getClientModuleResolutionPriority(existingResolvedKind) >= + getClientModuleResolutionPriority(finalClientIdKind) + ) { + finalClientModuleId = existingResolvedId; + finalClientIdKind = existingResolvedKind || 'remote'; + if (isObject(nextValue)) { + nextValue.id = finalClientModuleId; + if (finalClientIdKind === 'shared') { + nextValue.chunks = []; + } else if (entryExpose) { + installSyntheticRemoteClientModuleFactory( + finalClientModuleId, + alias, + entryExpose, + ); + nextValue.chunks = []; + nextValue.async = true; + } + } + } + } + resolvedClientIdsByRawId[rawRemoteClientModuleId] = finalClientModuleId; + resolvedClientIdKindByRawId[rawRemoteClientModuleId] = + finalClientIdKind; + const prefixedRawRemoteClientModuleId = + rawRemoteClientModuleId.startsWith(SSR_LAYER_PREFIX) + ? rawRemoteClientModuleId + : `${SSR_LAYER_PREFIX}${rawRemoteClientModuleId}`; + const existingPrefixedResolvedId = + resolvedClientIdsByRawId[prefixedRawRemoteClientModuleId]; + const existingPrefixedResolvedKind = + resolvedClientIdKindByRawId[prefixedRawRemoteClientModuleId]; + if ( + existingPrefixedResolvedId && + existingPrefixedResolvedId !== finalClientModuleId + ) { + if ( + getClientModuleResolutionPriority(existingPrefixedResolvedKind) >= + getClientModuleResolutionPriority(finalClientIdKind) + ) { + finalClientModuleId = existingPrefixedResolvedId; + finalClientIdKind = existingPrefixedResolvedKind || 'remote'; + if (isObject(nextValue)) { + nextValue.id = finalClientModuleId; + if (finalClientIdKind === 'shared') { + nextValue.chunks = []; + } else if (entryExpose) { + installSyntheticRemoteClientModuleFactory( + finalClientModuleId, + alias, + entryExpose, + ); + nextValue.chunks = []; + nextValue.async = true; + } + } + } + } + resolvedClientIdsByRawId[prefixedRawRemoteClientModuleId] = + finalClientModuleId; + resolvedClientIdKindByRawId[prefixedRawRemoteClientModuleId] = + finalClientIdKind; + const rawIdEntries = + clientManifestEntriesByRawId[rawRemoteClientModuleId]; + if (Array.isArray(rawIdEntries)) { + for (const entryValue of rawIdEntries) { + entryValue.id = finalClientModuleId; + if (finalClientIdKind === 'shared') { + entryValue.chunks = []; + } else if (entryExpose) { + installSyntheticRemoteClientModuleFactory( + finalClientModuleId, + alias, + entryExpose, + ); + entryValue.chunks = []; + entryValue.async = true; + } + } + } + } + } + + if (isObject(remoteManifest.serverConsumerModuleMap)) { + const remoteServerConsumerModuleMap = + remoteManifest.serverConsumerModuleMap as Record; + const ssrPreloadPromises: Array> = []; + for (const [rawModuleId, value] of Object.entries( + remoteServerConsumerModuleMap, + )) { + const normalizedRawModuleId = String(rawModuleId); + const rscLayerFallbackNode = normalizedRawModuleId.startsWith( + RSC_LAYER_PREFIX, + ) + ? remoteServerConsumerModuleMap[ + `${SSR_LAYER_PREFIX}${stripLayerPrefix(normalizedRawModuleId)}` + ] || + remoteServerConsumerModuleMap[ + stripLayerPrefix(normalizedRawModuleId) + ] + : undefined; + const sourceNode = rscLayerFallbackNode || value; + const inferredExposeHint = inferExposeHintFromConsumerNode(sourceNode); + const resolvedModuleId = + resolveClientModuleIdFromMap(resolvedClientIdsByRawId, rawModuleId) || + namespaceModuleIdDeterministically(alias, rawModuleId); + + const nextValue = remapConsumerNode( + sourceNode, + (rawServerModuleId, exportName) => { + const exportScopedHint = + exportName !== '*' && exportName !== '__esModule' + ? normalizeExpose(exportName) + : inferredExposeHint; + const namespacedServerModuleId = + namespaceServerModuleIdDeterministically( + alias, + rawServerModuleId, + ); + ssrPreloadPromises.push( + preloadSsrModule( + alias, + rawServerModuleId, + exportScopedHint, + args, + ), + ); + return namespacedServerModuleId; + }, + ); + + const serverConsumerModuleMap = + hostManifest.serverConsumerModuleMap as Record; + const upsertServerConsumerNode = ( + key: string, + node: unknown, + preferIncoming = false, + ) => { + if ( + !Object.prototype.hasOwnProperty.call(serverConsumerModuleMap, key) + ) { + serverConsumerModuleMap[key] = node; + return; + } + const existingNode = serverConsumerModuleMap[key]; + if (stableStringify(existingNode) === stableStringify(node)) { + return; + } + if (preferIncoming) { + serverConsumerModuleMap[key] = node; + return; + } + if (!isNamespacedRemoteIdForAlias(alias, key)) { + return; + } + throw new Error( + `[mf:rsc-bridge] serverConsumerModuleMap conflict for "${key}" while merging remote "${alias}"`, + ); + }; + const isLayerSpecificRawModuleId = + rawModuleId.startsWith(SSR_LAYER_PREFIX) || + rawModuleId.startsWith(RSC_LAYER_PREFIX); + const shouldPreferIncomingLayerSpecificNode = + isLayerSpecificRawModuleId && + isNamespacedRemoteIdForAlias(alias, resolvedModuleId); + upsertServerConsumerNode( + resolvedModuleId, + nextValue, + shouldPreferIncomingLayerSpecificNode, + ); + + const prefixedResolvedModuleId = + resolvedModuleId.startsWith(SSR_LAYER_PREFIX) || + resolvedModuleId.startsWith(RSC_LAYER_PREFIX) + ? resolvedModuleId + : `${SSR_LAYER_PREFIX}${resolvedModuleId}`; + upsertServerConsumerNode( + prefixedResolvedModuleId, + nextValue, + shouldPreferIncomingLayerSpecificNode && + isNamespacedRemoteIdForAlias(alias, prefixedResolvedModuleId), + ); + } + if (ssrPreloadPromises.length > 0) { + await Promise.all(ssrPreloadPromises); + } + } + + const remoteScopedManifest = { + moduleLoading: remoteManifest.moduleLoading, + entryCssFiles: remoteManifest.entryCssFiles, + entryJsFiles: remoteManifest.entryJsFiles, + }; + + if (!isObject(hostManifest.remoteManifests)) { + hostManifest.remoteManifests = Object.create(null); + } + (hostManifest.remoteManifests as Record)[alias] = + remoteScopedManifest; + + if (remoteManifest.moduleLoading != null) { + if (isObject(hostManifest.moduleLoading)) { + (hostManifest.moduleLoading as Record)[alias] = + remoteManifest.moduleLoading; + } else { + hostManifest.moduleLoading = { + [alias]: remoteManifest.moduleLoading, + }; + } + } + + if (remoteManifest.entryJsFiles != null) { + if (!Array.isArray(hostManifest.entryJsFiles)) { + const nextEntryJsFiles: unknown[] = []; + if (isObject(hostManifest.entryJsFiles)) { + Object.assign(nextEntryJsFiles, hostManifest.entryJsFiles); + } + hostManifest.entryJsFiles = nextEntryJsFiles; + } + (hostManifest.entryJsFiles as Record)[alias] = + remoteManifest.entryJsFiles; + } + + if (remoteManifest.entryCssFiles != null) { + if ( + isObject(hostManifest.entryCssFiles) || + Array.isArray(hostManifest.entryCssFiles) + ) { + (hostManifest.entryCssFiles as Record)[alias] = + remoteManifest.entryCssFiles; + } else { + hostManifest.entryCssFiles = { + [alias]: remoteManifest.entryCssFiles, + }; + } + } + + if (!isObject(remoteManifest.serverManifest)) { + return; + } + + for (const [rawActionId, actionEntry] of Object.entries( + remoteManifest.serverManifest, + )) { + const prefixedActionId = `${ACTION_PREFIX}${alias}:${rawActionId}`; + const prefixedHostActionEntry = { + id: proxyModuleId, + name: prefixedActionId, + chunks: [], + async: + isObject(actionEntry) && 'async' in actionEntry + ? Boolean((actionEntry as Record)['async']) + : true, + }; + assertNoConflict( + hostManifest.serverManifest as Record, + prefixedActionId, + prefixedHostActionEntry, + alias, + 'serverManifest', + ); + (hostManifest.serverManifest as Record)[prefixedActionId] = + prefixedHostActionEntry; + + const rawHostActionEntry = { + ...prefixedHostActionEntry, + name: rawActionId, + }; + + assertNoConflict( + hostManifest.serverManifest as Record, + rawActionId, + rawHostActionEntry, + alias, + 'serverManifest', + ); + (hostManifest.serverManifest as Record)[rawActionId] = + rawHostActionEntry; + + registerActionProxyExport(actionMap, prefixedActionId, { + alias, + rawActionId, + }); + registerActionProxyExport(actionMap, rawActionId, { + alias, + rawActionId, + }); + registerActionRemap(rawActionId, prefixedActionId); + } + }; + + const ensureRemoteAliasMerged = async (alias: string, args: any) => { + const existingMergePromise = aliasMergePromises[alias]; + if (existingMergePromise) { + await existingMergePromise; + return; + } + if (mergedRemoteAliases.has(alias)) { + return; + } + + const mergePromise = (async () => { + installActionProxyModule({ + actionMap, + ensureBridge, + }); + + let bridge: BridgeModule | undefined; + if (isBridgeExposeRequest(args)) { + bridge = await resolveBridgeFromLoadArgs(args); + if (!bridge) { + return; + } + bridgePromises[alias] = Promise.resolve(bridge); + } else { + bridge = await ensureBridge(alias, args); + } + const remoteManifest = + typeof bridge.getManifest === 'function' + ? await Promise.resolve(bridge.getManifest()) + : {}; + + await mergeRemoteManifest( + alias, + remoteManifest || {}, + ACTION_PROXY_MODULE_ID, + args, + ); + mergedRemoteAliases.add(alias); + })(); + + aliasMergePromises[alias] = mergePromise; + try { + await mergePromise; + } finally { + delete aliasMergePromises[alias]; + } + }; + + seedRemoteClientModuleFactoriesFromRuntimeMapping(); + + return { + name: 'rspack-rsc-bridge-runtime-plugin', + async afterResolve(args: any) { + seedRemoteClientModuleFactoriesFromRuntimeMapping(); + const alias = resolveRemoteAlias(args); + if (!alias) { + return args; + } + if (isBridgeExposeRequest(args)) { + return args; + } + await ensureRemoteAliasMerged(alias, args); + return args; + }, + async onLoad(args: any) { + seedRemoteClientModuleFactoriesFromRuntimeMapping(); + const alias = resolveRemoteAlias(args); + if (!alias) { + return args; + } + if (isBridgeExposeRequest(args) && aliasMergePromises[alias]) { + return args; + } + await ensureRemoteAliasMerged(alias, args); + return args; + }, + }; +}; + +export default rscBridgeRuntimePlugin; diff --git a/packages/webpack-bundler-runtime/src/rscManifest.ts b/packages/webpack-bundler-runtime/src/rscManifest.ts new file mode 100644 index 00000000000..39a546993f9 --- /dev/null +++ b/packages/webpack-bundler-runtime/src/rscManifest.ts @@ -0,0 +1,130 @@ +export type ManifestExport = { + id: string; + name: string; + chunks?: Array; + async?: boolean; +}; + +export type ManifestNode = Record; + +export type RscClientReferenceResolution = + | { + kind: 'local'; + } + | { + kind: 'shared'; + shareKey: string; + shareScope?: string[]; + } + | { + kind: 'remote'; + remoteAlias: string; + request: string; + shareScope?: string[]; + }; + +export type RscClientReference = { + exportName: string; + moduleId: string; + chunks?: Array; + async?: boolean; + resolution: RscClientReferenceResolution; +}; + +export type RscActionReference = { + localActionId: string; + exportName?: string; + moduleResource?: string; +}; + +export type RscServerModuleReference = { + localModuleId?: string; + ssrExpose?: string; + expose?: string; +}; + +export type ManifestLike = { + version?: number; + serverManifest?: Record; + clientManifest?: Record; + serverConsumerModuleMap?: Record; + moduleLoading?: unknown; + entryCssFiles?: Record | Array; + entryJsFiles?: Array | Record; + remoteManifests?: Record< + string, + { + moduleLoading?: unknown; + entryCssFiles?: unknown; + entryJsFiles?: unknown; + } + >; + clientExposes?: Record; + clientReferences?: Record; + actions?: Record; + serverModules?: Record; +}; + +export type RscBridgeModuleV1 = { + getManifest?: () => ManifestLike | Promise; + executeAction?: (actionId: string, args: unknown[]) => Promise; + preloadSSRModule?: ( + moduleId: string, + exposeHint?: string, + ) => Promise; + getSSRModule?: (moduleId: string) => unknown; +}; + +export const getClientManifestKeyWithoutHash = ( + rawClientManifestKey: string, +) => { + const hashIndex = rawClientManifestKey.indexOf('#'); + return hashIndex >= 0 + ? rawClientManifestKey.slice(0, hashIndex) + : rawClientManifestKey; +}; + +export const resolveClientReferenceEntry = ( + manifest: ManifestLike | undefined, + rawClientManifestKey: string, +) => { + const clientReferences = manifest?.clientReferences; + if (!clientReferences) { + return undefined; + } + if ( + Object.prototype.hasOwnProperty.call(clientReferences, rawClientManifestKey) + ) { + return clientReferences[rawClientManifestKey]; + } + const keyWithoutHash = getClientManifestKeyWithoutHash(rawClientManifestKey); + if ( + keyWithoutHash !== rawClientManifestKey && + Object.prototype.hasOwnProperty.call(clientReferences, keyWithoutHash) + ) { + return clientReferences[keyWithoutHash]; + } + return undefined; +}; + +export const resolveActionReference = ( + manifest: ManifestLike | undefined, + actionId: string, +) => { + const actions = manifest?.actions; + if (!actions) { + return undefined; + } + return actions[actionId]; +}; + +export const resolveServerModuleReference = ( + manifest: ManifestLike | undefined, + moduleId: string, +) => { + const serverModules = manifest?.serverModules; + if (!serverModules) { + return undefined; + } + return serverModules[moduleId]; +}; diff --git a/packages/webpack-bundler-runtime/src/types.ts b/packages/webpack-bundler-runtime/src/types.ts index 8fecd6a3736..dcd033f6959 100644 --- a/packages/webpack-bundler-runtime/src/types.ts +++ b/packages/webpack-bundler-runtime/src/types.ts @@ -216,6 +216,12 @@ export interface GetSharedFallbackGetterOptions { libraryType?: string; } +export interface ResolveRemoteModuleIdOptions { + alias: string; + expose: string; + webpackRequire: WebpackRequire; +} + export interface Federation { runtime?: typeof runtime; instance?: runtime.ModuleFederation; @@ -235,6 +241,9 @@ export interface Federation { webpackRequire: WebpackRequire; libraryType: string; }) => void; + resolveRemoteModuleId: ( + options: ResolveRemoteModuleIdOptions, + ) => string | undefined; getSharedFallbackGetter: ( options: GetSharedFallbackGetterOptions, ) => SharedGetter; diff --git a/packages/webpack-bundler-runtime/tsdown.config.ts b/packages/webpack-bundler-runtime/tsdown.config.ts index fb9b36f5f1b..5fdce14d257 100644 --- a/packages/webpack-bundler-runtime/tsdown.config.ts +++ b/packages/webpack-bundler-runtime/tsdown.config.ts @@ -14,6 +14,8 @@ export default defineConfig([ entry: { index: 'src/index.ts', constant: 'src/constant.ts', + 'rsc-bridge-runtime-plugin': 'src/rscBridgeRuntimePlugin.ts', + 'rsc-bridge-expose': 'src/rscBridgeExpose.ts', }, external: ['@module-federation/*', 'webpack'], dts: {