@modern-js/server-core 3.9.2 → 3.10.0
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/node.js +3 -1
- package/dist/cjs/adapters/node/plugins/resource.js +38 -10
- package/dist/cjs/plugins/render/ssrCache.js +54 -20
- package/dist/cjs/serverBase.js +3 -1
- package/dist/esm/adapters/node/node.mjs +3 -1
- package/dist/esm/adapters/node/plugins/resource.mjs +38 -10
- package/dist/esm/plugins/render/render.mjs +1 -7
- package/dist/esm/plugins/render/ssrCache.mjs +54 -20
- package/dist/esm/serverBase.mjs +3 -1
- package/dist/esm-node/adapters/node/node.mjs +3 -1
- package/dist/esm-node/adapters/node/plugins/resource.mjs +38 -10
- package/dist/esm-node/plugins/render/render.mjs +1 -7
- package/dist/esm-node/plugins/render/ssrCache.mjs +54 -20
- package/dist/esm-node/serverBase.mjs +3 -1
- package/dist/types/adapters/node/index.d.ts +1 -1
- package/dist/types/adapters/node/plugins/resource.d.ts +3 -1
- package/dist/types/adapters/node/plugins/static.d.ts +2 -2
- package/dist/types/hono.d.ts +1 -1
- package/package.json +8 -9
- package/rstack.config.mts +4 -0
- package/dist/esm/rslib-runtime~1.mjs +0 -18
- package/dist/esm-node/rslib-runtime~0.mjs +0 -19
- package/rslib.config.mts +0 -4
|
@@ -68,7 +68,9 @@ const createWebRequest = (req, res, body)=>{
|
|
|
68
68
|
headers: headerRecord,
|
|
69
69
|
signal: controller.signal
|
|
70
70
|
};
|
|
71
|
-
res.on('close', ()=>
|
|
71
|
+
res.on('close', ()=>{
|
|
72
|
+
if (!res.writableFinished || req.aborted) controller.abort('res closed');
|
|
73
|
+
});
|
|
72
74
|
const url = `http://${req.headers.host}${req.url}`;
|
|
73
75
|
const needsRequestBody = body || !('GET' === method || 'HEAD' === method);
|
|
74
76
|
const cloneableReq = needsRequestBody ? external_cloneable_readable_default()(req) : null;
|
|
@@ -77,28 +77,51 @@ function injectTemplates(pwd, routes, htmlTemplatePromise) {
|
|
|
77
77
|
await next();
|
|
78
78
|
};
|
|
79
79
|
}
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
function reportBundleError(filepath, error, monitors) {
|
|
81
|
+
const detail = error instanceof Error ? error.stack || error.message : error;
|
|
82
|
+
if (monitors) monitors.error(`Load ${filepath} bundle failed, error = %s`, detail);
|
|
83
|
+
else console.error(`Load ${filepath} bundle failed, error = ${detail}`);
|
|
84
|
+
}
|
|
85
|
+
const loadBundle = async (filepath, monitors, { required = false, failOnError = false } = {})=>{
|
|
86
|
+
if (!required && !failOnError && !await utils_namespaceObject.fs.pathExists(filepath)) return;
|
|
82
87
|
try {
|
|
88
|
+
if (!required && failOnError) try {
|
|
89
|
+
await utils_namespaceObject.fs.access(filepath);
|
|
90
|
+
} catch (e) {
|
|
91
|
+
const code = e?.code;
|
|
92
|
+
if ('ENOENT' === code || 'ENOTDIR' === code) return;
|
|
93
|
+
throw e;
|
|
94
|
+
}
|
|
83
95
|
const module = await (0, utils_namespaceObject.compatibleRequire)(filepath, false);
|
|
84
96
|
return module;
|
|
85
97
|
} catch (e) {
|
|
86
|
-
|
|
87
|
-
|
|
98
|
+
reportBundleError(filepath, e, monitors);
|
|
99
|
+
if (failOnError) throw e;
|
|
88
100
|
return;
|
|
89
101
|
}
|
|
90
102
|
};
|
|
91
|
-
async function getServerManifest(pwd, routes, monitors) {
|
|
103
|
+
async function getServerManifest(pwd, routes, monitors, { failOnError = false } = {}) {
|
|
92
104
|
const loaderBundles = {};
|
|
93
105
|
const renderBundles = {};
|
|
94
106
|
await Promise.all(routes.filter((route)=>Boolean(route.bundle)).map(async (route)=>{
|
|
107
|
+
const failOnBundleError = failOnError && Boolean(route.isSSR);
|
|
95
108
|
const entryName = route.entryName || utils_namespaceObject.MAIN_ENTRY_NAME;
|
|
96
109
|
const renderBundlePath = external_path_default().join(pwd, route.bundle || '');
|
|
97
110
|
const loaderBundlePath = external_path_default().join(pwd, utils_namespaceObject.SERVER_BUNDLE_DIRECTORY, `${entryName}-server-loaders.js`);
|
|
98
|
-
const renderBundle = await loadBundle(renderBundlePath, monitors
|
|
99
|
-
|
|
111
|
+
const renderBundle = await loadBundle(renderBundlePath, monitors, {
|
|
112
|
+
required: failOnBundleError,
|
|
113
|
+
failOnError: failOnBundleError
|
|
114
|
+
});
|
|
115
|
+
const loaderBundle = await loadBundle(loaderBundlePath, monitors, {
|
|
116
|
+
failOnError: failOnBundleError
|
|
117
|
+
});
|
|
100
118
|
renderBundle && (renderBundles[entryName] = renderBundle);
|
|
101
|
-
|
|
119
|
+
if (loaderBundle) try {
|
|
120
|
+
loaderBundles[entryName] = loaderBundle.loadModules ? await loaderBundle.loadModules() : loaderBundle;
|
|
121
|
+
} catch (e) {
|
|
122
|
+
if (failOnBundleError) reportBundleError(loaderBundlePath, e, monitors);
|
|
123
|
+
throw e;
|
|
124
|
+
}
|
|
102
125
|
}));
|
|
103
126
|
const loadableUri = external_path_default().join(pwd, utils_namespaceObject.LOADABLE_STATS_FILE);
|
|
104
127
|
const loadableStats = await (0, utils_namespaceObject.compatibleRequire)(loadableUri).catch((_)=>({}));
|
|
@@ -166,13 +189,18 @@ const injectRscManifestPlugin = (enableRsc)=>({
|
|
|
166
189
|
const injectResourcePlugin = ()=>({
|
|
167
190
|
name: '@modern-js/plugin-inject-resource',
|
|
168
191
|
setup (api) {
|
|
169
|
-
api.onPrepare(()=>{
|
|
192
|
+
api.onPrepare(async ()=>{
|
|
170
193
|
const { middlewares, routes, distDirectory: pwd } = api.getServerContext();
|
|
171
194
|
let htmlTemplatePromise;
|
|
172
195
|
let manifestPromise;
|
|
173
196
|
if ((0, utils_namespaceObject.isProd)()) {
|
|
174
|
-
|
|
197
|
+
const warmupRoutes = routes || [];
|
|
198
|
+
const failOnError = warmupRoutes.some((route)=>route.isSSR && route.bundle) && !warmupRoutes.some((route)=>route.isRSC);
|
|
199
|
+
manifestPromise = getServerManifest(pwd, warmupRoutes, void 0, {
|
|
200
|
+
failOnError
|
|
201
|
+
});
|
|
175
202
|
htmlTemplatePromise = getHtmlTemplates(pwd, routes || []);
|
|
203
|
+
if (failOnError) await manifestPromise;
|
|
176
204
|
}
|
|
177
205
|
middlewares.push({
|
|
178
206
|
name: 'inject-server-manifest',
|
|
@@ -59,30 +59,59 @@ async function processCache({ request, key, requestHandler, requestHandlerOption
|
|
|
59
59
|
const reader = response.body.getReader();
|
|
60
60
|
const writer = stream.writable.getWriter();
|
|
61
61
|
let html = '';
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
let cancelled = false;
|
|
63
|
+
const cancel = async (reason)=>{
|
|
64
|
+
cancelled = true;
|
|
65
|
+
await reader.cancel(reason);
|
|
66
|
+
};
|
|
67
|
+
writer.closed.catch(cancel).catch(()=>{});
|
|
68
|
+
const onAbort = ()=>{
|
|
69
|
+
Promise.allSettled([
|
|
70
|
+
cancel(request.signal.reason),
|
|
71
|
+
writer.abort(request.signal.reason)
|
|
72
|
+
]);
|
|
73
|
+
};
|
|
74
|
+
request.signal.addEventListener('abort', onAbort, {
|
|
75
|
+
once: true
|
|
76
|
+
});
|
|
77
|
+
if (request.signal.aborted) onAbort();
|
|
78
|
+
const pumping = (async ()=>{
|
|
79
|
+
try {
|
|
80
|
+
for(;;){
|
|
81
|
+
const { done, value } = await reader.read();
|
|
82
|
+
if (cancelled) return;
|
|
83
|
+
if (done) break;
|
|
84
|
+
html += decoder.decode(value, {
|
|
85
|
+
stream: true
|
|
86
|
+
});
|
|
87
|
+
await writer.write(value);
|
|
88
|
+
}
|
|
89
|
+
html += decoder.decode();
|
|
90
|
+
await writer.close();
|
|
91
|
+
if (!cancelled && !ZERO_RENDER_LEVEL.test(html) && !NO_SSR_CACHE.test(html)) {
|
|
67
92
|
const cache = {
|
|
68
93
|
val: html,
|
|
69
|
-
cursor:
|
|
94
|
+
cursor: Date.now()
|
|
70
95
|
};
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
96
|
+
try {
|
|
97
|
+
await container.set(key, JSON.stringify(cache), {
|
|
98
|
+
ttl
|
|
99
|
+
});
|
|
100
|
+
} catch (error) {
|
|
101
|
+
if (onError) onError(error, 'render-cache');
|
|
102
|
+
else console.error(`[render-cache] set cache failed, key: ${key}`, error);
|
|
103
|
+
}
|
|
79
104
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
105
|
+
} catch (error) {
|
|
106
|
+
await Promise.allSettled([
|
|
107
|
+
cancel(error),
|
|
108
|
+
writer.abort(error)
|
|
109
|
+
]);
|
|
110
|
+
} finally{
|
|
111
|
+
request.signal.removeEventListener('abort', onAbort);
|
|
112
|
+
}
|
|
113
|
+
})();
|
|
114
|
+
pumping.catch(()=>{});
|
|
86
115
|
cacheStatus && response.headers.set(external_constants_js_namespaceObject.X_RENDER_CACHE, cacheStatus);
|
|
87
116
|
return new Response(stream.readable, {
|
|
88
117
|
status: response.status,
|
|
@@ -178,6 +207,11 @@ async function getCacheResult(request, options) {
|
|
|
178
207
|
container
|
|
179
208
|
}).then(async (response)=>{
|
|
180
209
|
await response.text();
|
|
210
|
+
}).catch((error)=>{
|
|
211
|
+
try {
|
|
212
|
+
if (onError) onError(error, 'render-cache');
|
|
213
|
+
else console.error('[render-cache] revalidation failed', error);
|
|
214
|
+
} catch {}
|
|
181
215
|
});
|
|
182
216
|
const cacheStatus = 'stale';
|
|
183
217
|
return new Response(cache.val, {
|
package/dist/cjs/serverBase.js
CHANGED
|
@@ -115,7 +115,9 @@ class ServerBase {
|
|
|
115
115
|
this.serverContext = null;
|
|
116
116
|
this.serverOptions = options;
|
|
117
117
|
this.app = new external_hono_namespaceObject.Hono();
|
|
118
|
-
this.app.use('*',
|
|
118
|
+
this.app.use('*', async (context, next)=>{
|
|
119
|
+
await (0, external_context_js_namespaceObject.run)(context, next);
|
|
120
|
+
});
|
|
119
121
|
}
|
|
120
122
|
}
|
|
121
123
|
function applyMiddlewares() {
|
|
@@ -24,7 +24,9 @@ const createWebRequest = (req, res, body)=>{
|
|
|
24
24
|
headers: headerRecord,
|
|
25
25
|
signal: controller.signal
|
|
26
26
|
};
|
|
27
|
-
res.on('close', ()=>
|
|
27
|
+
res.on('close', ()=>{
|
|
28
|
+
if (!res.writableFinished || req.aborted) controller.abort('res closed');
|
|
29
|
+
});
|
|
28
30
|
const url = `http://${req.headers.host}${req.url}`;
|
|
29
31
|
const needsRequestBody = body || !('GET' === method || 'HEAD' === method);
|
|
30
32
|
const cloneableReq = needsRequestBody ? cloneable_readable(req) : null;
|
|
@@ -27,28 +27,51 @@ function injectTemplates(pwd, routes, htmlTemplatePromise) {
|
|
|
27
27
|
await next();
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
function reportBundleError(filepath, error, monitors) {
|
|
31
|
+
const detail = error instanceof Error ? error.stack || error.message : error;
|
|
32
|
+
if (monitors) monitors.error(`Load ${filepath} bundle failed, error = %s`, detail);
|
|
33
|
+
else console.error(`Load ${filepath} bundle failed, error = ${detail}`);
|
|
34
|
+
}
|
|
35
|
+
const loadBundle = async (filepath, monitors, { required = false, failOnError = false } = {})=>{
|
|
36
|
+
if (!required && !failOnError && !await fs.pathExists(filepath)) return;
|
|
32
37
|
try {
|
|
38
|
+
if (!required && failOnError) try {
|
|
39
|
+
await fs.access(filepath);
|
|
40
|
+
} catch (e) {
|
|
41
|
+
const code = e?.code;
|
|
42
|
+
if ('ENOENT' === code || 'ENOTDIR' === code) return;
|
|
43
|
+
throw e;
|
|
44
|
+
}
|
|
33
45
|
const module = await compatibleRequire(filepath, false);
|
|
34
46
|
return module;
|
|
35
47
|
} catch (e) {
|
|
36
|
-
|
|
37
|
-
|
|
48
|
+
reportBundleError(filepath, e, monitors);
|
|
49
|
+
if (failOnError) throw e;
|
|
38
50
|
return;
|
|
39
51
|
}
|
|
40
52
|
};
|
|
41
|
-
async function getServerManifest(pwd, routes, monitors) {
|
|
53
|
+
async function getServerManifest(pwd, routes, monitors, { failOnError = false } = {}) {
|
|
42
54
|
const loaderBundles = {};
|
|
43
55
|
const renderBundles = {};
|
|
44
56
|
await Promise.all(routes.filter((route)=>Boolean(route.bundle)).map(async (route)=>{
|
|
57
|
+
const failOnBundleError = failOnError && Boolean(route.isSSR);
|
|
45
58
|
const entryName = route.entryName || MAIN_ENTRY_NAME;
|
|
46
59
|
const renderBundlePath = path.join(pwd, route.bundle || '');
|
|
47
60
|
const loaderBundlePath = path.join(pwd, SERVER_BUNDLE_DIRECTORY, `${entryName}-server-loaders.js`);
|
|
48
|
-
const renderBundle = await loadBundle(renderBundlePath, monitors
|
|
49
|
-
|
|
61
|
+
const renderBundle = await loadBundle(renderBundlePath, monitors, {
|
|
62
|
+
required: failOnBundleError,
|
|
63
|
+
failOnError: failOnBundleError
|
|
64
|
+
});
|
|
65
|
+
const loaderBundle = await loadBundle(loaderBundlePath, monitors, {
|
|
66
|
+
failOnError: failOnBundleError
|
|
67
|
+
});
|
|
50
68
|
renderBundle && (renderBundles[entryName] = renderBundle);
|
|
51
|
-
|
|
69
|
+
if (loaderBundle) try {
|
|
70
|
+
loaderBundles[entryName] = loaderBundle.loadModules ? await loaderBundle.loadModules() : loaderBundle;
|
|
71
|
+
} catch (e) {
|
|
72
|
+
if (failOnBundleError) reportBundleError(loaderBundlePath, e, monitors);
|
|
73
|
+
throw e;
|
|
74
|
+
}
|
|
52
75
|
}));
|
|
53
76
|
const loadableUri = path.join(pwd, LOADABLE_STATS_FILE);
|
|
54
77
|
const loadableStats = await compatibleRequire(loadableUri).catch((_)=>({}));
|
|
@@ -116,13 +139,18 @@ const injectRscManifestPlugin = (enableRsc)=>({
|
|
|
116
139
|
const injectResourcePlugin = ()=>({
|
|
117
140
|
name: '@modern-js/plugin-inject-resource',
|
|
118
141
|
setup (api) {
|
|
119
|
-
api.onPrepare(()=>{
|
|
142
|
+
api.onPrepare(async ()=>{
|
|
120
143
|
const { middlewares, routes, distDirectory: pwd } = api.getServerContext();
|
|
121
144
|
let htmlTemplatePromise;
|
|
122
145
|
let manifestPromise;
|
|
123
146
|
if (isProd()) {
|
|
124
|
-
|
|
147
|
+
const warmupRoutes = routes || [];
|
|
148
|
+
const failOnError = warmupRoutes.some((route)=>route.isSSR && route.bundle) && !warmupRoutes.some((route)=>route.isRSC);
|
|
149
|
+
manifestPromise = getServerManifest(pwd, warmupRoutes, void 0, {
|
|
150
|
+
failOnError
|
|
151
|
+
});
|
|
125
152
|
htmlTemplatePromise = getHtmlTemplates(pwd, routes || []);
|
|
153
|
+
if (failOnError) await manifestPromise;
|
|
126
154
|
}
|
|
127
155
|
middlewares.push({
|
|
128
156
|
name: 'inject-server-manifest',
|
|
@@ -7,12 +7,6 @@ import { dataHandler } from "./dataHandler.mjs";
|
|
|
7
7
|
import { renderRscHandler } from "./renderRscHandler.mjs";
|
|
8
8
|
import { serverActionHandler } from "./serverActionHandler.mjs";
|
|
9
9
|
import { ssrRender } from "./ssrRender.mjs";
|
|
10
|
-
import { __webpack_require__ } from "../../rslib-runtime~1.mjs";
|
|
11
|
-
__webpack_require__.add({
|
|
12
|
-
718 (module) {
|
|
13
|
-
module.exports = require("@modern-js/runtime-utils/router");
|
|
14
|
-
}
|
|
15
|
-
});
|
|
16
10
|
const DYNAMIC_ROUTE_REG = /\/:./;
|
|
17
11
|
function getRouter(routes) {
|
|
18
12
|
const dynamicRoutes = [];
|
|
@@ -161,7 +155,7 @@ async function renderHandler(request, options, mode, fallbackWrapper, framework)
|
|
|
161
155
|
const { nestedRoutesJson } = serverManifest;
|
|
162
156
|
const routes = nestedRoutesJson?.[options.routeInfo.entryName];
|
|
163
157
|
if (routes) {
|
|
164
|
-
const { matchRoutes } =
|
|
158
|
+
const { matchRoutes } = require("@modern-js/runtime-utils/router");
|
|
165
159
|
const url = new URL(request.url);
|
|
166
160
|
const matchedRoutes = matchRoutes(routes, url.pathname, options.routeInfo.urlPath);
|
|
167
161
|
if (matchedRoutes) {
|
|
@@ -25,30 +25,59 @@ async function processCache({ request, key, requestHandler, requestHandlerOption
|
|
|
25
25
|
const reader = response.body.getReader();
|
|
26
26
|
const writer = stream.writable.getWriter();
|
|
27
27
|
let html = '';
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
let cancelled = false;
|
|
29
|
+
const cancel = async (reason)=>{
|
|
30
|
+
cancelled = true;
|
|
31
|
+
await reader.cancel(reason);
|
|
32
|
+
};
|
|
33
|
+
writer.closed.catch(cancel).catch(()=>{});
|
|
34
|
+
const onAbort = ()=>{
|
|
35
|
+
Promise.allSettled([
|
|
36
|
+
cancel(request.signal.reason),
|
|
37
|
+
writer.abort(request.signal.reason)
|
|
38
|
+
]);
|
|
39
|
+
};
|
|
40
|
+
request.signal.addEventListener('abort', onAbort, {
|
|
41
|
+
once: true
|
|
42
|
+
});
|
|
43
|
+
if (request.signal.aborted) onAbort();
|
|
44
|
+
const pumping = (async ()=>{
|
|
45
|
+
try {
|
|
46
|
+
for(;;){
|
|
47
|
+
const { done, value } = await reader.read();
|
|
48
|
+
if (cancelled) return;
|
|
49
|
+
if (done) break;
|
|
50
|
+
html += decoder.decode(value, {
|
|
51
|
+
stream: true
|
|
52
|
+
});
|
|
53
|
+
await writer.write(value);
|
|
54
|
+
}
|
|
55
|
+
html += decoder.decode();
|
|
56
|
+
await writer.close();
|
|
57
|
+
if (!cancelled && !ZERO_RENDER_LEVEL.test(html) && !NO_SSR_CACHE.test(html)) {
|
|
33
58
|
const cache = {
|
|
34
59
|
val: html,
|
|
35
|
-
cursor:
|
|
60
|
+
cursor: Date.now()
|
|
36
61
|
};
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
62
|
+
try {
|
|
63
|
+
await container.set(key, JSON.stringify(cache), {
|
|
64
|
+
ttl
|
|
65
|
+
});
|
|
66
|
+
} catch (error) {
|
|
67
|
+
if (onError) onError(error, 'render-cache');
|
|
68
|
+
else console.error(`[render-cache] set cache failed, key: ${key}`, error);
|
|
69
|
+
}
|
|
45
70
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
71
|
+
} catch (error) {
|
|
72
|
+
await Promise.allSettled([
|
|
73
|
+
cancel(error),
|
|
74
|
+
writer.abort(error)
|
|
75
|
+
]);
|
|
76
|
+
} finally{
|
|
77
|
+
request.signal.removeEventListener('abort', onAbort);
|
|
78
|
+
}
|
|
79
|
+
})();
|
|
80
|
+
pumping.catch(()=>{});
|
|
52
81
|
cacheStatus && response.headers.set(X_RENDER_CACHE, cacheStatus);
|
|
53
82
|
return new Response(stream.readable, {
|
|
54
83
|
status: response.status,
|
|
@@ -144,6 +173,11 @@ async function getCacheResult(request, options) {
|
|
|
144
173
|
container
|
|
145
174
|
}).then(async (response)=>{
|
|
146
175
|
await response.text();
|
|
176
|
+
}).catch((error)=>{
|
|
177
|
+
try {
|
|
178
|
+
if (onError) onError(error, 'render-cache');
|
|
179
|
+
else console.error('[render-cache] revalidation failed', error);
|
|
180
|
+
} catch {}
|
|
147
181
|
});
|
|
148
182
|
const cacheStatus = 'stale';
|
|
149
183
|
return new Response(cache.val, {
|
package/dist/esm/serverBase.mjs
CHANGED
|
@@ -82,7 +82,9 @@ class ServerBase {
|
|
|
82
82
|
this.serverContext = null;
|
|
83
83
|
this.serverOptions = options;
|
|
84
84
|
this.app = new Hono();
|
|
85
|
-
this.app.use('*',
|
|
85
|
+
this.app.use('*', async (context, next)=>{
|
|
86
|
+
await run(context, next);
|
|
87
|
+
});
|
|
86
88
|
}
|
|
87
89
|
}
|
|
88
90
|
function applyMiddlewares() {
|
|
@@ -25,7 +25,9 @@ const createWebRequest = (req, res, body)=>{
|
|
|
25
25
|
headers: headerRecord,
|
|
26
26
|
signal: controller.signal
|
|
27
27
|
};
|
|
28
|
-
res.on('close', ()=>
|
|
28
|
+
res.on('close', ()=>{
|
|
29
|
+
if (!res.writableFinished || req.aborted) controller.abort('res closed');
|
|
30
|
+
});
|
|
29
31
|
const url = `http://${req.headers.host}${req.url}`;
|
|
30
32
|
const needsRequestBody = body || !('GET' === method || 'HEAD' === method);
|
|
31
33
|
const cloneableReq = needsRequestBody ? cloneable_readable(req) : null;
|
|
@@ -28,28 +28,51 @@ function injectTemplates(pwd, routes, htmlTemplatePromise) {
|
|
|
28
28
|
await next();
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
function reportBundleError(filepath, error, monitors) {
|
|
32
|
+
const detail = error instanceof Error ? error.stack || error.message : error;
|
|
33
|
+
if (monitors) monitors.error(`Load ${filepath} bundle failed, error = %s`, detail);
|
|
34
|
+
else console.error(`Load ${filepath} bundle failed, error = ${detail}`);
|
|
35
|
+
}
|
|
36
|
+
const loadBundle = async (filepath, monitors, { required = false, failOnError = false } = {})=>{
|
|
37
|
+
if (!required && !failOnError && !await fs.pathExists(filepath)) return;
|
|
33
38
|
try {
|
|
39
|
+
if (!required && failOnError) try {
|
|
40
|
+
await fs.access(filepath);
|
|
41
|
+
} catch (e) {
|
|
42
|
+
const code = e?.code;
|
|
43
|
+
if ('ENOENT' === code || 'ENOTDIR' === code) return;
|
|
44
|
+
throw e;
|
|
45
|
+
}
|
|
34
46
|
const module = await compatibleRequire(filepath, false);
|
|
35
47
|
return module;
|
|
36
48
|
} catch (e) {
|
|
37
|
-
|
|
38
|
-
|
|
49
|
+
reportBundleError(filepath, e, monitors);
|
|
50
|
+
if (failOnError) throw e;
|
|
39
51
|
return;
|
|
40
52
|
}
|
|
41
53
|
};
|
|
42
|
-
async function getServerManifest(pwd, routes, monitors) {
|
|
54
|
+
async function getServerManifest(pwd, routes, monitors, { failOnError = false } = {}) {
|
|
43
55
|
const loaderBundles = {};
|
|
44
56
|
const renderBundles = {};
|
|
45
57
|
await Promise.all(routes.filter((route)=>Boolean(route.bundle)).map(async (route)=>{
|
|
58
|
+
const failOnBundleError = failOnError && Boolean(route.isSSR);
|
|
46
59
|
const entryName = route.entryName || MAIN_ENTRY_NAME;
|
|
47
60
|
const renderBundlePath = path.join(pwd, route.bundle || '');
|
|
48
61
|
const loaderBundlePath = path.join(pwd, SERVER_BUNDLE_DIRECTORY, `${entryName}-server-loaders.js`);
|
|
49
|
-
const renderBundle = await loadBundle(renderBundlePath, monitors
|
|
50
|
-
|
|
62
|
+
const renderBundle = await loadBundle(renderBundlePath, monitors, {
|
|
63
|
+
required: failOnBundleError,
|
|
64
|
+
failOnError: failOnBundleError
|
|
65
|
+
});
|
|
66
|
+
const loaderBundle = await loadBundle(loaderBundlePath, monitors, {
|
|
67
|
+
failOnError: failOnBundleError
|
|
68
|
+
});
|
|
51
69
|
renderBundle && (renderBundles[entryName] = renderBundle);
|
|
52
|
-
|
|
70
|
+
if (loaderBundle) try {
|
|
71
|
+
loaderBundles[entryName] = loaderBundle.loadModules ? await loaderBundle.loadModules() : loaderBundle;
|
|
72
|
+
} catch (e) {
|
|
73
|
+
if (failOnBundleError) reportBundleError(loaderBundlePath, e, monitors);
|
|
74
|
+
throw e;
|
|
75
|
+
}
|
|
53
76
|
}));
|
|
54
77
|
const loadableUri = path.join(pwd, LOADABLE_STATS_FILE);
|
|
55
78
|
const loadableStats = await compatibleRequire(loadableUri).catch((_)=>({}));
|
|
@@ -117,13 +140,18 @@ const injectRscManifestPlugin = (enableRsc)=>({
|
|
|
117
140
|
const injectResourcePlugin = ()=>({
|
|
118
141
|
name: '@modern-js/plugin-inject-resource',
|
|
119
142
|
setup (api) {
|
|
120
|
-
api.onPrepare(()=>{
|
|
143
|
+
api.onPrepare(async ()=>{
|
|
121
144
|
const { middlewares, routes, distDirectory: pwd } = api.getServerContext();
|
|
122
145
|
let htmlTemplatePromise;
|
|
123
146
|
let manifestPromise;
|
|
124
147
|
if (isProd()) {
|
|
125
|
-
|
|
148
|
+
const warmupRoutes = routes || [];
|
|
149
|
+
const failOnError = warmupRoutes.some((route)=>route.isSSR && route.bundle) && !warmupRoutes.some((route)=>route.isRSC);
|
|
150
|
+
manifestPromise = getServerManifest(pwd, warmupRoutes, void 0, {
|
|
151
|
+
failOnError
|
|
152
|
+
});
|
|
126
153
|
htmlTemplatePromise = getHtmlTemplates(pwd, routes || []);
|
|
154
|
+
if (failOnError) await manifestPromise;
|
|
127
155
|
}
|
|
128
156
|
middlewares.push({
|
|
129
157
|
name: 'inject-server-manifest',
|
|
@@ -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 = [];
|
|
@@ -164,7 +158,7 @@ async function renderHandler(request, options, mode, fallbackWrapper, framework)
|
|
|
164
158
|
const { nestedRoutesJson } = serverManifest;
|
|
165
159
|
const routes = nestedRoutesJson?.[options.routeInfo.entryName];
|
|
166
160
|
if (routes) {
|
|
167
|
-
const { matchRoutes } =
|
|
161
|
+
const { matchRoutes } = __rspack_createRequire_require("@modern-js/runtime-utils/router");
|
|
168
162
|
const url = new URL(request.url);
|
|
169
163
|
const matchedRoutes = matchRoutes(routes, url.pathname, options.routeInfo.urlPath);
|
|
170
164
|
if (matchedRoutes) {
|
|
@@ -26,30 +26,59 @@ async function processCache({ request, key, requestHandler, requestHandlerOption
|
|
|
26
26
|
const reader = response.body.getReader();
|
|
27
27
|
const writer = stream.writable.getWriter();
|
|
28
28
|
let html = '';
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
let cancelled = false;
|
|
30
|
+
const cancel = async (reason)=>{
|
|
31
|
+
cancelled = true;
|
|
32
|
+
await reader.cancel(reason);
|
|
33
|
+
};
|
|
34
|
+
writer.closed.catch(cancel).catch(()=>{});
|
|
35
|
+
const onAbort = ()=>{
|
|
36
|
+
Promise.allSettled([
|
|
37
|
+
cancel(request.signal.reason),
|
|
38
|
+
writer.abort(request.signal.reason)
|
|
39
|
+
]);
|
|
40
|
+
};
|
|
41
|
+
request.signal.addEventListener('abort', onAbort, {
|
|
42
|
+
once: true
|
|
43
|
+
});
|
|
44
|
+
if (request.signal.aborted) onAbort();
|
|
45
|
+
const pumping = (async ()=>{
|
|
46
|
+
try {
|
|
47
|
+
for(;;){
|
|
48
|
+
const { done, value } = await reader.read();
|
|
49
|
+
if (cancelled) return;
|
|
50
|
+
if (done) break;
|
|
51
|
+
html += decoder.decode(value, {
|
|
52
|
+
stream: true
|
|
53
|
+
});
|
|
54
|
+
await writer.write(value);
|
|
55
|
+
}
|
|
56
|
+
html += decoder.decode();
|
|
57
|
+
await writer.close();
|
|
58
|
+
if (!cancelled && !ZERO_RENDER_LEVEL.test(html) && !NO_SSR_CACHE.test(html)) {
|
|
34
59
|
const cache = {
|
|
35
60
|
val: html,
|
|
36
|
-
cursor:
|
|
61
|
+
cursor: Date.now()
|
|
37
62
|
};
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
63
|
+
try {
|
|
64
|
+
await container.set(key, JSON.stringify(cache), {
|
|
65
|
+
ttl
|
|
66
|
+
});
|
|
67
|
+
} catch (error) {
|
|
68
|
+
if (onError) onError(error, 'render-cache');
|
|
69
|
+
else console.error(`[render-cache] set cache failed, key: ${key}`, error);
|
|
70
|
+
}
|
|
46
71
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
72
|
+
} catch (error) {
|
|
73
|
+
await Promise.allSettled([
|
|
74
|
+
cancel(error),
|
|
75
|
+
writer.abort(error)
|
|
76
|
+
]);
|
|
77
|
+
} finally{
|
|
78
|
+
request.signal.removeEventListener('abort', onAbort);
|
|
79
|
+
}
|
|
80
|
+
})();
|
|
81
|
+
pumping.catch(()=>{});
|
|
53
82
|
cacheStatus && response.headers.set(X_RENDER_CACHE, cacheStatus);
|
|
54
83
|
return new Response(stream.readable, {
|
|
55
84
|
status: response.status,
|
|
@@ -145,6 +174,11 @@ async function getCacheResult(request, options) {
|
|
|
145
174
|
container
|
|
146
175
|
}).then(async (response)=>{
|
|
147
176
|
await response.text();
|
|
177
|
+
}).catch((error)=>{
|
|
178
|
+
try {
|
|
179
|
+
if (onError) onError(error, 'render-cache');
|
|
180
|
+
else console.error('[render-cache] revalidation failed', error);
|
|
181
|
+
} catch {}
|
|
148
182
|
});
|
|
149
183
|
const cacheStatus = 'stale';
|
|
150
184
|
return new Response(cache.val, {
|
|
@@ -83,7 +83,9 @@ class ServerBase {
|
|
|
83
83
|
this.serverContext = null;
|
|
84
84
|
this.serverOptions = options;
|
|
85
85
|
this.app = new Hono();
|
|
86
|
-
this.app.use('*',
|
|
86
|
+
this.app.use('*', async (context, next)=>{
|
|
87
|
+
await run(context, next);
|
|
88
|
+
});
|
|
87
89
|
}
|
|
88
90
|
}
|
|
89
91
|
function applyMiddlewares() {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { httpCallBack2HonoMid, connectMid2HonoMid, connectMockMid2HonoMid, } from './hono.js';
|
|
2
2
|
export type { ServerNodeContext, ServerNodeMiddleware } from './hono.js';
|
|
3
|
-
export { createNodeServer, sendResponse, createWebRequest
|
|
3
|
+
export { createNodeServer, sendResponse, createWebRequest } from './node.js';
|
|
4
4
|
export { serverStaticPlugin, injectResourcePlugin, getHtmlTemplates, getServerManifest, injectNodeSeverPlugin, injectRscManifestPlugin, } from './plugins/index.js';
|
|
5
5
|
export { loadServerPlugins, loadServerEnv, loadServerRuntimeConfig, loadServerCliConfig, loadCacheConfig, } from './helper/index.js';
|
|
@@ -2,7 +2,9 @@ import type { Monitors, ServerRoute } from '@modern-js/types';
|
|
|
2
2
|
import type { Middleware, ServerEnv, ServerManifest, ServerPlugin } from '../../../types/index.js';
|
|
3
3
|
export declare function getHtmlTemplates(pwd: string, routes: ServerRoute[]): Promise<Record<string, string>>;
|
|
4
4
|
export declare function injectTemplates(pwd: string, routes?: ServerRoute[], htmlTemplatePromise?: ReturnType<typeof getHtmlTemplates>): Middleware<ServerEnv>;
|
|
5
|
-
export declare function getServerManifest(pwd: string, routes: ServerRoute[], monitors?: Monitors
|
|
5
|
+
export declare function getServerManifest(pwd: string, routes: ServerRoute[], monitors?: Monitors, { failOnError }?: {
|
|
6
|
+
failOnError?: boolean | undefined;
|
|
7
|
+
}): Promise<ServerManifest>;
|
|
6
8
|
export declare function injectServerManifest(pwd: string, routes?: ServerRoute[], manifestPromise?: Promise<ServerManifest>): Middleware<ServerEnv>;
|
|
7
9
|
export declare function getRscServerManifest(pwd: string): Promise<any>;
|
|
8
10
|
export declare function getClientManifest(pwd: string): Promise<any>;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { ServerRoute } from '@modern-js/types';
|
|
2
2
|
import type { HtmlNormalizedConfig, Middleware, OutputNormalizedConfig, ServerNormalizedConfig, ServerPlugin } from '../../../types/index.js';
|
|
3
3
|
export declare const serverStaticPlugin: () => ServerPlugin;
|
|
4
|
-
export type
|
|
4
|
+
export type PublicMiddlewareOptions = {
|
|
5
5
|
pwd: string;
|
|
6
6
|
routes: ServerRoute[];
|
|
7
7
|
};
|
|
8
|
-
export declare function createPublicMiddleware({ pwd, routes, }:
|
|
8
|
+
export declare function createPublicMiddleware({ pwd, routes, }: PublicMiddlewareOptions): Middleware;
|
|
9
9
|
export interface ServerStaticOptions {
|
|
10
10
|
pwd: string;
|
|
11
11
|
output: OutputNormalizedConfig;
|
package/dist/types/hono.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export type { Context, Next, MiddlewareHandler, MiddlewareHandler as Middleware, HonoRequest, } from 'hono';
|
|
2
|
-
export { setCookie, getCookie, deleteCookie
|
|
2
|
+
export { setCookie, getCookie, deleteCookie } from 'hono/cookie';
|
|
3
3
|
export { languageDetector } from 'hono/language';
|
package/package.json
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"modern",
|
|
16
16
|
"modern.js"
|
|
17
17
|
],
|
|
18
|
-
"version": "3.
|
|
18
|
+
"version": "3.10.0",
|
|
19
19
|
"types": "./dist/types/index.d.ts",
|
|
20
20
|
"main": "./dist/cjs/index.js",
|
|
21
21
|
"exports": {
|
|
@@ -70,20 +70,19 @@
|
|
|
70
70
|
"cloneable-readable": "^3.0.0",
|
|
71
71
|
"flatted": "^3.4.4",
|
|
72
72
|
"hono": "^4.11.7",
|
|
73
|
-
"ts-deepmerge": "
|
|
74
|
-
"@modern-js/plugin": "3.
|
|
75
|
-
"@modern-js/runtime-utils": "3.
|
|
76
|
-
"@modern-js/utils": "3.
|
|
73
|
+
"ts-deepmerge": "8.0.0",
|
|
74
|
+
"@modern-js/plugin": "3.10.0",
|
|
75
|
+
"@modern-js/runtime-utils": "3.10.0",
|
|
76
|
+
"@modern-js/utils": "3.10.0"
|
|
77
77
|
},
|
|
78
78
|
"devDependencies": {
|
|
79
|
-
"@rslib/core": "1.0.0",
|
|
80
79
|
"@types/cloneable-readable": "^2.0.3",
|
|
81
80
|
"@types/merge-deep": "^3.0.3",
|
|
82
81
|
"@types/node": "^20",
|
|
83
82
|
"http-proxy-middleware": "^2.0.10",
|
|
84
83
|
"typescript": "^5",
|
|
85
84
|
"@modern-js/rslib": "2.68.10",
|
|
86
|
-
"@modern-js/types": "3.
|
|
85
|
+
"@modern-js/types": "3.10.0",
|
|
87
86
|
"@scripts/rstest-config": "2.66.0"
|
|
88
87
|
},
|
|
89
88
|
"sideEffects": false,
|
|
@@ -92,8 +91,8 @@
|
|
|
92
91
|
"access": "public"
|
|
93
92
|
},
|
|
94
93
|
"scripts": {
|
|
95
|
-
"dev": "
|
|
96
|
-
"build": "
|
|
94
|
+
"dev": "rs lib --watch",
|
|
95
|
+
"build": "rs lib",
|
|
97
96
|
"test": "rstest --passWithNoTests"
|
|
98
97
|
}
|
|
99
98
|
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
var __webpack_modules__ = {};
|
|
2
|
-
var __webpack_module_cache__ = {};
|
|
3
|
-
function __webpack_require__(moduleId) {
|
|
4
|
-
var cachedModule = __webpack_module_cache__[moduleId];
|
|
5
|
-
if (void 0 !== cachedModule) return cachedModule.exports;
|
|
6
|
-
var module = __webpack_module_cache__[moduleId] = {
|
|
7
|
-
exports: {}
|
|
8
|
-
};
|
|
9
|
-
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
|
10
|
-
return module.exports;
|
|
11
|
-
}
|
|
12
|
-
__webpack_require__.m = __webpack_modules__;
|
|
13
|
-
(()=>{
|
|
14
|
-
__webpack_require__.add = function(modules) {
|
|
15
|
-
Object.assign(__webpack_require__.m, modules);
|
|
16
|
-
};
|
|
17
|
-
})();
|
|
18
|
-
export { __webpack_require__ };
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import "node:module";
|
|
2
|
-
var __webpack_modules__ = {};
|
|
3
|
-
var __webpack_module_cache__ = {};
|
|
4
|
-
function __webpack_require__(moduleId) {
|
|
5
|
-
var cachedModule = __webpack_module_cache__[moduleId];
|
|
6
|
-
if (void 0 !== cachedModule) return cachedModule.exports;
|
|
7
|
-
var module = __webpack_module_cache__[moduleId] = {
|
|
8
|
-
exports: {}
|
|
9
|
-
};
|
|
10
|
-
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
|
11
|
-
return module.exports;
|
|
12
|
-
}
|
|
13
|
-
__webpack_require__.m = __webpack_modules__;
|
|
14
|
-
(()=>{
|
|
15
|
-
__webpack_require__.add = function(modules) {
|
|
16
|
-
Object.assign(__webpack_require__.m, modules);
|
|
17
|
-
};
|
|
18
|
-
})();
|
|
19
|
-
export { __webpack_require__ };
|
package/rslib.config.mts
DELETED