@bleedingdev/modern-js-server-core 3.5.0-ultramodern.6 → 3.5.0-ultramodern.75

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.
@@ -2,12 +2,18 @@ import { fileReader } from "@modern-js/runtime-utils/fileReader";
2
2
  import { fs } from "@modern-js/utils";
3
3
  import path from "path";
4
4
  const MODULE_FEDERATION_MANIFEST_FILE = 'mf-manifest.json';
5
+ const BACKEND_MODULE_FEDERATION_MANIFEST_FILE = 'backend-mf-manifest.json';
6
+ const MODULE_FEDERATION_MANIFEST_FILES = [
7
+ MODULE_FEDERATION_MANIFEST_FILE,
8
+ BACKEND_MODULE_FEDERATION_MANIFEST_FILE
9
+ ];
5
10
  const MODULE_FEDERATION_OPTIONAL_FILES = [
6
11
  'mf-stats.json'
7
12
  ];
8
13
  const trimLeadingSlash = (value)=>value.replace(/^\/+/, '');
9
14
  const getModuleFederationRequestPath = (pathname, pathPrefix)=>trimLeadingSlash(pathname.replace(pathPrefix, ()=>''));
10
- const isModuleFederationManifestRequest = (requestPath)=>requestPath === MODULE_FEDERATION_MANIFEST_FILE;
15
+ const isModuleFederationManifestRequest = (requestPath)=>MODULE_FEDERATION_MANIFEST_FILES.includes(requestPath);
16
+ const isBackendModuleFederationManifestRequest = (requestPath)=>requestPath === BACKEND_MODULE_FEDERATION_MANIFEST_FILE;
11
17
  const applyModuleFederationAssetHeaders = (c)=>{
12
18
  c.header('Access-Control-Allow-Origin', '*');
13
19
  c.header('Access-Control-Allow-Headers', '*');
@@ -18,8 +24,7 @@ const joinModuleFederationAssetPath = (assetPath, assetName)=>{
18
24
  return trimLeadingSlash(path.posix.join(assetPath || '', assetName));
19
25
  };
20
26
  const appendModuleFederationAsset = (set, assetPath)=>{
21
- if (!assetPath) return;
22
- set.add(trimLeadingSlash(assetPath));
27
+ if (assetPath) set.add(trimLeadingSlash(assetPath));
23
28
  };
24
29
  const appendModuleFederationAssets = (set, assets)=>{
25
30
  assets?.js?.sync?.forEach((asset)=>appendModuleFederationAsset(set, asset));
@@ -60,37 +65,36 @@ const patchModuleFederationRemoteEntryPublicPath = (c, remoteEntryBuffer, pathPr
60
65
  };
61
66
  const getModuleFederationAssetList = async (pwd)=>{
62
67
  const assets = new Set();
63
- const manifestPath = path.join(pwd, MODULE_FEDERATION_MANIFEST_FILE);
64
- if (!await fs.pathExists(manifestPath)) return {
65
- assets,
66
- remoteEntry: null
67
- };
68
- assets.add(MODULE_FEDERATION_MANIFEST_FILE);
69
- const manifestBuffer = await fileReader.readFileFromSystem(manifestPath, 'buffer');
70
- if (null === manifestBuffer) return {
71
- assets,
72
- remoteEntry: null
73
- };
74
- for (const filename of MODULE_FEDERATION_OPTIONAL_FILES)if (await fs.pathExists(path.join(pwd, filename))) assets.add(filename);
75
- let remoteEntryFile = null;
76
- try {
77
- const manifest = JSON.parse(manifestBuffer.toString('utf-8'));
78
- const remoteEntry = joinModuleFederationAssetPath(manifest.metaData?.remoteEntry?.path, manifest.metaData?.remoteEntry?.name);
79
- const dtsZip = joinModuleFederationAssetPath(manifest.metaData?.types?.path, manifest.metaData?.types?.zip);
80
- const dtsApi = joinModuleFederationAssetPath(manifest.metaData?.types?.path, manifest.metaData?.types?.api);
81
- if (remoteEntry) {
82
- assets.add(remoteEntry);
83
- remoteEntryFile = remoteEntry;
84
- }
85
- if (dtsZip) assets.add(dtsZip);
86
- if (dtsApi) assets.add(dtsApi);
87
- manifest.shared?.forEach((item)=>appendModuleFederationAssets(assets, item.assets));
88
- manifest.remotes?.forEach((item)=>appendModuleFederationAssets(assets, item.assets));
89
- manifest.exposes?.forEach((item)=>appendModuleFederationAssets(assets, item.assets));
90
- } catch {}
68
+ const remoteEntries = new Set();
69
+ let manifestFound = false;
70
+ for (const manifestFile of MODULE_FEDERATION_MANIFEST_FILES){
71
+ const manifestPath = path.join(pwd, manifestFile);
72
+ if (!await fs.pathExists(manifestPath)) continue;
73
+ manifestFound = true;
74
+ assets.add(manifestFile);
75
+ const manifestBuffer = await fileReader.readFileFromSystem(manifestPath, 'buffer');
76
+ if (null !== manifestBuffer) try {
77
+ const manifest = JSON.parse(manifestBuffer.toString('utf-8'));
78
+ const remoteEntry = joinModuleFederationAssetPath(manifest.metaData?.remoteEntry?.path, manifest.metaData?.remoteEntry?.name);
79
+ const dtsZip = joinModuleFederationAssetPath(manifest.metaData?.types?.path, manifest.metaData?.types?.zip);
80
+ const dtsApi = joinModuleFederationAssetPath(manifest.metaData?.types?.path, manifest.metaData?.types?.api);
81
+ if (remoteEntry) {
82
+ assets.add(remoteEntry);
83
+ remoteEntries.add(remoteEntry);
84
+ }
85
+ appendModuleFederationAsset(assets, dtsZip);
86
+ appendModuleFederationAsset(assets, dtsApi);
87
+ manifest.shared?.forEach((item)=>appendModuleFederationAssets(assets, item.assets));
88
+ manifest.remotes?.forEach((item)=>appendModuleFederationAssets(assets, item.assets));
89
+ manifest.exposes?.forEach((item)=>appendModuleFederationAssets(assets, item.assets));
90
+ } catch {}
91
+ }
92
+ if (manifestFound) {
93
+ for (const filename of MODULE_FEDERATION_OPTIONAL_FILES)if (await fs.pathExists(path.join(pwd, filename))) assets.add(filename);
94
+ }
91
95
  return {
92
96
  assets,
93
- remoteEntry: remoteEntryFile
97
+ remoteEntries
94
98
  };
95
99
  };
96
- export { MODULE_FEDERATION_MANIFEST_FILE, applyModuleFederationAssetHeaders, getModuleFederationAssetList, getModuleFederationRequestPath, isModuleFederationManifestRequest, patchModuleFederationManifestPublicPath, patchModuleFederationRemoteEntryPublicPath, trimLeadingSlash };
100
+ export { MODULE_FEDERATION_MANIFEST_FILE, applyModuleFederationAssetHeaders, getModuleFederationAssetList, getModuleFederationRequestPath, isBackendModuleFederationManifestRequest, isModuleFederationManifestRequest, patchModuleFederationManifestPublicPath, patchModuleFederationRemoteEntryPublicPath };
@@ -88,4 +88,4 @@ const applyPreCompressedAssetHeaders = (c, preCompressedAsset)=>{
88
88
  if (preCompressedAsset.hasVariant) appendVaryHeader(c, 'Accept-Encoding');
89
89
  if (preCompressedAsset.selected) c.header('Content-Encoding', preCompressedAsset.selected.encoding);
90
90
  };
91
- export { appendVaryHeader, applyPreCompressedAssetHeaders, resolvePreCompressedAsset };
91
+ export { applyPreCompressedAssetHeaders, resolvePreCompressedAsset };
@@ -0,0 +1,129 @@
1
+ import { fileReader } from "@modern-js/runtime-utils/fileReader";
2
+ import { fs } from "@modern-js/utils";
3
+ import { getMimeType } from "hono/utils/mime";
4
+ import path from "path";
5
+ import { applyModuleFederationAssetHeaders, getModuleFederationAssetList, getModuleFederationRequestPath, isBackendModuleFederationManifestRequest, isModuleFederationManifestRequest, patchModuleFederationManifestPublicPath, patchModuleFederationRemoteEntryPublicPath } from "./staticModuleFederation.mjs";
6
+ import { applyPreCompressedAssetHeaders, resolvePreCompressedAsset } from "./staticPrecompressed.mjs";
7
+ const getStaticMimeType = (filename)=>getMimeType(filename) ?? ('.cjs' === path.extname(filename).toLowerCase() ? "text/javascript; charset=UTF-8" : void 0);
8
+ const servePreCompressedPublicRouteAsset = async (c, pwd, route)=>{
9
+ const { entryPath } = route;
10
+ const originFilename = path.join(pwd, entryPath);
11
+ const preCompressedAsset = await resolvePreCompressedAsset(c, originFilename);
12
+ const filename = preCompressedAsset.selected?.filepath ?? originFilename;
13
+ const data = await fileReader.readFile(filename, 'buffer');
14
+ const mimeType = getStaticMimeType(originFilename);
15
+ if (null === data) return null;
16
+ const body = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
17
+ if (mimeType) c.header('Content-Type', mimeType);
18
+ Object.entries(route.responseHeaders || {}).forEach(([k, v])=>{
19
+ c.header(k, v);
20
+ });
21
+ applyPreCompressedAssetHeaders(c, preCompressedAsset);
22
+ c.header('Content-Length', String(data.byteLength));
23
+ return c.body(body, 200);
24
+ };
25
+ const isPathInside = (target, root)=>{
26
+ const relative = path.relative(path.resolve(root), path.resolve(target));
27
+ return '' === relative || !relative.startsWith(`..${path.sep}`) && '..' !== relative && !path.isAbsolute(relative);
28
+ };
29
+ const resolvePublicDirectoryAsset = async (pwd, pathname)=>{
30
+ let decodedPathname;
31
+ try {
32
+ decodedPathname = decodeURIComponent(pathname).replace(/\\/gu, '/');
33
+ } catch {
34
+ return null;
35
+ }
36
+ if (decodedPathname.includes('\0') || decodedPathname.split('/').includes('..')) return null;
37
+ const publicDirectory = path.join(pwd, 'public');
38
+ const filepath = path.resolve(publicDirectory, decodedPathname.replace(/^\/+/u, ''));
39
+ if (!isPathInside(filepath, publicDirectory)) return null;
40
+ try {
41
+ const [realPublicDirectory, realFilepath, stat] = await Promise.all([
42
+ fs.realpath(publicDirectory),
43
+ fs.realpath(filepath),
44
+ fs.stat(filepath)
45
+ ]);
46
+ if (!stat.isFile() || !isPathInside(realFilepath, realPublicDirectory)) return null;
47
+ return realFilepath;
48
+ } catch {
49
+ return null;
50
+ }
51
+ };
52
+ const servePublicDirectoryAsset = async (c, pwd)=>{
53
+ const method = c.req.raw.method.toUpperCase();
54
+ if ('GET' !== method && 'HEAD' !== method) return null;
55
+ const originFilename = await resolvePublicDirectoryAsset(pwd, c.req.path);
56
+ if (null === originFilename) return null;
57
+ const preCompressedAsset = await resolvePreCompressedAsset(c, originFilename);
58
+ const selectedFilename = preCompressedAsset.selected?.filepath ?? originFilename;
59
+ const publicDirectory = await fs.realpath(path.join(pwd, 'public'));
60
+ let realSelectedFilename;
61
+ try {
62
+ realSelectedFilename = await fs.realpath(selectedFilename);
63
+ } catch {
64
+ return null;
65
+ }
66
+ if (!isPathInside(realSelectedFilename, publicDirectory)) return null;
67
+ const data = await fileReader.readFileFromSystem(realSelectedFilename, 'buffer');
68
+ if (null === data) return null;
69
+ const mimeType = getStaticMimeType(originFilename);
70
+ if (mimeType) c.header('Content-Type', mimeType);
71
+ applyPreCompressedAssetHeaders(c, preCompressedAsset);
72
+ c.header('Content-Length', String(data.byteLength));
73
+ if ('HEAD' === method) return c.body(null, 200);
74
+ const body = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
75
+ return c.body(body, 200);
76
+ };
77
+ const createModuleFederationStaticServing = ({ pwd, pathPrefix })=>{
78
+ let moduleFederationAssetsPromise = null;
79
+ const getModuleFederationAssets = async ()=>{
80
+ if (!moduleFederationAssetsPromise) moduleFederationAssetsPromise = getModuleFederationAssetList(pwd);
81
+ return moduleFederationAssetsPromise;
82
+ };
83
+ const resolveRequest = async (pathname)=>{
84
+ const requestPath = getModuleFederationRequestPath(pathname, pathPrefix);
85
+ if (requestPath.includes('..')) return null;
86
+ const moduleFederationAssetMeta = await getModuleFederationAssets();
87
+ return {
88
+ requestPath,
89
+ isModuleFederationAsset: moduleFederationAssetMeta.assets.has(requestPath),
90
+ isModuleFederationRemoteEntry: moduleFederationAssetMeta.remoteEntries.has(requestPath)
91
+ };
92
+ };
93
+ const serveFile = async (c, filepath, moduleFederationAsset = false, moduleFederationRemoteEntry = false, requestPath = '')=>{
94
+ if (moduleFederationAsset) applyModuleFederationAssetHeaders(c);
95
+ const mimeType = getStaticMimeType(filepath);
96
+ if (mimeType) c.header('Content-Type', mimeType);
97
+ const shouldPatchManifest = moduleFederationAsset && isModuleFederationManifestRequest(requestPath) && !isBackendModuleFederationManifestRequest(requestPath);
98
+ const shouldPatchRemoteEntry = moduleFederationRemoteEntry;
99
+ const canUsePreCompressed = !shouldPatchManifest && !shouldPatchRemoteEntry;
100
+ const preCompressedAsset = canUsePreCompressed ? await resolvePreCompressedAsset(c, filepath) : {
101
+ selected: null,
102
+ hasVariant: false
103
+ };
104
+ const targetFilepath = preCompressedAsset.selected?.filepath ?? filepath;
105
+ const chunk = await fileReader.readFileFromSystem(targetFilepath, 'buffer');
106
+ if (null === chunk) return null;
107
+ const responseChunk = shouldPatchManifest ? patchModuleFederationManifestPublicPath(c, chunk, pathPrefix) : shouldPatchRemoteEntry ? patchModuleFederationRemoteEntryPublicPath(c, chunk, pathPrefix) : chunk;
108
+ applyPreCompressedAssetHeaders(c, preCompressedAsset);
109
+ c.header('Content-Length', String(responseChunk.byteLength));
110
+ const body = new Uint8Array(responseChunk.buffer, responseChunk.byteOffset, responseChunk.byteLength);
111
+ return c.body(body, 200);
112
+ };
113
+ const serveByPath = async (c, filepath, request, moduleFederationAsset = false, moduleFederationRemoteEntry = false)=>{
114
+ if (!isPathInside(filepath, pwd)) return null;
115
+ if (!await fs.pathExists(filepath)) return null;
116
+ return serveFile(c, filepath, moduleFederationAsset, moduleFederationRemoteEntry, request.requestPath);
117
+ };
118
+ const serveStaticHit = (c, request)=>serveByPath(c, path.join(pwd, request.requestPath), request, request.isModuleFederationAsset, request.isModuleFederationRemoteEntry);
119
+ const serveModuleFederationAsset = (c, request)=>{
120
+ if (!request.isModuleFederationAsset) return null;
121
+ return serveByPath(c, path.join(pwd, request.requestPath), request, true, request.isModuleFederationRemoteEntry);
122
+ };
123
+ return {
124
+ resolveRequest,
125
+ serveStaticHit,
126
+ serveModuleFederationAsset
127
+ };
128
+ };
129
+ export { createModuleFederationStaticServing, servePreCompressedPublicRouteAsset, servePublicDirectoryAsset };
@@ -8,10 +8,10 @@ import { renderRscHandler } from "./renderRscHandler.mjs";
8
8
  import { serverActionHandler } from "./serverActionHandler.mjs";
9
9
  import { ssrRender } from "./ssrRender.mjs";
10
10
  import { __webpack_require__ } from "../../rslib-runtime.mjs";
11
- import * as __rspack_external__modern_js_runtime_utils_router_2aa8d96f from "@modern-js/runtime-utils/router";
11
+ import * as __rspack_external__modern_js_runtime_utils_router_4aa9f9b0 from "@modern-js/runtime-utils/router";
12
12
  __webpack_require__.add({
13
- 2420 (module) {
14
- module.exports = __rspack_external__modern_js_runtime_utils_router_2aa8d96f;
13
+ 5327 (module) {
14
+ module.exports = __rspack_external__modern_js_runtime_utils_router_4aa9f9b0;
15
15
  }
16
16
  });
17
17
  const DYNAMIC_ROUTE_REG = /\/:./;
@@ -163,7 +163,7 @@ async function renderHandler(request, options, mode, fallbackWrapper, framework)
163
163
  const { nestedRoutesJson } = serverManifest;
164
164
  const routes = nestedRoutesJson?.[options.routeInfo.entryName];
165
165
  if (routes) {
166
- const { matchRoutes } = __webpack_require__(2420);
166
+ const { matchRoutes } = __webpack_require__(5327);
167
167
  const url = new URL(request.url);
168
168
  const matchedRoutes = matchRoutes(routes, url.pathname, options.routeInfo.urlPath);
169
169
  if (matchedRoutes) {
@@ -1,12 +1,8 @@
1
1
  import "node:module";
2
- import { fileReader } from "@modern-js/runtime-utils/fileReader";
3
- import { fs } from "@modern-js/utils";
4
- import { getMimeType } from "hono/utils/mime";
5
2
  import path from "path";
6
3
  import { sortRoutes } from "../../../utils/index.mjs";
7
4
  import { getPublicDirPatterns } from "../../../utils/publicDir.mjs";
8
- import { applyModuleFederationAssetHeaders, getModuleFederationAssetList, getModuleFederationRequestPath, isModuleFederationManifestRequest, patchModuleFederationManifestPublicPath, patchModuleFederationRemoteEntryPublicPath } from "./staticModuleFederation.mjs";
9
- import { applyPreCompressedAssetHeaders, resolvePreCompressedAsset } from "./staticPrecompressed.mjs";
5
+ import { createModuleFederationStaticServing, servePreCompressedPublicRouteAsset, servePublicDirectoryAsset } from "./staticServing.mjs";
10
6
  const serverStaticPlugin = ()=>({
11
7
  name: '@modern-js/plugin-server-static',
12
8
  setup (api) {
@@ -31,23 +27,11 @@ function createPublicMiddleware({ pwd, routes }) {
31
27
  return async (c, next)=>{
32
28
  const route = matchPublicRoute(c.req, routes);
33
29
  if (route) {
34
- const { entryPath } = route;
35
- const originFilename = path.join(pwd, entryPath);
36
- const preCompressedAsset = await resolvePreCompressedAsset(c, originFilename);
37
- const filename = preCompressedAsset.selected?.filepath ?? originFilename;
38
- const data = await fileReader.readFile(filename, 'buffer');
39
- const mimeType = getMimeType(originFilename);
40
- if (null !== data) {
41
- const body = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
42
- if (mimeType) c.header('Content-Type', mimeType);
43
- Object.entries(route.responseHeaders || {}).forEach(([k, v])=>{
44
- c.header(k, v);
45
- });
46
- applyPreCompressedAssetHeaders(c, preCompressedAsset);
47
- c.header('Content-Length', String(data.byteLength));
48
- return c.body(body, 200);
49
- }
30
+ const response = await servePreCompressedPublicRouteAsset(c, pwd, route);
31
+ if (null !== response) return response;
50
32
  }
33
+ const generatedPublicAsset = await servePublicDirectoryAsset(c, pwd);
34
+ if (null !== generatedPublicAsset) return generatedPublicAsset;
51
35
  return await next();
52
36
  };
53
37
  }
@@ -97,54 +81,24 @@ function createStaticMiddleware(options) {
97
81
  pwd,
98
82
  routes: routes || []
99
83
  });
100
- let moduleFederationAssetsPromise = null;
101
- const getModuleFederationAssets = async ()=>{
102
- if (!moduleFederationAssetsPromise) moduleFederationAssetsPromise = getModuleFederationAssetList(pwd);
103
- return moduleFederationAssetsPromise;
104
- };
105
- const serveFile = async (c, filepath, moduleFederationAsset = false, moduleFederationRemoteEntry = false, requestPath = '')=>{
106
- if (moduleFederationAsset) applyModuleFederationAssetHeaders(c);
107
- const mimeType = getMimeType(filepath);
108
- if (mimeType) c.header('Content-Type', mimeType);
109
- const shouldPatchManifest = moduleFederationAsset && isModuleFederationManifestRequest(requestPath);
110
- const shouldPatchRemoteEntry = moduleFederationRemoteEntry;
111
- const canUsePreCompressed = !shouldPatchManifest && !shouldPatchRemoteEntry;
112
- const preCompressedAsset = canUsePreCompressed ? await resolvePreCompressedAsset(c, filepath) : {
113
- selected: null,
114
- hasVariant: false
115
- };
116
- const targetFilepath = preCompressedAsset.selected?.filepath ?? filepath;
117
- const chunk = await fileReader.readFileFromSystem(targetFilepath, 'buffer');
118
- if (null === chunk) return null;
119
- const responseChunk = shouldPatchManifest ? patchModuleFederationManifestPublicPath(c, chunk, pathPrefix) : shouldPatchRemoteEntry ? patchModuleFederationRemoteEntryPublicPath(c, chunk, pathPrefix) : chunk;
120
- applyPreCompressedAssetHeaders(c, preCompressedAsset);
121
- c.header('Content-Length', String(responseChunk.byteLength));
122
- const body = new Uint8Array(responseChunk.buffer, responseChunk.byteOffset, responseChunk.byteLength);
123
- return c.body(body, 200);
124
- };
84
+ const moduleFederationStaticServing = createModuleFederationStaticServing({
85
+ pwd,
86
+ pathPrefix
87
+ });
125
88
  return async (c, next)=>{
126
89
  const pageRoute = c.get('route');
127
90
  const pathname = c.req.path;
128
91
  if (pageRoute && '' === path.extname(pathname)) return next();
129
92
  const hit = staticPathRegExp.test(pathname);
130
- const requestPath = getModuleFederationRequestPath(pathname, pathPrefix);
131
- if (requestPath.includes('..')) return next();
132
- const moduleFederationAssetMeta = await getModuleFederationAssets();
133
- const isModuleFederationAsset = moduleFederationAssetMeta.assets.has(requestPath);
134
- const isModuleFederationRemoteEntry = moduleFederationAssetMeta.remoteEntry === requestPath;
135
- const serveByPath = async (filepath, moduleFederationAsset = false, moduleFederationRemoteEntry = false)=>{
136
- if (!await fs.pathExists(filepath)) return null;
137
- return serveFile(c, filepath, moduleFederationAsset, moduleFederationRemoteEntry, requestPath);
138
- };
93
+ const staticServingRequest = await moduleFederationStaticServing.resolveRequest(pathname);
94
+ if (null === staticServingRequest) return next();
139
95
  if (hit) {
140
- const response = await serveByPath(path.join(pwd, requestPath), isModuleFederationAsset, isModuleFederationRemoteEntry);
96
+ const response = await moduleFederationStaticServing.serveStaticHit(c, staticServingRequest);
141
97
  if (null !== response) return response;
142
98
  return next();
143
99
  }
144
- if (isModuleFederationAsset) {
145
- const response = await serveByPath(path.join(pwd, requestPath), true, isModuleFederationRemoteEntry);
146
- if (null !== response) return response;
147
- }
100
+ const moduleFederationResponse = await moduleFederationStaticServing.serveModuleFederationAsset(c, staticServingRequest);
101
+ if (null !== moduleFederationResponse) return moduleFederationResponse;
148
102
  return publicMiddleware(c, next);
149
103
  };
150
104
  }
@@ -3,12 +3,18 @@ import { fileReader } from "@modern-js/runtime-utils/fileReader";
3
3
  import { fs } from "@modern-js/utils";
4
4
  import path from "path";
5
5
  const MODULE_FEDERATION_MANIFEST_FILE = 'mf-manifest.json';
6
+ const BACKEND_MODULE_FEDERATION_MANIFEST_FILE = 'backend-mf-manifest.json';
7
+ const MODULE_FEDERATION_MANIFEST_FILES = [
8
+ MODULE_FEDERATION_MANIFEST_FILE,
9
+ BACKEND_MODULE_FEDERATION_MANIFEST_FILE
10
+ ];
6
11
  const MODULE_FEDERATION_OPTIONAL_FILES = [
7
12
  'mf-stats.json'
8
13
  ];
9
14
  const trimLeadingSlash = (value)=>value.replace(/^\/+/, '');
10
15
  const getModuleFederationRequestPath = (pathname, pathPrefix)=>trimLeadingSlash(pathname.replace(pathPrefix, ()=>''));
11
- const isModuleFederationManifestRequest = (requestPath)=>requestPath === MODULE_FEDERATION_MANIFEST_FILE;
16
+ const isModuleFederationManifestRequest = (requestPath)=>MODULE_FEDERATION_MANIFEST_FILES.includes(requestPath);
17
+ const isBackendModuleFederationManifestRequest = (requestPath)=>requestPath === BACKEND_MODULE_FEDERATION_MANIFEST_FILE;
12
18
  const applyModuleFederationAssetHeaders = (c)=>{
13
19
  c.header('Access-Control-Allow-Origin', '*');
14
20
  c.header('Access-Control-Allow-Headers', '*');
@@ -19,8 +25,7 @@ const joinModuleFederationAssetPath = (assetPath, assetName)=>{
19
25
  return trimLeadingSlash(path.posix.join(assetPath || '', assetName));
20
26
  };
21
27
  const appendModuleFederationAsset = (set, assetPath)=>{
22
- if (!assetPath) return;
23
- set.add(trimLeadingSlash(assetPath));
28
+ if (assetPath) set.add(trimLeadingSlash(assetPath));
24
29
  };
25
30
  const appendModuleFederationAssets = (set, assets)=>{
26
31
  assets?.js?.sync?.forEach((asset)=>appendModuleFederationAsset(set, asset));
@@ -61,37 +66,36 @@ const patchModuleFederationRemoteEntryPublicPath = (c, remoteEntryBuffer, pathPr
61
66
  };
62
67
  const getModuleFederationAssetList = async (pwd)=>{
63
68
  const assets = new Set();
64
- const manifestPath = path.join(pwd, MODULE_FEDERATION_MANIFEST_FILE);
65
- if (!await fs.pathExists(manifestPath)) return {
66
- assets,
67
- remoteEntry: null
68
- };
69
- assets.add(MODULE_FEDERATION_MANIFEST_FILE);
70
- const manifestBuffer = await fileReader.readFileFromSystem(manifestPath, 'buffer');
71
- if (null === manifestBuffer) return {
72
- assets,
73
- remoteEntry: null
74
- };
75
- for (const filename of MODULE_FEDERATION_OPTIONAL_FILES)if (await fs.pathExists(path.join(pwd, filename))) assets.add(filename);
76
- let remoteEntryFile = null;
77
- try {
78
- const manifest = JSON.parse(manifestBuffer.toString('utf-8'));
79
- const remoteEntry = joinModuleFederationAssetPath(manifest.metaData?.remoteEntry?.path, manifest.metaData?.remoteEntry?.name);
80
- const dtsZip = joinModuleFederationAssetPath(manifest.metaData?.types?.path, manifest.metaData?.types?.zip);
81
- const dtsApi = joinModuleFederationAssetPath(manifest.metaData?.types?.path, manifest.metaData?.types?.api);
82
- if (remoteEntry) {
83
- assets.add(remoteEntry);
84
- remoteEntryFile = remoteEntry;
85
- }
86
- if (dtsZip) assets.add(dtsZip);
87
- if (dtsApi) assets.add(dtsApi);
88
- manifest.shared?.forEach((item)=>appendModuleFederationAssets(assets, item.assets));
89
- manifest.remotes?.forEach((item)=>appendModuleFederationAssets(assets, item.assets));
90
- manifest.exposes?.forEach((item)=>appendModuleFederationAssets(assets, item.assets));
91
- } catch {}
69
+ const remoteEntries = new Set();
70
+ let manifestFound = false;
71
+ for (const manifestFile of MODULE_FEDERATION_MANIFEST_FILES){
72
+ const manifestPath = path.join(pwd, manifestFile);
73
+ if (!await fs.pathExists(manifestPath)) continue;
74
+ manifestFound = true;
75
+ assets.add(manifestFile);
76
+ const manifestBuffer = await fileReader.readFileFromSystem(manifestPath, 'buffer');
77
+ if (null !== manifestBuffer) try {
78
+ const manifest = JSON.parse(manifestBuffer.toString('utf-8'));
79
+ const remoteEntry = joinModuleFederationAssetPath(manifest.metaData?.remoteEntry?.path, manifest.metaData?.remoteEntry?.name);
80
+ const dtsZip = joinModuleFederationAssetPath(manifest.metaData?.types?.path, manifest.metaData?.types?.zip);
81
+ const dtsApi = joinModuleFederationAssetPath(manifest.metaData?.types?.path, manifest.metaData?.types?.api);
82
+ if (remoteEntry) {
83
+ assets.add(remoteEntry);
84
+ remoteEntries.add(remoteEntry);
85
+ }
86
+ appendModuleFederationAsset(assets, dtsZip);
87
+ appendModuleFederationAsset(assets, dtsApi);
88
+ manifest.shared?.forEach((item)=>appendModuleFederationAssets(assets, item.assets));
89
+ manifest.remotes?.forEach((item)=>appendModuleFederationAssets(assets, item.assets));
90
+ manifest.exposes?.forEach((item)=>appendModuleFederationAssets(assets, item.assets));
91
+ } catch {}
92
+ }
93
+ if (manifestFound) {
94
+ for (const filename of MODULE_FEDERATION_OPTIONAL_FILES)if (await fs.pathExists(path.join(pwd, filename))) assets.add(filename);
95
+ }
92
96
  return {
93
97
  assets,
94
- remoteEntry: remoteEntryFile
98
+ remoteEntries
95
99
  };
96
100
  };
97
- export { MODULE_FEDERATION_MANIFEST_FILE, applyModuleFederationAssetHeaders, getModuleFederationAssetList, getModuleFederationRequestPath, isModuleFederationManifestRequest, patchModuleFederationManifestPublicPath, patchModuleFederationRemoteEntryPublicPath, trimLeadingSlash };
101
+ export { MODULE_FEDERATION_MANIFEST_FILE, applyModuleFederationAssetHeaders, getModuleFederationAssetList, getModuleFederationRequestPath, isBackendModuleFederationManifestRequest, isModuleFederationManifestRequest, patchModuleFederationManifestPublicPath, patchModuleFederationRemoteEntryPublicPath };
@@ -89,4 +89,4 @@ const applyPreCompressedAssetHeaders = (c, preCompressedAsset)=>{
89
89
  if (preCompressedAsset.hasVariant) appendVaryHeader(c, 'Accept-Encoding');
90
90
  if (preCompressedAsset.selected) c.header('Content-Encoding', preCompressedAsset.selected.encoding);
91
91
  };
92
- export { appendVaryHeader, applyPreCompressedAssetHeaders, resolvePreCompressedAsset };
92
+ export { applyPreCompressedAssetHeaders, resolvePreCompressedAsset };
@@ -0,0 +1,130 @@
1
+ import "node:module";
2
+ import { fileReader } from "@modern-js/runtime-utils/fileReader";
3
+ import { fs } from "@modern-js/utils";
4
+ import { getMimeType } from "hono/utils/mime";
5
+ import path from "path";
6
+ import { applyModuleFederationAssetHeaders, getModuleFederationAssetList, getModuleFederationRequestPath, isBackendModuleFederationManifestRequest, isModuleFederationManifestRequest, patchModuleFederationManifestPublicPath, patchModuleFederationRemoteEntryPublicPath } from "./staticModuleFederation.mjs";
7
+ import { applyPreCompressedAssetHeaders, resolvePreCompressedAsset } from "./staticPrecompressed.mjs";
8
+ const getStaticMimeType = (filename)=>getMimeType(filename) ?? ('.cjs' === path.extname(filename).toLowerCase() ? "text/javascript; charset=UTF-8" : void 0);
9
+ const servePreCompressedPublicRouteAsset = async (c, pwd, route)=>{
10
+ const { entryPath } = route;
11
+ const originFilename = path.join(pwd, entryPath);
12
+ const preCompressedAsset = await resolvePreCompressedAsset(c, originFilename);
13
+ const filename = preCompressedAsset.selected?.filepath ?? originFilename;
14
+ const data = await fileReader.readFile(filename, 'buffer');
15
+ const mimeType = getStaticMimeType(originFilename);
16
+ if (null === data) return null;
17
+ const body = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
18
+ if (mimeType) c.header('Content-Type', mimeType);
19
+ Object.entries(route.responseHeaders || {}).forEach(([k, v])=>{
20
+ c.header(k, v);
21
+ });
22
+ applyPreCompressedAssetHeaders(c, preCompressedAsset);
23
+ c.header('Content-Length', String(data.byteLength));
24
+ return c.body(body, 200);
25
+ };
26
+ const isPathInside = (target, root)=>{
27
+ const relative = path.relative(path.resolve(root), path.resolve(target));
28
+ return '' === relative || !relative.startsWith(`..${path.sep}`) && '..' !== relative && !path.isAbsolute(relative);
29
+ };
30
+ const resolvePublicDirectoryAsset = async (pwd, pathname)=>{
31
+ let decodedPathname;
32
+ try {
33
+ decodedPathname = decodeURIComponent(pathname).replace(/\\/gu, '/');
34
+ } catch {
35
+ return null;
36
+ }
37
+ if (decodedPathname.includes('\0') || decodedPathname.split('/').includes('..')) return null;
38
+ const publicDirectory = path.join(pwd, 'public');
39
+ const filepath = path.resolve(publicDirectory, decodedPathname.replace(/^\/+/u, ''));
40
+ if (!isPathInside(filepath, publicDirectory)) return null;
41
+ try {
42
+ const [realPublicDirectory, realFilepath, stat] = await Promise.all([
43
+ fs.realpath(publicDirectory),
44
+ fs.realpath(filepath),
45
+ fs.stat(filepath)
46
+ ]);
47
+ if (!stat.isFile() || !isPathInside(realFilepath, realPublicDirectory)) return null;
48
+ return realFilepath;
49
+ } catch {
50
+ return null;
51
+ }
52
+ };
53
+ const servePublicDirectoryAsset = async (c, pwd)=>{
54
+ const method = c.req.raw.method.toUpperCase();
55
+ if ('GET' !== method && 'HEAD' !== method) return null;
56
+ const originFilename = await resolvePublicDirectoryAsset(pwd, c.req.path);
57
+ if (null === originFilename) return null;
58
+ const preCompressedAsset = await resolvePreCompressedAsset(c, originFilename);
59
+ const selectedFilename = preCompressedAsset.selected?.filepath ?? originFilename;
60
+ const publicDirectory = await fs.realpath(path.join(pwd, 'public'));
61
+ let realSelectedFilename;
62
+ try {
63
+ realSelectedFilename = await fs.realpath(selectedFilename);
64
+ } catch {
65
+ return null;
66
+ }
67
+ if (!isPathInside(realSelectedFilename, publicDirectory)) return null;
68
+ const data = await fileReader.readFileFromSystem(realSelectedFilename, 'buffer');
69
+ if (null === data) return null;
70
+ const mimeType = getStaticMimeType(originFilename);
71
+ if (mimeType) c.header('Content-Type', mimeType);
72
+ applyPreCompressedAssetHeaders(c, preCompressedAsset);
73
+ c.header('Content-Length', String(data.byteLength));
74
+ if ('HEAD' === method) return c.body(null, 200);
75
+ const body = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
76
+ return c.body(body, 200);
77
+ };
78
+ const createModuleFederationStaticServing = ({ pwd, pathPrefix })=>{
79
+ let moduleFederationAssetsPromise = null;
80
+ const getModuleFederationAssets = async ()=>{
81
+ if (!moduleFederationAssetsPromise) moduleFederationAssetsPromise = getModuleFederationAssetList(pwd);
82
+ return moduleFederationAssetsPromise;
83
+ };
84
+ const resolveRequest = async (pathname)=>{
85
+ const requestPath = getModuleFederationRequestPath(pathname, pathPrefix);
86
+ if (requestPath.includes('..')) return null;
87
+ const moduleFederationAssetMeta = await getModuleFederationAssets();
88
+ return {
89
+ requestPath,
90
+ isModuleFederationAsset: moduleFederationAssetMeta.assets.has(requestPath),
91
+ isModuleFederationRemoteEntry: moduleFederationAssetMeta.remoteEntries.has(requestPath)
92
+ };
93
+ };
94
+ const serveFile = async (c, filepath, moduleFederationAsset = false, moduleFederationRemoteEntry = false, requestPath = '')=>{
95
+ if (moduleFederationAsset) applyModuleFederationAssetHeaders(c);
96
+ const mimeType = getStaticMimeType(filepath);
97
+ if (mimeType) c.header('Content-Type', mimeType);
98
+ const shouldPatchManifest = moduleFederationAsset && isModuleFederationManifestRequest(requestPath) && !isBackendModuleFederationManifestRequest(requestPath);
99
+ const shouldPatchRemoteEntry = moduleFederationRemoteEntry;
100
+ const canUsePreCompressed = !shouldPatchManifest && !shouldPatchRemoteEntry;
101
+ const preCompressedAsset = canUsePreCompressed ? await resolvePreCompressedAsset(c, filepath) : {
102
+ selected: null,
103
+ hasVariant: false
104
+ };
105
+ const targetFilepath = preCompressedAsset.selected?.filepath ?? filepath;
106
+ const chunk = await fileReader.readFileFromSystem(targetFilepath, 'buffer');
107
+ if (null === chunk) return null;
108
+ const responseChunk = shouldPatchManifest ? patchModuleFederationManifestPublicPath(c, chunk, pathPrefix) : shouldPatchRemoteEntry ? patchModuleFederationRemoteEntryPublicPath(c, chunk, pathPrefix) : chunk;
109
+ applyPreCompressedAssetHeaders(c, preCompressedAsset);
110
+ c.header('Content-Length', String(responseChunk.byteLength));
111
+ const body = new Uint8Array(responseChunk.buffer, responseChunk.byteOffset, responseChunk.byteLength);
112
+ return c.body(body, 200);
113
+ };
114
+ const serveByPath = async (c, filepath, request, moduleFederationAsset = false, moduleFederationRemoteEntry = false)=>{
115
+ if (!isPathInside(filepath, pwd)) return null;
116
+ if (!await fs.pathExists(filepath)) return null;
117
+ return serveFile(c, filepath, moduleFederationAsset, moduleFederationRemoteEntry, request.requestPath);
118
+ };
119
+ const serveStaticHit = (c, request)=>serveByPath(c, path.join(pwd, request.requestPath), request, request.isModuleFederationAsset, request.isModuleFederationRemoteEntry);
120
+ const serveModuleFederationAsset = (c, request)=>{
121
+ if (!request.isModuleFederationAsset) return null;
122
+ return serveByPath(c, path.join(pwd, request.requestPath), request, true, request.isModuleFederationRemoteEntry);
123
+ };
124
+ return {
125
+ resolveRequest,
126
+ serveStaticHit,
127
+ serveModuleFederationAsset
128
+ };
129
+ };
130
+ export { createModuleFederationStaticServing, servePreCompressedPublicRouteAsset, servePublicDirectoryAsset };
@@ -9,10 +9,10 @@ import { renderRscHandler } from "./renderRscHandler.mjs";
9
9
  import { serverActionHandler } from "./serverActionHandler.mjs";
10
10
  import { ssrRender } from "./ssrRender.mjs";
11
11
  import { __webpack_require__ } from "../../rslib-runtime.mjs";
12
- import * as __rspack_external__modern_js_runtime_utils_router_2aa8d96f from "@modern-js/runtime-utils/router";
12
+ import * as __rspack_external__modern_js_runtime_utils_router_4aa9f9b0 from "@modern-js/runtime-utils/router";
13
13
  __webpack_require__.add({
14
14
  "@modern-js/runtime-utils/router" (module) {
15
- module.exports = __rspack_external__modern_js_runtime_utils_router_2aa8d96f;
15
+ module.exports = __rspack_external__modern_js_runtime_utils_router_4aa9f9b0;
16
16
  }
17
17
  });
18
18
  const DYNAMIC_ROUTE_REG = /\/:./;
@@ -2,11 +2,11 @@ import type { Middleware } from '../../../types';
2
2
  export declare const MODULE_FEDERATION_MANIFEST_FILE = "mf-manifest.json";
3
3
  export type ModuleFederationServeAssets = {
4
4
  assets: Set<string>;
5
- remoteEntry: string | null;
5
+ remoteEntries: Set<string>;
6
6
  };
7
- export declare const trimLeadingSlash: (value: string) => string;
8
7
  export declare const getModuleFederationRequestPath: (pathname: string, pathPrefix: string) => string;
9
- export declare const isModuleFederationManifestRequest: (requestPath: string) => requestPath is "mf-manifest.json";
8
+ export declare const isModuleFederationManifestRequest: (requestPath: string) => boolean;
9
+ export declare const isBackendModuleFederationManifestRequest: (requestPath: string) => requestPath is "backend-mf-manifest.json";
10
10
  export declare const applyModuleFederationAssetHeaders: (c: Parameters<Middleware>[0]) => void;
11
11
  export declare const patchModuleFederationManifestPublicPath: (c: Parameters<Middleware>[0], manifestBuffer: Buffer, pathPrefix: string) => Buffer<ArrayBufferLike>;
12
12
  export declare const patchModuleFederationRemoteEntryPublicPath: (c: Parameters<Middleware>[0], remoteEntryBuffer: Buffer, pathPrefix: string) => Buffer<ArrayBufferLike>;