@modern-js/runtime 3.9.1 → 3.9.3

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 (45) hide show
  1. package/dist/cjs/cli/template.server.js +1 -2
  2. package/dist/cjs/core/browser/hydrate.js +13 -11
  3. package/dist/cjs/core/server/requestHandler.js +2 -0
  4. package/dist/cjs/core/server/stream/beforeTemplate.js +3 -3
  5. package/dist/cjs/core/server/stream/createReadableStream.js +51 -8
  6. package/dist/cjs/core/server/stream/createReadableStream.worker.js +37 -15
  7. package/dist/cjs/core/server/stream/deferredScript.js +12 -6
  8. package/dist/cjs/core/server/stream/deferredScriptOutputCoordinator.js +370 -0
  9. package/dist/cjs/core/server/string/loadable.js +4 -4
  10. package/dist/cjs/router/cli/code/nestedRoutes.js +1 -2
  11. package/dist/cjs/router/cli/code/templates.js +1 -1
  12. package/dist/cjs/router/cli/code/utils.js +2 -2
  13. package/dist/cjs/router/runtime/DeferredDataScripts.node.js +13 -10
  14. package/dist/cjs/router/runtime/plugin.node.js +1 -1
  15. package/dist/esm/cli/template.server.mjs +1 -2
  16. package/dist/esm/core/browser/hydrate.mjs +13 -11
  17. package/dist/esm/core/server/requestHandler.mjs +2 -0
  18. package/dist/esm/core/server/stream/beforeTemplate.mjs +3 -3
  19. package/dist/esm/core/server/stream/createReadableStream.mjs +51 -8
  20. package/dist/esm/core/server/stream/createReadableStream.worker.mjs +37 -15
  21. package/dist/esm/core/server/stream/deferredScript.mjs +12 -6
  22. package/dist/esm/core/server/stream/deferredScriptOutputCoordinator.mjs +332 -0
  23. package/dist/esm/core/server/string/loadable.mjs +4 -4
  24. package/dist/esm/router/cli/code/nestedRoutes.mjs +1 -2
  25. package/dist/esm/router/cli/code/templates.mjs +1 -1
  26. package/dist/esm/router/cli/code/utils.mjs +2 -2
  27. package/dist/esm/router/runtime/DeferredDataScripts.node.mjs +13 -10
  28. package/dist/esm/router/runtime/plugin.node.mjs +1 -1
  29. package/dist/esm-node/cli/template.server.mjs +1 -2
  30. package/dist/esm-node/core/browser/hydrate.mjs +13 -11
  31. package/dist/esm-node/core/server/requestHandler.mjs +2 -0
  32. package/dist/esm-node/core/server/stream/beforeTemplate.mjs +3 -3
  33. package/dist/esm-node/core/server/stream/createReadableStream.mjs +51 -8
  34. package/dist/esm-node/core/server/stream/createReadableStream.worker.mjs +37 -15
  35. package/dist/esm-node/core/server/stream/deferredScript.mjs +12 -6
  36. package/dist/esm-node/core/server/stream/deferredScriptOutputCoordinator.mjs +333 -0
  37. package/dist/esm-node/core/server/string/loadable.mjs +4 -4
  38. package/dist/esm-node/router/cli/code/nestedRoutes.mjs +1 -2
  39. package/dist/esm-node/router/cli/code/templates.mjs +1 -1
  40. package/dist/esm-node/router/cli/code/utils.mjs +2 -2
  41. package/dist/esm-node/router/runtime/DeferredDataScripts.node.mjs +13 -10
  42. package/dist/esm-node/router/runtime/plugin.node.mjs +1 -1
  43. package/dist/types/core/server/stream/deferredScript.d.ts +1 -1
  44. package/dist/types/core/server/stream/deferredScriptOutputCoordinator.d.ts +72 -0
  45. package/package.json +12 -12
