@bleedingdev/modern-js-runtime 3.9.0-ultramodern.4 → 3.9.0-ultramodern.5

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 (154) hide show
  1. package/dist/cjs/cli/ssr/index.js +2 -74
  2. package/dist/cjs/core/context/index.js +4 -43
  3. package/dist/cjs/core/context/public.js +4 -8
  4. package/dist/cjs/core/context/runtime.js +17 -6
  5. package/dist/cjs/core/react/wrapper.js +22 -65
  6. package/dist/cjs/core/server/helmet.js +5 -4
  7. package/dist/cjs/core/server/requestResponse.js +4 -4
  8. package/dist/cjs/core/server/routerCleanup.js +2 -2
  9. package/dist/cjs/core/server/shared.js +171 -4
  10. package/dist/cjs/core/server/stream/afterTemplate.js +21 -16
  11. package/dist/cjs/core/server/stream/beforeTemplate.js +34 -25
  12. package/dist/cjs/core/server/stream/beforeTemplate.worker.js +28 -21
  13. package/dist/cjs/core/server/stream/createReadableStream.js +208 -123
  14. package/dist/cjs/core/server/stream/createReadableStream.worker.js +159 -90
  15. package/dist/cjs/core/server/stream/deferredScript.js +3 -3
  16. package/dist/cjs/core/server/stream/shared.js +2 -2
  17. package/dist/cjs/core/server/string/index.js +100 -64
  18. package/dist/cjs/core/server/string/loadable.js +34 -19
  19. package/dist/cjs/core/server/string/ssrData.js +5 -12
  20. package/dist/cjs/core/server/utils.js +1 -2
  21. package/dist/cjs/exports/head.js +7 -14
  22. package/dist/cjs/router/runtime/PrefetchLink.js +67 -123
  23. package/dist/cjs/router/runtime/hooks.js +13 -2
  24. package/dist/cjs/router/runtime/internal.js +7 -45
  25. package/dist/cjs/router/runtime/plugin.js +2 -8
  26. package/dist/cjs/router/runtime/plugin.node.js +2 -30
  27. package/dist/cjs/router/runtime/redirect.js +1 -1
  28. package/dist/esm/cli/ssr/index.mjs +3 -72
  29. package/dist/esm/core/context/index.mjs +2 -5
  30. package/dist/esm/core/context/public.mjs +1 -6
  31. package/dist/esm/core/context/runtime.mjs +12 -4
  32. package/dist/esm/core/react/wrapper.mjs +21 -67
  33. package/dist/esm/core/server/helmet.mjs +5 -4
  34. package/dist/esm/core/server/requestResponse.mjs +3 -3
  35. package/dist/esm/core/server/routerCleanup.mjs +1 -1
  36. package/dist/esm/core/server/shared.mjs +153 -1
  37. package/dist/esm/core/server/stream/afterTemplate.mjs +23 -18
  38. package/dist/esm/core/server/stream/beforeTemplate.mjs +37 -28
  39. package/dist/esm/core/server/stream/beforeTemplate.worker.mjs +31 -24
  40. package/dist/esm/core/server/stream/createReadableStream.mjs +207 -122
  41. package/dist/esm/core/server/stream/createReadableStream.worker.mjs +149 -90
  42. package/dist/esm/core/server/stream/deferredScript.mjs +3 -3
  43. package/dist/esm/core/server/stream/shared.mjs +2 -2
  44. package/dist/esm/core/server/string/index.mjs +104 -68
  45. package/dist/esm/core/server/string/loadable.mjs +34 -19
  46. package/dist/esm/core/server/string/ssrData.mjs +5 -12
  47. package/dist/esm/core/server/utils.mjs +1 -2
  48. package/dist/esm/exports/head.mjs +7 -14
  49. package/dist/esm/router/runtime/PrefetchLink.mjs +67 -123
  50. package/dist/esm/router/runtime/hooks.mjs +9 -1
  51. package/dist/esm/router/runtime/internal.mjs +2 -33
  52. package/dist/esm/router/runtime/plugin.mjs +1 -7
  53. package/dist/esm/router/runtime/plugin.node.mjs +1 -29
  54. package/dist/esm/router/runtime/redirect.mjs +1 -1
  55. package/dist/esm-node/cli/ssr/index.mjs +3 -72
  56. package/dist/esm-node/core/context/index.mjs +2 -5
  57. package/dist/esm-node/core/context/public.mjs +1 -6
  58. package/dist/esm-node/core/context/runtime.mjs +12 -4
  59. package/dist/esm-node/core/react/wrapper.mjs +21 -67
  60. package/dist/esm-node/core/server/helmet.mjs +5 -4
  61. package/dist/esm-node/core/server/requestResponse.mjs +3 -3
  62. package/dist/esm-node/core/server/routerCleanup.mjs +1 -1
  63. package/dist/esm-node/core/server/shared.mjs +153 -1
  64. package/dist/esm-node/core/server/stream/afterTemplate.mjs +23 -18
  65. package/dist/esm-node/core/server/stream/beforeTemplate.mjs +37 -28
  66. package/dist/esm-node/core/server/stream/beforeTemplate.worker.mjs +31 -24
  67. package/dist/esm-node/core/server/stream/createReadableStream.mjs +207 -122
  68. package/dist/esm-node/core/server/stream/createReadableStream.worker.mjs +149 -90
  69. package/dist/esm-node/core/server/stream/deferredScript.mjs +3 -3
  70. package/dist/esm-node/core/server/stream/shared.mjs +2 -2
  71. package/dist/esm-node/core/server/string/index.mjs +104 -68
  72. package/dist/esm-node/core/server/string/loadable.mjs +34 -19
  73. package/dist/esm-node/core/server/string/ssrData.mjs +5 -12
  74. package/dist/esm-node/core/server/utils.mjs +1 -2
  75. package/dist/esm-node/exports/head.mjs +7 -14
  76. package/dist/esm-node/router/runtime/PrefetchLink.mjs +67 -123
  77. package/dist/esm-node/router/runtime/hooks.mjs +9 -1
  78. package/dist/esm-node/router/runtime/internal.mjs +2 -33
  79. package/dist/esm-node/router/runtime/plugin.mjs +1 -7
  80. package/dist/esm-node/router/runtime/plugin.node.mjs +1 -29
  81. package/dist/esm-node/router/runtime/redirect.mjs +1 -1
  82. package/dist/types/cli/ssr/index.d.ts +0 -3
  83. package/dist/types/core/context/index.d.ts +4 -6
  84. package/dist/types/core/context/public.d.ts +1 -23
  85. package/dist/types/core/context/runtime.d.ts +26 -2
  86. package/dist/types/core/react/wrapper.d.ts +6 -0
  87. package/dist/types/core/server/helmet.d.ts +4 -5
  88. package/dist/types/core/server/shared.d.ts +23 -0
  89. package/dist/types/core/server/stream/afterTemplate.d.ts +2 -1
  90. package/dist/types/core/server/stream/beforeTemplate.d.ts +4 -1
  91. package/dist/types/core/server/stream/beforeTemplate.worker.d.ts +4 -1
  92. package/dist/types/core/server/stream/shared.d.ts +1 -1
  93. package/dist/types/core/server/string/loadable.d.ts +6 -3
  94. package/dist/types/core/server/string/ssrData.d.ts +2 -1
  95. package/dist/types/core/server/string/types.d.ts +1 -5
  96. package/dist/types/exports/head.d.ts +2 -2
  97. package/dist/types/router/runtime/PrefetchLink.d.ts +27 -2
  98. package/dist/types/router/runtime/hooks.d.ts +19 -2
  99. package/dist/types/router/runtime/internal.d.ts +4 -21
  100. package/dist/types/router/runtime/types.d.ts +2 -43
  101. package/package.json +9 -38
  102. package/rstest.config.mts +32 -2
  103. package/dist/cjs/boundary-debugger/index.js +0 -317
  104. package/dist/cjs/core/context/extensions.js +0 -74
  105. package/dist/cjs/core/context/helmetContext.js +0 -54
  106. package/dist/cjs/core/server/federatedCss.js +0 -58
  107. package/dist/cjs/core/server/scriptOrder.js +0 -140
  108. package/dist/cjs/module-federation/consume-surface.js +0 -199
  109. package/dist/cjs/module-federation/distributed-ssr.js +0 -135
  110. package/dist/cjs/module-federation/index.js +0 -316
  111. package/dist/cjs/module-federation/last-known-good.js +0 -157
  112. package/dist/cjs/module-federation/manifest-recovery-runtime-plugin.js +0 -129
  113. package/dist/cjs/module-federation/surface-resolution-types.js +0 -61
  114. package/dist/cjs/router/runtime/lifecycle.js +0 -167
  115. package/dist/cjs/router/runtime/provider.js +0 -153
  116. package/dist/esm/boundary-debugger/index.mjs +0 -277
  117. package/dist/esm/core/context/extensions.mjs +0 -33
  118. package/dist/esm/core/context/helmetContext.mjs +0 -13
  119. package/dist/esm/core/server/federatedCss.mjs +0 -20
  120. package/dist/esm/core/server/scriptOrder.mjs +0 -90
  121. package/dist/esm/module-federation/consume-surface.mjs +0 -158
  122. package/dist/esm/module-federation/distributed-ssr.mjs +0 -82
  123. package/dist/esm/module-federation/index.mjs +0 -115
  124. package/dist/esm/module-federation/last-known-good.mjs +0 -113
  125. package/dist/esm/module-federation/manifest-recovery-runtime-plugin.mjs +0 -89
  126. package/dist/esm/module-federation/surface-resolution-types.mjs +0 -8
  127. package/dist/esm/router/runtime/lifecycle.mjs +0 -105
  128. package/dist/esm/router/runtime/provider.mjs +0 -100
  129. package/dist/esm-node/boundary-debugger/index.mjs +0 -278
  130. package/dist/esm-node/core/context/extensions.mjs +0 -34
  131. package/dist/esm-node/core/context/helmetContext.mjs +0 -14
  132. package/dist/esm-node/core/server/federatedCss.mjs +0 -21
  133. package/dist/esm-node/core/server/scriptOrder.mjs +0 -91
  134. package/dist/esm-node/module-federation/consume-surface.mjs +0 -159
  135. package/dist/esm-node/module-federation/distributed-ssr.mjs +0 -83
  136. package/dist/esm-node/module-federation/index.mjs +0 -116
  137. package/dist/esm-node/module-federation/last-known-good.mjs +0 -114
  138. package/dist/esm-node/module-federation/manifest-recovery-runtime-plugin.mjs +0 -90
  139. package/dist/esm-node/module-federation/surface-resolution-types.mjs +0 -9
  140. package/dist/esm-node/router/runtime/lifecycle.mjs +0 -106
  141. package/dist/esm-node/router/runtime/provider.mjs +0 -101
  142. package/dist/types/boundary-debugger/index.d.ts +0 -28
  143. package/dist/types/core/context/extensions.d.ts +0 -35
  144. package/dist/types/core/context/helmetContext.d.ts +0 -10
  145. package/dist/types/core/server/federatedCss.d.ts +0 -5
  146. package/dist/types/core/server/scriptOrder.d.ts +0 -25
  147. package/dist/types/module-federation/consume-surface.d.ts +0 -104
  148. package/dist/types/module-federation/distributed-ssr.d.ts +0 -61
  149. package/dist/types/module-federation/index.d.ts +0 -71
  150. package/dist/types/module-federation/last-known-good.d.ts +0 -52
  151. package/dist/types/module-federation/manifest-recovery-runtime-plugin.d.ts +0 -25
  152. package/dist/types/module-federation/surface-resolution-types.d.ts +0 -23
  153. package/dist/types/router/runtime/lifecycle.d.ts +0 -27
  154. package/dist/types/router/runtime/provider.d.ts +0 -78
