@bleedingdev/modern-js-server-core 3.2.0-ultramodern.9 → 3.2.0-ultramodern.91

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,172 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.n = (module)=>{
5
+ var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
6
+ __webpack_require__.d(getter, {
7
+ a: getter
8
+ });
9
+ return getter;
10
+ };
11
+ })();
12
+ (()=>{
13
+ __webpack_require__.d = (exports1, definition)=>{
14
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
15
+ enumerable: true,
16
+ get: definition[key]
17
+ });
18
+ };
19
+ })();
20
+ (()=>{
21
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
22
+ })();
23
+ (()=>{
24
+ __webpack_require__.r = (exports1)=>{
25
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
26
+ value: 'Module'
27
+ });
28
+ Object.defineProperty(exports1, '__esModule', {
29
+ value: true
30
+ });
31
+ };
32
+ })();
33
+ var __webpack_exports__ = {};
34
+ __webpack_require__.r(__webpack_exports__);
35
+ __webpack_require__.d(__webpack_exports__, {
36
+ collectDirectRemoteModuleFederationCss: ()=>collectDirectRemoteModuleFederationCss,
37
+ collectModuleFederationManifestCss: ()=>collectModuleFederationManifestCss
38
+ });
39
+ const fileReader_namespaceObject = require("@modern-js/runtime-utils/fileReader");
40
+ const utils_namespaceObject = require("@modern-js/utils");
41
+ const external_path_namespaceObject = require("path");
42
+ var external_path_default = /*#__PURE__*/ __webpack_require__.n(external_path_namespaceObject);
43
+ const MODULE_FEDERATION_MANIFEST_FILE = 'mf-manifest.json';
44
+ const DEFAULT_REMOTE_MANIFEST_TIMEOUT = 1500;
45
+ const warn = (monitors, message, ...args)=>{
46
+ if (monitors) return void monitors.warn(message, ...args);
47
+ console.warn(message, ...args);
48
+ };
49
+ const ensureTrailingSlash = (value)=>value.endsWith('/') ? value : `${value}/`;
50
+ const tryResolveUrl = (value, base)=>{
51
+ try {
52
+ return new URL(value, base).toString();
53
+ } catch {
54
+ return;
55
+ }
56
+ };
57
+ const normalizeRemoteEntry = (entry)=>{
58
+ const value = entry.trim();
59
+ if (!value) return;
60
+ const atIndex = value.lastIndexOf('@');
61
+ return atIndex >= 0 ? value.slice(atIndex + 1) : value;
62
+ };
63
+ const getCssAssets = (assets)=>[
64
+ ...assets?.css?.sync || [],
65
+ ...assets?.css?.async || []
66
+ ];
67
+ const getManifestFallbackBase = (manifestUrl)=>{
68
+ try {
69
+ return new URL('.', manifestUrl).toString();
70
+ } catch {
71
+ return ensureTrailingSlash(manifestUrl);
72
+ }
73
+ };
74
+ const getManifestPublicPathBase = (publicPath, manifestUrl)=>{
75
+ if (!publicPath || 'auto' === publicPath) return getManifestFallbackBase(manifestUrl);
76
+ const base = tryResolveUrl(ensureTrailingSlash(publicPath), manifestUrl);
77
+ return base || getManifestFallbackBase(manifestUrl);
78
+ };
79
+ const appendResolvedCssAssets = (result, seen, assets, base)=>{
80
+ for (const asset of assets){
81
+ if (!asset) continue;
82
+ const resolved = tryResolveUrl(asset, base);
83
+ if (resolved && !seen.has(resolved)) {
84
+ seen.add(resolved);
85
+ result.push(resolved);
86
+ }
87
+ }
88
+ };
89
+ const collectModuleFederationManifestCss = (manifest, manifestUrl)=>{
90
+ const base = getManifestPublicPathBase(manifest.metaData?.publicPath, manifestUrl);
91
+ const result = [];
92
+ const seen = new Set();
93
+ for (const item of manifest.shared || [])appendResolvedCssAssets(result, seen, getCssAssets(item.assets), base);
94
+ for (const item of manifest.exposes || [])appendResolvedCssAssets(result, seen, getCssAssets(item.assets), base);
95
+ return result;
96
+ };
97
+ const fetchJsonWithTimeout = async (url, fetcher, timeout)=>{
98
+ const controller = new AbortController();
99
+ let timeoutId;
100
+ try {
101
+ const response = await Promise.race([
102
+ fetcher(url, {
103
+ signal: controller.signal
104
+ }),
105
+ new Promise((_, reject)=>{
106
+ timeoutId = setTimeout(()=>{
107
+ controller.abort();
108
+ reject(new Error(`Request timed out after ${timeout}ms`));
109
+ }, timeout);
110
+ })
111
+ ]);
112
+ if (!response.ok) throw new Error(`Unexpected status ${response.status}`);
113
+ return await response.json();
114
+ } finally{
115
+ if (timeoutId) clearTimeout(timeoutId);
116
+ }
117
+ };
118
+ const getHostManifest = async (pwd, monitors)=>{
119
+ const manifestPath = external_path_default().join(pwd, MODULE_FEDERATION_MANIFEST_FILE);
120
+ if (!await utils_namespaceObject.fs.pathExists(manifestPath)) return;
121
+ const manifestBuffer = await fileReader_namespaceObject.fileReader.readFileFromSystem(manifestPath, 'buffer');
122
+ if (null === manifestBuffer) return;
123
+ try {
124
+ return JSON.parse(manifestBuffer.toString('utf-8'));
125
+ } catch (error) {
126
+ warn(monitors, 'Parse module federation manifest failed, error = %s', error instanceof Error ? error.message : error);
127
+ return;
128
+ }
129
+ };
130
+ const collectDirectRemoteModuleFederationCss = async (pwd, options = {})=>{
131
+ const hostManifest = await getHostManifest(pwd, options.monitors);
132
+ if (!hostManifest) return [];
133
+ const fetcher = options.fetcher || globalThis.fetch?.bind(globalThis);
134
+ if (!fetcher) {
135
+ warn(options.monitors, 'Skip module federation remote CSS collection because fetch is unavailable.');
136
+ return [];
137
+ }
138
+ const timeout = options.timeout ?? DEFAULT_REMOTE_MANIFEST_TIMEOUT;
139
+ const cssAssets = [];
140
+ const seen = new Set();
141
+ for (const remote of hostManifest.remotes || []){
142
+ if (!remote.entry) continue;
143
+ const remoteEntry = normalizeRemoteEntry(remote.entry);
144
+ if (!remoteEntry) continue;
145
+ let remoteManifestUrl;
146
+ try {
147
+ remoteManifestUrl = new URL(remoteEntry).toString();
148
+ } catch {
149
+ warn(options.monitors, 'Skip module federation remote CSS collection for non-absolute manifest URL %s', remoteEntry);
150
+ continue;
151
+ }
152
+ try {
153
+ const remoteManifest = await fetchJsonWithTimeout(remoteManifestUrl, fetcher, timeout);
154
+ for (const asset of collectModuleFederationManifestCss(remoteManifest, remoteManifestUrl))if (!seen.has(asset)) {
155
+ seen.add(asset);
156
+ cssAssets.push(asset);
157
+ }
158
+ } catch (error) {
159
+ warn(options.monitors, 'Load module federation remote manifest %s failed, error = %s', remoteManifestUrl, error instanceof Error ? error.message : error);
160
+ }
161
+ }
162
+ return cssAssets;
163
+ };
164
+ exports.collectDirectRemoteModuleFederationCss = __webpack_exports__.collectDirectRemoteModuleFederationCss;
165
+ exports.collectModuleFederationManifestCss = __webpack_exports__.collectModuleFederationManifestCss;
166
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
167
+ "collectDirectRemoteModuleFederationCss",
168
+ "collectModuleFederationManifestCss"
169
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
170
+ Object.defineProperty(exports, '__esModule', {
171
+ value: true
172
+ });
@@ -48,6 +48,7 @@ const utils_namespaceObject = require("@modern-js/utils");
48
48
  const external_path_namespaceObject = require("path");
