@bleedingdev/modern-js-runtime 3.8.2-ultramodern.9 → 3.8.3-ultramodern.2

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 (84) hide show
  1. package/dist/cjs/cli/ssr/index.js +1 -1
  2. package/dist/cjs/cli/ssr/loadable-bundler-plugin.js +1 -1
  3. package/dist/cjs/core/browser/hydrate.js +1 -11
  4. package/dist/cjs/core/context/extensions.js +7 -2
  5. package/dist/cjs/core/context/runtime.js +13 -2
  6. package/dist/cjs/core/react/wrapper.js +10 -8
  7. package/dist/cjs/core/server/federatedCss.js +7 -4
  8. package/dist/cjs/core/server/helmet.js +2 -0
  9. package/dist/cjs/core/server/requestResponse.js +35 -11
  10. package/dist/cjs/core/server/routerCleanup.js +10 -1
  11. package/dist/cjs/core/server/scriptOrder.js +5 -2
  12. package/dist/cjs/core/server/stream/beforeTemplate.worker.js +1 -1
  13. package/dist/cjs/core/server/stream/createReadableStream.js +18 -5
  14. package/dist/cjs/core/server/stream/createReadableStream.worker.js +10 -2
  15. package/dist/cjs/core/server/stream/deferredScript.js +2 -1
  16. package/dist/cjs/core/server/string/index.js +4 -0
  17. package/dist/cjs/core/server/string/loadable.js +11 -2
  18. package/dist/cjs/core/server/utils.js +12 -5
  19. package/dist/cjs/exports/head.js +7 -242
  20. package/dist/cjs/module-federation/last-known-good.js +35 -2
  21. package/dist/cjs/router/cli/nestedRoutesSpec.js +7 -38
  22. package/dist/cjs/router/runtime/PrefetchLink.js +55 -8
  23. package/dist/cjs/router/runtime/redirect.js +11 -1
  24. package/dist/cjs/router/runtime/utils.js +2 -0
  25. package/dist/esm/cli/ssr/index.mjs +1 -1
  26. package/dist/esm/cli/ssr/loadable-bundler-plugin.mjs +1 -1
  27. package/dist/esm/core/browser/hydrate.mjs +1 -11
  28. package/dist/esm/core/context/extensions.mjs +7 -2
  29. package/dist/esm/core/context/runtime.mjs +13 -2
  30. package/dist/esm/core/react/wrapper.mjs +11 -9
  31. package/dist/esm/core/server/federatedCss.mjs +7 -4
  32. package/dist/esm/core/server/helmet.mjs +2 -0
  33. package/dist/esm/core/server/requestResponse.mjs +35 -11
  34. package/dist/esm/core/server/routerCleanup.mjs +10 -1
  35. package/dist/esm/core/server/scriptOrder.mjs +6 -3
  36. package/dist/esm/core/server/stream/beforeTemplate.worker.mjs +2 -2
  37. package/dist/esm/core/server/stream/createReadableStream.mjs +16 -3
  38. package/dist/esm/core/server/stream/createReadableStream.worker.mjs +10 -2
  39. package/dist/esm/core/server/stream/deferredScript.mjs +2 -1
  40. package/dist/esm/core/server/string/index.mjs +4 -0
  41. package/dist/esm/core/server/string/loadable.mjs +11 -2
  42. package/dist/esm/core/server/utils.mjs +8 -4
  43. package/dist/esm/exports/head.mjs +8 -237
  44. package/dist/esm/module-federation/last-known-good.mjs +35 -2
  45. package/dist/esm/router/cli/nestedRoutesSpec.mjs +7 -38
  46. package/dist/esm/router/runtime/PrefetchLink.mjs +28 -8
  47. package/dist/esm/router/runtime/redirect.mjs +11 -1
  48. package/dist/esm/router/runtime/utils.mjs +2 -0
  49. package/dist/esm/rslib-runtime.mjs +9 -0
  50. package/dist/esm-node/cli/ssr/index.mjs +1 -1
  51. package/dist/esm-node/cli/ssr/loadable-bundler-plugin.mjs +1 -1
  52. package/dist/esm-node/core/browser/hydrate.mjs +1 -11
  53. package/dist/esm-node/core/context/extensions.mjs +7 -2
  54. package/dist/esm-node/core/context/runtime.mjs +13 -2
  55. package/dist/esm-node/core/react/wrapper.mjs +11 -9
  56. package/dist/esm-node/core/server/federatedCss.mjs +7 -4
  57. package/dist/esm-node/core/server/helmet.mjs +2 -0
  58. package/dist/esm-node/core/server/requestResponse.mjs +35 -11
  59. package/dist/esm-node/core/server/routerCleanup.mjs +10 -1
  60. package/dist/esm-node/core/server/scriptOrder.mjs +6 -3
  61. package/dist/esm-node/core/server/stream/beforeTemplate.worker.mjs +2 -2
  62. package/dist/esm-node/core/server/stream/createReadableStream.mjs +16 -3
  63. package/dist/esm-node/core/server/stream/createReadableStream.worker.mjs +10 -2
  64. package/dist/esm-node/core/server/stream/deferredScript.mjs +2 -1
  65. package/dist/esm-node/core/server/string/index.mjs +4 -0
  66. package/dist/esm-node/core/server/string/loadable.mjs +11 -2
  67. package/dist/esm-node/core/server/utils.mjs +8 -4
  68. package/dist/esm-node/exports/head.mjs +8 -237
  69. package/dist/esm-node/module-federation/last-known-good.mjs +35 -2
  70. package/dist/esm-node/router/cli/nestedRoutesSpec.mjs +7 -38
  71. package/dist/esm-node/router/runtime/PrefetchLink.mjs +28 -8
  72. package/dist/esm-node/router/runtime/redirect.mjs +11 -1
  73. package/dist/esm-node/router/runtime/utils.mjs +2 -0
  74. package/dist/esm-node/rslib-runtime.mjs +10 -0
  75. package/dist/types/core/context/extensions.d.ts +2 -4
  76. package/dist/types/core/context/public.d.ts +4 -8
  77. package/dist/types/core/server/requestResponse.d.ts +1 -1
  78. package/dist/types/core/server/routerCleanup.d.ts +2 -0
  79. package/dist/types/core/server/string/loadable.d.ts +1 -0
  80. package/dist/types/core/server/utils.d.ts +1 -0
  81. package/dist/types/exports/head.d.ts +1 -2
  82. package/dist/types/module-federation/last-known-good.d.ts +0 -20
  83. package/package.json +11 -10
  84. package/rstest.config.mts +11 -4