@@ -141,8 +141,7 @@ function genHandlerCode({ mode, metaName, customServerEntry, srcDirectory, inter
141
141
  return `
142
142
  export * from '${realEntryPath}';
143
143
  export { default as requestHandler } from '${realEntryPath}'`;
144
- }
145
- {
144
+ } else {
146
145
  const entrySource = enableRsc ? SERVER_ENTRY_RSC : SERVER_ENTRY;
147
146
  const serverEntry = transformServerEntry(entrySource, {
148
147
  metaName: metaName || 'modern-js',
@@ -59,7 +59,7 @@ function hydrateRoot(App, context, ModernRender, ModernHydrate) {
59
59
  if ('stream' === renderMode) return streamSSRHydrate();
60
60
  function streamSSRHydrate() {
61
61
  if (renderLevel !== external_constants_js_namespaceObject.RenderLevel.SERVER_RENDER) return ModernRender((0, wrapper_js_namespaceObject.wrapRuntimeContextProvider)(App, context));
62
- {
62
+ else {
63
63
  const SSRApp = ()=>/*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_withCallback_js_namespaceObject.WithCallback, {
64
64
  callback: callback,
65
65
  children: App
@@ -70,19 +70,21 @@ function hydrateRoot(App, context, ModernRender, ModernHydrate) {
70
70
  return stringSSRHydrate();
71
71
  function stringSSRHydrate() {
72
72
  if (renderLevel === external_constants_js_namespaceObject.RenderLevel.CLIENT_RENDER) return ModernRender((0, wrapper_js_namespaceObject.wrapRuntimeContextProvider)(App, context));
73
- if (renderLevel === external_constants_js_namespaceObject.RenderLevel.SERVER_RENDER) return new Promise((resolve)=>{
74
- (0, component_namespaceObject.loadableReady)(()=>{
75
- const SSRApp = ()=>/*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_withCallback_js_namespaceObject.WithCallback, {
76
- callback: callback,
77
- children: App
73
+ else {
74
+ if (renderLevel === external_constants_js_namespaceObject.RenderLevel.SERVER_RENDER) return new Promise((resolve)=>{
75
+ (0, component_namespaceObject.loadableReady)(()=>{
76
+ const SSRApp = ()=>/*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_withCallback_js_namespaceObject.WithCallback, {
77
+ callback: callback,
78
+ children: App
79
+ });
80
+ ModernHydrate((0, wrapper_js_namespaceObject.wrapRuntimeContextProvider)(/*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(SSRApp, {}), hydrateContext)).then((root)=>{
81
+ resolve(root);
78
82
  });
79
- ModernHydrate((0, wrapper_js_namespaceObject.wrapRuntimeContextProvider)(/*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(SSRApp, {}), hydrateContext)).then((root)=>{
80
- resolve(root);
81
83
  });
82
84
  });
83
- });
84
- console.warn(`unknow render level: ${renderLevel}, execute render()`);
85
- return ModernRender((0, wrapper_js_namespaceObject.wrapRuntimeContextProvider)(App, context));
85
+ console.warn(`unknow render level: ${renderLevel}, execute render()`);
86
+ return ModernRender((0, wrapper_js_namespaceObject.wrapRuntimeContextProvider)(App, context));
87
+ }
86
88
  }
87
89
  }
88
90
  exports.hydrateRoot = __webpack_exports__.hydrateRoot;
@@ -143,12 +143,14 @@ const createRequestHandler = async (handleRequest, createRequestOptions)=>{
143
143
  status: -1
144
144
  };
145
145
  const activeDeferreds = new Map();
146
+ const deferredScriptKeys = new Map();
146
147
  return node_namespaceObject.storage.run({
147
148
  headers: headersData,
148
149
  request,
149
150
  monitors: options.monitors,
150
151
  responseProxy,
151
152
  activeDeferreds,
153
+ deferredScriptKeys,
152
154
  serverPayload: void 0
153
155
  }, async ()=>{
154
156
  const Root = (0, external_react_index_js_namespaceObject.createRoot)();
@@ -54,8 +54,8 @@ const readAsset = async (chunk)=>{
54
54
  };
55
55
  const checkIsInline = (chunk, enableInline)=>{
56
56
  if ('production' !== process.env.NODE_ENV) return false;
57
- if (enableInline instanceof RegExp) return enableInline.test(chunk);
58
- return Boolean(enableInline);
57
+ else if (enableInline instanceof RegExp) return enableInline.test(chunk);
58
+ else return Boolean(enableInline);
59
59
  };
60
60
  async function buildShellBeforeTemplate(beforeAppTemplate, options) {
61
61
  const { config, runtimeContext, styledComponentsStyleTags, entryName } = options;
@@ -96,7 +96,7 @@ async function buildShellBeforeTemplate(beforeAppTemplate, options) {
96
96
  const styles = await Promise.all(cssChunks.map(async (chunk)=>{
97
97
  const link = `<link href="${chunk}" rel="stylesheet" />`;
98
98
  if ((0, external_utils_js_namespaceObject.checkIsNode)() && checkIsInline(chunk, inlineStyles)) return readAsset(chunk).then((content)=>`<style>${content}</style>`).catch((_)=>link);
99
- return link;
99
+ else return link;
100
100
  }));
101
101
  return `${styles.join('')}`;
102
102
  }
@@ -31,6 +31,7 @@ __webpack_require__.d(__webpack_exports__, {
31
31
  createReadableStreamFromElement: ()=>createReadableStreamFromElement
32
32
  });
33
33
  const external_stream_namespaceObject = require("stream");
34
+ const external_string_decoder_namespaceObject = require("string_decoder");
34
35
  const node_namespaceObject = require("@modern-js/runtime-utils/node");
35
36
  const constants_namespaceObject = require("@modern-js/utils/universal/constants");
36
37
  const external_common_js_namespaceObject = require("../../../common.js");
@@ -38,6 +39,7 @@ const external_constants_js_namespaceObject = require("../../constants.js");
38
39
  const index_js_namespaceObject = require("../../context/index.js");
39
40
  const monitors_index_js_namespaceObject = require("../../context/monitors/index.js");
40
41
  const external_deferredScript_js_namespaceObject = require("./deferredScript.js");
42
+ const external_deferredScriptOutputCoordinator_js_namespaceObject = require("./deferredScriptOutputCoordinator.js");
41
43
  const external_shared_js_namespaceObject = require("./shared.js");
42
44
  const external_template_js_namespaceObject = require("./template.js");
43
45
  const defaultExtender = {
@@ -86,7 +88,14 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
86
88
  entryName,
87
89
  styledComponentsStyleTags
88
90
  }).then(({ shellAfter, shellBefore })=>{
91
+ const decoder = new external_string_decoder_namespaceObject.StringDecoder('utf8');
89
92
  const pendingScripts = [];
93
+ const outputState = {};
94
+ let deferredResolversComplete = Promise.resolve();
95
+ const writeReact = (content)=>{
96
+ if (!outputState.coordinator) throw new Error("Deferred script coordinator is not ready");
97
+ outputState.coordinator.writeReact(content);
98
+ };
90
99
  const body = new external_stream_namespaceObject.Transform({
91
100
  transform (chunk, _encoding, callback) {
92
101
  try {
@@ -98,17 +107,49 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
98
107
  const beforeMark = concatedChunk.slice(0, markerIndex);
99
108
  const afterMark = concatedChunk.slice(markerIndex + external_common_js_namespaceObject.ESCAPED_SHELL_STREAM_END_MARK.length);
100
109
  shellChunkStatus = external_shared_js_namespaceObject.ShellChunkStatus.FINISH;
101
- this.push(`${shellBefore}${beforeMark}${shellAfter}`);
102
- if (afterMark) this.push(afterMark);
103
- if (pendingScripts.length > 0) for (const s of pendingScripts)this.push(s);
110
+ writeReact(`${shellBefore}${beforeMark}${shellAfter}`);
111
+ if (afterMark) writeReact(afterMark);
112
+ if (outputState.coordinator) {
113
+ outputState.coordinator.markShellFinished();
114
+ for (const script of pendingScripts)outputState.coordinator.enqueueResolver(script);
115
+ pendingScripts.length = 0;
116
+ }
104
117
  }
105
- } else this.push(chunk);
118
+ } else {
119
+ const decodedChunk = decoder.write(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
120
+ writeReact(decodedChunk);
121
+ }
106
122
  callback();
107
123
  } catch (e) {
108
124
  e instanceof Error ? callback(e) : callback(new Error('Received unknown error when streaming'));
109
125
  }
126
+ },
127
+ flush (callback) {
128
+ try {
129
+ writeReact(decoder.end());
130
+ } catch (error) {
131
+ callback(error instanceof Error ? error : new Error('Received unknown error when streaming'));
132
+ return;
133
+ }
134
+ deferredResolversComplete.then(()=>{
135
+ try {
136
+ outputState.coordinator?.finish();
137
+ callback();
138
+ } catch (error) {
139
+ callback(error instanceof Error ? error : new Error('Received unknown error when streaming'));
140
+ }
141
+ }, (error)=>{
142
+ callback(error instanceof Error ? error : new Error('Received unknown error when streaming'));
143
+ });
110
144
  }
111
145
  });
146
+ const coordinator = new external_deferredScriptOutputCoordinator_js_namespaceObject.DeferredScriptOutputCoordinator((content)=>{
147
+ body.push(content);
148
+ });
149
+ outputState.coordinator = coordinator;
150
+ body.once('close', ()=>{
151
+ coordinator.abort();
152
+ });
112
153
  const passThrough = new external_stream_namespaceObject.PassThrough();
113
154
  const stream = external_stream_namespaceObject.Readable.toWeb(body);
114
155
  resolve(stream);
@@ -116,23 +157,25 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
116
157
  extenders.forEach((extender)=>{
117
158
  if (extender.processStream) processedStream = extender.processStream(processedStream);
118
159
  });
119
- reactStreamingPipe(passThrough);
120
- processedStream.pipe(body);
121
160
  try {
122
161
  const storageContext = node_namespaceObject.storage.useContext?.();
123
162
  const activeDeferreds = storageContext?.activeDeferreds;
163
+ const deferredScriptKeys = storageContext?.deferredScriptKeys;
124
164
  const entries = activeDeferreds instanceof Map ? Array.from(activeDeferreds.entries()) : [];
125
165
  if (entries.length > 0) {
126
166
  const enqueueScript = (s)=>{
127
- if (shellChunkStatus === external_shared_js_namespaceObject.ShellChunkStatus.FINISH) body.write(s);
167
+ if (!outputState.coordinator) throw new Error("Deferred script coordinator is not ready");
168
+ if (shellChunkStatus === external_shared_js_namespaceObject.ShellChunkStatus.FINISH) outputState.coordinator.enqueueResolver(s);
128
169
  else pendingScripts.push(s);
129
170
  };
130
- (0, external_deferredScript_js_namespaceObject.enqueueFromEntries)(entries, config.nonce, (s)=>enqueueScript(s));
171
+ deferredResolversComplete = (0, external_deferredScript_js_namespaceObject.enqueueFromEntries)(entries, deferredScriptKeys, config.nonce, enqueueScript);
131
172
  }
132
173
  } catch (err) {
133
174
  const monitors = (0, monitors_index_js_namespaceObject.getMonitors)();
134
175
  monitors.error("cannot inject router data script", err);
135
176
  }
177
+ reactStreamingPipe(passThrough);
178
+ processedStream.pipe(body);
136
179
  });
137
180
  },
138
181
  onShellError (error) {
@@ -35,6 +35,7 @@ const node_namespaceObject = require("@modern-js/runtime-utils/node");
35
35
  const external_common_js_namespaceObject = require("../../../common.js");
36
36
  const external_constants_js_namespaceObject = require("../../constants.js");
37
37
  const external_deferredScript_js_namespaceObject = require("./deferredScript.js");
38
+ const external_deferredScriptOutputCoordinator_js_namespaceObject = require("./deferredScriptOutputCoordinator.js");
38
39
  const external_shared_js_namespaceObject = require("./shared.js");
39
40
  const external_template_js_namespaceObject = require("./template.js");
40
41
  const createReadableStreamFromElement = async (request, rootElement, options)=>{
@@ -67,10 +68,13 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
67
68
  const { waitForAllReady } = (0, external_shared_js_namespaceObject.resolveStreamingMode)(request, forceStreamToString);
68
69
  if (waitForAllReady) await readableOriginal.allReady;
69
70
  const reader = readableOriginal.getReader();
71
+ let coordinator;
70
72
  const stream = new ReadableStream({
71
73
  start (controller) {
74
+ const decoder = new TextDecoder();
72
75
  const pendingScripts = [];
73
76
  let isClosed = false;
77
+ let deferredResolversComplete = Promise.resolve();
74
78
  const safeEnqueue = (chunk)=>{
75
79
  if (isClosed) return;
76
80
  try {
@@ -87,38 +91,55 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
87
91
  } catch {}
88
92
  }
89
93
  };
90
- const flushPendingScripts = ()=>{
91
- for (const s of pendingScripts)safeEnqueue((0, external_shared_js_namespaceObject.encodeForWebStream)(s));
92
- pendingScripts.length = 0;
93
- };
94
- const enqueueScript = (script)=>{
95
- if (shellChunkStatus === external_shared_js_namespaceObject.ShellChunkStatus.FINISH) safeEnqueue((0, external_shared_js_namespaceObject.encodeForWebStream)(script));
96
- else pendingScripts.push(script);
97
- };
94
+ coordinator = new external_deferredScriptOutputCoordinator_js_namespaceObject.DeferredScriptOutputCoordinator((content)=>{
95
+ safeEnqueue((0, external_shared_js_namespaceObject.encodeForWebStream)(content));
96
+ });
98
97
  const storageContext = node_namespaceObject.storage.useContext?.();
99
98
  const activeDeferreds = storageContext?.activeDeferreds;
99
+ const deferredScriptKeys = storageContext?.deferredScriptKeys;
100
100
  const entries = activeDeferreds instanceof Map ? Array.from(activeDeferreds.entries()) : [];
101
- if (entries.length > 0) (0, external_deferredScript_js_namespaceObject.enqueueFromEntries)(entries, config.nonce, enqueueScript);
101
+ if (entries.length > 0) deferredResolversComplete = (0, external_deferredScript_js_namespaceObject.enqueueFromEntries)(entries, deferredScriptKeys, config.nonce, (script)=>{
102
+ if (!coordinator) throw new Error("Deferred script coordinator is not ready");
103
+ if (shellChunkStatus === external_shared_js_namespaceObject.ShellChunkStatus.FINISH) coordinator.enqueueResolver(script);
104
+ else pendingScripts.push(script);
105
+ });
102
106
  async function push() {
103
107
  try {
104
108
  const { done, value } = await reader.read();
105
- if (done) return void closeController();
109
+ if (done) {
110
+ const trailingText = decoder.decode();
111
+ if (trailingText) coordinator?.writeReact(trailingText);
112
+ await deferredResolversComplete;
113
+ coordinator?.finish();
114
+ closeController();
115
+ return;
116
+ }
106
117
  if (isClosed) return;
107
118
  if (shellChunkStatus !== external_shared_js_namespaceObject.ShellChunkStatus.FINISH) {
108
- chunkVec.push(new TextDecoder().decode(value));
119
+ chunkVec.push(decoder.decode(value, {
120
+ stream: true
121
+ }));
109
122
  const concatedChunk = chunkVec.join('');
110
123
  const markerIndex = concatedChunk.indexOf(external_common_js_namespaceObject.ESCAPED_SHELL_STREAM_END_MARK);
111
124
  if (-1 !== markerIndex) {
112
125
  const beforeMark = concatedChunk.slice(0, markerIndex);
113
126
  const afterMark = concatedChunk.slice(markerIndex + external_common_js_namespaceObject.ESCAPED_SHELL_STREAM_END_MARK.length);
114
127
  shellChunkStatus = external_shared_js_namespaceObject.ShellChunkStatus.FINISH;
115
- safeEnqueue((0, external_shared_js_namespaceObject.encodeForWebStream)(`${shellBefore}${beforeMark}${shellAfter}`));
116
- if (afterMark) safeEnqueue((0, external_shared_js_namespaceObject.encodeForWebStream)(afterMark));
117
- flushPendingScripts();
128
+ coordinator?.writeReact(`${shellBefore}${beforeMark}${shellAfter}`);
129
+ if (afterMark) coordinator?.writeReact(afterMark);
130
+ coordinator?.markShellFinished();
131
+ for (const script of pendingScripts)coordinator?.enqueueResolver(script);
132
+ pendingScripts.length = 0;
118
133
  }
119
- } else safeEnqueue(value);
134
+ } else {
135
+ const decodedChunk = decoder.decode(value, {
136
+ stream: true
137
+ });
138
+ if (decodedChunk) coordinator?.writeReact(decodedChunk);
139
+ }
120
140
  if (!isClosed) push();
121
141
  } catch (error) {
142
+ coordinator?.abort();
122
143
  if (!isClosed) {
123
144
  isClosed = true;
124
145
  try {
@@ -130,6 +151,7 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
130
151
  push();
131
152
  },
132
153
  cancel (reason) {
154
+ coordinator?.abort();
133
155
  reader.cancel(reason).catch(()=>{});
134
156
  }
135
157
  });
@@ -78,24 +78,30 @@ function buildDeferredDataScript(nonce, args) {
78
78
  const nonceAttr = nonce ? ` nonce="${nonce}"` : '';
79
79
  return `<script async${nonceAttr} data-fn-name="r" data-script-src="modern-run-router-data-fn" data-fn-args='${escaped}' suppressHydrationWarning>${constants_js_namespaceObject.runRouterDataFnStr}</script>`;
80
80
  }
81
- function enqueueFromEntries(entries, nonce, emit) {
81
+ function enqueueFromEntries(entries, deferredScriptKeys, nonce, emit) {
82
+ const pendingResolvers = [];
82
83
  entries.forEach(([routeId, value])=>{
83
84
  if (!isDeferredDataLike(value)) return;
84
- const pendingKeys = new Set(value.pendingKeys ?? []);
85
- pendingKeys.forEach((key)=>{
85
+ const serializedKeys = deferredScriptKeys ? deferredScriptKeys.get(routeId) ?? [] : value.pendingKeys ?? [];
86
+ serializedKeys.forEach((key)=>{
86
87
  const tracked = value.data?.[key];
87
- if (isPromiseLike(tracked)) tracked.then((val)=>emit(buildDeferredDataScript(nonce, [
88
+ if (isPromiseLike(tracked)) pendingResolvers.push(Promise.resolve(tracked).then((val)=>{
89
+ emit(buildDeferredDataScript(nonce, [
88
90
  routeId,
89
91
  key,
90
92
  val
91
- ])), (err)=>emit(buildDeferredDataScript(nonce, [
93
+ ]));
94
+ }, (err)=>{
95
+ emit(buildDeferredDataScript(nonce, [
92
96
  routeId,
93
97
  key,
94
98
  void 0,
95
99
  toErrorInfo(err)
96
- ])));
100
+ ]));
101
+ }));
97
102
  });
98
103
  });
104
+ return Promise.all(pendingResolvers).then(()=>void 0);
99
105
  }
100
106
  exports.buildDeferredDataScript = __webpack_exports__.buildDeferredDataScript;
101
107
  exports.enqueueFromEntries = __webpack_exports__.enqueueFromEntries;