@@ -1,13 +1,11 @@
1
1
  import "node:module";
2
2
  import { serializeJson } from "@modern-js/runtime-utils/node";
3
- import { getRouterHydrationScripts } from "../../../router/runtime/lifecycle.mjs";
4
3
  import { SSR_DATA_JSON_ID } from "../../constants.mjs";
5
- import { SSR_DATA_PLACEHOLDER } from "../constants.mjs";
6
- import { createRouteHydrationScriptTags, replaceChunkJsPlaceholder } from "../scriptOrder.mjs";
7
- import { buildHtml } from "../shared.mjs";
4
+ import { CHUNK_JS_PLACEHOLDER, SSR_DATA_PLACEHOLDER } from "../constants.mjs";
5
+ import { buildHtml, replaceSSRTemplateChunk } from "../shared.mjs";
8
6
  import { attributesToString } from "../utils.mjs";
9
7
  function buildShellAfterTemplate(afterAppTemplate, options) {
10
- const { request, config, ssrConfig, runtimeContext, renderLevel, entryName } = options;
8
+ const { request, config, ssrConfig, runtimeContext, renderLevel, entryName, lifecycle } = options;
11
9
  const callbacks = [
12
10
  (template)=>injectJs(template, entryName, config.nonce),
13
11
  createReplaceSSRData({
@@ -17,21 +15,28 @@ function buildShellAfterTemplate(afterAppTemplate, options) {
17
15
  useJsonScript: config.useJsonScript,
18
16
  runtimeContext,
19
17
  renderLevel,
20
- entryName
18
+ lifecycle
21
19
  })
22
20
  ];
23
21
  async function injectJs(template, entryName, nonce) {
24
- const jsChunkStr = createRouteHydrationScriptTags(runtimeContext, entryName, {
25
- nonce,
26
- template
22
+ const assets = runtimeContext.routeManifest?.routeAssets?.[`async-${entryName}`]?.assets ?? [];
23
+ const jsChunkStr = assets.filter((asset)=>asset.endsWith('.js')).map((asset)=>`<script${attributesToString({
24
+ src: asset,
25
+ nonce
26
+ })}></script>`).join(' ');
27
+ return replaceSSRTemplateChunk({
28
+ name: "scripts",
29
+ template,
30
+ placeholder: CHUNK_JS_PLACEHOLDER,
31
+ content: jsChunkStr
32
+ }, lifecycle, {
33
+ preserveEmpty: true
27
34
  });
28
- if (!jsChunkStr) return template;
29
- return replaceChunkJsPlaceholder(template, jsChunkStr, entryName);
30
35
  }
31
36
  return buildHtml(afterAppTemplate, callbacks);
32
37
  }
33
38
  function createReplaceSSRData(options) {
34
- const { runtimeContext, nonce, renderLevel, useJsonScript, ssrConfig, entryName } = options;
39
+ const { runtimeContext, nonce, renderLevel, useJsonScript, ssrConfig, lifecycle } = options;
35
40
  const { request, reporter } = runtimeContext.ssrContext;
36
41
  const headers = 'object' == typeof ssrConfig && ssrConfig.unsafeHeaders ? Object.fromEntries(Object.entries(request.headers).filter(([key, _])=>ssrConfig.unsafeHeaders?.map((header)=>header.toLowerCase())?.includes(key.toLowerCase()))) : void 0;
37
42
  const ssrData = {
@@ -60,11 +65,11 @@ function createReplaceSSRData(options) {
60
65
  });
61
66
  const serializeSSRData = serializeJson(ssrData);
62
67
  const ssrDataScript = useJsonScript ? `<script type="application/json" id="${SSR_DATA_JSON_ID}">${serializeSSRData}</script>` : `<script${attrsStr}>window._SSR_DATA = ${serializeSSRData}</script>`;
63
- const hydrationScripts = getRouterHydrationScripts(runtimeContext);
64
- const ssrScripts = hydrationScripts.length ? `${ssrDataScript}\n${hydrationScripts.join('\n')}` : ssrDataScript;
65
- return (template)=>{
66
- if (!template.includes(SSR_DATA_PLACEHOLDER)) return template;
67
- return replaceChunkJsPlaceholder(template, ssrScripts, entryName, SSR_DATA_PLACEHOLDER);
68
- };
68
+ return (template)=>replaceSSRTemplateChunk({
69
+ name: 'data',
70
+ template,
71
+ placeholder: SSR_DATA_PLACEHOLDER,
72
+ content: ssrDataScript
73
+ }, lifecycle);
69
74
  }
70
75
  export { buildShellAfterTemplate };
@@ -1,11 +1,9 @@
1
1
  import "node:module";
2
2
  import { matchRoutes } from "@modern-js/runtime-utils/router";
3
- import { getRouterMatchedRouteIds } from "../../../router/runtime/lifecycle.mjs";
4
3
  import { CHUNK_CSS_PLACEHOLDER } from "../constants.mjs";
5
- import { createFederatedCssLinks } from "../federatedCss.mjs";
6
- import { createReplaceHelemt, getHelmetData } from "../helmet.mjs";
7
- import { buildHtml } from "../shared.mjs";
8
- import { checkIsNode, hasStylesheetLink, safeReplace } from "../utils.mjs";
4
+ import { createReplaceHelemt } from "../helmet.mjs";
5
+ import { buildHtml, replaceSSRTemplateChunk } from "../shared.mjs";
6
+ import { checkIsNode, hasStylesheetLink } from "../utils.mjs";
9
7
  import { fileURLToPath as __rspack_fileURLToPath } from "node:url";
10
8
  import { dirname as __rspack_dirname } from "node:path";
11
9
  var __rspack_import_meta_dirname__ = __rspack_dirname(__rspack_fileURLToPath(import.meta.url));
@@ -21,8 +19,7 @@ const checkIsInline = (chunk, enableInline)=>{
21
19
  return Boolean(enableInline);
22
20
  };
23
21
  async function buildShellBeforeTemplate(beforeAppTemplate, options) {
24
- const { config, runtimeContext, styledComponentsStyleTags, entryName, moduleFederationCssAssets } = options;
25
- const helmetData = getHelmetData(runtimeContext);
22
+ const { config, runtimeContext, styledComponentsStyleTags, entryName, lifecycle, helmetData } = options;
26
23
  const callbacks = [
27
24
  createReplaceHelemt(helmetData),
28
25
  (template)=>injectCss(template, entryName, styledComponentsStyleTags)
@@ -31,36 +28,48 @@ async function buildShellBeforeTemplate(beforeAppTemplate, options) {
31
28
  async function injectCss(template, entryName, styledComponentsStyleTags) {
32
29
  let css = await getCssChunks();
33
30
  if (styledComponentsStyleTags) css += styledComponentsStyleTags;
34
- css += createFederatedCssLinks(moduleFederationCssAssets, {
31
+ return replaceSSRTemplateChunk({
32
+ name: 'styles',
35
33
  template,
36
- existingAssets: css.match(/href="([^"]+)"/g)?.map((item)=>item.replace(/^href="/, '').replace(/"$/, ''))
37
- });
38
- return safeReplace(template, CHUNK_CSS_PLACEHOLDER, css);
34
+ placeholder: CHUNK_CSS_PLACEHOLDER,
35
+ content: css,
36
+ emittedAssets: css.match(/href="([^"]+)"/g)?.map((item)=>item.replace(/^href="/, '').replace(/"$/, ''))
37
+ }, lifecycle);
39
38
  async function getCssChunks() {
40
39
  const { routeManifest, routerContext, routes } = runtimeContext;
41
40
  const routeAssets = routeManifest?.routeAssets;
42
41
  if (!routeAssets) return '';
43
42
  let matchedRouteManifests = [];
44
- const matchedRouteIds = getRouterMatchedRouteIds(runtimeContext);
45
- if (matchedRouteIds?.length) matchedRouteManifests = matchedRouteIds.map((routeId)=>routeAssets[routeId]).filter(Boolean);
46
- else if (routerContext && routes) {
43
+ if (routerContext && routes) {
47
44
  const matches = matchRoutes(routes, routerContext.location, routerContext.basename);
48
- matchedRouteManifests = matches?.map((match, index)=>{
49
- if (!index) return;
45
+ matchedRouteManifests = matches?.flatMap((match, index)=>{
50
46
  const routeId = match.route.id;
51
- if (routeId) return routeAssets[routeId];
52
- }).filter((routeManifest)=>void 0 !== routeManifest) ?? [];
47
+ const manifest = 0 !== index && routeId ? routeAssets[routeId] : void 0;
48
+ return void 0 === manifest ? [] : [
49
+ manifest
50
+ ];
51
+ }) ?? [];
53
52
  }
54
- const asyncEntry = routeAssets[`async-${entryName}`];
55
- if (asyncEntry) matchedRouteManifests.push(asyncEntry);
56
- const cssChunks = matchedRouteManifests.reduce((chunks, routeManifest)=>{
57
- const { referenceCssAssets = [] } = routeManifest;
58
- const _cssChunks = referenceCssAssets.filter((asset)=>asset?.endsWith('.css') && !hasStylesheetLink(template, asset));
59
- return [
60
- ...chunks,
61
- ..._cssChunks
62
- ];
63
- }, []);
53
+ const createAsset = (url)=>({
54
+ url
55
+ });
56
+ const groups = [
57
+ {
58
+ name: 'route',
59
+ assets: matchedRouteManifests.flatMap((manifest)=>manifest.referenceCssAssets ?? []).map(createAsset)
60
+ },
61
+ {
62
+ name: 'async-entry',
63
+ assets: (routeAssets[`async-${entryName}`]?.referenceCssAssets ?? []).map(createAsset)
64
+ }
65
+ ];
66
+ const assets = lifecycle?.transformAssets(groups, {
67
+ kind: 'style',
68
+ source: 'template',
69
+ template,
70
+ createAsset
71
+ }) ?? groups.flatMap((group)=>group.assets);
72
+ const cssChunks = assets.map((asset)=>asset.url).filter((asset)=>asset.endsWith('.css') && !hasStylesheetLink(template, asset));
64
73
  const { inlineStyles } = config;
65
74
  const styles = await Promise.all(cssChunks.map(async (chunk)=>{
66
75
  const link = `<link href="${chunk}" rel="stylesheet" />`;
@@ -1,18 +1,15 @@
1
1
  import "node:module";
2
- import { getRouterMatchedRouteIds } from "../../../router/runtime/lifecycle.mjs";
3
2
  import { CHUNK_CSS_PLACEHOLDER } from "../constants.mjs";
4
- import { createFederatedCssLinks } from "../federatedCss.mjs";
5
- import { createReplaceHelemt, getHelmetData } from "../helmet.mjs";
6
- import { buildHtml } from "../shared.mjs";
7
- import { hasStylesheetLink, safeReplace } from "../utils.mjs";
3
+ import { createReplaceHelemt } from "../helmet.mjs";
4
+ import { buildHtml, replaceSSRTemplateChunk } from "../shared.mjs";
5
+ import { hasStylesheetLink } from "../utils.mjs";
8
6
  const checkIsInline = (chunk, enableInline)=>{
9
7
  if ('production' !== process.env.NODE_ENV) return false;
10
8
  if (enableInline instanceof RegExp) return enableInline.test(chunk);
11
9
  return Boolean(enableInline);
12
10
  };
13
11
  async function buildShellBeforeTemplate(beforeAppTemplate, options) {
14
- const { config, runtimeContext, styledComponentsStyleTags, entryName, moduleFederationCssAssets } = options;
15
- const helmetData = getHelmetData(runtimeContext);
12
+ const { config, runtimeContext, styledComponentsStyleTags, entryName, lifecycle, helmetData } = options;
16
13
  const callbacks = [
17
14
  createReplaceHelemt(helmetData),
18
15
  (template)=>injectCss(template, entryName, styledComponentsStyleTags)
@@ -21,28 +18,38 @@ async function buildShellBeforeTemplate(beforeAppTemplate, options) {
21
18
  async function injectCss(template, entryName, styledComponentsStyleTags) {
22
19
  let css = await getCssChunks();
23
20
  if (styledComponentsStyleTags) css += styledComponentsStyleTags;
24
- css += createFederatedCssLinks(moduleFederationCssAssets, {
21
+ return replaceSSRTemplateChunk({
22
+ name: 'styles',
25
23
  template,
26
- existingAssets: css.match(/href="([^"]+)"/g)?.map((item)=>item.replace(/^href="/, '').replace(/"$/, ''))
27
- });
28
- return safeReplace(template, CHUNK_CSS_PLACEHOLDER, css);
24
+ placeholder: CHUNK_CSS_PLACEHOLDER,
25
+ content: css,
26
+ emittedAssets: css.match(/href="([^"]+)"/g)?.map((item)=>item.replace(/^href="/, '').replace(/"$/, ''))
27
+ }, lifecycle);
29
28
  async function getCssChunks() {
30
29
  const { routeManifest } = runtimeContext;
31
30
  const routeAssets = routeManifest?.routeAssets;
32
31
  if (!routeAssets) return '';
33
- let matchedRouteManifests = [];
34
- const matchedRouteIds = getRouterMatchedRouteIds(runtimeContext);
35
- if (matchedRouteIds?.length) matchedRouteManifests = matchedRouteIds.map((routeId)=>routeAssets[routeId]).filter(Boolean);
36
- const asyncEntry = routeAssets[`async-${entryName}`];
37
- if (asyncEntry) matchedRouteManifests.push(asyncEntry);
38
- const cssChunks = matchedRouteManifests.reduce((chunks, routeManifest)=>{
39
- const { referenceCssAssets = [] } = routeManifest;
40
- const _cssChunks = referenceCssAssets.filter((asset)=>asset?.endsWith('.css') && !hasStylesheetLink(template, asset));
41
- return [
42
- ...chunks,
43
- ..._cssChunks
44
- ];
45
- }, []);
32
+ const matchedRouteManifests = [];
33
+ const createAsset = (url)=>({
34
+ url
35
+ });
36
+ const groups = [
37
+ {
38
+ name: 'route',
39
+ assets: matchedRouteManifests.flatMap((manifest)=>manifest.referenceCssAssets ?? []).map(createAsset)
40
+ },
41
+ {
42
+ name: 'async-entry',
43
+ assets: (routeAssets[`async-${entryName}`]?.referenceCssAssets ?? []).map(createAsset)
44
+ }
45
+ ];
46
+ const assets = lifecycle?.transformAssets(groups, {
47
+ kind: 'style',
48
+ source: 'template',
49
+ template,
50
+ createAsset
51
+ }) ?? groups.flatMap((group)=>group.assets);
52
+ const cssChunks = assets.map((asset)=>asset.url).filter((asset)=>asset.endsWith('.css') && !hasStylesheetLink(template, asset));
46
53
  const { inlineStyles } = config;
47
54
  const styles = cssChunks.map((chunk)=>{
48
55
  const link = `<link href="${chunk}" rel="stylesheet" />`;
@@ -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 };