@bleedingdev/modern-js-server-core 3.9.0-ultramodern.1 → 3.9.0-ultramodern.11
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/index.js +4 -0
- package/dist/cjs/adapters/node/plugins/static.js +107 -29
- package/dist/cjs/index.js +10 -34
- package/dist/cjs/plugins/compat/index.js +1 -0
- package/dist/cjs/plugins/render/ssrCache.js +51 -29
- package/dist/cjs/serverBase.js +58 -17
- package/dist/cjs/utils/error.js +0 -10
- package/dist/esm/adapters/node/index.mjs +1 -0
- package/dist/esm/adapters/node/plugins/static.mjs +107 -32
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/plugins/compat/index.mjs +1 -0
- package/dist/esm/plugins/render/ssrCache.mjs +51 -29
- package/dist/esm/serverBase.mjs +58 -17
- package/dist/esm/utils/error.mjs +0 -1
- package/dist/esm-node/adapters/node/index.mjs +1 -0
- package/dist/esm-node/adapters/node/plugins/static.mjs +107 -32
- package/dist/esm-node/index.mjs +1 -1
- package/dist/esm-node/plugins/compat/index.mjs +1 -0
- package/dist/esm-node/plugins/render/ssrCache.mjs +51 -29
- package/dist/esm-node/serverBase.mjs +58 -17
- package/dist/esm-node/utils/error.mjs +0 -1
- package/dist/types/adapters/node/index.d.ts +2 -0
- package/dist/types/adapters/node/plugins/static.d.ts +42 -4
- package/dist/types/index.d.ts +1 -2
- package/dist/types/serverBase.d.ts +7 -1
- package/dist/types/types/config/bff.d.ts +4 -15
- package/dist/types/types/config/server.d.ts +1 -3
- package/dist/types/types/plugins/plugin.d.ts +15 -1
- package/dist/types/types/requestHandler.d.ts +2 -3
- package/dist/types/utils/error.d.ts +0 -2
- package/package.json +7 -8
- package/dist/cjs/adapters/node/plugins/staticModuleFederation.js +0 -173
- package/dist/cjs/adapters/node/plugins/staticPrecompressed.js +0 -152
- package/dist/cjs/adapters/node/plugins/staticServing.js +0 -206
- package/dist/cjs/types/config/bffRuntime.js +0 -18
- package/dist/cjs/types/config/serverTelemetry.js +0 -18
- package/dist/esm/adapters/node/plugins/staticModuleFederation.mjs +0 -104
- package/dist/esm/adapters/node/plugins/staticPrecompressed.mjs +0 -111
- package/dist/esm/adapters/node/plugins/staticServing.mjs +0 -152
- package/dist/esm/types/config/bffRuntime.mjs +0 -0
- package/dist/esm/types/config/serverTelemetry.mjs +0 -0
- package/dist/esm-node/adapters/node/plugins/staticModuleFederation.mjs +0 -105
- package/dist/esm-node/adapters/node/plugins/staticPrecompressed.mjs +0 -112
- package/dist/esm-node/adapters/node/plugins/staticServing.mjs +0 -153
- package/dist/esm-node/types/config/bffRuntime.mjs +0 -1
- package/dist/esm-node/types/config/serverTelemetry.mjs +0 -1
- package/dist/types/adapters/node/plugins/staticModuleFederation.d.ts +0 -13
- package/dist/types/adapters/node/plugins/staticPrecompressed.d.ts +0 -13
- package/dist/types/adapters/node/plugins/staticServing.d.ts +0 -25
- package/dist/types/types/config/bffRuntime.d.ts +0 -116
- package/dist/types/types/config/serverTelemetry.d.ts +0 -319
package/dist/cjs/utils/error.js
CHANGED
|
@@ -30,12 +30,8 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
30
30
|
__webpack_require__.d(__webpack_exports__, {
|
|
31
31
|
ErrorDigest: ()=>error_ErrorDigest,
|
|
32
32
|
createErrorHtml: ()=>createErrorHtml,
|
|
33
|
-
createSafeFailureHttpResult: ()=>safe_failure_namespaceObject.createSafeFailureHttpResult,
|
|
34
|
-
createSafeJsonFailureResponse: ()=>safe_failure_namespaceObject.createSafeJsonFailureResponse,
|
|
35
|
-
getSafeFailureStatus: ()=>safe_failure_namespaceObject.getSafeFailureStatus,
|
|
36
33
|
onError: ()=>onError
|
|
37
34
|
});
|
|
38
|
-
const safe_failure_namespaceObject = require("@modern-js/runtime-extensions/safe-failure");
|
|
39
35
|
const external_request_js_namespaceObject = require("./request.js");
|
|
40
36
|
const ERROR_PAGE_TEXT = {
|
|
41
37
|
404: 'This page could not be found.',
|
|
@@ -90,16 +86,10 @@ function onError(digest, error, monitors, req) {
|
|
|
90
86
|
}
|
|
91
87
|
exports.ErrorDigest = __webpack_exports__.ErrorDigest;
|
|
92
88
|
exports.createErrorHtml = __webpack_exports__.createErrorHtml;
|
|
93
|
-
exports.createSafeFailureHttpResult = __webpack_exports__.createSafeFailureHttpResult;
|
|
94
|
-
exports.createSafeJsonFailureResponse = __webpack_exports__.createSafeJsonFailureResponse;
|
|
95
|
-
exports.getSafeFailureStatus = __webpack_exports__.getSafeFailureStatus;
|
|
96
89
|
exports.onError = __webpack_exports__.onError;
|
|
97
90
|
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
98
91
|
"ErrorDigest",
|
|
99
92
|
"createErrorHtml",
|
|
100
|
-
"createSafeFailureHttpResult",
|
|
101
|
-
"createSafeJsonFailureResponse",
|
|
102
|
-
"getSafeFailureStatus",
|
|
103
93
|
"onError"
|
|
104
94
|
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
105
95
|
Object.defineProperty(exports, '__esModule', {
|
|
@@ -2,3 +2,4 @@ export { loadCacheConfig, loadServerCliConfig, loadServerEnv, loadServerPlugins,
|
|
|
2
2
|
export { connectMid2HonoMid, connectMockMid2HonoMid, httpCallBack2HonoMid } from "./hono.mjs";
|
|
3
3
|
export { createNodeServer, createWebRequest, sendResponse } from "./node.mjs";
|
|
4
4
|
export { getHtmlTemplates, getServerManifest, injectNodeSeverPlugin, injectResourcePlugin, injectRscManifestPlugin, serverStaticPlugin } from "./plugins/index.mjs";
|
|
5
|
+
export { serveStaticAsset } from "./plugins/static.mjs";
|
|
@@ -1,8 +1,55 @@
|
|
|
1
|
-
import
|
|
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";
|
|
2
5
|
import { sortRoutes } from "../../../utils/index.mjs";
|
|
3
6
|
import { getPublicDirPatterns } from "../../../utils/publicDir.mjs";
|
|
4
|
-
|
|
5
|
-
const
|
|
7
|
+
const isPathInside = (target, root)=>{
|
|
8
|
+
const relative = path.relative(path.resolve(root), path.resolve(target));
|
|
9
|
+
return '' === relative || !relative.startsWith(`..${path.sep}`) && '..' !== relative && !path.isAbsolute(relative);
|
|
10
|
+
};
|
|
11
|
+
async function serveStaticAsset(context, asset, respond) {
|
|
12
|
+
let { filename } = asset;
|
|
13
|
+
const { kind } = asset;
|
|
14
|
+
if (asset.root && !isPathInside(filename, asset.root)) return null;
|
|
15
|
+
if (asset.realpath) try {
|
|
16
|
+
const [realFilename, realRoot, stat] = await Promise.all([
|
|
17
|
+
fs.realpath(filename),
|
|
18
|
+
asset.root ? fs.realpath(asset.root) : Promise.resolve(void 0),
|
|
19
|
+
fs.stat(filename)
|
|
20
|
+
]);
|
|
21
|
+
if (!stat.isFile() || realRoot && !isPathInside(realFilename, realRoot)) return null;
|
|
22
|
+
filename = realFilename;
|
|
23
|
+
asset = {
|
|
24
|
+
...asset,
|
|
25
|
+
filename,
|
|
26
|
+
root: realRoot
|
|
27
|
+
};
|
|
28
|
+
} catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
if ('static' === kind && !await fs.pathExists(filename)) return null;
|
|
32
|
+
if (respond) return respond(asset, (representation)=>serveStaticAsset(context, {
|
|
33
|
+
...asset,
|
|
34
|
+
...representation,
|
|
35
|
+
filename: representation?.filename ?? asset.filename,
|
|
36
|
+
mimeFilename: asset.mimeFilename ?? filename
|
|
37
|
+
}));
|
|
38
|
+
const mimeType = getMimeType(asset.mimeFilename ?? filename);
|
|
39
|
+
if ('static' === kind && mimeType) context.header('Content-Type', mimeType);
|
|
40
|
+
const size = 'static' === kind ? (await fs.lstat(filename)).size : void 0;
|
|
41
|
+
const data = 'static' === kind ? await fileReader.readFileFromSystem(filename, 'buffer') : await fileReader.readFile(filename, 'buffer');
|
|
42
|
+
if ('static' === kind && (asset.contentLength ?? true)) context.header('Content-Length', String(true === asset.contentLength && data ? data.byteLength : size));
|
|
43
|
+
if (null === data) return null;
|
|
44
|
+
if ('public' === kind && mimeType) context.header('Content-Type', mimeType);
|
|
45
|
+
Object.entries(asset.responseHeaders || {}).forEach(([key, value])=>{
|
|
46
|
+
context.header(key, value);
|
|
47
|
+
});
|
|
48
|
+
if ('public' === kind && asset.contentLength) context.header('Content-Length', String(data.byteLength));
|
|
49
|
+
const body = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
50
|
+
return context.body(body, 200);
|
|
51
|
+
}
|
|
52
|
+
const serverStaticPlugin = (options = {})=>({
|
|
6
53
|
name: '@modern-js/plugin-server-static',
|
|
7
54
|
setup (api) {
|
|
8
55
|
api.onPrepare(()=>{
|
|
@@ -13,7 +60,9 @@ const serverStaticPlugin = ()=>({
|
|
|
13
60
|
routes,
|
|
14
61
|
output: config.output || {},
|
|
15
62
|
html: config.html || {},
|
|
16
|
-
server: config.server || {}
|
|
63
|
+
server: config.server || {},
|
|
64
|
+
...api.getServerContext().staticAssetResponders,
|
|
65
|
+
...options
|
|
17
66
|
});
|
|
18
67
|
middlewares.push({
|
|
19
68
|
name: 'server-static',
|
|
@@ -22,16 +71,33 @@ const serverStaticPlugin = ()=>({
|
|
|
22
71
|
});
|
|
23
72
|
}
|
|
24
73
|
});
|
|
25
|
-
function createPublicMiddleware({ pwd, routes }) {
|
|
74
|
+
function createPublicMiddleware({ pwd, routes, pathPrefix = '/', respondAsset, respondPublicFallback }) {
|
|
26
75
|
return async (c, next)=>{
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
76
|
+
const respondPublic = async ()=>{
|
|
77
|
+
const route = matchPublicRoute(c.req, routes);
|
|
78
|
+
if (!route) return null;
|
|
79
|
+
const asset = {
|
|
80
|
+
filename: path.join(pwd, route.entryPath),
|
|
81
|
+
kind: 'public',
|
|
82
|
+
responseHeaders: route.responseHeaders
|
|
83
|
+
};
|
|
84
|
+
const serve = (representation)=>serveStaticAsset(c, {
|
|
85
|
+
...asset,
|
|
86
|
+
...representation,
|
|
87
|
+
filename: representation?.filename ?? asset.filename,
|
|
88
|
+
mimeFilename: asset.filename
|
|
89
|
+
});
|
|
90
|
+
const response = respondAsset ? await respondAsset(c, asset, serve, {
|
|
91
|
+
root: pwd,
|
|
92
|
+
pathPrefix
|
|
93
|
+
}) : void 0;
|
|
94
|
+
return void 0 === response ? serve() : response;
|
|
95
|
+
};
|
|
96
|
+
const response = respondPublicFallback ? await respondPublicFallback(c, respondPublic, {
|
|
97
|
+
root: pwd,
|
|
98
|
+
pathPrefix
|
|
99
|
+
}) : await respondPublic();
|
|
100
|
+
return response ?? next();
|
|
35
101
|
};
|
|
36
102
|
}
|
|
37
103
|
function matchPublicRoute(req, routes) {
|
|
@@ -76,29 +142,38 @@ function createStaticMiddleware(options) {
|
|
|
76
142
|
...staticReg,
|
|
77
143
|
...iconReg
|
|
78
144
|
].join('|')})`);
|
|
79
|
-
const publicMiddleware = createPublicMiddleware({
|
|
80
|
-
pwd,
|
|
81
|
-
routes: routes || []
|
|
82
|
-
});
|
|
83
|
-
const moduleFederationStaticServing = createModuleFederationStaticServing({
|
|
84
|
-
pwd,
|
|
85
|
-
pathPrefix
|
|
86
|
-
});
|
|
87
145
|
return async (c, next)=>{
|
|
88
146
|
const pageRoute = c.get('route');
|
|
89
147
|
const pathname = c.req.path;
|
|
90
|
-
if (pageRoute && '' ===
|
|
148
|
+
if (pageRoute && '' === path.extname(pathname)) return next();
|
|
91
149
|
const hit = staticPathRegExp.test(pathname);
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
150
|
+
if (!hit) return createPublicMiddleware({
|
|
151
|
+
pwd,
|
|
152
|
+
routes: routes || [],
|
|
153
|
+
pathPrefix,
|
|
154
|
+
respondAsset: options.respondAsset,
|
|
155
|
+
respondPublicFallback: options.respondPublicFallback
|
|
156
|
+
})(c, next);
|
|
157
|
+
{
|
|
158
|
+
const filepath = path.join(pwd, pathname.replace(pathPrefix, ()=>''));
|
|
159
|
+
if (!isPathInside(filepath, pwd)) return next();
|
|
160
|
+
if (!await fs.pathExists(filepath)) return next();
|
|
161
|
+
const asset = {
|
|
162
|
+
filename: filepath,
|
|
163
|
+
kind: 'static'
|
|
164
|
+
};
|
|
165
|
+
const serve = (representation)=>serveStaticAsset(c, {
|
|
166
|
+
...asset,
|
|
167
|
+
...representation,
|
|
168
|
+
filename: representation?.filename ?? asset.filename,
|
|
169
|
+
mimeFilename: filepath
|
|
170
|
+
});
|
|
171
|
+
const response = options.respondAsset ? await options.respondAsset(c, asset, serve, {
|
|
172
|
+
root: pwd,
|
|
173
|
+
pathPrefix
|
|
174
|
+
}) : void 0;
|
|
175
|
+
return (void 0 === response ? await serve() : response) ?? next();
|
|
98
176
|
}
|
|
99
|
-
const moduleFederationResponse = await moduleFederationStaticServing.serveModuleFederationAsset(c, staticServingRequest);
|
|
100
|
-
if (null !== moduleFederationResponse) return moduleFederationResponse;
|
|
101
|
-
return publicMiddleware(c, next);
|
|
102
177
|
};
|
|
103
178
|
}
|
|
104
179
|
const prepareFavicons = (favicon)=>{
|
|
@@ -106,4 +181,4 @@ const prepareFavicons = (favicon)=>{
|
|
|
106
181
|
if (favicon && 'string' == typeof favicon) faviconNames.push(favicon.substring(favicon.lastIndexOf('/') + 1));
|
|
107
182
|
return faviconNames;
|
|
108
183
|
};
|
|
109
|
-
export { createPublicMiddleware, createStaticMiddleware, serverStaticPlugin };
|
|
184
|
+
export { createPublicMiddleware, createStaticMiddleware, serveStaticAsset, serverStaticPlugin };
|
package/dist/esm/index.mjs
CHANGED
|
@@ -8,5 +8,5 @@ export { AGGRED_DIR } from "./constants.mjs";
|
|
|
8
8
|
export { run, useHonoContext } from "./context.mjs";
|
|
9
9
|
export { getLoaderCtx } from "./helper.mjs";
|
|
10
10
|
export { createServerBase } from "./serverBase.mjs";
|
|
11
|
-
export { ErrorDigest, createErrorHtml,
|
|
11
|
+
export { ErrorDigest, createErrorHtml, onError } from "./utils/index.mjs";
|
|
12
12
|
export { getPublicDirConfig, getPublicDirPatterns, getPublicDirRoutePrefixes, normalizePublicDir, normalizePublicDirPath, resolvePublicDirPaths } from "./utils/publicDir.mjs";
|
|
@@ -3,6 +3,7 @@ import { getHookRunners, handleSetupResult } from "./hooks.mjs";
|
|
|
3
3
|
const compatPlugin = ()=>({
|
|
4
4
|
name: '@modern-js/server-compat',
|
|
5
5
|
registryHooks: {
|
|
6
|
+
handleError: createAsyncPipelineHook(),
|
|
6
7
|
prepareWebServer: createAsyncPipelineHook(),
|
|
7
8
|
prepareApiServer: createAsyncPipelineHook(),
|
|
8
9
|
afterMatch: createAsyncPipelineHook(),
|
|
@@ -1,54 +1,67 @@
|
|
|
1
1
|
import { createMemoryStorage } from "@modern-js/runtime-utils/storer";
|
|
2
2
|
import { X_RENDER_CACHE } from "../../constants.mjs";
|
|
3
3
|
import { createTransformStream, getPathname } from "../../utils/index.mjs";
|
|
4
|
+
const preventsSharedCaching = (headers)=>/(?:^|,)\s*(?:private|no-store|no-cache)\s*(?:[=,]|$)/i.test(headers.get('cache-control') || '');
|
|
5
|
+
const isCacheableResponse = (response)=>200 === response.status && !preventsSharedCaching(response.headers) && !response.headers.has('set-cookie') && !response.headers.get('vary');
|
|
4
6
|
const removeTailSlash = (s)=>s.replace(/\/+$/, '');
|
|
5
7
|
const ZERO_RENDER_LEVEL = /"renderLevel":0/;
|
|
6
8
|
const NO_SSR_CACHE = /<meta\s+[^>]*name=["']no-ssr-cache["'][^>]*>/i;
|
|
7
9
|
async function processCache({ request, key, requestHandler, requestHandlerOptions, ttl, container, cacheStatus }) {
|
|
8
10
|
const response = await requestHandler(request, requestHandlerOptions);
|
|
9
11
|
const { onError } = requestHandlerOptions;
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
12
|
+
const deleteCache = async ()=>{
|
|
13
|
+
try {
|
|
14
|
+
await container.delete(key);
|
|
15
|
+
} catch {
|
|
16
|
+
(onError || console.error)('[render-cache] delete cache failed');
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
if (!isCacheableResponse(response) || !response.body) {
|
|
20
|
+
await deleteCache();
|
|
21
|
+
return response;
|
|
22
|
+
}
|
|
23
|
+
const headers = Object.fromEntries(response.headers);
|
|
22
24
|
const decoder = new TextDecoder();
|
|
23
25
|
if (response.body) {
|
|
24
26
|
const stream = createTransformStream();
|
|
25
27
|
const reader = response.body.getReader();
|
|
26
28
|
const writer = stream.writable.getWriter();
|
|
27
29
|
let html = '';
|
|
28
|
-
const push = ()=>reader.read().then(({ done, value })=>{
|
|
30
|
+
const push = ()=>reader.read().then(async ({ done, value })=>{
|
|
29
31
|
if (done) {
|
|
32
|
+
html += decoder.decode();
|
|
30
33
|
const match = ZERO_RENDER_LEVEL.test(html) || NO_SSR_CACHE.test(html);
|
|
31
|
-
if (match)
|
|
34
|
+
if (match) {
|
|
35
|
+
await deleteCache();
|
|
36
|
+
return writer.close();
|
|
37
|
+
}
|
|
32
38
|
const current = Date.now();
|
|
33
39
|
const cache = {
|
|
34
40
|
val: html,
|
|
35
|
-
cursor: current
|
|
41
|
+
cursor: current,
|
|
42
|
+
headers
|
|
36
43
|
};
|
|
37
44
|
container.set(key, JSON.stringify(cache), {
|
|
38
45
|
ttl
|
|
39
46
|
}).catch(()=>{
|
|
40
|
-
|
|
41
|
-
else console.error(`[render-cache] set cache failed, key: ${key}, value: ${JSON.stringify(cache)}`);
|
|
47
|
+
(onError || console.error)('[render-cache] set cache failed');
|
|
42
48
|
});
|
|
43
|
-
writer.close();
|
|
44
|
-
return;
|
|
49
|
+
return writer.close();
|
|
45
50
|
}
|
|
46
|
-
const content = decoder.decode(value
|
|
51
|
+
const content = decoder.decode(value, {
|
|
52
|
+
stream: true
|
|
53
|
+
});
|
|
47
54
|
html += content;
|
|
48
|
-
writer.write(value);
|
|
49
|
-
push();
|
|
55
|
+
await writer.write(value);
|
|
56
|
+
return push();
|
|
50
57
|
});
|
|
51
|
-
push()
|
|
58
|
+
push().catch(async (error)=>{
|
|
59
|
+
await Promise.allSettled([
|
|
60
|
+
writer.abort(error),
|
|
61
|
+
reader.cancel(error)
|
|
62
|
+
]);
|
|
63
|
+
(onError || console.error)('[render-cache] response stream failed');
|
|
64
|
+
});
|
|
52
65
|
cacheStatus && response.headers.set(X_RENDER_CACHE, cacheStatus);
|
|
53
66
|
return new Response(stream.readable, {
|
|
54
67
|
status: response.status,
|
|
@@ -63,9 +76,12 @@ function computedKey(req, cacheControl) {
|
|
|
63
76
|
const pathname = getPathname(req);
|
|
64
77
|
const { customKey } = cacheControl;
|
|
65
78
|
const defaultKey = '/' === pathname ? pathname : removeTailSlash(pathname);
|
|
66
|
-
if (
|
|
67
|
-
|
|
68
|
-
|
|
79
|
+
if (customKey) if ('string' == typeof customKey) return customKey;
|
|
80
|
+
else return customKey(defaultKey);
|
|
81
|
+
{
|
|
82
|
+
const url = new URL(req.url);
|
|
83
|
+
return `${url.origin}${defaultKey}${url.search}`;
|
|
84
|
+
}
|
|
69
85
|
}
|
|
70
86
|
function shouldUseCache(request) {
|
|
71
87
|
const url = new URL(request.url);
|
|
@@ -95,13 +111,15 @@ function matchCacheControl(cacheOption, req) {
|
|
|
95
111
|
async function getCacheResult(request, options) {
|
|
96
112
|
const { cacheControl, container = storage, requestHandler, requestHandlerOptions } = options;
|
|
97
113
|
const { onError } = requestHandlerOptions;
|
|
98
|
-
const
|
|
114
|
+
const hasCredentials = request.headers.has('cookie') || request.headers.has('authorization');
|
|
115
|
+
if ('GET' !== request.method || !shouldUseCache(request) || preventsSharedCaching(request.headers) || hasCredentials && !cacheControl.customKey) return requestHandler(request, requestHandlerOptions);
|
|
116
|
+
const key = `${CACHE_NAMESPACE}:v2:${computedKey(request, cacheControl)}`;
|
|
99
117
|
let value;
|
|
100
118
|
try {
|
|
101
119
|
value = await container.get(key);
|
|
102
120
|
} catch (_) {
|
|
103
|
-
if (onError) onError(
|
|
104
|
-
else console.error(
|
|
121
|
+
if (onError) onError('[render-cache] get cache failed');
|
|
122
|
+
else console.error('[render-cache] get cache failed');
|
|
105
123
|
value = void 0;
|
|
106
124
|
}
|
|
107
125
|
const { maxAge, staleWhileRevalidate } = cacheControl;
|
|
@@ -122,6 +140,7 @@ async function getCacheResult(request, options) {
|
|
|
122
140
|
const cacheStatus = 'hit';
|
|
123
141
|
return new Response(cache.val, {
|
|
124
142
|
headers: {
|
|
143
|
+
...cache.headers,
|
|
125
144
|
[X_RENDER_CACHE]: cacheStatus
|
|
126
145
|
}
|
|
127
146
|
});
|
|
@@ -145,10 +164,13 @@ async function getCacheResult(request, options) {
|
|
|
145
164
|
container
|
|
146
165
|
}).then(async (response)=>{
|
|
147
166
|
await response.text();
|
|
167
|
+
}).catch(()=>{
|
|
168
|
+
(onError || console.error)('[render-cache] revalidation failed');
|
|
148
169
|
});
|
|
149
170
|
const cacheStatus = 'stale';
|
|
150
171
|
return new Response(cache.val, {
|
|
151
172
|
headers: {
|
|
173
|
+
...cache.headers,
|
|
152
174
|
[X_RENDER_CACHE]: cacheStatus
|
|
153
175
|
}
|
|
154
176
|
});
|
package/dist/esm/serverBase.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { server as server_server } from "@modern-js/plugin/server";
|
|
2
|
+
import { logger } from "@modern-js/utils";
|
|
2
3
|
import { Hono } from "hono";
|
|
3
4
|
import { run } from "./context.mjs";
|
|
4
5
|
import { handleSetupResult } from "./plugins/compat/hooks.mjs";
|
|
@@ -17,22 +18,54 @@ function _class_private_method_init(obj, privateSet) {
|
|
|
17
18
|
var _applyMiddlewares = /*#__PURE__*/ new WeakSet();
|
|
18
19
|
class ServerBase {
|
|
19
20
|
async init() {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
21
|
+
try {
|
|
22
|
+
const { serverConfig, config: cliConfig } = this.serverOptions;
|
|
23
|
+
const mergedConfig = loadConfig({
|
|
24
|
+
cliConfig,
|
|
25
|
+
serverConfig: serverConfig || {}
|
|
26
|
+
});
|
|
27
|
+
const { serverContext } = await server_server.run({
|
|
28
|
+
plugins: this.plugins,
|
|
29
|
+
options: this.serverOptions,
|
|
30
|
+
config: mergedConfig,
|
|
31
|
+
handleSetupResult: handleSetupResult
|
|
32
|
+
});
|
|
33
|
+
serverContext.serverBase = this;
|
|
34
|
+
this.serverContext = serverContext;
|
|
35
|
+
await serverContext.hooks.onPrepare.call();
|
|
36
|
+
_class_private_method_get(this, _applyMiddlewares, applyMiddlewares).call(this);
|
|
37
|
+
return this;
|
|
38
|
+
} catch (error) {
|
|
39
|
+
await this.dispose().catch((disposeError)=>{
|
|
40
|
+
logger.error(disposeError);
|
|
41
|
+
});
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
onDispose(disposer) {
|
|
46
|
+
if (this.disposePromise) throw new Error('Cannot register a disposer on a retired server.');
|
|
47
|
+
this.disposers.add(disposer);
|
|
48
|
+
return ()=>{
|
|
49
|
+
this.disposers.delete(disposer);
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
dispose() {
|
|
53
|
+
if (!this.disposePromise) {
|
|
54
|
+
const disposers = [
|
|
55
|
+
...this.disposers
|
|
56
|
+
].reverse();
|
|
57
|
+
this.disposers.clear();
|
|
58
|
+
this.disposePromise = Promise.resolve().then(async ()=>{
|
|
59
|
+
const errors = [];
|
|
60
|
+
for (const disposer of disposers)try {
|
|
61
|
+
await disposer();
|
|
62
|
+
} catch (error) {
|
|
63
|
+
errors.push(error);
|
|
64
|
+
}
|
|
65
|
+
if (errors.length > 0) throw new AggregateError(errors, 'Failed to dispose server.');
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return this.disposePromise;
|
|
36
69
|
}
|
|
37
70
|
addPlugins(plugins) {
|
|
38
71
|
this.plugins.push(...plugins);
|
|
@@ -78,7 +111,15 @@ class ServerBase {
|
|
|
78
111
|
}
|
|
79
112
|
constructor(options){
|
|
80
113
|
_class_private_method_init(this, _applyMiddlewares);
|
|
81
|
-
this.plugins = [
|
|
114
|
+
this.plugins = [
|
|
115
|
+
{
|
|
116
|
+
name: '@modern-js/server-lifecycle',
|
|
117
|
+
_registryApi: ()=>({
|
|
118
|
+
onDispose: (disposer)=>this.onDispose(disposer)
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
];
|
|
122
|
+
this.disposers = new Set();
|
|
82
123
|
this.serverContext = null;
|
|
83
124
|
this.serverOptions = options;
|
|
84
125
|
this.app = new Hono();
|
package/dist/esm/utils/error.mjs
CHANGED
|
@@ -50,5 +50,4 @@ function onError(digest, error, monitors, req) {
|
|
|
50
50
|
else if (req) console.error(`Server Error - ${digest}, error = ${error instanceof Error ? error.stack || error.message : error}, req.url = ${req.url}, req.headers = ${JSON.stringify(headerData)}`);
|
|
51
51
|
else console.error(`Server Error - ${digest}, error = ${error instanceof Error ? error.stack || error.message : error} `);
|
|
52
52
|
}
|
|
53
|
-
export { createSafeFailureHttpResult, createSafeJsonFailureResponse, getSafeFailureStatus } from "@modern-js/runtime-extensions/safe-failure";
|
|
54
53
|
export { createErrorHtml, error_ErrorDigest as ErrorDigest, onError };
|
|
@@ -3,3 +3,4 @@ export { loadCacheConfig, loadServerCliConfig, loadServerEnv, loadServerPlugins,
|
|
|
3
3
|
export { connectMid2HonoMid, connectMockMid2HonoMid, httpCallBack2HonoMid } from "./hono.mjs";
|
|
4
4
|
export { createNodeServer, createWebRequest, sendResponse } from "./node.mjs";
|
|
5
5
|
export { getHtmlTemplates, getServerManifest, injectNodeSeverPlugin, injectResourcePlugin, injectRscManifestPlugin, serverStaticPlugin } from "./plugins/index.mjs";
|
|
6
|
+
export { serveStaticAsset } from "./plugins/static.mjs";
|
|
@@ -1,9 +1,56 @@
|
|
|
1
1
|
import "node:module";
|
|
2
|
-
import
|
|
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";
|
|
3
6
|
import { sortRoutes } from "../../../utils/index.mjs";
|
|
4
7
|
import { getPublicDirPatterns } from "../../../utils/publicDir.mjs";
|
|
5
|
-
|
|
6
|
-
const
|
|
8
|
+
const isPathInside = (target, root)=>{
|
|
9
|
+
const relative = path.relative(path.resolve(root), path.resolve(target));
|
|
10
|
+
return '' === relative || !relative.startsWith(`..${path.sep}`) && '..' !== relative && !path.isAbsolute(relative);
|
|
11
|
+
};
|
|
12
|
+
async function serveStaticAsset(context, asset, respond) {
|
|
13
|
+
let { filename } = asset;
|
|
14
|
+
const { kind } = asset;
|
|
15
|
+
if (asset.root && !isPathInside(filename, asset.root)) return null;
|
|
16
|
+
if (asset.realpath) try {
|
|
17
|
+
const [realFilename, realRoot, stat] = await Promise.all([
|
|
18
|
+
fs.realpath(filename),
|
|
19
|
+
asset.root ? fs.realpath(asset.root) : Promise.resolve(void 0),
|
|
20
|
+
fs.stat(filename)
|
|
21
|
+
]);
|
|
22
|
+
if (!stat.isFile() || realRoot && !isPathInside(realFilename, realRoot)) return null;
|
|
23
|
+
filename = realFilename;
|
|
24
|
+
asset = {
|
|
25
|
+
...asset,
|
|
26
|
+
filename,
|
|
27
|
+
root: realRoot
|
|
28
|
+
};
|
|
29
|
+
} catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
if ('static' === kind && !await fs.pathExists(filename)) return null;
|
|
33
|
+
if (respond) return respond(asset, (representation)=>serveStaticAsset(context, {
|
|
34
|
+
...asset,
|
|
35
|
+
...representation,
|
|
36
|
+
filename: representation?.filename ?? asset.filename,
|
|
37
|
+
mimeFilename: asset.mimeFilename ?? filename
|
|
38
|
+
}));
|
|
39
|
+
const mimeType = getMimeType(asset.mimeFilename ?? filename);
|
|
40
|
+
if ('static' === kind && mimeType) context.header('Content-Type', mimeType);
|
|
41
|
+
const size = 'static' === kind ? (await fs.lstat(filename)).size : void 0;
|
|
42
|
+
const data = 'static' === kind ? await fileReader.readFileFromSystem(filename, 'buffer') : await fileReader.readFile(filename, 'buffer');
|
|
43
|
+
if ('static' === kind && (asset.contentLength ?? true)) context.header('Content-Length', String(true === asset.contentLength && data ? data.byteLength : size));
|
|
44
|
+
if (null === data) return null;
|
|
45
|
+
if ('public' === kind && mimeType) context.header('Content-Type', mimeType);
|
|
46
|
+
Object.entries(asset.responseHeaders || {}).forEach(([key, value])=>{
|
|
47
|
+
context.header(key, value);
|
|
48
|
+
});
|
|
49
|
+
if ('public' === kind && asset.contentLength) context.header('Content-Length', String(data.byteLength));
|
|
50
|
+
const body = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
51
|
+
return context.body(body, 200);
|
|
52
|
+
}
|
|
53
|
+
const serverStaticPlugin = (options = {})=>({
|
|
7
54
|
name: '@modern-js/plugin-server-static',
|
|
8
55
|
setup (api) {
|
|
9
56
|
api.onPrepare(()=>{
|
|
@@ -14,7 +61,9 @@ const serverStaticPlugin = ()=>({
|
|
|
14
61
|
routes,
|
|
15
62
|
output: config.output || {},
|
|
16
63
|
html: config.html || {},
|
|
17
|
-
server: config.server || {}
|
|
64
|
+
server: config.server || {},
|
|
65
|
+
...api.getServerContext().staticAssetResponders,
|
|
66
|
+
...options
|
|
18
67
|
});
|
|
19
68
|
middlewares.push({
|
|
20
69
|
name: 'server-static',
|
|
@@ -23,16 +72,33 @@ const serverStaticPlugin = ()=>({
|
|
|
23
72
|
});
|
|
24
73
|
}
|
|
25
74
|
});
|
|
26
|
-
function createPublicMiddleware({ pwd, routes }) {
|
|
75
|
+
function createPublicMiddleware({ pwd, routes, pathPrefix = '/', respondAsset, respondPublicFallback }) {
|
|
27
76
|
return async (c, next)=>{
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
77
|
+
const respondPublic = async ()=>{
|
|
78
|
+
const route = matchPublicRoute(c.req, routes);
|
|
79
|
+
if (!route) return null;
|
|
80
|
+
const asset = {
|
|
81
|
+
filename: path.join(pwd, route.entryPath),
|
|
82
|
+
kind: 'public',
|
|
83
|
+
responseHeaders: route.responseHeaders
|
|
84
|
+
};
|
|
85
|
+
const serve = (representation)=>serveStaticAsset(c, {
|
|
86
|
+
...asset,
|
|
87
|
+
...representation,
|
|
88
|
+
filename: representation?.filename ?? asset.filename,
|
|
89
|
+
mimeFilename: asset.filename
|
|
90
|
+
});
|
|
91
|
+
const response = respondAsset ? await respondAsset(c, asset, serve, {
|
|
92
|
+
root: pwd,
|
|
93
|
+
pathPrefix
|
|
94
|
+
}) : void 0;
|
|
95
|
+
return void 0 === response ? serve() : response;
|
|
96
|
+
};
|
|
97
|
+
const response = respondPublicFallback ? await respondPublicFallback(c, respondPublic, {
|
|
98
|
+
root: pwd,
|
|
99
|
+
pathPrefix
|
|
100
|
+
}) : await respondPublic();
|
|
101
|
+
return response ?? next();
|
|
36
102
|
};
|
|
37
103
|
}
|
|
38
104
|
function matchPublicRoute(req, routes) {
|
|
@@ -77,29 +143,38 @@ function createStaticMiddleware(options) {
|
|
|
77
143
|
...staticReg,
|
|
78
144
|
...iconReg
|
|
79
145
|
].join('|')})`);
|
|
80
|
-
const publicMiddleware = createPublicMiddleware({
|
|
81
|
-
pwd,
|
|
82
|
-
routes: routes || []
|
|
83
|
-
});
|
|
84
|
-
const moduleFederationStaticServing = createModuleFederationStaticServing({
|
|
85
|
-
pwd,
|
|
86
|
-
pathPrefix
|
|
87
|
-
});
|
|
88
146
|
return async (c, next)=>{
|
|
89
147
|
const pageRoute = c.get('route');
|
|
90
148
|
const pathname = c.req.path;
|
|
91
|
-
if (pageRoute && '' ===
|
|
149
|
+
if (pageRoute && '' === path.extname(pathname)) return next();
|
|
92
150
|
const hit = staticPathRegExp.test(pathname);
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
151
|
+
if (!hit) return createPublicMiddleware({
|
|
152
|
+
pwd,
|
|
153
|
+
routes: routes || [],
|
|
154
|
+
pathPrefix,
|
|
155
|
+
respondAsset: options.respondAsset,
|
|
156
|
+
respondPublicFallback: options.respondPublicFallback
|
|
157
|
+
})(c, next);
|
|
158
|
+
{
|
|
159
|
+
const filepath = path.join(pwd, pathname.replace(pathPrefix, ()=>''));
|
|
160
|
+
if (!isPathInside(filepath, pwd)) return next();
|
|
161
|
+
if (!await fs.pathExists(filepath)) return next();
|
|
162
|
+
const asset = {
|
|
163
|
+
filename: filepath,
|
|
164
|
+
kind: 'static'
|
|
165
|
+
};
|
|
166
|
+
const serve = (representation)=>serveStaticAsset(c, {
|
|
167
|
+
...asset,
|
|
168
|
+
...representation,
|
|
169
|
+
filename: representation?.filename ?? asset.filename,
|
|
170
|
+
mimeFilename: filepath
|
|
171
|
+
});
|
|
172
|
+
const response = options.respondAsset ? await options.respondAsset(c, asset, serve, {
|
|
173
|
+
root: pwd,
|
|
174
|
+
pathPrefix
|
|
175
|
+
}) : void 0;
|
|
176
|
+
return (void 0 === response ? await serve() : response) ?? next();
|
|
99
177
|
}
|
|
100
|
-
const moduleFederationResponse = await moduleFederationStaticServing.serveModuleFederationAsset(c, staticServingRequest);
|
|
101
|
-
if (null !== moduleFederationResponse) return moduleFederationResponse;
|
|
102
|
-
return publicMiddleware(c, next);
|
|
103
178
|
};
|
|
104
179
|
}
|
|
105
180
|
const prepareFavicons = (favicon)=>{
|
|
@@ -107,4 +182,4 @@ const prepareFavicons = (favicon)=>{
|
|
|
107
182
|
if (favicon && 'string' == typeof favicon) faviconNames.push(favicon.substring(favicon.lastIndexOf('/') + 1));
|
|
108
183
|
return faviconNames;
|
|
109
184
|
};
|
|
110
|
-
export { createPublicMiddleware, createStaticMiddleware, serverStaticPlugin };
|
|
185
|
+
export { createPublicMiddleware, createStaticMiddleware, serveStaticAsset, serverStaticPlugin };
|