@bleedingdev/modern-js-server-core 3.5.0-ultramodern.5 → 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.
- package/dist/cjs/adapters/node/plugins/static.js +14 -60
- package/dist/cjs/adapters/node/plugins/staticModuleFederation.js +41 -37
- package/dist/cjs/adapters/node/plugins/staticPrecompressed.js +0 -3
- package/dist/cjs/adapters/node/plugins/staticServing.js +183 -0
- package/dist/esm/adapters/node/plugins/static.mjs +14 -60
- package/dist/esm/adapters/node/plugins/staticModuleFederation.mjs +37 -33
- package/dist/esm/adapters/node/plugins/staticPrecompressed.mjs +1 -1
- package/dist/esm/adapters/node/plugins/staticServing.mjs +129 -0
- package/dist/esm/plugins/render/render.mjs +4 -4
- package/dist/esm-node/adapters/node/plugins/static.mjs +14 -60
- package/dist/esm-node/adapters/node/plugins/staticModuleFederation.mjs +37 -33
- package/dist/esm-node/adapters/node/plugins/staticPrecompressed.mjs +1 -1
- package/dist/esm-node/adapters/node/plugins/staticServing.mjs +130 -0
- package/dist/esm-node/plugins/render/render.mjs +2 -2
- package/dist/types/adapters/node/plugins/staticModuleFederation.d.ts +3 -3
- package/dist/types/adapters/node/plugins/staticPrecompressed.d.ts +1 -2
- package/dist/types/adapters/node/plugins/staticServing.d.ts +25 -0
- package/package.json +12 -15
|
@@ -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
|
|
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 (
|
|
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
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
97
|
+
remoteEntries
|
|
94
98
|
};
|
|
95
99
|
};
|
|
96
|
-
export { MODULE_FEDERATION_MANIFEST_FILE, applyModuleFederationAssetHeaders, getModuleFederationAssetList, getModuleFederationRequestPath, isModuleFederationManifestRequest, patchModuleFederationManifestPublicPath, patchModuleFederationRemoteEntryPublicPath
|
|
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 {
|
|
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
|
|
11
|
+
import * as __rspack_external__modern_js_runtime_utils_router_4aa9f9b0 from "@modern-js/runtime-utils/router";
|
|
12
12
|
__webpack_require__.add({
|
|
13
|
-
|
|
14
|
-
module.exports =
|
|
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__(
|
|
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 {
|
|
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
|
|
35
|
-
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
|
131
|
-
if (
|
|
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
|
|
96
|
+
const response = await moduleFederationStaticServing.serveStaticHit(c, staticServingRequest);
|
|
141
97
|
if (null !== response) return response;
|
|
142
98
|
return next();
|
|
143
99
|
}
|
|
144
|
-
|
|
145
|
-
|
|
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
|
|
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 (
|
|
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
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
98
|
+
remoteEntries
|
|
95
99
|
};
|
|
96
100
|
};
|
|
97
|
-
export { MODULE_FEDERATION_MANIFEST_FILE, applyModuleFederationAssetHeaders, getModuleFederationAssetList, getModuleFederationRequestPath, isModuleFederationManifestRequest, patchModuleFederationManifestPublicPath, patchModuleFederationRemoteEntryPublicPath
|
|
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 {
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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) =>
|
|
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>;
|