@bleedingdev/modern-js-runtime 3.9.0-ultramodern.1 → 3.9.0-ultramodern.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/dist/cjs/cli/code.js +3 -1
  2. package/dist/cjs/cli/ssr/index.js +2 -74
  3. package/dist/cjs/core/browser/hydrate.js +6 -1
  4. package/dist/cjs/core/context/index.js +4 -43
  5. package/dist/cjs/core/context/public.js +4 -7
  6. package/dist/cjs/core/context/runtime.js +17 -5
  7. package/dist/cjs/core/react/wrapper.js +22 -65
  8. package/dist/cjs/core/server/helmet.js +5 -4
  9. package/dist/cjs/core/server/requestResponse.js +4 -4
  10. package/dist/cjs/core/server/routerCleanup.js +2 -2
  11. package/dist/cjs/core/server/shared.js +171 -4
  12. package/dist/cjs/core/server/stream/afterTemplate.js +21 -16
  13. package/dist/cjs/core/server/stream/beforeTemplate.js +34 -25
  14. package/dist/cjs/core/server/stream/beforeTemplate.worker.js +28 -21
  15. package/dist/cjs/core/server/stream/createReadableStream.js +208 -123
  16. package/dist/cjs/core/server/stream/createReadableStream.worker.js +159 -90
  17. package/dist/cjs/core/server/stream/deferredScript.js +3 -3
  18. package/dist/cjs/core/server/stream/shared.js +2 -2
  19. package/dist/cjs/core/server/string/index.js +100 -64
  20. package/dist/cjs/core/server/string/loadable.js +34 -19
  21. package/dist/cjs/core/server/string/ssrData.js +5 -12
  22. package/dist/cjs/core/server/utils.js +1 -2
  23. package/dist/cjs/exports/head.js +7 -14
  24. package/dist/cjs/router/runtime/PrefetchLink.js +67 -123
  25. package/dist/cjs/router/runtime/hooks.js +13 -2
  26. package/dist/cjs/router/runtime/internal.js +7 -45
  27. package/dist/cjs/router/runtime/plugin.js +2 -8
  28. package/dist/cjs/router/runtime/plugin.node.js +2 -30
  29. package/dist/cjs/router/runtime/redirect.js +1 -1
  30. package/dist/esm/cli/code.mjs +3 -1
  31. package/dist/esm/cli/ssr/index.mjs +3 -72
  32. package/dist/esm/core/browser/hydrate.mjs +7 -2
  33. package/dist/esm/core/context/index.mjs +2 -5
  34. package/dist/esm/core/context/public.mjs +1 -5
  35. package/dist/esm/core/context/runtime.mjs +13 -4
  36. package/dist/esm/core/react/wrapper.mjs +21 -67
  37. package/dist/esm/core/server/helmet.mjs +5 -4
  38. package/dist/esm/core/server/requestResponse.mjs +3 -3
  39. package/dist/esm/core/server/routerCleanup.mjs +1 -1
  40. package/dist/esm/core/server/shared.mjs +153 -1
  41. package/dist/esm/core/server/stream/afterTemplate.mjs +23 -18
  42. package/dist/esm/core/server/stream/beforeTemplate.mjs +37 -28
  43. package/dist/esm/core/server/stream/beforeTemplate.worker.mjs +31 -24
  44. package/dist/esm/core/server/stream/createReadableStream.mjs +207 -122
  45. package/dist/esm/core/server/stream/createReadableStream.worker.mjs +149 -90
  46. package/dist/esm/core/server/stream/deferredScript.mjs +3 -3
  47. package/dist/esm/core/server/stream/shared.mjs +2 -2
  48. package/dist/esm/core/server/string/index.mjs +104 -68
  49. package/dist/esm/core/server/string/loadable.mjs +34 -19
  50. package/dist/esm/core/server/string/ssrData.mjs +5 -12
  51. package/dist/esm/core/server/utils.mjs +1 -2
  52. package/dist/esm/exports/head.mjs +7 -14
  53. package/dist/esm/router/runtime/PrefetchLink.mjs +67 -123
  54. package/dist/esm/router/runtime/hooks.mjs +9 -1
  55. package/dist/esm/router/runtime/internal.mjs +2 -33
  56. package/dist/esm/router/runtime/plugin.mjs +1 -7
  57. package/dist/esm/router/runtime/plugin.node.mjs +1 -29
  58. package/dist/esm/router/runtime/redirect.mjs +1 -1
  59. package/dist/esm-node/cli/code.mjs +3 -1
  60. package/dist/esm-node/cli/ssr/index.mjs +3 -72
  61. package/dist/esm-node/core/browser/hydrate.mjs +7 -2
  62. package/dist/esm-node/core/context/index.mjs +2 -5
  63. package/dist/esm-node/core/context/public.mjs +1 -5
  64. package/dist/esm-node/core/context/runtime.mjs +13 -4
  65. package/dist/esm-node/core/react/wrapper.mjs +21 -67
  66. package/dist/esm-node/core/server/helmet.mjs +5 -4
  67. package/dist/esm-node/core/server/requestResponse.mjs +3 -3
  68. package/dist/esm-node/core/server/routerCleanup.mjs +1 -1
  69. package/dist/esm-node/core/server/shared.mjs +153 -1
  70. package/dist/esm-node/core/server/stream/afterTemplate.mjs +23 -18
  71. package/dist/esm-node/core/server/stream/beforeTemplate.mjs +37 -28
  72. package/dist/esm-node/core/server/stream/beforeTemplate.worker.mjs +31 -24
  73. package/dist/esm-node/core/server/stream/createReadableStream.mjs +207 -122
  74. package/dist/esm-node/core/server/stream/createReadableStream.worker.mjs +149 -90
  75. package/dist/esm-node/core/server/stream/deferredScript.mjs +3 -3
  76. package/dist/esm-node/core/server/stream/shared.mjs +2 -2
  77. package/dist/esm-node/core/server/string/index.mjs +104 -68
  78. package/dist/esm-node/core/server/string/loadable.mjs +34 -19
  79. package/dist/esm-node/core/server/string/ssrData.mjs +5 -12
  80. package/dist/esm-node/core/server/utils.mjs +1 -2
  81. package/dist/esm-node/exports/head.mjs +7 -14
  82. package/dist/esm-node/router/runtime/PrefetchLink.mjs +67 -123
  83. package/dist/esm-node/router/runtime/hooks.mjs +9 -1
  84. package/dist/esm-node/router/runtime/internal.mjs +2 -33
  85. package/dist/esm-node/router/runtime/plugin.mjs +1 -7
  86. package/dist/esm-node/router/runtime/plugin.node.mjs +1 -29
  87. package/dist/esm-node/router/runtime/redirect.mjs +1 -1
  88. package/dist/types/cli/ssr/index.d.ts +0 -3
  89. package/dist/types/core/context/index.d.ts +4 -6
  90. package/dist/types/core/context/public.d.ts +1 -23
  91. package/dist/types/core/context/runtime.d.ts +26 -2
  92. package/dist/types/core/react/wrapper.d.ts +6 -0
  93. package/dist/types/core/server/helmet.d.ts +4 -5
  94. package/dist/types/core/server/requestHandler.d.ts +2 -2
  95. package/dist/types/core/server/requestResponse.d.ts +1 -1
  96. package/dist/types/core/server/routerCleanup.d.ts +1 -1
  97. package/dist/types/core/server/shared.d.ts +24 -1
  98. package/dist/types/core/server/stream/afterTemplate.d.ts +2 -1
  99. package/dist/types/core/server/stream/beforeTemplate.d.ts +4 -1
  100. package/dist/types/core/server/stream/beforeTemplate.worker.d.ts +4 -1
  101. package/dist/types/core/server/stream/shared.d.ts +2 -2
  102. package/dist/types/core/server/string/loadable.d.ts +6 -3
  103. package/dist/types/core/server/string/ssrData.d.ts +2 -1
  104. package/dist/types/core/server/string/types.d.ts +1 -5
  105. package/dist/types/core/server/tracer.d.ts +1 -1
  106. package/dist/types/core/server/utils.d.ts +1 -1
  107. package/dist/types/core/types.d.ts +1 -2
  108. package/dist/types/exports/head.d.ts +2 -2
  109. package/dist/types/router/runtime/PrefetchLink.d.ts +27 -2
  110. package/dist/types/router/runtime/hooks.d.ts +19 -2
  111. package/dist/types/router/runtime/internal.d.ts +4 -21
  112. package/dist/types/router/runtime/types.d.ts +2 -43
  113. package/package.json +18 -46
  114. package/rstest.config.mts +32 -2
  115. package/dist/cjs/boundary-debugger/index.js +0 -317
  116. package/dist/cjs/core/context/extensions.js +0 -74
  117. package/dist/cjs/core/context/helmetContext.js +0 -54
  118. package/dist/cjs/core/server/federatedCss.js +0 -58
  119. package/dist/cjs/core/server/scriptOrder.js +0 -140
  120. package/dist/cjs/module-federation/consume-surface.js +0 -199
  121. package/dist/cjs/module-federation/distributed-ssr.js +0 -135
  122. package/dist/cjs/module-federation/index.js +0 -316
  123. package/dist/cjs/module-federation/last-known-good.js +0 -157
  124. package/dist/cjs/module-federation/manifest-recovery-runtime-plugin.js +0 -129
  125. package/dist/cjs/module-federation/surface-resolution-types.js +0 -61
  126. package/dist/cjs/router/runtime/lifecycle.js +0 -167
  127. package/dist/cjs/router/runtime/provider.js +0 -153
  128. package/dist/esm/boundary-debugger/index.mjs +0 -277
  129. package/dist/esm/core/context/extensions.mjs +0 -33
  130. package/dist/esm/core/context/helmetContext.mjs +0 -13
  131. package/dist/esm/core/server/federatedCss.mjs +0 -20
  132. package/dist/esm/core/server/scriptOrder.mjs +0 -90
  133. package/dist/esm/module-federation/consume-surface.mjs +0 -158
  134. package/dist/esm/module-federation/distributed-ssr.mjs +0 -82
  135. package/dist/esm/module-federation/index.mjs +0 -115
  136. package/dist/esm/module-federation/last-known-good.mjs +0 -113
  137. package/dist/esm/module-federation/manifest-recovery-runtime-plugin.mjs +0 -89
  138. package/dist/esm/module-federation/surface-resolution-types.mjs +0 -8
  139. package/dist/esm/router/runtime/lifecycle.mjs +0 -105
  140. package/dist/esm/router/runtime/provider.mjs +0 -100
  141. package/dist/esm-node/boundary-debugger/index.mjs +0 -278
  142. package/dist/esm-node/core/context/extensions.mjs +0 -34
  143. package/dist/esm-node/core/context/helmetContext.mjs +0 -14
  144. package/dist/esm-node/core/server/federatedCss.mjs +0 -21
  145. package/dist/esm-node/core/server/scriptOrder.mjs +0 -91
  146. package/dist/esm-node/module-federation/consume-surface.mjs +0 -159
  147. package/dist/esm-node/module-federation/distributed-ssr.mjs +0 -83
  148. package/dist/esm-node/module-federation/index.mjs +0 -116
  149. package/dist/esm-node/module-federation/last-known-good.mjs +0 -114
  150. package/dist/esm-node/module-federation/manifest-recovery-runtime-plugin.mjs +0 -90
  151. package/dist/esm-node/module-federation/surface-resolution-types.mjs +0 -9
  152. package/dist/esm-node/router/runtime/lifecycle.mjs +0 -106
  153. package/dist/esm-node/router/runtime/provider.mjs +0 -101
  154. package/dist/types/boundary-debugger/index.d.ts +0 -28
  155. package/dist/types/core/context/extensions.d.ts +0 -35
  156. package/dist/types/core/context/helmetContext.d.ts +0 -10
  157. package/dist/types/core/server/federatedCss.d.ts +0 -5
  158. package/dist/types/core/server/scriptOrder.d.ts +0 -25
  159. package/dist/types/module-federation/consume-surface.d.ts +0 -104
  160. package/dist/types/module-federation/distributed-ssr.d.ts +0 -61
  161. package/dist/types/module-federation/index.d.ts +0 -71
  162. package/dist/types/module-federation/last-known-good.d.ts +0 -52
  163. package/dist/types/module-federation/manifest-recovery-runtime-plugin.d.ts +0 -25
  164. package/dist/types/module-federation/surface-resolution-types.d.ts +0 -23
  165. package/dist/types/router/runtime/lifecycle.d.ts +0 -27
  166. package/dist/types/router/runtime/provider.d.ts +0 -78
