@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.
@@ -68,7 +68,9 @@ const createWebRequest = (req, res, body)=>{
68
68
  headers: headerRecord,
69
69
  signal: controller.signal
70
70
  };
71
- res.on('close', ()=>controller.abort('res closed'));
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
- const loadBundle = async (filepath, monitors)=>{
81
- if (!await utils_namespaceObject.fs.pathExists(filepath)) return;
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
- if (monitors) monitors.error(`Load ${filepath} bundle failed, error = %s`, e instanceof Error ? e.stack || e.message : e);
87
- else console.error(`Load ${filepath} bundle failed, error = ${e instanceof Error ? e.stack || e.message : e}`);
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
- const loaderBundle = await loadBundle(loaderBundlePath, monitors);
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
- loaderBundle && (loaderBundles[entryName] = loaderBundle?.loadModules ? await loaderBundle?.loadModules() : loaderBundle);
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
- manifestPromise = getServerManifest(pwd, routes || [], void 0);
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
- const push = ()=>reader.read().then(({ done, value })=>{
63
- if (done) {
64
- const match = ZERO_RENDER_LEVEL.test(html) || NO_SSR_CACHE.test(html);
65
- if (match) return void writer.close();
66
- const current = Date.now();
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: current
94
+ cursor: Date.now()
70
95
  };
71
- container.set(key, JSON.stringify(cache), {
72
- ttl
73
- }).catch(()=>{
74
- if (onError) onError(`[render-cache] set cache failed, key: ${key}, value: ${JSON.stringify(cache)}`);
75
- else console.error(`[render-cache] set cache failed, key: ${key}, value: ${JSON.stringify(cache)}`);
76
- });
77
- writer.close();
78
- return;
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
- const content = decoder.decode(value);
81
- html += content;
82
- writer.write(value);
83
- push();
84
- });
85
- push();
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, {
@@ -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('*', external_context_js_namespaceObject.run);
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', ()=>controller.abort('res closed'));
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
- const loadBundle = async (filepath, monitors)=>{
31
- if (!await fs.pathExists(filepath)) return;
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
- if (monitors) monitors.error(`Load ${filepath} bundle failed, error = %s`, e instanceof Error ? e.stack || e.message : e);
37
- else console.error(`Load ${filepath} bundle failed, error = ${e instanceof Error ? e.stack || e.message : e}`);
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
- const loaderBundle = await loadBundle(loaderBundlePath, monitors);
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
- loaderBundle && (loaderBundles[entryName] = loaderBundle?.loadModules ? await loaderBundle?.loadModules() : loaderBundle);
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
- manifestPromise = getServerManifest(pwd, routes || [], void 0);
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 } = __webpack_require__(718);
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
- const push = ()=>reader.read().then(({ done, value })=>{
29
- if (done) {
30
- const match = ZERO_RENDER_LEVEL.test(html) || NO_SSR_CACHE.test(html);
31
- if (match) return void writer.close();
32
- const current = Date.now();
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: current
60
+ cursor: Date.now()
36
61
  };
37
- container.set(key, JSON.stringify(cache), {
38
- ttl
39
- }).catch(()=>{
40
- if (onError) onError(`[render-cache] set cache failed, key: ${key}, value: ${JSON.stringify(cache)}`);
41
- else console.error(`[render-cache] set cache failed, key: ${key}, value: ${JSON.stringify(cache)}`);
42
- });
43
- writer.close();
44
- return;
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
- const content = decoder.decode(value);
47
- html += content;
48
- writer.write(value);
49
- push();
50
- });
51
- push();
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, {
@@ -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('*', run);
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', ()=>controller.abort('res closed'));
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
- const loadBundle = async (filepath, monitors)=>{
32
- if (!await fs.pathExists(filepath)) return;
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
- if (monitors) monitors.error(`Load ${filepath} bundle failed, error = %s`, e instanceof Error ? e.stack || e.message : e);
38
- else console.error(`Load ${filepath} bundle failed, error = ${e instanceof Error ? e.stack || e.message : e}`);
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
- const loaderBundle = await loadBundle(loaderBundlePath, monitors);
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
- loaderBundle && (loaderBundles[entryName] = loaderBundle?.loadModules ? await loaderBundle?.loadModules() : loaderBundle);
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
- manifestPromise = getServerManifest(pwd, routes || [], void 0);
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 } = __webpack_require__("@modern-js/runtime-utils/router");
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
- const push = ()=>reader.read().then(({ done, value })=>{
30
- if (done) {
31
- const match = ZERO_RENDER_LEVEL.test(html) || NO_SSR_CACHE.test(html);
32
- if (match) return void writer.close();
33
- const current = Date.now();
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: current
61
+ cursor: Date.now()
37
62
  };
38
- container.set(key, JSON.stringify(cache), {
39
- ttl
40
- }).catch(()=>{
41
- if (onError) onError(`[render-cache] set cache failed, key: ${key}, value: ${JSON.stringify(cache)}`);
42
- else console.error(`[render-cache] set cache failed, key: ${key}, value: ${JSON.stringify(cache)}`);
43
- });
44
- writer.close();
45
- return;
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
- const content = decoder.decode(value);
48
- html += content;
49
- writer.write(value);
50
- push();
51
- });
52
- push();
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('*', run);
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, } from './node.js';
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): Promise<ServerManifest>;
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 PublicMiddlwareOptions = {
4
+ export type PublicMiddlewareOptions = {
5
5
  pwd: string;
6
6
  routes: ServerRoute[];
7
7
  };
8
- export declare function createPublicMiddleware({ pwd, routes, }: PublicMiddlwareOptions): Middleware;
8
+ export declare function createPublicMiddleware({ pwd, routes, }: PublicMiddlewareOptions): Middleware;
9
9
  export interface ServerStaticOptions {
10
10
  pwd: string;
11
11
  output: OutputNormalizedConfig;
@@ -1,3 +1,3 @@
1
1
  export type { Context, Next, MiddlewareHandler, MiddlewareHandler as Middleware, HonoRequest, } from 'hono';
2
- export { setCookie, getCookie, deleteCookie, } from 'hono/cookie';
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.9.2",
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": "7.0.3",
74
- "@modern-js/plugin": "3.9.2",
75
- "@modern-js/runtime-utils": "3.9.2",
76
- "@modern-js/utils": "3.9.2"
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.9.2",
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": "rslib build --watch",
96
- "build": "rslib build",
94
+ "dev": "rs lib --watch",
95
+ "build": "rs lib",
97
96
  "test": "rstest --passWithNoTests"
98
97
  }
99
98
  }
@@ -0,0 +1,4 @@
1
+ import { rslibConfig } from '@modern-js/rslib';
2
+ import { define } from 'rstack';
3
+
4
+ define.lib(rslibConfig);
@@ -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
@@ -1,4 +0,0 @@
1
- import { rslibConfig } from '@modern-js/rslib';
2
- import { defineConfig } from '@rslib/core';
3
-
4
- export default defineConfig(rslibConfig);