@bleedingdev/modern-js-runtime 3.9.0-ultramodern.9 → 3.9.3-ultramodern.1
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/cli/code.js +2 -2
- package/dist/cjs/cli/index.js +0 -7
- package/dist/cjs/cli/ssr/loadable-bundler-plugin.js +2 -0
- package/dist/cjs/cli/template.server.js +1 -2
- package/dist/cjs/core/browser/hydrate.js +3 -9
- package/dist/cjs/core/browser/index.js +4 -5
- package/dist/cjs/core/context/index.js +8 -2
- package/dist/cjs/core/context/public.js +4 -4
- package/dist/cjs/core/context/runtime.js +3 -9
- package/dist/cjs/core/index.js +3 -3
- package/dist/cjs/core/plugin/index.js +2 -2
- package/dist/cjs/core/react/index.js +3 -3
- package/dist/cjs/core/react/wrapper.js +7 -8
- package/dist/cjs/core/server/requestHandler.js +253 -25
- package/dist/cjs/core/server/stream/beforeTemplate.js +3 -3
- package/dist/cjs/core/server/stream/beforeTemplate.worker.js +2 -2
- package/dist/cjs/core/server/stream/createReadableStream.js +51 -19
- package/dist/cjs/core/server/stream/createReadableStream.worker.js +54 -27
- package/dist/cjs/core/server/stream/deferredScript.js +12 -6
- package/dist/cjs/core/server/stream/deferredScriptOutputCoordinator.js +370 -0
- package/dist/cjs/core/server/stream/shared.js +4 -0
- package/dist/cjs/core/server/string/index.js +6 -4
- package/dist/cjs/core/server/string/loadable.js +4 -4
- package/dist/cjs/document/cli/index.js +14 -5
- package/dist/cjs/exports/head.js +2 -2
- package/dist/cjs/internal.js +2 -2
- package/dist/cjs/router/cli/code/nestedRoutes.js +1 -2
- package/dist/cjs/router/cli/code/templates.js +2 -2
- package/dist/cjs/router/cli/code/utils.js +5 -4
- package/dist/cjs/router/runtime/DeferredDataScripts.node.js +13 -10
- package/dist/cjs/router/runtime/PrefetchLink.js +4 -4
- package/dist/cjs/router/runtime/hooks.js +6 -7
- package/dist/cjs/router/runtime/plugin.js +22 -27
- package/dist/cjs/router/runtime/plugin.node.js +8 -9
- package/dist/esm/cli/code.mjs +2 -2
- package/dist/esm/cli/index.mjs +0 -7
- package/dist/esm/cli/ssr/loadable-bundler-plugin.mjs +2 -0
- package/dist/esm/cli/template.server.mjs +1 -2
- package/dist/esm/core/browser/hydrate.mjs +3 -9
- package/dist/esm/core/browser/index.mjs +1 -2
- package/dist/esm/core/context/index.mjs +1 -1
- package/dist/esm/core/context/public.mjs +1 -1
- package/dist/esm/core/context/runtime.mjs +3 -9
- package/dist/esm/core/index.mjs +1 -1
- package/dist/esm/core/plugin/index.mjs +1 -1
- package/dist/esm/core/react/index.mjs +1 -1
- package/dist/esm/core/react/wrapper.mjs +1 -2
- package/dist/esm/core/server/requestHandler.mjs +223 -19
- package/dist/esm/core/server/stream/beforeTemplate.mjs +3 -3
- package/dist/esm/core/server/stream/beforeTemplate.worker.mjs +2 -2
- package/dist/esm/core/server/stream/createReadableStream.mjs +48 -16
- package/dist/esm/core/server/stream/createReadableStream.worker.mjs +53 -26
- package/dist/esm/core/server/stream/deferredScript.mjs +12 -6
- package/dist/esm/core/server/stream/deferredScriptOutputCoordinator.mjs +332 -0
- package/dist/esm/core/server/stream/shared.mjs +2 -1
- package/dist/esm/core/server/string/index.mjs +3 -1
- package/dist/esm/core/server/string/loadable.mjs +4 -4
- package/dist/esm/document/cli/index.mjs +12 -6
- package/dist/esm/exports/head.mjs +1 -1
- package/dist/esm/internal.mjs +1 -1
- package/dist/esm/router/cli/code/nestedRoutes.mjs +1 -2
- package/dist/esm/router/cli/code/templates.mjs +2 -2
- package/dist/esm/router/cli/code/utils.mjs +6 -5
- package/dist/esm/router/runtime/DeferredDataScripts.node.mjs +13 -10
- package/dist/esm/router/runtime/PrefetchLink.mjs +1 -1
- package/dist/esm/router/runtime/hooks.mjs +6 -7
- package/dist/esm/router/runtime/plugin.mjs +14 -19
- package/dist/esm/router/runtime/plugin.node.mjs +3 -4
- package/dist/esm-node/cli/code.mjs +2 -2
- package/dist/esm-node/cli/index.mjs +1 -9
- package/dist/esm-node/cli/ssr/loadable-bundler-plugin.mjs +2 -0
- package/dist/esm-node/cli/template.server.mjs +1 -2
- package/dist/esm-node/core/browser/hydrate.mjs +3 -9
- package/dist/esm-node/core/browser/index.mjs +1 -2
- package/dist/esm-node/core/context/index.mjs +1 -1
- package/dist/esm-node/core/context/public.mjs +1 -1
- package/dist/esm-node/core/context/runtime.mjs +3 -9
- package/dist/esm-node/core/index.mjs +1 -1
- package/dist/esm-node/core/plugin/index.mjs +1 -1
- package/dist/esm-node/core/react/index.mjs +1 -1
- package/dist/esm-node/core/react/wrapper.mjs +1 -2
- package/dist/esm-node/core/server/requestHandler.mjs +223 -19
- package/dist/esm-node/core/server/stream/beforeTemplate.mjs +3 -3
- package/dist/esm-node/core/server/stream/beforeTemplate.worker.mjs +2 -2
- package/dist/esm-node/core/server/stream/createReadableStream.mjs +48 -16
- package/dist/esm-node/core/server/stream/createReadableStream.worker.mjs +53 -26
- package/dist/esm-node/core/server/stream/deferredScript.mjs +12 -6
- package/dist/esm-node/core/server/stream/deferredScriptOutputCoordinator.mjs +333 -0
- package/dist/esm-node/core/server/stream/shared.mjs +2 -1
- package/dist/esm-node/core/server/string/index.mjs +3 -1
- package/dist/esm-node/core/server/string/loadable.mjs +4 -4
- package/dist/esm-node/document/cli/index.mjs +12 -6
- package/dist/esm-node/exports/head.mjs +1 -1
- package/dist/esm-node/internal.mjs +1 -1
- package/dist/esm-node/router/cli/code/nestedRoutes.mjs +1 -2
- package/dist/esm-node/router/cli/code/templates.mjs +2 -2
- package/dist/esm-node/router/cli/code/utils.mjs +6 -5
- package/dist/esm-node/router/runtime/DeferredDataScripts.node.mjs +13 -10
- package/dist/esm-node/router/runtime/PrefetchLink.mjs +1 -1
- package/dist/esm-node/router/runtime/hooks.mjs +6 -7
- package/dist/esm-node/router/runtime/plugin.mjs +14 -19
- package/dist/esm-node/router/runtime/plugin.node.mjs +3 -4
- package/dist/types/common.d.ts +2 -8
- package/dist/types/core/context/index.d.ts +1 -1
- package/dist/types/core/context/public.d.ts +1 -1
- package/dist/types/core/index.d.ts +1 -1
- package/dist/types/core/react/wrapper.d.ts +1 -1
- package/dist/types/core/server/requestHandler.d.ts +30 -1
- package/dist/types/core/server/stream/deferredScript.d.ts +1 -1
- package/dist/types/core/server/stream/deferredScriptOutputCoordinator.d.ts +72 -0
- package/dist/types/core/server/stream/shared.d.ts +14 -0
- package/dist/types/document/cli/index.d.ts +1 -0
- package/dist/types/internal.d.ts +1 -1
- package/dist/types/router/cli/code/utils.d.ts +1 -1
- package/dist/types/router/runtime/hooks.d.ts +12 -16
- package/package.json +32 -26
- package/rslib.config.mts +3 -0
- package/rstest.config.mts +0 -4
- package/dist/cjs/core/server/requestResponse.js +0 -107
- package/dist/cjs/core/server/routerCleanup.js +0 -129
- package/dist/cjs/rsc/server.worker.js +0 -62
- package/dist/esm/core/server/requestResponse.mjs +0 -63
- package/dist/esm/core/server/routerCleanup.mjs +0 -82
- package/dist/esm/rsc/server.worker.mjs +0 -1
- package/dist/esm-node/core/server/requestResponse.mjs +0 -64
- package/dist/esm-node/core/server/routerCleanup.mjs +0 -83
- package/dist/esm-node/rsc/server.worker.mjs +0 -2
- package/dist/types/core/server/requestResponse.d.ts +0 -15
- package/dist/types/core/server/routerCleanup.d.ts +0 -31
- package/dist/types/rsc/server.worker.d.ts +0 -1
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
import { createContext, useContext } from "react";
|
|
2
2
|
const ROUTE_MANIFEST = '_MODERNJS_ROUTE_MANIFEST';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
contexts[key] ??= create();
|
|
7
|
-
return contexts[key];
|
|
8
|
-
}
|
|
9
|
-
const InternalRuntimeContext = getRuntimeReactContext('internal', ()=>createContext({}));
|
|
10
|
-
const RuntimeContext = getRuntimeReactContext('public', ()=>createContext({}));
|
|
11
|
-
const RuntimeComponentResolverContext = getRuntimeReactContext('component-resolver', ()=>createContext(void 0));
|
|
3
|
+
const InternalRuntimeContext = createContext({});
|
|
4
|
+
const RuntimeContext = createContext({});
|
|
5
|
+
const RuntimeComponentResolverContext = createContext(void 0);
|
|
12
6
|
const ReactRuntimeContext = RuntimeContext;
|
|
13
7
|
const useRuntimeContext = ()=>useContext(RuntimeContext);
|
|
14
8
|
const getInitialContext = (isBrowser = true, routeManifest)=>{
|
package/dist/esm/core/index.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { runtime } from "@modern-js/plugin/runtime";
|
|
2
|
+
import { setGlobalInternalRuntimeContext } from "@modern-js/runtime/context";
|
|
2
3
|
import { merge } from "@modern-js/runtime-utils/merge";
|
|
3
4
|
import { compatPlugin } from "../compat/index.mjs";
|
|
4
5
|
import { handleSetupResult } from "../compat/hooks.mjs";
|
|
5
6
|
import { requestContextPlugin } from "../compat/requestContext.mjs";
|
|
6
|
-
import { setGlobalInternalRuntimeContext } from "../context/index.mjs";
|
|
7
7
|
function registerPlugin(internalPlugins, runtimeConfig) {
|
|
8
8
|
const { plugins = [] } = runtimeConfig || {};
|
|
9
9
|
const { runtimeContext } = runtime.run({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { getGlobalApp, getGlobalInternalRuntimeContext } from "@modern-js/runtime/context";
|
|
1
2
|
import { parsedJSONFromElement } from "@modern-js/runtime-utils/parsed";
|
|
2
3
|
import { isBrowser } from "../../common.mjs";
|
|
3
4
|
import { ROUTER_DATA_JSON_ID, SSR_DATA_JSON_ID } from "../constants.mjs";
|
|
4
|
-
import { getGlobalApp, getGlobalInternalRuntimeContext } from "../context/index.mjs";
|
|
5
5
|
function createRoot(UserApp) {
|
|
6
6
|
const App = UserApp || getGlobalApp();
|
|
7
7
|
if (isBrowser()) {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import { InternalRuntimeContext, RuntimeContext, getGlobalEnableRsc, getGlobalInternalRuntimeContext } from "
|
|
3
|
-
import { RuntimeComponentResolverContext } from "../context/runtime.mjs";
|
|
2
|
+
import { InternalRuntimeContext, RuntimeComponentResolverContext, RuntimeContext, getGlobalEnableRsc, getGlobalInternalRuntimeContext } from "@modern-js/runtime/context";
|
|
4
3
|
function wrapRuntimeComponentResolver(root, hooks = getGlobalInternalRuntimeContext()?.hooks) {
|
|
5
4
|
return /*#__PURE__*/ jsx(RuntimeComponentResolverContext.Provider, {
|
|
6
5
|
value: hooks?.resolveComponent?.call,
|
|
@@ -1,15 +1,195 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { getGlobalInternalRuntimeContext, getGlobalRSCRoot, getInitialContext } from "@modern-js/runtime/context";
|
|
2
3
|
import { storage } from "@modern-js/runtime-utils/node";
|
|
3
4
|
import { getPathname, parseCookie, parseHeaders, parseQuery } from "@modern-js/runtime-utils/universal/request";
|
|
4
5
|
import react, { Fragment } from "react";
|
|
5
|
-
import {
|
|
6
|
-
import { getInitialContext } from "../context/runtime.mjs";
|
|
6
|
+
import { handleRSCRedirect } from "../../router/runtime/redirect.mjs";
|
|
7
7
|
import { getServerPayload } from "../context/serverPayload/index.mjs";
|
|
8
8
|
import { createRoot } from "../react/index.mjs";
|
|
9
9
|
import { CHUNK_CSS_PLACEHOLDER } from "./constants.mjs";
|
|
10
|
-
import {
|
|
11
|
-
import { createRouterCleanup, finishWithRouterCleanup, runWithRouterCleanupOnError } from "./routerCleanup.mjs";
|
|
10
|
+
import { SSRErrors } from "./tracer.mjs";
|
|
12
11
|
import { getSSRConfigByEntry, getSSRMode } from "./utils.mjs";
|
|
12
|
+
const REQUEST_END_ERROR = 'An error occurs during request lifecycle completion';
|
|
13
|
+
const RESPONSE_BODY_CANCEL_ERROR = 'An error occurs while cancelling a discarded response body';
|
|
14
|
+
async function runWithRequestLifecycleOnError(lifecycle, callback) {
|
|
15
|
+
try {
|
|
16
|
+
return await callback();
|
|
17
|
+
} catch (error) {
|
|
18
|
+
await lifecycle.run({
|
|
19
|
+
status: 'error',
|
|
20
|
+
error
|
|
21
|
+
});
|
|
22
|
+
throw error;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async function finishWithRequestLifecycle(lifecycle, callback) {
|
|
26
|
+
try {
|
|
27
|
+
return await callback();
|
|
28
|
+
} catch (error) {
|
|
29
|
+
if (!lifecycle.deferred) await lifecycle.run({
|
|
30
|
+
status: 'error',
|
|
31
|
+
error
|
|
32
|
+
});
|
|
33
|
+
throw error;
|
|
34
|
+
} finally{
|
|
35
|
+
if (!lifecycle.deferred) await lifecycle.run();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function createRequestLifecycle(onEnd, onError) {
|
|
39
|
+
let deferred = false;
|
|
40
|
+
let finished;
|
|
41
|
+
const run = (terminal = {
|
|
42
|
+
status: 'complete'
|
|
43
|
+
})=>{
|
|
44
|
+
finished ??= Promise.resolve().then(()=>onEnd(terminal)).catch((error)=>{
|
|
45
|
+
onError(error, REQUEST_END_ERROR);
|
|
46
|
+
});
|
|
47
|
+
return finished;
|
|
48
|
+
};
|
|
49
|
+
const deferUntilBodyDone = (response)=>{
|
|
50
|
+
const { body } = response;
|
|
51
|
+
if (!body) return response;
|
|
52
|
+
deferred = true;
|
|
53
|
+
if (body.locked) throw new TypeError('Cannot observe a locked response body before request completion');
|
|
54
|
+
let cancelled = false;
|
|
55
|
+
const reader = body.getReader();
|
|
56
|
+
const wrappedBody = new ReadableStream({
|
|
57
|
+
async pull (controller) {
|
|
58
|
+
let result;
|
|
59
|
+
try {
|
|
60
|
+
result = await reader.read();
|
|
61
|
+
} catch (error) {
|
|
62
|
+
if (!cancelled) {
|
|
63
|
+
await run({
|
|
64
|
+
status: 'error',
|
|
65
|
+
error
|
|
66
|
+
});
|
|
67
|
+
controller.error(error);
|
|
68
|
+
}
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (cancelled) return;
|
|
72
|
+
if (result.done) {
|
|
73
|
+
await run();
|
|
74
|
+
if (!cancelled) controller.close();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
controller.enqueue(result.value);
|
|
78
|
+
},
|
|
79
|
+
async cancel (reason) {
|
|
80
|
+
cancelled = true;
|
|
81
|
+
try {
|
|
82
|
+
await reader.cancel(reason);
|
|
83
|
+
} catch {}
|
|
84
|
+
await run({
|
|
85
|
+
status: 'cancelled',
|
|
86
|
+
reason
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
return new Response(wrappedBody, {
|
|
91
|
+
status: response.status,
|
|
92
|
+
statusText: response.statusText,
|
|
93
|
+
headers: response.headers
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
const discardBody = async (response, terminal = {
|
|
97
|
+
status: 'discarded'
|
|
98
|
+
})=>{
|
|
99
|
+
const { body } = response;
|
|
100
|
+
if (!body) return void await run(terminal);
|
|
101
|
+
deferred = true;
|
|
102
|
+
if (body.locked) throw new TypeError('Cannot discard a locked response body before request completion');
|
|
103
|
+
try {
|
|
104
|
+
await body.cancel('Response body discarded during finalization');
|
|
105
|
+
} catch (error) {
|
|
106
|
+
await run('error' === terminal.status ? terminal : {
|
|
107
|
+
status: 'error',
|
|
108
|
+
error
|
|
109
|
+
});
|
|
110
|
+
if ('error' === terminal.status) onError(error, RESPONSE_BODY_CANCEL_ERROR);
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
await run(terminal);
|
|
114
|
+
};
|
|
115
|
+
return {
|
|
116
|
+
get deferred () {
|
|
117
|
+
return deferred;
|
|
118
|
+
},
|
|
119
|
+
run,
|
|
120
|
+
deferUntilBodyDone,
|
|
121
|
+
discardBody
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
const isRedirectStatus = (status)=>301 === status || 302 === status || 303 === status || 307 === status || 308 === status;
|
|
125
|
+
const isNullBodyStatus = (status)=>204 === status || 205 === status || 304 === status;
|
|
126
|
+
const getRedirectLocation = (headers)=>{
|
|
127
|
+
const location = headers.get('Location');
|
|
128
|
+
return null !== location && '' !== location && URL.canParse(location, 'http://localhost') ? location : void 0;
|
|
129
|
+
};
|
|
130
|
+
const processRedirect = (headers, status, ctx)=>{
|
|
131
|
+
headers.delete('content-length');
|
|
132
|
+
headers.delete('transfer-encoding');
|
|
133
|
+
if (ctx.enableRsc && ctx.isRSCNavigation) return handleRSCRedirect(headers, ctx.basename, status);
|
|
134
|
+
return new Response(null, {
|
|
135
|
+
status,
|
|
136
|
+
headers
|
|
137
|
+
});
|
|
138
|
+
};
|
|
139
|
+
const applyRouterResult = (context, routerResult, onError)=>{
|
|
140
|
+
const routerStatusCode = routerResult?.statusCode;
|
|
141
|
+
if (void 0 !== routerStatusCode && 0 !== routerStatusCode && !Number.isNaN(routerStatusCode) && 200 !== routerStatusCode) context.ssrContext?.response.status(routerStatusCode);
|
|
142
|
+
const errors = Object.values(routerResult?.errors || {});
|
|
143
|
+
if (errors.length > 0) onError(errors[0], SSRErrors.LOADER_ERROR);
|
|
144
|
+
};
|
|
145
|
+
const createLoaderRedirectResponse = (beforeRenderResult, redirectCtx)=>{
|
|
146
|
+
if (void 0 === beforeRenderResult || !isRedirectStatus(beforeRenderResult.status)) return;
|
|
147
|
+
if (beforeRenderResult.headers.has('X-Modernjs-Redirect')) return beforeRenderResult;
|
|
148
|
+
const redirectUrl = getRedirectLocation(beforeRenderResult.headers);
|
|
149
|
+
if (void 0 === redirectUrl) return;
|
|
150
|
+
return processRedirect(new Headers(beforeRenderResult.headers), beforeRenderResult.status, redirectCtx);
|
|
151
|
+
};
|
|
152
|
+
const finalizeRenderResponse = async (response, responseProxy, redirectCtx, lifecycle)=>{
|
|
153
|
+
try {
|
|
154
|
+
const proxyHeaders = new Headers(responseProxy.headers);
|
|
155
|
+
if (-1 !== responseProxy.status && isRedirectStatus(responseProxy.status) && void 0 !== getRedirectLocation(proxyHeaders)) {
|
|
156
|
+
await lifecycle.discardBody(response);
|
|
157
|
+
return processRedirect(proxyHeaders, responseProxy.status, redirectCtx);
|
|
158
|
+
}
|
|
159
|
+
const headers = new Headers(response.headers);
|
|
160
|
+
Object.entries(responseProxy.headers).forEach(([key, value])=>{
|
|
161
|
+
headers.set(key, value);
|
|
162
|
+
});
|
|
163
|
+
if (-1 !== responseProxy.status) {
|
|
164
|
+
if (isNullBodyStatus(responseProxy.status)) {
|
|
165
|
+
await lifecycle.discardBody(response);
|
|
166
|
+
headers.delete('content-length');
|
|
167
|
+
headers.delete('transfer-encoding');
|
|
168
|
+
return new Response(null, {
|
|
169
|
+
status: responseProxy.status,
|
|
170
|
+
headers
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
if (response.body?.locked) lifecycle.deferUntilBodyDone(response);
|
|
174
|
+
return lifecycle.deferUntilBodyDone(new Response(response.body, {
|
|
175
|
+
status: responseProxy.status,
|
|
176
|
+
headers
|
|
177
|
+
}));
|
|
178
|
+
}
|
|
179
|
+
Object.entries(responseProxy.headers).forEach(([key, value])=>{
|
|
180
|
+
response.headers.set(key, value);
|
|
181
|
+
});
|
|
182
|
+
return lifecycle.deferUntilBodyDone(response);
|
|
183
|
+
} catch (error) {
|
|
184
|
+
if (!lifecycle.deferred) try {
|
|
185
|
+
await lifecycle.discardBody(response, {
|
|
186
|
+
status: 'error',
|
|
187
|
+
error
|
|
188
|
+
});
|
|
189
|
+
} catch {}
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
};
|
|
13
193
|
async function handleRSCRequest(request, Root, context, options, handleRequest) {
|
|
14
194
|
const serverPayload = getServerPayload();
|
|
15
195
|
if (void 0 !== serverPayload) return await handleRequest(request, Root, {
|
|
@@ -100,12 +280,14 @@ const createRequestHandler = async (handleRequest, createRequestOptions)=>{
|
|
|
100
280
|
status: -1
|
|
101
281
|
};
|
|
102
282
|
const activeDeferreds = new Map();
|
|
283
|
+
const deferredScriptKeys = new Map();
|
|
103
284
|
return storage.run({
|
|
104
285
|
headers: headersData,
|
|
105
286
|
request,
|
|
106
287
|
monitors: options.monitors,
|
|
107
288
|
responseProxy,
|
|
108
289
|
activeDeferreds,
|
|
290
|
+
deferredScriptKeys,
|
|
109
291
|
serverPayload: void 0
|
|
110
292
|
}, async ()=>{
|
|
111
293
|
const Root = createRoot();
|
|
@@ -130,28 +312,50 @@ const createRequestHandler = async (handleRequest, createRequestOptions)=>{
|
|
|
130
312
|
isRSCNavigation: 'true' === request.headers.get('x-rsc-tree'),
|
|
131
313
|
basename: ssrContext.baseUrl || '/'
|
|
132
314
|
};
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
315
|
+
const lifecycle = createRequestLifecycle(async (terminal)=>{
|
|
316
|
+
await hooks.onRequestEnd?.call({
|
|
317
|
+
runtimeContext: context,
|
|
318
|
+
terminal
|
|
319
|
+
});
|
|
320
|
+
}, options.onError);
|
|
321
|
+
const beforeRenderResult = await runWithRequestLifecycleOnError(lifecycle, ()=>runBeforeRender(context));
|
|
322
|
+
let redirectResponse;
|
|
323
|
+
try {
|
|
324
|
+
const prepared = {
|
|
325
|
+
runtimeContext: context,
|
|
326
|
+
routerResult: context.routerContext
|
|
327
|
+
};
|
|
328
|
+
const result = await hooks.onRenderPrepared?.call(prepared) ?? prepared;
|
|
329
|
+
applyRouterResult(context, result.routerResult, options.onError);
|
|
330
|
+
if ("u" > typeof Response) redirectResponse = createLoaderRedirectResponse(beforeRenderResult, redirectCtx);
|
|
331
|
+
} catch (error) {
|
|
332
|
+
if (beforeRenderResult) try {
|
|
333
|
+
await lifecycle.discardBody(beforeRenderResult, {
|
|
334
|
+
status: 'error',
|
|
335
|
+
error
|
|
336
|
+
});
|
|
337
|
+
} catch {}
|
|
338
|
+
else await lifecycle.run({
|
|
339
|
+
status: 'error',
|
|
340
|
+
error
|
|
341
|
+
});
|
|
342
|
+
throw error;
|
|
343
|
+
}
|
|
344
|
+
if (redirectResponse) {
|
|
345
|
+
const response = redirectResponse;
|
|
346
|
+
if (beforeRenderResult?.body && response !== beforeRenderResult) await lifecycle.discardBody(beforeRenderResult);
|
|
347
|
+
return finishWithRequestLifecycle(lifecycle, ()=>lifecycle.deferUntilBodyDone(response));
|
|
144
348
|
}
|
|
145
|
-
await
|
|
349
|
+
await runWithRequestLifecycleOnError(lifecycle, ()=>{
|
|
146
350
|
if (!createRequestOptions?.enableRsc) {
|
|
147
351
|
const { htmlTemplate } = options.resource;
|
|
148
352
|
options.resource.htmlTemplate = htmlTemplate.replace('</head>', `${CHUNK_CSS_PLACEHOLDER}</head>`);
|
|
149
353
|
}
|
|
150
354
|
});
|
|
151
|
-
const response = await
|
|
152
|
-
return
|
|
355
|
+
const response = await runWithRequestLifecycleOnError(lifecycle, ()=>renderRequest(request, Root, context, options, handleRequest, !!createRequestOptions?.enableRsc));
|
|
356
|
+
return finishWithRequestLifecycle(lifecycle, ()=>finalizeRenderResponse(response, responseProxy, redirectCtx, lifecycle));
|
|
153
357
|
});
|
|
154
358
|
};
|
|
155
359
|
return requestHandler;
|
|
156
360
|
};
|
|
157
|
-
export { createRequestHandler };
|
|
361
|
+
export { REQUEST_END_ERROR, RESPONSE_BODY_CANCEL_ERROR, applyRouterResult, createLoaderRedirectResponse, createRequestHandler, createRequestLifecycle, finalizeRenderResponse, finishWithRequestLifecycle, runWithRequestLifecycleOnError };
|
|
@@ -11,8 +11,8 @@ const readAsset = async (chunk)=>{
|
|
|
11
11
|
};
|
|
12
12
|
const checkIsInline = (chunk, enableInline)=>{
|
|
13
13
|
if ('production' !== process.env.NODE_ENV) return false;
|
|
14
|
-
if (enableInline instanceof RegExp) return enableInline.test(chunk);
|
|
15
|
-
return Boolean(enableInline);
|
|
14
|
+
else if (enableInline instanceof RegExp) return enableInline.test(chunk);
|
|
15
|
+
else return Boolean(enableInline);
|
|
16
16
|
};
|
|
17
17
|
async function buildShellBeforeTemplate(beforeAppTemplate, options) {
|
|
18
18
|
const { config, runtimeContext, styledComponentsStyleTags, entryName, lifecycle, helmetData } = options;
|
|
@@ -70,7 +70,7 @@ async function buildShellBeforeTemplate(beforeAppTemplate, options) {
|
|
|
70
70
|
const styles = await Promise.all(cssChunks.map(async (chunk)=>{
|
|
71
71
|
const link = `<link href="${chunk}" rel="stylesheet" />`;
|
|
72
72
|
if (checkIsNode() && checkIsInline(chunk, inlineStyles)) return readAsset(chunk).then((content)=>`<style>${content}</style>`).catch((_)=>link);
|
|
73
|
-
return link;
|
|
73
|
+
else return link;
|
|
74
74
|
}));
|
|
75
75
|
return `${styles.join('')}`;
|
|
76
76
|
}
|
|
@@ -4,8 +4,8 @@ import { buildHtml, replaceSSRTemplateChunk } from "../shared.mjs";
|
|
|
4
4
|
import { hasStylesheetLink } from "../utils.mjs";
|
|
5
5
|
const checkIsInline = (chunk, enableInline)=>{
|
|
6
6
|
if ('production' !== process.env.NODE_ENV) return false;
|
|
7
|
-
if (enableInline instanceof RegExp) return enableInline.test(chunk);
|
|
8
|
-
return Boolean(enableInline);
|
|
7
|
+
else if (enableInline instanceof RegExp) return enableInline.test(chunk);
|
|
8
|
+
else return Boolean(enableInline);
|
|
9
9
|
};
|
|
10
10
|
async function buildShellBeforeTemplate(beforeAppTemplate, options) {
|
|
11
11
|
const { config, runtimeContext, styledComponentsStyleTags, entryName, lifecycle, helmetData } = options;
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
+
import { getGlobalEnableRsc, getGlobalInternalRuntimeContext } from "@modern-js/runtime/context";
|
|
1
2
|
import { storage } from "@modern-js/runtime-utils/node";
|
|
2
3
|
import { SSR_HYDRATION_ID_PREFIX } from "@modern-js/utils/universal/constants";
|
|
3
4
|
import { PassThrough, Readable, Transform, finished, pipeline } from "stream";
|
|
5
|
+
import { StringDecoder } from "string_decoder";
|
|
4
6
|
import { ESCAPED_SHELL_STREAM_END_MARK } from "../../../common.mjs";
|
|
5
7
|
import { RenderLevel } from "../../constants.mjs";
|
|
6
|
-
import { getGlobalEnableRsc, getGlobalInternalRuntimeContext } from "../../context/index.mjs";
|
|
7
8
|
import { getMonitors } from "../../context/monitors/index.mjs";
|
|
8
9
|
import { wrapRuntimeComponentResolver } from "../../react/wrapper.mjs";
|
|
9
10
|
import { createReplaceHelemt, getHelmetData } from "../helmet.mjs";
|
|
10
11
|
import { createSSRRenderLifecycle, createSSRStreamErrorReporter, observeSSRStream, orderSSRStreamTransforms } from "../shared.mjs";
|
|
11
12
|
import { enqueueFromEntries } from "./deferredScript.mjs";
|
|
12
|
-
import {
|
|
13
|
+
import { DeferredScriptOutputCoordinator } from "./deferredScriptOutputCoordinator.mjs";
|
|
14
|
+
import { SHELL_PROGRESSIVE_CHUNK_SIZE, ShellChunkStatus, getReadableStreamFromString, resolveStreamingMode } from "./shared.mjs";
|
|
13
15
|
import { getTemplates } from "./template.mjs";
|
|
14
16
|
const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
15
17
|
const { renderToPipeableStream } = await import("react-dom/server");
|
|
@@ -23,6 +25,7 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
23
25
|
config,
|
|
24
26
|
platform: 'node',
|
|
25
27
|
mode: 'stream',
|
|
28
|
+
monitors: getMonitors(),
|
|
26
29
|
isRsc,
|
|
27
30
|
terminalMarker: ESCAPED_SHELL_STREAM_END_MARK
|
|
28
31
|
}) || [];
|
|
@@ -121,29 +124,42 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
121
124
|
});
|
|
122
125
|
if (failed) return;
|
|
123
126
|
const chunks = [];
|
|
127
|
+
let bufferedLength = 0;
|
|
124
128
|
const marker = Buffer.from(ESCAPED_SHELL_STREAM_END_MARK);
|
|
129
|
+
let markerTail = Buffer.alloc(0);
|
|
130
|
+
const decoder = new StringDecoder('utf8');
|
|
125
131
|
const pendingScripts = [];
|
|
132
|
+
let deferredResolversComplete = Promise.resolve();
|
|
126
133
|
let shellChunkStatus = ShellChunkStatus.START;
|
|
127
|
-
const emitShell = (
|
|
134
|
+
const emitShell = (buffered, markerIndex)=>{
|
|
128
135
|
const beforeMark = lifecycle.completedBody(buffered.subarray(0, markerIndex).toString('utf8'), 'shell');
|
|
129
136
|
const completedShellBefore = createReplaceHelemt(getHelmetData(extenders))(shellBefore);
|
|
130
137
|
shellChunkStatus = ShellChunkStatus.FINISH;
|
|
131
138
|
chunks.length = 0;
|
|
132
|
-
|
|
139
|
+
bufferedLength = 0;
|
|
140
|
+
coordinator.writeReact(`${completedShellBefore}${beforeMark}${shellAfter}`);
|
|
133
141
|
const afterMark = buffered.subarray(markerIndex + marker.length);
|
|
134
|
-
if (afterMark.length > 0)
|
|
135
|
-
|
|
142
|
+
if (afterMark.length > 0) coordinator.writeReact(decoder.write(afterMark));
|
|
143
|
+
coordinator.markShellFinished();
|
|
144
|
+
for (const script of pendingScripts)coordinator.enqueueResolver(script);
|
|
136
145
|
pendingScripts.length = 0;
|
|
137
146
|
};
|
|
138
147
|
const body = new Transform({
|
|
139
148
|
transform (chunk, _encoding, callback) {
|
|
140
149
|
try {
|
|
141
|
-
|
|
150
|
+
const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
151
|
+
if (shellChunkStatus === ShellChunkStatus.FINISH) coordinator.writeReact(decoder.write(bytes));
|
|
142
152
|
else {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
153
|
+
const window = Buffer.concat([
|
|
154
|
+
markerTail,
|
|
155
|
+
bytes
|
|
156
|
+
]);
|
|
157
|
+
const windowIndex = window.indexOf(marker);
|
|
158
|
+
const windowStart = bufferedLength - markerTail.length;
|
|
159
|
+
chunks.push(bytes);
|
|
160
|
+
bufferedLength += bytes.length;
|
|
161
|
+
if (-1 === windowIndex) markerTail = window.subarray(Math.max(0, window.length - marker.length + 1));
|
|
162
|
+
else emitShell(Buffer.concat(chunks, bufferedLength), windowStart + windowIndex);
|
|
147
163
|
}
|
|
148
164
|
callback();
|
|
149
165
|
} catch (error) {
|
|
@@ -154,14 +170,27 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
154
170
|
try {
|
|
155
171
|
if (shellChunkStatus !== ShellChunkStatus.FINISH) {
|
|
156
172
|
const buffered = Buffer.concat(chunks);
|
|
157
|
-
emitShell(
|
|
173
|
+
emitShell(buffered, buffered.length);
|
|
158
174
|
}
|
|
159
|
-
|
|
175
|
+
const trailing = decoder.end();
|
|
176
|
+
if (trailing) coordinator.writeReact(trailing);
|
|
177
|
+
deferredResolversComplete.then(()=>{
|
|
178
|
+
try {
|
|
179
|
+
coordinator.finish();
|
|
180
|
+
callback();
|
|
181
|
+
} catch (error) {
|
|
182
|
+
callback(error instanceof Error ? error : new Error(String(error)));
|
|
183
|
+
}
|
|
184
|
+
}, (error)=>callback(error instanceof Error ? error : new Error(String(error))));
|
|
160
185
|
} catch (error) {
|
|
161
186
|
callback(error instanceof Error ? error : new Error(String(error)));
|
|
162
187
|
}
|
|
163
188
|
}
|
|
164
189
|
});
|
|
190
|
+
const coordinator = new DeferredScriptOutputCoordinator((content)=>{
|
|
191
|
+
body.push(content);
|
|
192
|
+
});
|
|
193
|
+
body.once('close', ()=>coordinator.abort());
|
|
165
194
|
own(body);
|
|
166
195
|
const passThrough = new PassThrough();
|
|
167
196
|
let processedStream = own(passThrough);
|
|
@@ -172,11 +201,13 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
172
201
|
deliver(Readable.toWeb(body));
|
|
173
202
|
reactStream.pipe(passThrough);
|
|
174
203
|
try {
|
|
175
|
-
const
|
|
204
|
+
const storageContext = storage.useContext?.();
|
|
205
|
+
const activeDeferreds = storageContext?.activeDeferreds;
|
|
206
|
+
const deferredScriptKeys = storageContext?.deferredScriptKeys;
|
|
176
207
|
const entries = activeDeferreds instanceof Map ? Array.from(activeDeferreds.entries()) : [];
|
|
177
|
-
enqueueFromEntries(entries, config.nonce, (script)=>{
|
|
208
|
+
if (entries.length > 0) deferredResolversComplete = enqueueFromEntries(entries, deferredScriptKeys, config.nonce, (script)=>{
|
|
178
209
|
if (failed || body.destroyed || body.writableEnded) return;
|
|
179
|
-
if (shellChunkStatus === ShellChunkStatus.FINISH)
|
|
210
|
+
if (shellChunkStatus === ShellChunkStatus.FINISH) coordinator.enqueueResolver(script);
|
|
180
211
|
else pendingScripts.push(script);
|
|
181
212
|
});
|
|
182
213
|
} catch (error) {
|
|
@@ -213,6 +244,7 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
213
244
|
reactStream = renderToPipeableStream(processedRootElement, {
|
|
214
245
|
nonce: config.nonce,
|
|
215
246
|
identifierPrefix: SSR_HYDRATION_ID_PREFIX,
|
|
247
|
+
progressiveChunkSize: SHELL_PROGRESSIVE_CHUNK_SIZE,
|
|
216
248
|
[onReady] () {
|
|
217
249
|
startOutput().catch(fail);
|
|
218
250
|
},
|