@@ -1,148 +1,233 @@
1
1
  import "node:module";
2
2
  import { storage } from "@modern-js/runtime-utils/node";
3
3
  import { SSR_HYDRATION_ID_PREFIX } from "@modern-js/utils/universal/constants";
4
- import { PassThrough, Readable, Transform } from "stream";
4
+ import { PassThrough, Readable, Transform, finished, pipeline } from "stream";
5
5
  import { ESCAPED_SHELL_STREAM_END_MARK } from "../../../common.mjs";
6
6
  import { RenderLevel } from "../../constants.mjs";
7
- import { getGlobalInternalRuntimeContext } from "../../context/index.mjs";
7
+ import { getGlobalEnableRsc, getGlobalInternalRuntimeContext } from "../../context/index.mjs";
8
8
  import { getMonitors } from "../../context/monitors/index.mjs";
9
+ import { wrapRuntimeComponentResolver } from "../../react/wrapper.mjs";
9
10
  import { createReplaceHelemt, getHelmetData } from "../helmet.mjs";
11
+ import { createSSRRenderLifecycle, createSSRStreamErrorReporter, observeSSRStream, orderSSRStreamTransforms } from "../shared.mjs";
10
12
  import { enqueueFromEntries } from "./deferredScript.mjs";
11
13
  import { ShellChunkStatus, getReadableStreamFromString, resolveStreamingMode } from "./shared.mjs";