@@ -1,15 +1,17 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { HelmetProvider } from "react-helmet-async";
3
- import { InternalRuntimeContext, RuntimeContext, getGlobalEnableRsc } from "../context/index.mjs";
3
+ import { InternalRuntimeContext, RuntimeContext, getGlobalEnableRsc, getInitialContext } from "../context/index.mjs";
4
4
  import { stripRuntimeContextExtensions } from "../context/extensions.mjs";
5
5
  import { ensureHelmetContext } from "../context/helmetContext.mjs";
6
6
  function createRscSafeRequestContext(ssrContext) {
7
- if (void 0 === ssrContext) return {
8
- request: {},
9
- response: {
10
- locals: {}
11
- }
12
- };
7
+ const requestContext = getInitialContext().requestContext;
8
+ Object.defineProperty(requestContext.response, 'setHeader', {
9
+ enumerable: false
10
+ });
11
+ Object.defineProperty(requestContext.response, 'status', {
12
+ enumerable: false
13
+ });
14
+ if (void 0 === ssrContext) return requestContext;
13
15
  const { request, response } = ssrContext;
14
16
  return {
15
17
  request: {
@@ -29,9 +31,9 @@ function createRscSafeRequestContext(ssrContext) {
29
31
  host: request.host,
30
32
  referer: request.referer
31
33
  },
32
- response: {
34
+ response: Object.assign(requestContext.response, {
33
35
  locals: response.locals ?? {}
34
- }
36
+ })
35
37
  };
36
38
  }
37
39
  function wrapRuntimeContextProvider(App, contextValue) {
@@ -1,13 +1,16 @@
1
- import { attributesToString } from "./utils.mjs";
2
- const escapeHtmlAttribute = (value)=>value.replaceAll('&', '&amp;').replaceAll('"', '&quot;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
1
+ import { escapeHtmlAttribute } from "@modern-js/runtime-extensions";
2
+ import { attributesToString, hasStylesheetLink } from "./utils.mjs";
3
3
  const createFederatedCssLinks = (assets, options)=>{
4
4
  if (void 0 === assets || 0 === assets.length) return '';
5
5
  const seen = new Set(options.existingAssets || []);
6
- const attributes = attributesToString(options.attributes || {});
6
+ const attributes = attributesToString(Object.fromEntries(Object.entries(options.attributes || {}).filter(([name])=>![
7
+ 'href',
8
+ 'rel'
9
+ ].includes(name.toLowerCase()))));
7
10
  const links = [];
8
11
  for (const asset of assets){
9
12
  const href = escapeHtmlAttribute(asset);
10
- if (!('' === asset || seen.has(asset) || options.template.includes(asset) || options.template.includes(href))) {
13
+ if (!('' === asset || seen.has(asset) || hasStylesheetLink(options.template, asset) || hasStylesheetLink(options.template, href))) {
11
14
  seen.add(asset);
12
15
  links.push(`<link${attributes} href="${href}" rel="stylesheet" />`);
13
16
  }
@@ -19,6 +19,7 @@ function helmetReplace(content, helmetData) {
19
19
  const htmlAttributes = helmetData.htmlAttributes.toString();
20
20
  if (htmlAttributes) result = safeReplace(result, RE_HTML_ATTR, `<html ${htmlAttributes}>`);
21
21
  const base = helmetData.base.toString();
22
+ const priority = helmetData.priority.toString();
22
23
  const link = helmetData.link.toString();
23
24
  const meta = helmetData.meta.toString();
24
25
  const noscript = helmetData.noscript.toString();
@@ -30,6 +31,7 @@ function helmetReplace(content, helmetData) {
30
31
  if (shouldReplaceTitle) result = safeReplace(result, RE_TITLE, title);
31
32
  const helmetStr = [
32
33
  base,
34
+ priority,
33
35
  link,
34
36
  meta,
35
37
  noscript,
@@ -1,9 +1,15 @@
1
1
  import { getRouterServerSnapshot } from "../../router/runtime/lifecycle.mjs";
2
2
  import { handleRSCRedirect } from "../../router/runtime/redirect.mjs";
3
3
  import { SSRErrors } from "./tracer.mjs";
4
- const isRedirectStatus = (status)=>status >= 300 && status <= 399;
4
+ const isRedirectStatus = (status)=>301 === status || 302 === status || 303 === status || 307 === status || 308 === status;
5
5
  const isNullBodyStatus = (status)=>204 === status || 205 === status || 304 === status;
6
+ const getRedirectLocation = (headers)=>{
7
+ const location = headers.get('Location');
8
+ return location && URL.canParse(location, 'http://localhost') ? location : void 0;
9
+ };
6
10
  const processRedirect = (headers, status, ctx)=>{
11
+ headers.delete('content-length');
12
+ headers.delete('transfer-encoding');
7
13
  if (ctx.enableRsc && ctx.isRSCNavigation) return handleRSCRedirect(headers, ctx.basename, status);
8
14
  return new Response(null, {
9
15
  status,
@@ -20,20 +26,38 @@ const applyRouterSnapshotResult = (context, onError)=>{
20
26
  const createLoaderRedirectResponse = (beforeRenderResult, redirectCtx)=>{
21
27
  if (void 0 === beforeRenderResult || !isRedirectStatus(beforeRenderResult.status)) return;
22
28
  if (beforeRenderResult.headers.has('X-Modernjs-Redirect')) return beforeRenderResult;
23
- const redirectUrl = beforeRenderResult.headers.get('Location') || '/';
24
- return processRedirect(new Headers({
25
- Location: redirectUrl
26
- }), beforeRenderResult.status, redirectCtx);
29
+ const redirectUrl = getRedirectLocation(beforeRenderResult.headers);
30
+ if (!redirectUrl) return;
31
+ return processRedirect(new Headers(beforeRenderResult.headers), beforeRenderResult.status, redirectCtx);
27
32
  };
28
- const finalizeRenderResponse = (response, responseProxy, redirectCtx, routerCleanup)=>{
29
- if (-1 !== responseProxy.status && isRedirectStatus(responseProxy.status) && void 0 !== responseProxy.headers.Location && '' !== responseProxy.headers.Location) return processRedirect(new Headers(responseProxy.headers), responseProxy.status, redirectCtx);
33
+ const finalizeRenderResponse = async (response, responseProxy, redirectCtx, routerCleanup)=>{
34
+ const proxyHeaders = new Headers(responseProxy.headers);
35
+ if (-1 !== responseProxy.status && isRedirectStatus(responseProxy.status) && void 0 !== getRedirectLocation(proxyHeaders)) {
36
+ await routerCleanup.discardBody(response);
37
+ return processRedirect(proxyHeaders, responseProxy.status, redirectCtx);
38
+ }
39
+ const headers = new Headers(response.headers);
40
+ Object.entries(responseProxy.headers).forEach(([key, value])=>{
41
+ headers.set(key, value);
42
+ });
43
+ if (-1 !== responseProxy.status) {
44
+ if (isNullBodyStatus(responseProxy.status)) {
45
+ await routerCleanup.discardBody(response);
46
+ headers.delete('content-length');
47
+ headers.delete('transfer-encoding');
48
+ return new Response(null, {
49
+ status: responseProxy.status,
50
+ headers
51
+ });
52
+ }
53
+ return routerCleanup.deferUntilBodyDone(new Response(response.body, {
54
+ status: responseProxy.status,
55
+ headers
56
+ }));
57
+ }
30
58
  Object.entries(responseProxy.headers).forEach(([key, value])=>{
31
59
  response.headers.set(key, value);
32
60
  });
33
- if (-1 !== responseProxy.status) return routerCleanup.deferUntilBodyDone(new Response(isNullBodyStatus(responseProxy.status) ? null : response.body, {
34
- status: responseProxy.status,
35
- headers: response.headers
36
- }));
37
61
  return routerCleanup.deferUntilBodyDone(response);
38
62
  };
39
63
  export { applyRouterSnapshotResult, createLoaderRedirectResponse, finalizeRenderResponse };
@@ -62,12 +62,21 @@ function createRouterCleanup(runtimeContext, onError) {
62
62
  headers: response.headers
63
63
  });
64
64
  };
65
+ const discardBody = async (response)=>{
66
+ const { body } = response;
67
+ if (!body) return void await run();
68
+ deferred = true;
69
+ if (body.locked) throw new TypeError('Cannot discard a locked response body before router cleanup');
70
+ await body.cancel('Response body discarded during finalization');
71
+ await run();
72
+ };
65
73
  return {
66
74
  get deferred () {
67
75
  return deferred;
68
76
  },
69
77
  run,
70
- deferUntilBodyDone
78
+ deferUntilBodyDone,
79
+ discardBody
71
80
  };
72
81
  }
73
82
  export { ROUTER_CLEANUP_ERROR, createRouterCleanup, finishWithRouterCleanup, runWithRouterCleanupOnError };
@@ -1,6 +1,6 @@
1
1
  import { getRouterMatchedRouteIds } from "../../router/runtime/lifecycle.mjs";
2
2
  import { CHUNK_JS_PLACEHOLDER } from "./constants.mjs";
3
- import { safeReplace } from "./utils.mjs";
3
+ import { attributesToString, safeReplace } from "./utils.mjs";
4
4
  function getScriptTags(template) {
5
5
  const scriptRegExp = /<script\b[^>]*\bsrc=(["'])(.*?)\1[^>]*><\/script>/g;
6
6
  return Array.from(template.matchAll(scriptRegExp)).map((match)=>({
@@ -66,6 +66,7 @@ function injectBeforeHydrationEntryScript(template, scripts, entryName = 'index'
66
66
  return `${template.slice(0, target.index)}${scripts}${template.slice(target.index)}`;
67
67
  }
68
68
  function replaceChunkJsPlaceholder(template, scripts, entryName, placeholder = CHUNK_JS_PLACEHOLDER) {
69
+ if (!template.includes(placeholder)) return template;
69
70
  if ('' === scripts) return safeReplace(template, placeholder, '');
70
71
  const withoutPlaceholder = safeReplace(template, placeholder, '');
71
72
  const withEarlyScripts = injectBeforeHydrationEntryScript(withoutPlaceholder, scripts, entryName);
@@ -81,7 +82,9 @@ function createRouteHydrationScriptTags(runtimeContext, entryName, options = {})
81
82
  ...getMatchedRouteAssets(runtimeContext),
82
83
  ...routeAssets[`async-${entryName}`]?.assets ?? []
83
84
  ].filter((asset)=>asset.endsWith('.js'))));
84
- const nonceAttr = void 0 === nonce || '' === nonce ? '' : ` nonce="${nonce}"`;
85
- return jsAssets.filter((asset)=>!existingScriptSrcs.has(asset)).map((asset)=>`<script src=${asset}${nonceAttr}></script>`).join(' ');
85
+ return jsAssets.filter((asset)=>!existingScriptSrcs.has(asset)).map((asset)=>`<script${attributesToString({
86
+ src: asset,
87
+ nonce: nonce || void 0
88
+ })}></script>`).join(' ');
86
89
  }
87
90
  export { createRouteHydrationScriptTags, getMatchedRouteChunks, injectBeforeHydrationEntryScript, orderHydrationScriptChunks, replaceChunkJsPlaceholder };
@@ -3,7 +3,7 @@ import { CHUNK_CSS_PLACEHOLDER } from "../constants.mjs";
3
3
  import { createFederatedCssLinks } from "../federatedCss.mjs";
4
4
  import { createReplaceHelemt, getHelmetData } from "../helmet.mjs";
5
5
  import { buildHtml } from "../shared.mjs";
6
- import { safeReplace } from "../utils.mjs";
6
+ import { hasStylesheetLink, safeReplace } from "../utils.mjs";
7
7
  const checkIsInline = (chunk, enableInline)=>{
8
8
  if ('production' !== process.env.NODE_ENV) return false;
9
9
  if (enableInline instanceof RegExp) return enableInline.test(chunk);
@@ -36,7 +36,7 @@ async function buildShellBeforeTemplate(beforeAppTemplate, options) {
36
36
  if (asyncEntry) matchedRouteManifests.push(asyncEntry);
37
37
  const cssChunks = matchedRouteManifests.reduce((chunks, routeManifest)=>{
38
38
  const { referenceCssAssets = [] } = routeManifest;
39
- const _cssChunks = referenceCssAssets.filter((asset)=>asset?.endsWith('.css') && !template.includes(asset));
39
+ const _cssChunks = referenceCssAssets.filter((asset)=>asset?.endsWith('.css') && !hasStylesheetLink(template, asset));
40
40
  return [
41
41
  ...chunks,
42
42
  ..._cssChunks
@@ -5,9 +5,11 @@ import { ESCAPED_SHELL_STREAM_END_MARK } from "../../../common.mjs";
5
5
  import { RenderLevel } from "../../constants.mjs";
6
6
  import { getGlobalInternalRuntimeContext } from "../../context/index.mjs";
7
7
  import { getMonitors } from "../../context/monitors/index.mjs";
8
+ import { createReplaceHelemt, getHelmetData } from "../helmet.mjs";
8
9
  import { enqueueFromEntries } from "./deferredScript.mjs";
9
10
  import { ShellChunkStatus, getReadableStreamFromString, resolveStreamingMode } from "./shared.mjs";
10
11
  import { getTemplates } from "./template.mjs";
12
+ import * as __rspack_external__modern_js_runtime_extensions_node_f016b1ee from "@modern-js/runtime-extensions/node";
11
13
  const defaultExtender = {
12
14
  modifyRootElement: (rootElement)=>rootElement,
13
15
  getStyleTags: ()=>'',
@@ -36,6 +38,8 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
36
38
  });
37
39
  const chunkVec = [];
38
40
  let hasStartedPipe = false;
41
+ __rspack_external__modern_js_runtime_extensions_node_f016b1ee.beginHeadRender(runtimeContext);
42
+ const reportError = __rspack_external__modern_js_runtime_extensions_node_f016b1ee.createOnceErrorReporter(options.onError);
39
43
  return new Promise((resolve)=>{
40
44
  const { pipe: reactStreamingPipe } = renderToPipeableStream(processedRootElement, {
41
45
  nonce: config.nonce,
@@ -69,8 +73,10 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
69
73
  if (-1 !== markerIndex) {
70
74
  const beforeMark = concatedChunk.slice(0, markerIndex);
71
75
  const afterMark = concatedChunk.slice(markerIndex + ESCAPED_SHELL_STREAM_END_MARK.length);
76
+ __rspack_external__modern_js_runtime_extensions_node_f016b1ee.publishHeadRender(runtimeContext);
77
+ const completedShellBefore = createReplaceHelemt(getHelmetData(runtimeContext))(shellBefore);
72
78
  shellChunkStatus = ShellChunkStatus.FINISH;
73
- this.push(`${shellBefore}${beforeMark}${shellAfter}`);
79
+ this.push(`${completedShellBefore}${beforeMark}${shellAfter}`);
74
80
  if (afterMark) this.push(afterMark);
75
81
  if (pendingScripts.length > 0) for (const s of pendingScripts)this.push(s);
76
82
  }
@@ -88,8 +94,14 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
88
94
  extenders.forEach((extender)=>{
89
95
  if (extender.processStream) processedStream = extender.processStream(processedStream);
90
96
  });
97
+ __rspack_external__modern_js_runtime_extensions_node_f016b1ee.pipeNodeHeadStream({
98
+ source: processedStream,
99
+ destination: body,
100
+ context: runtimeContext,
101
+ terminalMarker: ESCAPED_SHELL_STREAM_END_MARK,
102
+ onError: reportError
103
+ });
91
104
  reactStreamingPipe(passThrough);
92
- processedStream.pipe(body);
93
105
  try {
94
106
  const storageContext = storage.useContext?.();
95
107
  const activeDeferreds = storageContext?.activeDeferreds;
@@ -108,6 +120,7 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
108
120
  });
109
121
  },
110
122
  onShellError (error) {
123
+ __rspack_external__modern_js_runtime_extensions_node_f016b1ee.abortHeadRender(runtimeContext);
111
124
  renderLevel = RenderLevel.CLIENT_RENDER;
112
125
  getTemplates(htmlTemplate, {
113
126
  request,
@@ -126,7 +139,7 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
126
139
  },
127
140
  onError (error) {
128
141
  renderLevel = RenderLevel.CLIENT_RENDER;
129
- options?.onError?.(error);
142
+ reportError(error);
130
143
  }
131
144
  });
132
145
  });
@@ -2,13 +2,16 @@ import { renderSSRStream } from "@modern-js/render/ssr";
2
2
  import { storage } from "@modern-js/runtime-utils/node";
3
3
  import { ESCAPED_SHELL_STREAM_END_MARK } from "../../../common.mjs";
4
4
  import { RenderLevel } from "../../constants.mjs";
5
+ import { createReplaceHelemt, getHelmetData } from "../helmet.mjs";
5
6
  import { enqueueFromEntries } from "./deferredScript.mjs";
6
7
  import { ShellChunkStatus, encodeForWebStream, getReadableStreamFromString, resolveStreamingMode } from "./shared.mjs";
7
8
  import { getTemplates } from "./template.mjs";
9
+ import * as __rspack_external__modern_js_runtime_extensions_ce611631 from "@modern-js/runtime-extensions";
8
10
  const createReadableStreamFromElement = async (request, rootElement, options)=>{
9
11
  let shellChunkStatus = ShellChunkStatus.START;
10
12
  const chunkVec = [];
11
13
  const { htmlTemplate, runtimeContext, config, ssrConfig, entryName, moduleFederationCssAssets, rscManifest, rscRoot } = options;
14
+ __rspack_external__modern_js_runtime_extensions_ce611631.beginHeadRender(runtimeContext);
12
15
  const { shellBefore, shellAfter } = await getTemplates(htmlTemplate, {
13
16
  renderLevel: RenderLevel.SERVER_RENDER,
14
17
  runtimeContext,
@@ -36,7 +39,7 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
36
39
  const forceStreamToString = Boolean("u" > typeof process && process.env?.MODERN_JS_STREAM_TO_STRING);
37
40
  const { waitForAllReady } = resolveStreamingMode(request, forceStreamToString);
38
41
  if (waitForAllReady) await readableOriginal.allReady;
39
- const reader = readableOriginal.getReader();
42
+ const reader = __rspack_external__modern_js_runtime_extensions_ce611631.createConservingWebShellStream(readableOriginal, runtimeContext, ESCAPED_SHELL_STREAM_END_MARK, options.onError).getReader();
40
43
  const stream = new ReadableStream({
41
44
  start (controller) {
42
45
  const pendingScripts = [];
@@ -81,14 +84,17 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
81
84
  if (-1 !== markerIndex) {
82
85
  const beforeMark = concatedChunk.slice(0, markerIndex);
83
86
  const afterMark = concatedChunk.slice(markerIndex + ESCAPED_SHELL_STREAM_END_MARK.length);
87
+ __rspack_external__modern_js_runtime_extensions_ce611631.publishHeadRender(runtimeContext);
88
+ const completedShellBefore = createReplaceHelemt(getHelmetData(runtimeContext))(shellBefore);
84
89
  shellChunkStatus = ShellChunkStatus.FINISH;
85
- safeEnqueue(encodeForWebStream(`${shellBefore}${beforeMark}${shellAfter}`));
90
+ safeEnqueue(encodeForWebStream(`${completedShellBefore}${beforeMark}${shellAfter}`));
86
91
  if (afterMark.length > 0) safeEnqueue(encodeForWebStream(afterMark));
87
92
  flushPendingScripts();
88
93
  }
89
94
  } else safeEnqueue(value);
90
95
  if (!isClosed) push();
91
96
  } catch (error) {
97
+ __rspack_external__modern_js_runtime_extensions_ce611631.abortHeadRender(runtimeContext);
92
98
  if (!isClosed) {
93
99
  isClosed = true;
94
100
  try {
@@ -100,11 +106,13 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
100
106
  push();
101
107
  },
102
108
  cancel (reason) {
109
+ __rspack_external__modern_js_runtime_extensions_ce611631.abortHeadRender(runtimeContext);
103
110
  reader.cancel(reason).catch(()=>{});
104
111
  }
105
112
  });
106
113
  return stream;
107
114
  } catch (e) {
115
+ __rspack_external__modern_js_runtime_extensions_ce611631.abortHeadRender(runtimeContext);
108
116
  const fallbackHtml = `${shellBefore}${shellAfter}`;
109
117
  const stream = getReadableStreamFromString(fallbackHtml);
110
118
  return stream;
@@ -1,4 +1,5 @@
1
1
  import { runRouterDataFnStr } from "../../../router/runtime/constants.mjs";
2
+ import { shouldRedactServerError } from "../utils.mjs";
2
3
  const HTML_ESCAPE_MAP = {
3
4
  '&': '&amp;',
4
5
  '<': '&lt;',
@@ -22,7 +23,7 @@ function isPromiseLike(value) {
22
23
  return !!value && 'function' == typeof value.then;
23
24
  }
24
25
  function toErrorInfo(error) {
25
- if ('production' === process.env.NODE_ENV) return {
26
+ if (shouldRedactServerError()) return {
26
27
  message: 'Unexpected Server Error'
27
28
  };
28
29
  if (error && 'object' == typeof error) {
@@ -12,6 +12,7 @@ import { SSRErrors, SSRTimings } from "../tracer.mjs";
12
12
  import { getSSRConfigByEntry, safeReplace } from "../utils.mjs";
13
13
  import { LoadableCollector } from "./loadable.mjs";
14
14
  import { SSRDataCollector } from "./ssrData.mjs";
15
+ import * as __rspack_external__modern_js_runtime_extensions_ce611631 from "@modern-js/runtime-extensions";
15
16
  const renderString = async (request, serverRoot, options)=>{
16
17
  const { resource, runtimeContext, config, onError, onTiming } = options;
17
18
  const tracer = {
@@ -66,16 +67,19 @@ async function generateHtml(App, htmlTemplate, chunkSet, collectors, htmlModifie
66
67
  let html = '';
67
68
  let helmetData;
68
69
  const finalApp = collectors.reduce((pre, creator)=>creator.collect?.(pre) || pre, App);
70
+ __rspack_external__modern_js_runtime_extensions_ce611631.beginHeadRender(runtimeContext);
69
71
  try {
70
72
  const end = time();
71
73
  html = server.renderToString(finalApp, {
72
74
  identifierPrefix: SSR_HYDRATION_ID_PREFIX
73
75
  });
76
+ html = __rspack_external__modern_js_runtime_extensions_ce611631.completeHeadRender(runtimeContext, html);
74
77
  chunkSet.renderLevel = RenderLevel.SERVER_RENDER;
75
78
  helmetData = getHelmetData(runtimeContext);
76
79
  const cost = end();
77
80
  onTiming(SSRTimings.RENDER_HTML, cost);
78
81
  } catch (e) {
82
+ __rspack_external__modern_js_runtime_extensions_ce611631.abortHeadRender(runtimeContext);
79
83
  chunkSet.renderLevel = RenderLevel.CLIENT_RENDER;
80
84
  onError(e, SSRErrors.RENDER_HTML);
81
85
  }
@@ -28,6 +28,10 @@ const readAsset = async (chunk)=>{
28
28
  return fs.readFile(filepath, 'utf-8');
29
29
  };
30
30
  class LoadableCollector {
31
+ get existsAssets() {
32
+ const { routeManifest, entryName } = this.options;
33
+ return routeManifest?.routeAssets?.[entryName]?.assets;
34
+ }
31
35
  collect(comopnent) {
32
36
  const { stats, entryName } = this.options;
33
37
  if (!stats) return comopnent;
@@ -89,7 +93,7 @@ class LoadableCollector {
89
93
  const matchs = template.matchAll(jsScriptRegExp);
90
94
  const existedScript = [];
91
95
  for (const match of matchs)existedScript.push(match[1]);
92
- const scripts = await Promise.all(chunks.filter((chunk)=>!existedScript.includes(chunk.url)).map(async (chunk)=>{
96
+ const scripts = await Promise.all(chunks.filter((chunk)=>!existedScript.includes(chunk.url) && !this.existsAssets?.includes(chunk.path)).map(async (chunk)=>{
93
97
  const script = `<script${attributes} src="${chunk.url}"></script>`;
94
98
  if (checkIsNode() && checkIsInline(chunk, inlineStyles)) return readAsset(chunk).then((content)=>`<script>${content}</script>`).catch((_)=>script);
95
99
  return script;
@@ -100,7 +104,12 @@ class LoadableCollector {
100
104
  const { template, chunkSet, config, moduleFederationCssAssets } = this.options;
101
105
  const { inlineStyles } = config;
102
106
  const atrributes = attributesToString(this.generateAttributes());
103
- const emittedChunks = chunks.filter((chunk)=>!hasStylesheetLink(template, chunk.url));
107
+ const emittedAssetUrls = new Set();
108
+ const emittedChunks = chunks.filter((chunk)=>{
109
+ const shouldEmit = !hasStylesheetLink(template, chunk.url) && !this.existsAssets?.includes(chunk.path) && !emittedAssetUrls.has(chunk.url);
110
+ if (shouldEmit) emittedAssetUrls.add(chunk.url);
111
+ return shouldEmit;
112
+ });
104
113
  const css = await Promise.all(emittedChunks.map(async (chunk)=>{
105
114
  const link = `<link${atrributes} href="${chunk.url}" rel="stylesheet" />`;
106
115
  if (checkIsNode() && checkIsInline(chunk, inlineStyles)) return readAsset(chunk).then((content)=>`<style>${content}</style>`).catch((_)=>link);
@@ -1,6 +1,7 @@
1
+ import { escapeHtmlAttribute, isSafeHtmlAttributeName } from "@modern-js/runtime-extensions";
1
2
  import { isRouteErrorResponse } from "../../router/runtime/routerHelper.mjs";
2
3
  function attributesToString(attributes) {
3
- return Object.entries(attributes).reduce((str, [key, value])=>void 0 === value ? str : `${str} ${key}="${value}"`, '');
4
+ return Object.entries(attributes).reduce((str, [key, value])=>void 0 !== value && isSafeHtmlAttributeName(key) ? `${str} ${key}="${escapeHtmlAttribute(value)}"` : str, '');
4
5
  }
5
6
  function safeReplace(source, searchValue, replaceValue) {
6
7
  return source.replace(searchValue, ()=>replaceValue);
@@ -38,12 +39,14 @@ function serializeRouteErrorResponse(error) {
38
39
  status: error.status,
39
40
  statusText: 'Internal Server Error',
40
41
  data: 'Unexpected Server Error',
42
+ internal: error.internal,
41
43
  __type: 'RouteErrorResponse'
42
44
  };
43
45
  return {
44
46
  status: error.status,
45
47
  statusText: error.statusText,
46
48
  data: error.data,
49
+ internal: error.internal,
47
50
  __type: 'RouteErrorResponse'
48
51
  };
49
52
  }
@@ -65,8 +68,9 @@ const getLinkAttributes = (linkTag)=>{
65
68
  let match;
66
69
  while(match = attributeRegExp.exec(linkTag)){
67
70
  const [, name, doubleQuotedValue, singleQuotedValue, unquotedValue] = match;
68
- if ('link' === name.toLowerCase()) continue;
69
- attributes.set(name.toLowerCase(), doubleQuotedValue ?? singleQuotedValue ?? unquotedValue ?? '');
71
+ const normalizedName = name.toLowerCase();
72
+ if ('link' === normalizedName || attributes.has(normalizedName)) continue;
73
+ attributes.set(normalizedName, doubleQuotedValue ?? singleQuotedValue ?? unquotedValue ?? '');
70
74
  }
71
75
  return attributes;
72
76
  };
@@ -79,4 +83,4 @@ const hasStylesheetLink = (template, href)=>{
79
83
  return linkHref === href && rel?.split(/\s+/).some((relToken)=>'stylesheet' === relToken.toLowerCase());
80
84
  });
81
85
  };
82
- export { attributesToString, checkIsNode, getSSRConfigByEntry, getSSRMode, hasStylesheetLink, safeReplace, serializeErrors };
86
+ export { attributesToString, checkIsNode, getSSRConfigByEntry, getSSRMode, hasStylesheetLink, safeReplace, serializeErrors, shouldRedactServerError };