49
49
  var external_path_default = /*#__PURE__*/ __webpack_require__.n(external_path_namespaceObject);
50
50
  const index_js_namespaceObject = require("../../../utils/index.js");
51
+ const external_moduleFederationCss_js_namespaceObject = require("./moduleFederationCss.js");
51
52
  async function getHtmlTemplates(pwd, routes) {
52
53
  const htmlRoutes = routes.filter((route)=>route.entryName);
53
54
  const htmls = await Promise.all(htmlRoutes.map(async (route)=>{
@@ -102,12 +103,16 @@ async function getServerManifest(pwd, routes, monitors) {
102
103
  const routeManifest = await (0, utils_namespaceObject.compatibleRequire)(routesManifestUri).catch((_)=>({}));
103
104
  const nestedRoutesJsonPath = external_path_default().join(pwd, utils_namespaceObject.NESTED_ROUTE_SPEC_FILE);
104
105
  const nestedRoutesJson = await (0, utils_namespaceObject.compatibleRequire)(nestedRoutesJsonPath).catch((_)=>({}));
106
+ const moduleFederationCssAssets = await (0, external_moduleFederationCss_js_namespaceObject.collectDirectRemoteModuleFederationCss)(pwd, {
107
+ monitors
108
+ });
105
109
  return {
106
110
  loaderBundles,
107
111
  renderBundles,
108
112
  loadableStats,
109
113
  routeManifest,
110
- nestedRoutesJson
114
+ nestedRoutesJson,
115
+ moduleFederationCssAssets
111
116
  };
112
117
  }
113
118
  function injectServerManifest(pwd, routes, manifestPromise) {
@@ -33,12 +33,14 @@ const csrRscRender = async (req, options)=>{
33
33
  const serverBundle = serverManifest?.renderBundles?.[routeInfo.entryName || constants_namespaceObject.MAIN_ENTRY_NAME];
34
34
  const loadableStats = serverManifest.loadableStats || {};
35
35
  const routeManifest = serverManifest.routeManifest || {};
36
+ const moduleFederationCssAssets = serverManifest.moduleFederationCssAssets || [];
36
37
  const config = (0, external_utils_js_namespaceObject.createRequestHandlerConfig)(options.config);
37
38
  const requestHandlerOptions = {
38
39
  resource: {
39
40
  route: routeInfo,
40
41
  loadableStats,
41
42
  routeManifest,
43
+ moduleFederationCssAssets,
42
44
  entryName: routeInfo.entryName || constants_namespaceObject.MAIN_ENTRY_NAME
43
45
  },
44
46
  config,
@@ -33,12 +33,14 @@ const renderRscHandler = async (req, options)=>{
33
33
  const serverBundle = serverManifest?.renderBundles?.[routeInfo.entryName || constants_namespaceObject.MAIN_ENTRY_NAME];
34
34
  const loadableStats = serverManifest.loadableStats || {};
35
35
  const routeManifest = serverManifest.routeManifest || {};
36
+ const moduleFederationCssAssets = serverManifest.moduleFederationCssAssets || [];
36
37
  const config = (0, external_utils_js_namespaceObject.createRequestHandlerConfig)(options.config);
37
38
  const requestHandlerOptions = {
38
39
  resource: {
39
40
  route: routeInfo,
40
41
  loadableStats,
41
42
  routeManifest,
43
+ moduleFederationCssAssets,
42
44
  entryName: routeInfo.entryName || constants_namespaceObject.MAIN_ENTRY_NAME
43
45
  },
44
46
  config,
@@ -36,6 +36,7 @@ async function ssrRender(request, { routeInfo, html, config: userConfig, staticG
36
36
  const { entryName } = routeInfo;
37
37
  const loadableStats = serverManifest.loadableStats || {};
38
38
  const routeManifest = serverManifest.routeManifest || {};
39
+ const moduleFederationCssAssets = serverManifest.moduleFederationCssAssets || [];
39
40
  const headers = (0, index_js_namespaceObject.parseHeaders)(request);
40
41
  if (nodeReq) {
41
42
  for(const key in nodeReq.headers)if (!headers[key]) headers[key] = nodeReq.headers[key];
@@ -49,6 +50,7 @@ async function ssrRender(request, { routeInfo, html, config: userConfig, staticG
49
50
  route: routeInfo,
50
51
  loadableStats,
51
52
  routeManifest,
53
+ moduleFederationCssAssets,
52
54
  htmlTemplate: html,
53
55
  entryName: entryName || constants_namespaceObject.MAIN_ENTRY_NAME
54
56
  },
@@ -0,0 +1,125 @@
1
+ import { fileReader } from "@modern-js/runtime-utils/fileReader";
2
+ import { fs } from "@modern-js/utils";
3
+ import path from "path";
4
+ const MODULE_FEDERATION_MANIFEST_FILE = 'mf-manifest.json';
5
+ const DEFAULT_REMOTE_MANIFEST_TIMEOUT = 1500;
6
+ const warn = (monitors, message, ...args)=>{
7
+ if (monitors) return void monitors.warn(message, ...args);
8
+ console.warn(message, ...args);
9
+ };
10
+ const ensureTrailingSlash = (value)=>value.endsWith('/') ? value : `${value}/`;
11
+ const tryResolveUrl = (value, base)=>{
12
+ try {
13
+ return new URL(value, base).toString();
14
+ } catch {
15
+ return;
16
+ }
17
+ };
18
+ const normalizeRemoteEntry = (entry)=>{
19
+ const value = entry.trim();
20
+ if (!value) return;
21
+ const atIndex = value.lastIndexOf('@');
22
+ return atIndex >= 0 ? value.slice(atIndex + 1) : value;
23
+ };
24
+ const getCssAssets = (assets)=>[
25
+ ...assets?.css?.sync || [],
26
+ ...assets?.css?.async || []
27
+ ];
28
+ const getManifestFallbackBase = (manifestUrl)=>{
29
+ try {
30
+ return new URL('.', manifestUrl).toString();
31
+ } catch {
32
+ return ensureTrailingSlash(manifestUrl);
33
+ }
34
+ };
35
+ const getManifestPublicPathBase = (publicPath, manifestUrl)=>{
36
+ if (!publicPath || 'auto' === publicPath) return getManifestFallbackBase(manifestUrl);
37
+ const base = tryResolveUrl(ensureTrailingSlash(publicPath), manifestUrl);
38
+ return base || getManifestFallbackBase(manifestUrl);
39
+ };
40
+ const appendResolvedCssAssets = (result, seen, assets, base)=>{
41
+ for (const asset of assets){
42
+ if (!asset) continue;
43
+ const resolved = tryResolveUrl(asset, base);
44
+ if (resolved && !seen.has(resolved)) {
45
+ seen.add(resolved);
46
+ result.push(resolved);
47
+ }
48
+ }
49
+ };
50
+ const collectModuleFederationManifestCss = (manifest, manifestUrl)=>{
51
+ const base = getManifestPublicPathBase(manifest.metaData?.publicPath, manifestUrl);
52
+ const result = [];
53
+ const seen = new Set();
54
+ for (const item of manifest.shared || [])appendResolvedCssAssets(result, seen, getCssAssets(item.assets), base);
55
+ for (const item of manifest.exposes || [])appendResolvedCssAssets(result, seen, getCssAssets(item.assets), base);
56
+ return result;
57
+ };
58
+ const fetchJsonWithTimeout = async (url, fetcher, timeout)=>{
59
+ const controller = new AbortController();
60
+ let timeoutId;
61
+ try {
62
+ const response = await Promise.race([
63
+ fetcher(url, {
64
+ signal: controller.signal
65
+ }),
66
+ new Promise((_, reject)=>{
67
+ timeoutId = setTimeout(()=>{
68
+ controller.abort();
69
+ reject(new Error(`Request timed out after ${timeout}ms`));
70
+ }, timeout);
71
+ })
72
+ ]);
73
+ if (!response.ok) throw new Error(`Unexpected status ${response.status}`);
74
+ return await response.json();
75
+ } finally{
76
+ if (timeoutId) clearTimeout(timeoutId);
77
+ }
78
+ };
79
+ const getHostManifest = async (pwd, monitors)=>{
80
+ const manifestPath = path.join(pwd, MODULE_FEDERATION_MANIFEST_FILE);
81
+ if (!await fs.pathExists(manifestPath)) return;
82
+ const manifestBuffer = await fileReader.readFileFromSystem(manifestPath, 'buffer');
83
+ if (null === manifestBuffer) return;
84
+ try {
85
+ return JSON.parse(manifestBuffer.toString('utf-8'));
86
+ } catch (error) {
87
+ warn(monitors, 'Parse module federation manifest failed, error = %s', error instanceof Error ? error.message : error);
88
+ return;
89
+ }
90
+ };
91
+ const collectDirectRemoteModuleFederationCss = async (pwd, options = {})=>{
92
+ const hostManifest = await getHostManifest(pwd, options.monitors);
93
+ if (!hostManifest) return [];
94
+ const fetcher = options.fetcher || globalThis.fetch?.bind(globalThis);
95
+ if (!fetcher) {
96
+ warn(options.monitors, 'Skip module federation remote CSS collection because fetch is unavailable.');
97
+ return [];
98
+ }
99
+ const timeout = options.timeout ?? DEFAULT_REMOTE_MANIFEST_TIMEOUT;
100
+ const cssAssets = [];
101
+ const seen = new Set();
102
+ for (const remote of hostManifest.remotes || []){
103
+ if (!remote.entry) continue;
104
+ const remoteEntry = normalizeRemoteEntry(remote.entry);
105
+ if (!remoteEntry) continue;
106
+ let remoteManifestUrl;
107
+ try {
108
+ remoteManifestUrl = new URL(remoteEntry).toString();
109
+ } catch {
110
+ warn(options.monitors, 'Skip module federation remote CSS collection for non-absolute manifest URL %s', remoteEntry);
111
+ continue;
112
+ }
113
+ try {
114
+ const remoteManifest = await fetchJsonWithTimeout(remoteManifestUrl, fetcher, timeout);
115
+ for (const asset of collectModuleFederationManifestCss(remoteManifest, remoteManifestUrl))if (!seen.has(asset)) {
116
+ seen.add(asset);
117
+ cssAssets.push(asset);
118
+ }
119
+ } catch (error) {
120
+ warn(options.monitors, 'Load module federation remote manifest %s failed, error = %s', remoteManifestUrl, error instanceof Error ? error.message : error);
121
+ }
122
+ }
123
+ return cssAssets;
124
+ };
125
+ export { collectDirectRemoteModuleFederationCss, collectModuleFederationManifestCss };
@@ -2,6 +2,7 @@ import { fileReader } from "@modern-js/runtime-utils/fileReader";
2
2
  import { LOADABLE_STATS_FILE, MAIN_ENTRY_NAME, NESTED_ROUTE_SPEC_FILE, ROUTE_MANIFEST_FILE, SERVER_BUNDLE_DIRECTORY, compatibleRequire, fs, isProd } from "@modern-js/utils";
3
3
  import path from "path";
4
4
  import { uniqueKeyByRoute } from "../../../utils/index.mjs";
5
+ import { collectDirectRemoteModuleFederationCss } from "./moduleFederationCss.mjs";
5
6
  async function getHtmlTemplates(pwd, routes) {
6
7
  const htmlRoutes = routes.filter((route)=>route.entryName);
7
8
  const htmls = await Promise.all(htmlRoutes.map(async (route)=>{
@@ -56,12 +57,16 @@ async function getServerManifest(pwd, routes, monitors) {
56
57
  const routeManifest = await compatibleRequire(routesManifestUri).catch((_)=>({}));
57
58
  const nestedRoutesJsonPath = path.join(pwd, NESTED_ROUTE_SPEC_FILE);
58
59
  const nestedRoutesJson = await compatibleRequire(nestedRoutesJsonPath).catch((_)=>({}));
60
+ const moduleFederationCssAssets = await collectDirectRemoteModuleFederationCss(pwd, {
61
+ monitors
62
+ });
59
63
  return {
60
64
  loaderBundles,
61
65
  renderBundles,
62
66
  loadableStats,
63
67
  routeManifest,
64
- nestedRoutesJson
68
+ nestedRoutesJson,
69
+ moduleFederationCssAssets
65
70
  };
66
71
  }
67
72
  function injectServerManifest(pwd, routes, manifestPromise) {
@@ -5,12 +5,14 @@ const csrRscRender = async (req, options)=>{
5
5
  const serverBundle = serverManifest?.renderBundles?.[routeInfo.entryName || MAIN_ENTRY_NAME];
6
6
  const loadableStats = serverManifest.loadableStats || {};
7
7
  const routeManifest = serverManifest.routeManifest || {};
8
+ const moduleFederationCssAssets = serverManifest.moduleFederationCssAssets || [];
8
9
  const config = createRequestHandlerConfig(options.config);
9
10
  const requestHandlerOptions = {
10
11
  resource: {
11
12
  route: routeInfo,
12
13
  loadableStats,
13
14
  routeManifest,
15
+ moduleFederationCssAssets,
14
16
  entryName: routeInfo.entryName || MAIN_ENTRY_NAME
15
17
  },
16
18
  config,
@@ -5,12 +5,14 @@ const renderRscHandler = async (req, options)=>{
5
5
  const serverBundle = serverManifest?.renderBundles?.[routeInfo.entryName || MAIN_ENTRY_NAME];
6
6
  const loadableStats = serverManifest.loadableStats || {};
7
7
  const routeManifest = serverManifest.routeManifest || {};
8
+ const moduleFederationCssAssets = serverManifest.moduleFederationCssAssets || [];
8
9
  const config = createRequestHandlerConfig(options.config);
9
10
  const requestHandlerOptions = {
10
11
  resource: {
11
12
  route: routeInfo,
12
13
  loadableStats,
13
14
  routeManifest,
15
+ moduleFederationCssAssets,
14
16
  entryName: routeInfo.entryName || MAIN_ENTRY_NAME
15
17
  },
16
18
  config,
@@ -8,6 +8,7 @@ async function ssrRender(request, { routeInfo, html, config: userConfig, staticG
8
8
  const { entryName } = routeInfo;
9
9
  const loadableStats = serverManifest.loadableStats || {};
10
10
  const routeManifest = serverManifest.routeManifest || {};
11
+ const moduleFederationCssAssets = serverManifest.moduleFederationCssAssets || [];
11
12
  const headers = parseHeaders(request);
12
13
  if (nodeReq) {
13
14
  for(const key in nodeReq.headers)if (!headers[key]) headers[key] = nodeReq.headers[key];
@@ -21,6 +22,7 @@ async function ssrRender(request, { routeInfo, html, config: userConfig, staticG
21
22
  route: routeInfo,
22
23
  loadableStats,
23
24
  routeManifest,
25
+ moduleFederationCssAssets,
24
26
  htmlTemplate: html,
25
27
  entryName: entryName || MAIN_ENTRY_NAME
26
28
  },
@@ -0,0 +1,126 @@
1
+ import "node:module";
2
+ import { fileReader } from "@modern-js/runtime-utils/fileReader";
3
+ import { fs } from "@modern-js/utils";
4
+ import path from "path";
5
+ const MODULE_FEDERATION_MANIFEST_FILE = 'mf-manifest.json';
6
+ const DEFAULT_REMOTE_MANIFEST_TIMEOUT = 1500;
7
+ const warn = (monitors, message, ...args)=>{
8
+ if (monitors) return void monitors.warn(message, ...args);
9
+ console.warn(message, ...args);
10
+ };
11
+ const ensureTrailingSlash = (value)=>value.endsWith('/') ? value : `${value}/`;
12
+ const tryResolveUrl = (value, base)=>{
13
+ try {
14
+ return new URL(value, base).toString();
15
+ } catch {
16
+ return;
17
+ }
18
+ };
19
+ const normalizeRemoteEntry = (entry)=>{
20
+ const value = entry.trim();
21
+ if (!value) return;
22
+ const atIndex = value.lastIndexOf('@');
23
+ return atIndex >= 0 ? value.slice(atIndex + 1) : value;
24
+ };
25
+ const getCssAssets = (assets)=>[
26
+ ...assets?.css?.sync || [],
27
+ ...assets?.css?.async || []
28
+ ];
29
+ const getManifestFallbackBase = (manifestUrl)=>{
30
+ try {
31
+ return new URL('.', manifestUrl).toString();
32
+ } catch {
33
+ return ensureTrailingSlash(manifestUrl);
34
+ }
35
+ };
36
+ const getManifestPublicPathBase = (publicPath, manifestUrl)=>{
37
+ if (!publicPath || 'auto' === publicPath) return getManifestFallbackBase(manifestUrl);
38
+ const base = tryResolveUrl(ensureTrailingSlash(publicPath), manifestUrl);
39
+ return base || getManifestFallbackBase(manifestUrl);
40
+ };
41
+ const appendResolvedCssAssets = (result, seen, assets, base)=>{
42
+ for (const asset of assets){
43
+ if (!asset) continue;
44
+ const resolved = tryResolveUrl(asset, base);
45
+ if (resolved && !seen.has(resolved)) {
46
+ seen.add(resolved);
47
+ result.push(resolved);
48
+ }
49
+ }
50
+ };
51
+ const collectModuleFederationManifestCss = (manifest, manifestUrl)=>{
52
+ const base = getManifestPublicPathBase(manifest.metaData?.publicPath, manifestUrl);
53
+ const result = [];
54
+ const seen = new Set();
55
+ for (const item of manifest.shared || [])appendResolvedCssAssets(result, seen, getCssAssets(item.assets), base);
56
+ for (const item of manifest.exposes || [])appendResolvedCssAssets(result, seen, getCssAssets(item.assets), base);
57
+ return result;
58
+ };
59
+ const fetchJsonWithTimeout = async (url, fetcher, timeout)=>{
60
+ const controller = new AbortController();
61
+ let timeoutId;
62
+ try {
63
+ const response = await Promise.race([
64
+ fetcher(url, {
65
+ signal: controller.signal
66
+ }),
67
+ new Promise((_, reject)=>{
68
+ timeoutId = setTimeout(()=>{
69
+ controller.abort();
70
+ reject(new Error(`Request timed out after ${timeout}ms`));
71
+ }, timeout);
72
+ })
73
+ ]);
74
+ if (!response.ok) throw new Error(`Unexpected status ${response.status}`);
75
+ return await response.json();
76
+ } finally{
77
+ if (timeoutId) clearTimeout(timeoutId);
78
+ }
79
+ };
80
+ const getHostManifest = async (pwd, monitors)=>{
81
+ const manifestPath = path.join(pwd, MODULE_FEDERATION_MANIFEST_FILE);
82
+ if (!await fs.pathExists(manifestPath)) return;
83
+ const manifestBuffer = await fileReader.readFileFromSystem(manifestPath, 'buffer');
84
+ if (null === manifestBuffer) return;
85
+ try {
86
+ return JSON.parse(manifestBuffer.toString('utf-8'));
87
+ } catch (error) {
88
+ warn(monitors, 'Parse module federation manifest failed, error = %s', error instanceof Error ? error.message : error);
89
+ return;
90
+ }
91
+ };
92
+ const collectDirectRemoteModuleFederationCss = async (pwd, options = {})=>{
93
+ const hostManifest = await getHostManifest(pwd, options.monitors);
94
+ if (!hostManifest) return [];
95
+ const fetcher = options.fetcher || globalThis.fetch?.bind(globalThis);
96
+ if (!fetcher) {
97
+ warn(options.monitors, 'Skip module federation remote CSS collection because fetch is unavailable.');
98
+ return [];
99
+ }
100
+ const timeout = options.timeout ?? DEFAULT_REMOTE_MANIFEST_TIMEOUT;
101
+ const cssAssets = [];
102
+ const seen = new Set();
103
+ for (const remote of hostManifest.remotes || []){
104
+ if (!remote.entry) continue;
105
+ const remoteEntry = normalizeRemoteEntry(remote.entry);
106
+ if (!remoteEntry) continue;
107
+ let remoteManifestUrl;
108
+ try {
109
+ remoteManifestUrl = new URL(remoteEntry).toString();
110
+ } catch {
111
+ warn(options.monitors, 'Skip module federation remote CSS collection for non-absolute manifest URL %s', remoteEntry);
112
+ continue;
113
+ }
114
+ try {
115
+ const remoteManifest = await fetchJsonWithTimeout(remoteManifestUrl, fetcher, timeout);
116
+ for (const asset of collectModuleFederationManifestCss(remoteManifest, remoteManifestUrl))if (!seen.has(asset)) {
117
+ seen.add(asset);
118
+ cssAssets.push(asset);
119
+ }
120
+ } catch (error) {
121
+ warn(options.monitors, 'Load module federation remote manifest %s failed, error = %s', remoteManifestUrl, error instanceof Error ? error.message : error);
122
+ }
123
+ }
124
+ return cssAssets;
125
+ };
126
+ export { collectDirectRemoteModuleFederationCss, collectModuleFederationManifestCss };
@@ -3,6 +3,7 @@ import { fileReader } from "@modern-js/runtime-utils/fileReader";
3
3
  import { LOADABLE_STATS_FILE, MAIN_ENTRY_NAME, NESTED_ROUTE_SPEC_FILE, ROUTE_MANIFEST_FILE, SERVER_BUNDLE_DIRECTORY, compatibleRequire, fs, isProd } from "@modern-js/utils";
4
4
  import path from "path";
5
5
  import { uniqueKeyByRoute } from "../../../utils/index.mjs";
6
+ import { collectDirectRemoteModuleFederationCss } from "./moduleFederationCss.mjs";
6
7
  async function getHtmlTemplates(pwd, routes) {
7
8
  const htmlRoutes = routes.filter((route)=>route.entryName);
8
9
  const htmls = await Promise.all(htmlRoutes.map(async (route)=>{
@@ -57,12 +58,16 @@ async function getServerManifest(pwd, routes, monitors) {
57
58
  const routeManifest = await compatibleRequire(routesManifestUri).catch((_)=>({}));
58
59
  const nestedRoutesJsonPath = path.join(pwd, NESTED_ROUTE_SPEC_FILE);
59
60
  const nestedRoutesJson = await compatibleRequire(nestedRoutesJsonPath).catch((_)=>({}));
61
+ const moduleFederationCssAssets = await collectDirectRemoteModuleFederationCss(pwd, {
62
+ monitors
63
+ });
60
64
  return {
61
65
  loaderBundles,
62
66
  renderBundles,
63
67
  loadableStats,
64
68
  routeManifest,
65
- nestedRoutesJson
69
+ nestedRoutesJson,
70
+ moduleFederationCssAssets
66
71
  };
67
72
  }
68
73
  function injectServerManifest(pwd, routes, manifestPromise) {
@@ -6,12 +6,14 @@ const csrRscRender = async (req, options)=>{
6
6
  const serverBundle = serverManifest?.renderBundles?.[routeInfo.entryName || MAIN_ENTRY_NAME];
7
7
  const loadableStats = serverManifest.loadableStats || {};
8
8
  const routeManifest = serverManifest.routeManifest || {};
9
+ const moduleFederationCssAssets = serverManifest.moduleFederationCssAssets || [];
9
10
  const config = createRequestHandlerConfig(options.config);
10
11
  const requestHandlerOptions = {
11
12
  resource: {
12
13
  route: routeInfo,
13
14
  loadableStats,
14
15
  routeManifest,
16
+ moduleFederationCssAssets,
15
17
  entryName: routeInfo.entryName || MAIN_ENTRY_NAME
16
18
  },
17
19
  config,
@@ -6,12 +6,14 @@ const renderRscHandler = async (req, options)=>{
6
6
  const serverBundle = serverManifest?.renderBundles?.[routeInfo.entryName || MAIN_ENTRY_NAME];
7
7
  const loadableStats = serverManifest.loadableStats || {};
8
8
  const routeManifest = serverManifest.routeManifest || {};
9
+ const moduleFederationCssAssets = serverManifest.moduleFederationCssAssets || [];
9
10
  const config = createRequestHandlerConfig(options.config);
10
11
  const requestHandlerOptions = {
11
12
  resource: {
12
13
  route: routeInfo,
13
14
  loadableStats,
14
15
  routeManifest,
16
+ moduleFederationCssAssets,
15
17
  entryName: routeInfo.entryName || MAIN_ENTRY_NAME
16
18
  },
17
19
  config,
@@ -9,6 +9,7 @@ async function ssrRender(request, { routeInfo, html, config: userConfig, staticG
9
9
  const { entryName } = routeInfo;
10
10
  const loadableStats = serverManifest.loadableStats || {};
11
11
  const routeManifest = serverManifest.routeManifest || {};
12
+ const moduleFederationCssAssets = serverManifest.moduleFederationCssAssets || [];
12
13
  const headers = parseHeaders(request);
13
14
  if (nodeReq) {
14
15
  for(const key in nodeReq.headers)if (!headers[key]) headers[key] = nodeReq.headers[key];
@@ -22,6 +23,7 @@ async function ssrRender(request, { routeInfo, html, config: userConfig, staticG
22
23
  route: routeInfo,
23
24
  loadableStats,
24
25
  routeManifest,
26
+ moduleFederationCssAssets,
25
27
  htmlTemplate: html,
26
28
  entryName: entryName || MAIN_ENTRY_NAME
27
29
  },
@@ -0,0 +1,33 @@
1
+ import type { Monitors } from '@modern-js/types';
2
+ type ModuleFederationAssets = {
3
+ css?: {
4
+ sync?: string[];
5
+ async?: string[];
6
+ };
7
+ };
8
+ export type ModuleFederationManifest = {
9
+ metaData?: {
10
+ publicPath?: string;
11
+ };
12
+ shared?: Array<{
13
+ assets?: ModuleFederationAssets;
14
+ }>;
15
+ remotes?: Array<{
16
+ entry?: string;
17
+ assets?: ModuleFederationAssets;
18
+ }>;
19
+ exposes?: Array<{
20
+ assets?: ModuleFederationAssets;
21
+ }>;
22
+ };
23
+ type FetchLike = (input: string, init?: {
24
+ signal?: AbortSignal;
25
+ }) => Promise<Response>;
26
+ export type CollectDirectRemoteModuleFederationCssOptions = {
27
+ fetcher?: FetchLike;
28
+ monitors?: Monitors;
29
+ timeout?: number;
30
+ };
31
+ export declare const collectModuleFederationManifestCss: (manifest: ModuleFederationManifest, manifestUrl: string) => string[];
32
+ export declare const collectDirectRemoteModuleFederationCss: (pwd: string, options?: CollectDirectRemoteModuleFederationCssOptions) => Promise<string[]>;
33
+ export {};
@@ -7,6 +7,7 @@ export type Resource = {
7
7
  route: ServerRoute;
8
8
  htmlTemplate: string;
9
9
  entryName: string;
10
+ moduleFederationCssAssets?: string[];
10
11
  };
11
12
  export type Params = Record<string, any>;
12
13
  export type RequestHandlerConfig = {
@@ -32,6 +32,7 @@ export type ServerManifest = {
32
32
  loadableStats?: Record<string, any>;
33
33
  routeManifest?: Record<string, any>;
34
34
  nestedRoutesJson?: Record<string, any>;
35
+ moduleFederationCssAssets?: string[];
35
36
  };
36
37
  type ServerVariables = {
37
38
  /** @deprecated */
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "modern",
18
18
  "modern.js"
19
19
  ],
20
- "version": "3.2.0-ultramodern.9",
20
+ "version": "3.2.0-ultramodern.91",
21
21
  "types": "./dist/types/index.d.ts",
22
22
  "main": "./dist/cjs/index.js",
23
23
  "exports": {
@@ -68,27 +68,27 @@
68
68
  "node": ">=20"
69
69
  },
70
70
  "dependencies": {
71
- "@swc/helpers": "^0.5.21",
71
+ "@swc/helpers": "^0.5.23",
72
72
  "@web-std/fetch": "^4.2.1",
73
73
  "@web-std/file": "^3.0.3",
74
74
  "@web-std/stream": "^1.0.3",
75
75
  "cloneable-readable": "^3.0.0",
76
76
  "flatted": "^3.4.2",
77
- "hono": "^4.12.19",
78
- "ts-deepmerge": "7.0.3",
79
- "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.2.0-ultramodern.9",
80
- "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.2.0-ultramodern.9",
81
- "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.2.0-ultramodern.9"
77
+ "hono": "^4.12.22",
78
+ "ts-deepmerge": "8.0.0",
79
+ "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.2.0-ultramodern.91",
80
+ "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.2.0-ultramodern.91",
81
+ "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.2.0-ultramodern.91"
82
82
  },
83
83
  "devDependencies": {
84
84
  "@rslib/core": "0.21.5",
85
85
  "@types/cloneable-readable": "^2.0.3",
86
86
  "@types/merge-deep": "^3.0.3",
87
- "@types/node": "^25.8.0",
88
- "@typescript/native-preview": "7.0.0-dev.20260516.1",
87
+ "@types/node": "^25.9.1",
88
+ "@typescript/native-preview": "7.0.0-dev.20260527.2",
89
89
  "http-proxy-middleware": "^4.0.0",
90
- "@scripts/rstest-config": "2.66.0",
91
- "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.2.0-ultramodern.9"
90
+ "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.2.0-ultramodern.91",
91
+ "@scripts/rstest-config": "2.66.0"
92
92
  },
93
93
  "sideEffects": false,
94
94
  "publishConfig": {