12
14
  import { getTemplates } from "./template.mjs";
13
- import * as __rspack_external__modern_js_runtime_extensions_node_f016b1ee from "@modern-js/runtime-extensions/node";
14
- const defaultExtender = {
15
- modifyRootElement: (rootElement)=>rootElement,
16
- getStyleTags: ()=>'',
17
- processStream: (stream)=>stream
18
- };
19
15
  const createReadableStreamFromElement = async (request, rootElement, options)=>{
20
16
  const { renderToPipeableStream } = await import("react-dom/server");
21
- const { runtimeContext, htmlTemplate, config, ssrConfig, entryName, moduleFederationCssAssets } = options;
22
- let shellChunkStatus = ShellChunkStatus.START;
23
- let renderLevel = RenderLevel.SERVER_RENDER;
17
+ const { runtimeContext, htmlTemplate, config, ssrConfig, entryName, resource } = options;
18
+ const hooks = getGlobalInternalRuntimeContext().hooks;
19
+ const isRsc = true === getGlobalEnableRsc();
20
+ const extenders = hooks.extendStreamSSR.call({
21
+ runtimeContext,
22
+ request,
23
+ resource,
24
+ config,
25
+ platform: 'node',
26
+ mode: 'stream',
27
+ isRsc,
28
+ terminalMarker: ESCAPED_SHELL_STREAM_END_MARK
29
+ }) || [];
30
+ const lifecycle = createSSRRenderLifecycle(extenders);
31
+ const reportError = createSSRStreamErrorReporter(options.onError);
24
32
  const forceStream2String = Boolean(process.env.MODERN_JS_STREAM_TO_STRING);
25
33
  const { onReady } = resolveStreamingMode(request, forceStream2String);
26
- const internalRuntimeContext = getGlobalInternalRuntimeContext();
27
- const hooks = internalRuntimeContext.hooks;
28
- const extenders = hooks.extendStreamSSR.call() || [];
29
- if (0 === extenders.length) extenders.push(defaultExtender);
30
- extenders.forEach((extender)=>{
31
- if (extender.init) extender.init({
32
- rootElement,
33
- forceStream2String
34
- });
35
- });
36
- let processedRootElement = rootElement;
37
- extenders.forEach((extender)=>{
38
- if (extender.modifyRootElement) processedRootElement = extender.modifyRootElement(processedRootElement);
39
- });
40
- const chunkVec = [];
34
+ let renderLevel = RenderLevel.SERVER_RENDER;
41
35
  let hasStartedPipe = false;
42
- __rspack_external__modern_js_runtime_extensions_node_f016b1ee.beginHeadRender(runtimeContext);
43
- const reportError = __rspack_external__modern_js_runtime_extensions_node_f016b1ee.createOnceErrorReporter(options.onError);
44
- return new Promise((resolve)=>{
45
- const { pipe: reactStreamingPipe } = renderToPipeableStream(processedRootElement, {
46
- nonce: config.nonce,
47
- identifierPrefix: SSR_HYDRATION_ID_PREFIX,
48
- [onReady] () {
49
- if (hasStartedPipe) return;
50
- hasStartedPipe = true;
51
- let styledComponentsStyleTags = '';
52
- extenders.forEach((extender)=>{
53
- if (extender.getStyleTags) styledComponentsStyleTags += extender.getStyleTags();
36
+ let failed = false;
37
+ let reactStream;
38
+ const ownedStreams = new Set();
39
+ const streamCompletions = [];
40
+ let teardown;
41
+ return new Promise((resolve, reject)=>{
42
+ const destroyOwnedStreams = (reason)=>{
43
+ if (teardown) return teardown;
44
+ const error = void 0 === reason ? void 0 : reason instanceof Error ? reason : new Error(String(reason));
45
+ for (const stream of ownedStreams){
46
+ const destroyable = stream;
47
+ destroyable.destroy?.(error);
48
+ }
49
+ teardown = Promise.all(streamCompletions).then((errors)=>{
50
+ const cleanupError = errors.find((value)=>void 0 !== value && value !== error && 'ERR_STREAM_PREMATURE_CLOSE' !== value.code);
51
+ if (cleanupError) throw cleanupError;
52
+ });
53
+ return teardown;
54
+ };
55
+ const abortReact = (reason)=>reactStream?.abort(reason);
56
+ const fail = (error)=>{
57
+ if (failed) return;
58
+ failed = true;
59
+ request.signal.removeEventListener('abort', onStartupAbort);
60
+ if (request.signal.aborted) lifecycle.finish({
61
+ status: 'cancelled',
62
+ reason: request.signal.reason
63
+ });
64
+ else {
65
+ lifecycle.finish({
66
+ status: 'error',
67
+ error
54
68
  });
55
- options[onReady]?.();
56
- getTemplates(htmlTemplate, {
57
- request,
58
- ssrConfig,
59
- renderLevel,
60
- runtimeContext,
61
- config,
62
- entryName,
63
- moduleFederationCssAssets,
64
- styledComponentsStyleTags
65
- }).then(({ shellAfter, shellBefore })=>{
66
- const pendingScripts = [];
67
- const body = new Transform({
68
- transform (chunk, _encoding, callback) {
69
- try {
70
- if (shellChunkStatus !== ShellChunkStatus.FINISH) {
71
- chunkVec.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
72
- const concatedChunk = Buffer.concat(chunkVec).toString('utf-8');
73
- const markerIndex = concatedChunk.indexOf(ESCAPED_SHELL_STREAM_END_MARK);
74
- if (-1 !== markerIndex) {
75
- const beforeMark = concatedChunk.slice(0, markerIndex);
76
- const afterMark = concatedChunk.slice(markerIndex + ESCAPED_SHELL_STREAM_END_MARK.length);
77
- __rspack_external__modern_js_runtime_extensions_node_f016b1ee.publishHeadRender(runtimeContext);
78
- const completedShellBefore = createReplaceHelemt(getHelmetData(runtimeContext))(shellBefore);
79
- shellChunkStatus = ShellChunkStatus.FINISH;
80
- this.push(`${completedShellBefore}${beforeMark}${shellAfter}`);
81
- if (afterMark) this.push(afterMark);
82
- if (pendingScripts.length > 0) for (const s of pendingScripts)this.push(s);
83
- }
84
- } else this.push(chunk);
85
- callback();
86
- } catch (e) {
87
- e instanceof Error ? callback(e) : callback(new Error('Received unknown error when streaming'));
88
- }
69
+ reportError(error);
70
+ }
71
+ abortReact(error);
72
+ destroyOwnedStreams(error).then(()=>reject(error), ()=>reject(error));
73
+ };
74
+ const own = (stream)=>{
75
+ if (!ownedStreams.has(stream)) {
76
+ ownedStreams.add(stream);
77
+ streamCompletions.push(new Promise((done)=>{
78
+ finished(stream, (error)=>done(error ?? void 0));
79
+ }));
80
+ stream.on('error', fail);
81
+ }
82
+ return stream;
83
+ };
84
+ const onStartupAbort = ()=>fail(request.signal.reason);
85
+ const deliver = (source)=>{
86
+ const stream = observeSSRStream(source, {
87
+ lifecycle,
88
+ signal: request.signal,
89
+ async onError (error) {
90
+ failed = true;
91
+ abortReact(error);
92
+ await destroyOwnedStreams(error);
93
+ reportError(error);
94
+ },
95
+ onCancel (reason) {
96
+ failed = true;
97
+ const cleanup = destroyOwnedStreams();
98
+ abortReact(reason);
99
+ return cleanup;
100
+ }
101
+ });
102
+ request.signal.removeEventListener('abort', onStartupAbort);
103
+ resolve(stream);
104
+ };
105
+ const templateOptions = ()=>({
106
+ request,
107
+ ssrConfig,
108
+ renderLevel,
109
+ runtimeContext,
110
+ config,
111
+ entryName,
112
+ lifecycle
113
+ });
114
+ const startOutput = async ()=>{
115
+ if (hasStartedPipe || failed) return;
116
+ hasStartedPipe = true;
117
+ const styledComponentsStyleTags = extenders.map((extender)=>extender.getStyleTags?.() ?? '').join('');
118
+ options[onReady]?.();
119
+ const { shellBefore, shellAfter } = await getTemplates(htmlTemplate, {
120
+ ...templateOptions(),
121
+ styledComponentsStyleTags
122
+ });
123
+ if (failed) return;
124
+ const chunks = [];
125
+ const marker = Buffer.from(ESCAPED_SHELL_STREAM_END_MARK);
126
+ const pendingScripts = [];
127
+ let shellChunkStatus = ShellChunkStatus.START;
128
+ const emitShell = (destination, buffered, markerIndex)=>{
129
+ const beforeMark = lifecycle.completedBody(buffered.subarray(0, markerIndex).toString('utf8'), 'shell');
130
+ const completedShellBefore = createReplaceHelemt(getHelmetData(extenders))(shellBefore);
131
+ shellChunkStatus = ShellChunkStatus.FINISH;
132
+ chunks.length = 0;
133
+ destination.push(`${completedShellBefore}${beforeMark}${shellAfter}`);
134
+ const afterMark = buffered.subarray(markerIndex + marker.length);
135
+ if (afterMark.length > 0) destination.push(afterMark);
136
+ for (const script of pendingScripts)destination.push(script);
137
+ pendingScripts.length = 0;
138
+ };
139
+ const body = new Transform({
140
+ transform (chunk, _encoding, callback) {
141
+ try {
142
+ if (shellChunkStatus === ShellChunkStatus.FINISH) this.push(chunk);
143
+ else {
144
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
145
+ const buffered = Buffer.concat(chunks);
146
+ const markerIndex = buffered.indexOf(marker);
147
+ if (-1 !== markerIndex) emitShell(this, buffered, markerIndex);
89
148
  }
90
- });
91
- const passThrough = new PassThrough();
92
- const stream = Readable.toWeb(body);
93
- resolve(stream);
94
- let processedStream = passThrough;
95
- extenders.forEach((extender)=>{
96
- if (extender.processStream) processedStream = extender.processStream(processedStream);
97
- });
98
- __rspack_external__modern_js_runtime_extensions_node_f016b1ee.pipeNodeHeadStream({
99
- source: processedStream,
100
- destination: body,
101
- context: runtimeContext,
102
- terminalMarker: ESCAPED_SHELL_STREAM_END_MARK,
103
- onError: reportError
104
- });
105
- reactStreamingPipe(passThrough);
149
+ callback();
150
+ } catch (error) {
151
+ callback(error instanceof Error ? error : new Error(String(error)));
152
+ }
153
+ },
154
+ flush (callback) {
106
155
  try {
107
- const storageContext = storage.useContext?.();
108
- const activeDeferreds = storageContext?.activeDeferreds;
109
- const entries = activeDeferreds instanceof Map ? Array.from(activeDeferreds.entries()) : [];
110
- if (entries.length > 0) {
111
- const enqueueScript = (s)=>{
112
- if (shellChunkStatus === ShellChunkStatus.FINISH) body.write(s);
113
- else pendingScripts.push(s);
114
- };
115
- enqueueFromEntries(entries, config.nonce, (s)=>enqueueScript(s));
156
+ if (shellChunkStatus !== ShellChunkStatus.FINISH) {
157
+ const buffered = Buffer.concat(chunks);
158
+ emitShell(this, buffered, buffered.length);
116
159
  }
117
- } catch (err) {
118
- const monitors = getMonitors();
119
- monitors.error("cannot inject router data script", err);
160
+ callback();
161
+ } catch (error) {
162
+ callback(error instanceof Error ? error : new Error(String(error)));
120
163
  }
164
+ }
165
+ });
166
+ own(body);
167
+ const passThrough = new PassThrough();
168
+ let processedStream = own(passThrough);
169
+ for (const extender of orderSSRStreamTransforms(extenders))if (extender.processStream) processedStream = own(extender.processStream(processedStream));
170
+ pipeline(processedStream, body, (error)=>{
171
+ if (null != error && !failed) fail(error);
172
+ });
173
+ deliver(Readable.toWeb(body));
174
+ reactStream.pipe(passThrough);
175
+ try {
176
+ const activeDeferreds = storage.useContext?.()?.activeDeferreds;
177
+ const entries = activeDeferreds instanceof Map ? Array.from(activeDeferreds.entries()) : [];
178
+ enqueueFromEntries(entries, config.nonce, (script)=>{
179
+ if (failed || body.destroyed || body.writableEnded) return;
180
+ if (shellChunkStatus === ShellChunkStatus.FINISH) body.write(script);
181
+ else pendingScripts.push(script);
121
182
  });
122
- },
123
- onShellError (error) {
124
- __rspack_external__modern_js_runtime_extensions_node_f016b1ee.abortHeadRender(runtimeContext);
125
- renderLevel = RenderLevel.CLIENT_RENDER;
126
- getTemplates(htmlTemplate, {
127
- request,
128
- ssrConfig,
129
- renderLevel,
130
- runtimeContext,
131
- entryName,
132
- config,
133
- moduleFederationCssAssets
134
- }).then(({ shellAfter, shellBefore })=>{
135
- const fallbackHtml = `${shellBefore}${shellAfter}`;
136
- const readableStream = getReadableStreamFromString(fallbackHtml);
137
- resolve(readableStream);
138
- options?.onShellError?.(error);
139
- });
140
- },
141
- onError (error) {
142
- renderLevel = RenderLevel.CLIENT_RENDER;
143
- reportError(error);
183
+ } catch (error) {
184
+ getMonitors().error("cannot inject router data script", error);
144
185
  }
186
+ };
187
+ const renderFallback = async (error)=>{
188
+ if (failed || hasStartedPipe) return;
189
+ lifecycle.finish({
190
+ status: 'fallback',
191
+ error
192
+ });
193
+ renderLevel = RenderLevel.CLIENT_RENDER;
194
+ const { shellBefore, shellAfter } = await getTemplates(htmlTemplate, {
195
+ ...templateOptions(),
196
+ helmetData: getHelmetData(extenders)
197
+ });
198
+ if (failed) return;
199
+ options.onShellError?.(error);
200
+ deliver(getReadableStreamFromString(`${shellBefore}${shellAfter}`));
201
+ };
202
+ request.signal.addEventListener('abort', onStartupAbort, {
203
+ once: true
145
204
  });
205
+ try {
206
+ request.signal.throwIfAborted();
207
+ for (const extender of extenders)extender.init?.({
208
+ rootElement,
209
+ forceStream2String
210
+ });
211
+ let processedRootElement = isRsc && false === runtimeContext.isBrowser ? wrapRuntimeComponentResolver(rootElement, hooks) : rootElement;
212
+ for (const extender of extenders)processedRootElement = extender.modifyRootElement?.(processedRootElement) ?? processedRootElement;
213
+ lifecycle.beforeReact();
214
+ reactStream = renderToPipeableStream(processedRootElement, {
215
+ nonce: config.nonce,
216
+ identifierPrefix: SSR_HYDRATION_ID_PREFIX,
217
+ [onReady] () {
218
+ startOutput().catch(fail);
219
+ },
220
+ onShellError (error) {
221
+ renderFallback(error).catch(fail);
222
+ },
223
+ onError (error) {
224
+ renderLevel = RenderLevel.CLIENT_RENDER;
225
+ if (!request.signal.aborted && !failed) reportError(error);
226
+ }
227
+ });
228
+ } catch (error) {
229
+ fail(error);
230
+ }
146
231
  });
147
232
  };
148
233
  export { createReadableStreamFromElement };
@@ -1,122 +1,181 @@
1
1
  import "node:module";
2
2
  import { renderSSRStream } from "@modern-js/render/ssr";
3
3
  import { storage } from "@modern-js/runtime-utils/node";
4
+ import react from "react";
4
5
  import { ESCAPED_SHELL_STREAM_END_MARK } from "../../../common.mjs";
5
6
  import { RenderLevel } from "../../constants.mjs";
7
+ import { getGlobalEnableRsc, getGlobalInternalRuntimeContext } from "../../context/index.mjs";
8
+ import { wrapRuntimeComponentResolver } from "../../react/wrapper.mjs";
6
9
  import { createReplaceHelemt, getHelmetData } from "../helmet.mjs";
10
+ import { createSSRRenderLifecycle, createSSRStreamErrorReporter, observeSSRStream, orderSSRStreamTransforms } from "../shared.mjs";
7
11
  import { enqueueFromEntries } from "./deferredScript.mjs";
8
- import { ShellChunkStatus, encodeForWebStream, getReadableStreamFromString, resolveStreamingMode } from "./shared.mjs";
12
+ import { ShellChunkStatus, getReadableStreamFromString, resolveStreamingMode } from "./shared.mjs";
9
13
  import { getTemplates } from "./template.mjs";
10
- import * as __rspack_external__modern_js_runtime_extensions_ce611631 from "@modern-js/runtime-extensions";
11
14
  const createReadableStreamFromElement = async (request, rootElement, options)=>{
12
- let shellChunkStatus = ShellChunkStatus.START;
13
- const chunkVec = [];
14
- const { htmlTemplate, runtimeContext, config, ssrConfig, entryName, moduleFederationCssAssets, rscManifest, rscRoot } = options;
15
- __rspack_external__modern_js_runtime_extensions_ce611631.beginHeadRender(runtimeContext);
16
- const { shellBefore, shellAfter } = await getTemplates(htmlTemplate, {
17
- renderLevel: RenderLevel.SERVER_RENDER,
15
+ const { htmlTemplate, runtimeContext, config, ssrConfig, entryName, resource, rscManifest, rscRoot } = options;
16
+ const hooks = getGlobalInternalRuntimeContext().hooks;
17
+ const isRsc = true === getGlobalEnableRsc();
18
+ const extenders = hooks.extendStreamSSR.call({
19
+ runtimeContext,
20
+ request,
21
+ resource,
22
+ config,
23
+ platform: 'web',
24
+ mode: 'stream',
25
+ isRsc,
26
+ terminalMarker: ESCAPED_SHELL_STREAM_END_MARK
27
+ }) || [];
28
+ const lifecycle = createSSRRenderLifecycle(extenders);
29
+ const reportOnce = createSSRStreamErrorReporter(options.onError);
30
+ const reportError = (error)=>{
31
+ if (!request.signal.aborted && lifecycle.terminal?.status !== 'cancelled') reportOnce(error);
32
+ };
33
+ const forceStream2String = Boolean("u" > typeof process && process.env?.MODERN_JS_STREAM_TO_STRING);
34
+ let pendingSource;
35
+ let bodyOpen = true;
36
+ let bodyPipe;
37
+ const templateOptions = {
18
38
  runtimeContext,
19
39
  ssrConfig,
20
40
  request,
21
41
  config,
22
42
  entryName,
23
- moduleFederationCssAssets
24
- });
43
+ lifecycle
44
+ };
25
45
  try {
46
+ request.signal.throwIfAborted();
47
+ for (const extender of extenders)extender.init?.({
48
+ rootElement,
49
+ forceStream2String
50
+ });
26
51
  const readableOriginal = await renderSSRStream(rootElement, {
27
52
  request,
53
+ signal: request.signal,
28
54
  nonce: config.nonce,
29
55
  rscManifest,
30
56
  rscRoot: rscRoot,
31
57
  routes: runtimeContext.routes,
32
- onError (error) {
33
- options.onError?.(error);
58
+ onError: reportError,
59
+ wrapHtmlRoot (root) {
60
+ let element = react.isValidElement(root) ? root : react.createElement(react.Fragment, null, root);
61
+ if (isRsc && false === runtimeContext.isBrowser) element = wrapRuntimeComponentResolver(element, hooks);
62
+ for (const extender of extenders)element = extender.modifyRootElement?.(element) ?? element;
63
+ lifecycle.beforeReact();
64
+ return element;
34
65
  }
35
66
  });
67
+ pendingSource = readableOriginal;
36
68
  options.onShellReady?.();
37
- readableOriginal.allReady.then(()=>{
38
- options?.onAllReady?.();
39
- });
40
- const forceStreamToString = Boolean("u" > typeof process && process.env?.MODERN_JS_STREAM_TO_STRING);
41
- const { waitForAllReady } = resolveStreamingMode(request, forceStreamToString);
69
+ readableOriginal.allReady.then(()=>options.onAllReady?.()).catch(reportError);
70
+ const { waitForAllReady } = resolveStreamingMode(request, forceStream2String);
42
71
  if (waitForAllReady) await readableOriginal.allReady;
43
- const reader = __rspack_external__modern_js_runtime_extensions_ce611631.createConservingWebShellStream(readableOriginal, runtimeContext, ESCAPED_SHELL_STREAM_END_MARK, options.onError).getReader();
44
- const stream = new ReadableStream({
72
+ request.signal.throwIfAborted();
73
+ const styledComponentsStyleTags = extenders.map((extender)=>extender.getStyleTags?.() ?? '').join('');
74
+ const { shellBefore, shellAfter } = await getTemplates(htmlTemplate, {
75
+ ...templateOptions,
76
+ renderLevel: RenderLevel.SERVER_RENDER,
77
+ styledComponentsStyleTags
78
+ });
79
+ for (const extender of orderSSRStreamTransforms(extenders))if (void 0 !== extender.processReadableStream) pendingSource = extender.processReadableStream(pendingSource);
80
+ const decoder = new TextDecoder();
81
+ const encoder = new TextEncoder();
82
+ const pendingScripts = [];
83
+ let buffered = '';
84
+ let shellChunkStatus = ShellChunkStatus.START;
85
+ let bodyController;
86
+ const emit = (chunk, controller)=>{
87
+ if (shellChunkStatus === ShellChunkStatus.FINISH) {
88
+ if (chunk.length > 0) controller.enqueue(encoder.encode(chunk));
89
+ return;
90
+ }
91
+ buffered += chunk;
92
+ const markerIndex = buffered.indexOf(ESCAPED_SHELL_STREAM_END_MARK);
93
+ if (-1 === markerIndex) return;
94
+ const beforeMark = lifecycle.completedBody(buffered.slice(0, markerIndex), 'shell');
95
+ const afterMark = buffered.slice(markerIndex + ESCAPED_SHELL_STREAM_END_MARK.length);
96
+ const completedShellBefore = createReplaceHelemt(getHelmetData(extenders))(shellBefore);
97
+ shellChunkStatus = ShellChunkStatus.FINISH;
98
+ buffered = '';
99
+ controller.enqueue(encoder.encode(`${completedShellBefore}${beforeMark}${shellAfter}`));
100
+ if (afterMark.length > 0) controller.enqueue(encoder.encode(afterMark));
101
+ for (const script of pendingScripts)controller.enqueue(encoder.encode(script));
102
+ pendingScripts.length = 0;
103
+ };
104
+ const body = new TransformStream({
45
105
  start (controller) {
46
- const pendingScripts = [];
47
- let isClosed = false;
48
- const safeEnqueue = (chunk)=>{
49
- if (isClosed) return;
50
- try {
51
- controller.enqueue(chunk);
52
- } catch {
53
- isClosed = true;
54
- }
55
- };
56
- const closeController = ()=>{
57
- if (!isClosed) {
58
- isClosed = true;
59
- try {
60
- controller.close();
61
- } catch {}
62
- }
63
- };
64
- const flushPendingScripts = ()=>{
65
- for (const s of pendingScripts)safeEnqueue(encodeForWebStream(s));
66
- pendingScripts.length = 0;
67
- };
68
- const enqueueScript = (script)=>{
69
- if (shellChunkStatus === ShellChunkStatus.FINISH) safeEnqueue(encodeForWebStream(script));
70
- else pendingScripts.push(script);
71
- };
72
- const storageContext = storage.useContext?.();
73
- const activeDeferreds = storageContext?.activeDeferreds;
74
- const entries = activeDeferreds instanceof Map ? Array.from(activeDeferreds.entries()) : [];
75
- if (entries.length > 0) enqueueFromEntries(entries, config.nonce, enqueueScript);
76
- async function push() {
77
- try {
78
- const { done, value } = await reader.read();
79
- if (done) return void closeController();
80
- if (isClosed) return;
81
- if (shellChunkStatus !== ShellChunkStatus.FINISH) {
82
- chunkVec.push(new TextDecoder().decode(value));
83
- const concatedChunk = chunkVec.join('');
84
- const markerIndex = concatedChunk.indexOf(ESCAPED_SHELL_STREAM_END_MARK);
85
- if (-1 !== markerIndex) {
86
- const beforeMark = concatedChunk.slice(0, markerIndex);
87
- const afterMark = concatedChunk.slice(markerIndex + ESCAPED_SHELL_STREAM_END_MARK.length);
88
- __rspack_external__modern_js_runtime_extensions_ce611631.publishHeadRender(runtimeContext);
89
- const completedShellBefore = createReplaceHelemt(getHelmetData(runtimeContext))(shellBefore);
90
- shellChunkStatus = ShellChunkStatus.FINISH;
91
- safeEnqueue(encodeForWebStream(`${completedShellBefore}${beforeMark}${shellAfter}`));
92
- if (afterMark.length > 0) safeEnqueue(encodeForWebStream(afterMark));
93
- flushPendingScripts();
94
- }
95
- } else safeEnqueue(value);
96
- if (!isClosed) push();
97
- } catch (error) {
98
- __rspack_external__modern_js_runtime_extensions_ce611631.abortHeadRender(runtimeContext);
99
- if (!isClosed) {
100
- isClosed = true;
101
- try {
102
- controller.error(error);
103
- } catch {}
104
- }
105
- }
106
- }
107
- push();
106
+ bodyController = controller;
107
+ },
108
+ transform (chunk, controller) {
109
+ emit(decoder.decode(chunk, {
110
+ stream: true
111
+ }), controller);
108
112
  },
109
- cancel (reason) {
110
- __rspack_external__modern_js_runtime_extensions_ce611631.abortHeadRender(runtimeContext);
111
- reader.cancel(reason).catch(()=>{});
113
+ flush (controller) {
114
+ bodyOpen = false;
115
+ emit(decoder.decode(), controller);
116
+ if (shellChunkStatus !== ShellChunkStatus.FINISH) throw new Error('React SSR stream ended before the shell marker');
112
117
  }
113
118
  });
114
- return stream;
115
- } catch (e) {
116
- __rspack_external__modern_js_runtime_extensions_ce611631.abortHeadRender(runtimeContext);
117
- const fallbackHtml = `${shellBefore}${shellAfter}`;
118
- const stream = getReadableStreamFromString(fallbackHtml);
119
- return stream;
119
+ bodyPipe = pendingSource.pipeTo(body.writable);
120
+ bodyPipe.catch(()=>{});
121
+ pendingSource = body.readable;
122
+ const activeDeferreds = storage.useContext?.()?.activeDeferreds;
123
+ const entries = activeDeferreds instanceof Map ? Array.from(activeDeferreds.entries()) : [];
124
+ enqueueFromEntries(entries, config.nonce, (script)=>{
125
+ if (!bodyOpen) return;
126
+ if (shellChunkStatus === ShellChunkStatus.FINISH) try {
127
+ bodyController.enqueue(encoder.encode(script));
128
+ } catch {
129
+ bodyOpen = false;
130
+ }
131
+ else pendingScripts.push(script);
132
+ });
133
+ return observeSSRStream(pendingSource, {
134
+ lifecycle,
135
+ async onError (error) {
136
+ bodyOpen = false;
137
+ await bodyPipe?.catch(()=>{});
138
+ reportError(error);
139
+ },
140
+ signal: request.signal,
141
+ async onCancel (reason) {
142
+ bodyOpen = false;
143
+ await bodyPipe?.catch((error)=>{
144
+ if (error !== reason) throw error;
145
+ });
146
+ },
147
+ onComplete: ()=>bodyPipe
148
+ });
149
+ } catch (error) {
150
+ bodyOpen = false;
151
+ if (void 0 !== pendingSource) try {
152
+ await pendingSource.cancel(error);
153
+ await bodyPipe?.catch(()=>{});
154
+ } catch (cancelError) {
155
+ if (!request.signal.aborted) reportError(cancelError);
156
+ }
157
+ if (request.signal.aborted) {
158
+ lifecycle.finish({
159
+ status: 'cancelled',
160
+ reason: request.signal.reason
161
+ });
162
+ throw request.signal.reason;
163
+ }
164
+ lifecycle.finish({
165
+ status: 'fallback',
166
+ error
167
+ });
168
+ reportError(error);
169
+ const { shellBefore, shellAfter } = await getTemplates(htmlTemplate, {
170
+ ...templateOptions,
171
+ renderLevel: RenderLevel.CLIENT_RENDER,
172
+ helmetData: getHelmetData(extenders)
173
+ });
174
+ return observeSSRStream(getReadableStreamFromString(`${shellBefore}${shellAfter}`), {
175
+ lifecycle,
176
+ onError: reportError,
177
+ signal: request.signal
178
+ });
120
179
  }
121
180
  };
122
181
  export { createReadableStreamFromElement };
@@ -21,13 +21,13 @@ function isDeferredDataLike(v) {
21
21
  return hasPending && hasData;
22
22
  }
23
23
  function isPromiseLike(value) {
24
- return !!value && 'function' == typeof value.then;
24
+ return Boolean(value) && 'function' == typeof value.then;
25
25
  }
26
26
  function toErrorInfo(error) {
27
27
  if (shouldRedactServerError()) return {
28
28
  message: 'Unexpected Server Error'
29
29
  };
30
- if (error && 'object' == typeof error) {
30
+ if (null !== error && 'object' == typeof error) {
31
31
  const maybeMsg = error.message;
32
32
  const maybeStack = error.stack;
33
33
  return {
@@ -42,7 +42,7 @@ function toErrorInfo(error) {
42
42
  function buildDeferredDataScript(nonce, args) {
43
43
  const payload = JSON.stringify(args);
44
44
  const escaped = escapeAttr(payload);
45
- const nonceAttr = nonce ? ` nonce="${nonce}"` : '';
45
+ const nonceAttr = void 0 !== nonce && '' !== nonce ? ` nonce="${nonce}"` : '';
46
46
  return `<script async${nonceAttr} data-fn-name="r" data-script-src="modern-run-router-data-fn" data-fn-args='${escaped}' suppressHydrationWarning>${runRouterDataFnStr}</script>`;
47
47
  }
48
48
  function enqueueFromEntries(entries, nonce, emit) {