@bleedingdev/modern-js-server-core 3.9.0-ultramodern.2 → 3.9.0-ultramodern.20
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/hono.js +15 -5
- package/dist/cjs/adapters/node/index.js +4 -0
- package/dist/cjs/adapters/node/plugins/static.js +107 -29
- package/dist/cjs/helper.js +1 -1
- package/dist/cjs/index.js +10 -34
- package/dist/cjs/plugins/compat/index.js +1 -0
- package/dist/cjs/plugins/render/render.js +3 -4
- package/dist/cjs/plugins/render/ssrCache.js +63 -42
- package/dist/cjs/serverBase.js +60 -19
- package/dist/cjs/utils/error.js +0 -10
- package/dist/cjs/utils/transformStream.js +1 -1
- package/dist/esm/adapters/node/hono.mjs +15 -5
- package/dist/esm/adapters/node/index.mjs +1 -0
- package/dist/esm/adapters/node/plugins/static.mjs +107 -32
- package/dist/esm/helper.mjs +1 -1
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/plugins/compat/index.mjs +1 -0
- package/dist/esm/plugins/render/render.mjs +4 -11
- package/dist/esm/plugins/render/ssrCache.mjs +63 -42
- package/dist/esm/serverBase.mjs +60 -19
- package/dist/esm/utils/error.mjs +0 -1
- package/dist/esm/utils/transformStream.mjs +1 -1
- package/dist/esm-node/adapters/node/hono.mjs +15 -5
- 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/helper.mjs +1 -1
- package/dist/esm-node/index.mjs +1 -1
- package/dist/esm-node/plugins/compat/index.mjs +1 -0
- package/dist/esm-node/plugins/render/render.mjs +4 -11
- package/dist/esm-node/plugins/render/ssrCache.mjs +63 -42
- package/dist/esm-node/serverBase.mjs +60 -19
- package/dist/esm-node/utils/error.mjs +0 -1
- package/dist/esm-node/utils/transformStream.mjs +1 -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 +6 -0
- 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 +9 -10
- 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/rslib-runtime~1.mjs +0 -18
- 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/rslib-runtime~0.mjs +0 -19
- 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/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();
|
|
@@ -105,7 +146,7 @@ function applyMiddlewares() {
|
|
|
105
146
|
if (middleware.before) {
|
|
106
147
|
const targetIndex = finalMiddlewares.findIndex((item)=>{
|
|
107
148
|
if (middleware.before?.includes(item.name)) return true;
|
|
108
|
-
return false;
|
|
149
|
+
else return false;
|
|
109
150
|
});
|
|
110
151
|
if (-1 !== targetIndex) finalMiddlewares.splice(targetIndex, 0, middleware);
|
|
111
152
|
else finalMiddlewares.push(middleware);
|
|
@@ -126,7 +167,7 @@ function applyMiddlewares() {
|
|
|
126
167
|
}
|
|
127
168
|
function handler2Handlers(handler) {
|
|
128
169
|
if (Array.isArray(handler)) return handler;
|
|
129
|
-
return [
|
|
170
|
+
else return [
|
|
130
171
|
handler
|
|
131
172
|
];
|
|
132
173
|
}
|
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 };
|
|
@@ -14,7 +14,7 @@ function transformResponse(response, transformCb) {
|
|
|
14
14
|
if (response.body) {
|
|
15
15
|
const stream = createTransformStream(async (before)=>{
|
|
16
16
|
if (Array.isArray(transformCb)) return transformCb.reduce(async (before, cb)=>cb(await before), Promise.resolve(before));
|
|
17
|
-
return transformCb(before);
|
|
17
|
+
else return transformCb(before);
|
|
18
18
|
});
|
|
19
19
|
response.body.pipeThrough(stream);
|
|
20
20
|
readable = stream.readable;
|
|
@@ -31,12 +31,22 @@ const httpCallBack2HonoMid = (handler)=>async (context, next)=>{
|
|
|
31
31
|
};
|
|
32
32
|
const noop = ()=>{};
|
|
33
33
|
const connectMid2HonoMid = (handler)=>async (context, next)=>new Promise((resolve, reject)=>{
|
|
34
|
-
const { req, res } = context.env.node;
|
|
34
|
+
const { req, res: rawRes } = context.env.node;
|
|
35
|
+
const res = rawRes;
|
|
35
36
|
if (handler.length < 3) resolve(handler(req, res, noop));
|
|
36
|
-
else
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
else {
|
|
38
|
+
const onFinish = ()=>{
|
|
39
|
+
context.res;
|
|
40
|
+
context.finalized = true;
|
|
41
|
+
resolve();
|
|
42
|
+
};
|
|
43
|
+
res.once?.('finish', onFinish);
|
|
44
|
+
handler(req, res, (err)=>{
|
|
45
|
+
res.removeListener?.('finish', onFinish);
|
|
46
|
+
if (err) reject(err);
|
|
47
|
+
else resolve(next());
|
|
48
|
+
});
|
|
49
|
+
}
|
|
40
50
|
});
|
|
41
51
|
const connectMockMid2HonoMid = (handler)=>async (context, next)=>new Promise((resolve, reject)=>{
|
|
42
52
|
const { req, res: rawRes } = context.env.node;
|
|
@@ -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
|
+
else {
|
|
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 };
|
package/dist/esm-node/helper.mjs
CHANGED
|
@@ -3,7 +3,7 @@ const LOADER_CONTEXT = 'loaderContext';
|
|
|
3
3
|
function getLoaderCtx(c) {
|
|
4
4
|
const loaderContext = c.get(LOADER_CONTEXT);
|
|
5
5
|
if (loaderContext) return loaderContext;
|
|
6
|
-
{
|
|
6
|
+
else {
|
|
7
7
|
const loaderContext = new Map();
|
|
8
8
|
c.set(LOADER_CONTEXT, loaderContext);
|
|
9
9
|
return loaderContext;
|
package/dist/esm-node/index.mjs
CHANGED
|
@@ -9,5 +9,5 @@ export { AGGRED_DIR } from "./constants.mjs";
|
|
|
9
9
|
export { run, useHonoContext } from "./context.mjs";
|
|
10
10
|
export { getLoaderCtx } from "./helper.mjs";
|
|
11
11
|
export { createServerBase } from "./serverBase.mjs";
|
|
12
|
-
export { ErrorDigest, createErrorHtml,
|
|
12
|
+
export { ErrorDigest, createErrorHtml, onError } from "./utils/index.mjs";
|
|
13
13
|
export { getPublicDirConfig, getPublicDirPatterns, getPublicDirRoutePrefixes, normalizePublicDir, normalizePublicDirPath, resolvePublicDirPaths } from "./utils/publicDir.mjs";
|
|
@@ -4,6 +4,7 @@ import { getHookRunners, handleSetupResult } from "./hooks.mjs";
|
|
|
4
4
|
const compatPlugin = ()=>({
|
|
5
5
|
name: '@modern-js/server-compat',
|
|
6
6
|
registryHooks: {
|
|
7
|
+
handleError: createAsyncPipelineHook(),
|
|
7
8
|
prepareWebServer: createAsyncPipelineHook(),
|
|
8
9
|
prepareApiServer: createAsyncPipelineHook(),
|
|
9
10
|
afterMatch: createAsyncPipelineHook(),
|
|
@@ -8,14 +8,8 @@ import { dataHandler } from "./dataHandler.mjs";
|
|
|
8
8
|
import { renderRscHandler } from "./renderRscHandler.mjs";
|
|
9
9
|
import { serverActionHandler } from "./serverActionHandler.mjs";
|
|
10
10
|
import { ssrRender } from "./ssrRender.mjs";
|
|
11
|
-
import { __webpack_require__ } from "../../rslib-runtime~0.mjs";
|
|
12
11
|
import { createRequire as __rspack_createRequire } from "node:module";
|
|
13
12
|
const __rspack_createRequire_require = __rspack_createRequire(import.meta.url);
|
|
14
|
-
__webpack_require__.add({
|
|
15
|
-
"@modern-js/runtime-utils/router" (module) {
|
|
16
|
-
module.exports = __rspack_createRequire_require("@modern-js/runtime-utils/router");
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
13
|
const DYNAMIC_ROUTE_REG = /\/:./;
|
|
20
14
|
function getRouter(routes) {
|
|
21
15
|
const dynamicRoutes = [];
|
|
@@ -48,8 +42,7 @@ function matchRoute(router, pathname, entryName) {
|
|
|
48
42
|
void 0,
|
|
49
43
|
{}
|
|
50
44
|
];
|
|
51
|
-
}
|
|
52
|
-
{
|
|
45
|
+
} else {
|
|
53
46
|
const result = matchedRoutes[0];
|
|
54
47
|
if (!result) return [
|
|
55
48
|
void 0,
|
|
@@ -166,7 +159,7 @@ async function renderHandler(request, options, mode, fallbackWrapper, framework)
|
|
|
166
159
|
const { nestedRoutesJson } = serverManifest;
|
|
167
160
|
const routes = nestedRoutesJson?.[options.routeInfo.entryName];
|
|
168
161
|
if (routes) {
|
|
169
|
-
const { matchRoutes } =
|
|
162
|
+
const { matchRoutes } = __rspack_createRequire_require("@modern-js/runtime-utils/router");
|
|
170
163
|
const url = new URL(request.url);
|
|
171
164
|
const matchedRoutes = matchRoutes(routes, url.pathname, options.routeInfo.urlPath);
|
|
172
165
|
if (matchedRoutes) {
|
|
@@ -204,7 +197,7 @@ async function getRenderMode(req, fallbackHeader, isSSR, forceCSR, nodeReq, onFa
|
|
|
204
197
|
if (req.headers.get('x-rsc-action')) return 'rsc-action';
|
|
205
198
|
if (req.headers.get('x-rsc-tree')) return 'rsc-tree';
|
|
206
199
|
if (!isSSR) return 'csr';
|
|
207
|
-
{
|
|
200
|
+
else {
|
|
208
201
|
if (query.__loader) return 'data';
|
|
209
202
|
const fallbackHeaderValue = req.headers.get(fallbackHeader) || nodeReq?.headers[fallbackHeader];
|
|
210
203
|
if (forceCSR && (query.csr || fallbackHeaderValue || contextForceCSR)) {
|
|
@@ -233,6 +226,6 @@ async function csrRender(request, options) {
|
|
|
233
226
|
[X_MODERNJS_RENDER]: 'client'
|
|
234
227
|
})
|
|
235
228
|
});
|
|
236
|
-
return csrRscRender(request, options);
|
|
229
|
+
else return csrRscRender(request, options);
|
|
237
230
|
}
|
|
238
231
|
export { createRender, matchRoute };
|
|
@@ -2,54 +2,67 @@ import "node:module";
|
|
|
2
2
|
import { createMemoryStorage } from "@modern-js/runtime-utils/storer";
|
|
3
3
|
import { X_RENDER_CACHE } from "../../constants.mjs";
|
|
4
4
|
import { createTransformStream, getPathname } from "../../utils/index.mjs";
|
|
5
|
+
const preventsSharedCaching = (headers)=>/(?:^|,)\s*(?:private|no-store|no-cache)\s*(?:[=,]|$)/i.test(headers.get('cache-control') || '');
|
|
6
|
+
const isCacheableResponse = (response)=>200 === response.status && !preventsSharedCaching(response.headers) && !response.headers.has('set-cookie') && !response.headers.get('vary');
|
|
5
7
|
const removeTailSlash = (s)=>s.replace(/\/+$/, '');
|
|
6
8
|
const ZERO_RENDER_LEVEL = /"renderLevel":0/;
|
|
7
9
|
const NO_SSR_CACHE = /<meta\s+[^>]*name=["']no-ssr-cache["'][^>]*>/i;
|
|
8
10
|
async function processCache({ request, key, requestHandler, requestHandlerOptions, ttl, container, cacheStatus }) {
|
|
9
11
|
const response = await requestHandler(request, requestHandlerOptions);
|
|
10
12
|
const { onError } = requestHandlerOptions;
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
13
|
+
const deleteCache = async ()=>{
|
|
14
|
+
try {
|
|
15
|
+
await container.delete(key);
|
|
16
|
+
} catch {
|
|
17
|
+
(onError || console.error)('[render-cache] delete cache failed');
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
if (!isCacheableResponse(response) || !response.body) {
|
|
21
|
+
await deleteCache();
|
|
22
|
+
return response;
|
|
23
|
+
}
|
|
24
|
+
const headers = Object.fromEntries(response.headers);
|
|
23
25
|
const decoder = new TextDecoder();
|
|
24
26
|
if (response.body) {
|
|
25
27
|
const stream = createTransformStream();
|
|
26
28
|
const reader = response.body.getReader();
|
|
27
29
|
const writer = stream.writable.getWriter();
|
|
28
30
|
let html = '';
|
|
29
|
-
const push = ()=>reader.read().then(({ done, value })=>{
|
|
31
|
+
const push = ()=>reader.read().then(async ({ done, value })=>{
|
|
30
32
|
if (done) {
|
|
33
|
+
html += decoder.decode();
|
|
31
34
|
const match = ZERO_RENDER_LEVEL.test(html) || NO_SSR_CACHE.test(html);
|
|
32
|
-
if (match)
|
|
35
|
+
if (match) {
|
|
36
|
+
await deleteCache();
|
|
37
|
+
return writer.close();
|
|
38
|
+
}
|
|
33
39
|
const current = Date.now();
|
|
34
40
|
const cache = {
|
|
35
41
|
val: html,
|
|
36
|
-
cursor: current
|
|
42
|
+
cursor: current,
|
|
43
|
+
headers
|
|
37
44
|
};
|
|
38
45
|
container.set(key, JSON.stringify(cache), {
|
|
39
46
|
ttl
|
|
40
47
|
}).catch(()=>{
|
|
41
|
-
|
|
42
|
-
else console.error(`[render-cache] set cache failed, key: ${key}, value: ${JSON.stringify(cache)}`);
|
|
48
|
+
(onError || console.error)('[render-cache] set cache failed');
|
|
43
49
|
});
|
|
44
|
-
writer.close();
|
|
45
|
-
return;
|
|
50
|
+
return writer.close();
|
|
46
51
|
}
|
|
47
|
-
const content = decoder.decode(value
|
|
52
|
+
const content = decoder.decode(value, {
|
|
53
|
+
stream: true
|
|
54
|
+
});
|
|
48
55
|
html += content;
|
|
49
|
-
writer.write(value);
|
|
50
|
-
push();
|
|
56
|
+
await writer.write(value);
|
|
57
|
+
return push();
|
|
51
58
|
});
|
|
52
|
-
push()
|
|
59
|
+
push().catch(async (error)=>{
|
|
60
|
+
await Promise.allSettled([
|
|
61
|
+
writer.abort(error),
|
|
62
|
+
reader.cancel(error)
|
|
63
|
+
]);
|
|
64
|
+
(onError || console.error)('[render-cache] response stream failed');
|
|
65
|
+
});
|
|
53
66
|
cacheStatus && response.headers.set(X_RENDER_CACHE, cacheStatus);
|
|
54
67
|
return new Response(stream.readable, {
|
|
55
68
|
status: response.status,
|
|
@@ -64,9 +77,12 @@ function computedKey(req, cacheControl) {
|
|
|
64
77
|
const pathname = getPathname(req);
|
|
65
78
|
const { customKey } = cacheControl;
|
|
66
79
|
const defaultKey = '/' === pathname ? pathname : removeTailSlash(pathname);
|
|
67
|
-
if (
|
|
68
|
-
|
|
69
|
-
|
|
80
|
+
if (customKey) if ('string' == typeof customKey) return customKey;
|
|
81
|
+
else return customKey(defaultKey);
|
|
82
|
+
else {
|
|
83
|
+
const url = new URL(req.url);
|
|
84
|
+
return `${url.origin}${defaultKey}${url.search}`;
|
|
85
|
+
}
|
|
70
86
|
}
|
|
71
87
|
function shouldUseCache(request) {
|
|
72
88
|
const url = new URL(request.url);
|
|
@@ -77,7 +93,7 @@ function shouldUseCache(request) {
|
|
|
77
93
|
}
|
|
78
94
|
function matchCacheControl(cacheOption, req) {
|
|
79
95
|
if (!cacheOption || !req) return;
|
|
80
|
-
{
|
|
96
|
+
else {
|
|
81
97
|
if (isCacheControl(cacheOption)) return cacheOption;
|
|
82
98
|
if (isCacheOptionProvider(cacheOption)) return cacheOption(req);
|
|
83
99
|
const url = req.url;
|
|
@@ -96,13 +112,15 @@ function matchCacheControl(cacheOption, req) {
|
|
|
96
112
|
async function getCacheResult(request, options) {
|
|
97
113
|
const { cacheControl, container = storage, requestHandler, requestHandlerOptions } = options;
|
|
98
114
|
const { onError } = requestHandlerOptions;
|
|
99
|
-
const
|
|
115
|
+
const hasCredentials = request.headers.has('cookie') || request.headers.has('authorization');
|
|
116
|
+
if ('GET' !== request.method || !shouldUseCache(request) || preventsSharedCaching(request.headers) || hasCredentials && !cacheControl.customKey) return requestHandler(request, requestHandlerOptions);
|
|
117
|
+
const key = `${CACHE_NAMESPACE}:v2:${computedKey(request, cacheControl)}`;
|
|
100
118
|
let value;
|
|
101
119
|
try {
|
|
102
120
|
value = await container.get(key);
|
|
103
121
|
} catch (_) {
|
|
104
|
-
if (onError) onError(
|
|
105
|
-
else console.error(
|
|
122
|
+
if (onError) onError('[render-cache] get cache failed');
|
|
123
|
+
else console.error('[render-cache] get cache failed');
|
|
106
124
|
value = void 0;
|
|
107
125
|
}
|
|
108
126
|
const { maxAge, staleWhileRevalidate } = cacheControl;
|
|
@@ -116,27 +134,27 @@ async function getCacheResult(request, options) {
|
|
|
116
134
|
container,
|
|
117
135
|
cacheStatus: 'miss'
|
|
118
136
|
});
|
|
119
|
-
{
|
|
137
|
+
else {
|
|
120
138
|
const cache = JSON.parse(value);
|
|
121
139
|
const interval = Date.now() - cache.cursor;
|
|
122
140
|
if (interval <= maxAge) {
|
|
123
141
|
const cacheStatus = 'hit';
|
|
124
142
|
return new Response(cache.val, {
|
|
125
143
|
headers: {
|
|
144
|
+
...cache.headers,
|
|
126
145
|
[X_RENDER_CACHE]: cacheStatus
|
|
127
146
|
}
|
|
128
147
|
});
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
{
|
|
148
|
+
} else {
|
|
149
|
+
if (!(interval <= staleWhileRevalidate + maxAge)) return processCache({
|
|
150
|
+
key,
|
|
151
|
+
request,
|
|
152
|
+
requestHandler,
|
|
153
|
+
requestHandlerOptions,
|
|
154
|
+
ttl,
|
|
155
|
+
container,
|
|
156
|
+
cacheStatus: 'expired'
|
|
157
|
+
});
|
|
140
158
|
processCache({
|
|
141
159
|
key,
|
|
142
160
|
request,
|
|
@@ -146,10 +164,13 @@ async function getCacheResult(request, options) {
|
|
|
146
164
|
container
|
|
147
165
|
}).then(async (response)=>{
|
|
148
166
|
await response.text();
|
|
167
|
+
}).catch(()=>{
|
|
168
|
+
(onError || console.error)('[render-cache] revalidation failed');
|
|
149
169
|
});
|
|
150
170
|
const cacheStatus = 'stale';
|
|
151
171
|
return new Response(cache.val, {
|
|
152
172
|
headers: {
|
|
173
|
+
...cache.headers,
|
|
153
174
|
[X_RENDER_CACHE]: cacheStatus
|
|
154
175
|
}
|
|
155
176
|
});
|