@bleedingdev/modern-js-runtime 3.2.0-ultramodern.9 → 3.2.0-ultramodern.90

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 (77) hide show
  1. package/dist/cjs/boundary-debugger/index.js +299 -0
  2. package/dist/cjs/cli/ssr/index.js +5 -15
  3. package/dist/cjs/cli/template.server.js +1 -0
  4. package/dist/cjs/core/react/wrapper.js +9 -3
  5. package/dist/cjs/core/server/federatedCss.js +47 -0
  6. package/dist/cjs/core/server/helmet.js +8 -2
  7. package/dist/cjs/core/server/stream/afterTemplate.js +9 -6
  8. package/dist/cjs/core/server/stream/beforeTemplate.js +13 -20
  9. package/dist/cjs/core/server/stream/beforeTemplate.worker.js +98 -0
  10. package/dist/cjs/core/server/stream/createReadableStream.js +7 -2
  11. package/dist/cjs/core/server/stream/createReadableStream.worker.js +4 -2
  12. package/dist/cjs/core/server/stream/shared.js +3 -1
  13. package/dist/cjs/core/server/string/index.js +6 -6
  14. package/dist/cjs/core/server/string/loadable.js +33 -7
  15. package/dist/cjs/exports/head.js +196 -5
  16. package/dist/cjs/router/cli/code/tanstackTypes.js +116 -51
  17. package/dist/cjs/router/cli/code/templates.js +1 -8
  18. package/dist/cjs/router/runtime/tanstack/plugin.js +4 -5
  19. package/dist/cjs/router/runtime/tanstack/plugin.node.js +2 -13
  20. package/dist/cjs/router/runtime/tanstack/routeTree.js +47 -4
  21. package/dist/cjs/rsc/server.worker.js +58 -0
  22. package/dist/cjs/ssr/serverRender/renderToString/entry.js +9 -8
  23. package/dist/esm/boundary-debugger/index.mjs +263 -0
  24. package/dist/esm/cli/ssr/index.mjs +5 -15
  25. package/dist/esm/cli/template.server.mjs +1 -0
  26. package/dist/esm/core/react/wrapper.mjs +9 -3
  27. package/dist/esm/core/server/federatedCss.mjs +13 -0
  28. package/dist/esm/core/server/helmet.mjs +5 -2
  29. package/dist/esm/core/server/stream/afterTemplate.mjs +10 -7
  30. package/dist/esm/core/server/stream/beforeTemplate.mjs +14 -11
  31. package/dist/esm/core/server/stream/beforeTemplate.worker.mjs +64 -0
  32. package/dist/esm/core/server/stream/createReadableStream.mjs +7 -2
  33. package/dist/esm/core/server/stream/createReadableStream.worker.mjs +4 -2
  34. package/dist/esm/core/server/stream/shared.mjs +3 -1
  35. package/dist/esm/core/server/string/index.mjs +7 -6
  36. package/dist/esm/core/server/string/loadable.mjs +33 -7
  37. package/dist/esm/exports/head.mjs +189 -4
  38. package/dist/esm/router/cli/code/tanstackTypes.mjs +116 -51
  39. package/dist/esm/router/cli/code/templates.mjs +1 -8
  40. package/dist/esm/router/runtime/tanstack/plugin.mjs +8 -9
  41. package/dist/esm/router/runtime/tanstack/plugin.node.mjs +3 -14
  42. package/dist/esm/router/runtime/tanstack/routeTree.mjs +47 -4
  43. package/dist/esm/rsc/server.worker.mjs +1 -0
  44. package/dist/esm/ssr/serverRender/renderToString/entry.mjs +9 -6
  45. package/dist/esm-node/boundary-debugger/index.mjs +264 -0
  46. package/dist/esm-node/cli/ssr/index.mjs +5 -15
  47. package/dist/esm-node/cli/template.server.mjs +1 -0
  48. package/dist/esm-node/core/react/wrapper.mjs +9 -3
  49. package/dist/esm-node/core/server/federatedCss.mjs +14 -0
  50. package/dist/esm-node/core/server/helmet.mjs +5 -2
  51. package/dist/esm-node/core/server/stream/afterTemplate.mjs +10 -7
  52. package/dist/esm-node/core/server/stream/beforeTemplate.mjs +14 -11
  53. package/dist/esm-node/core/server/stream/beforeTemplate.worker.mjs +65 -0
  54. package/dist/esm-node/core/server/stream/createReadableStream.mjs +7 -2
  55. package/dist/esm-node/core/server/stream/createReadableStream.worker.mjs +4 -2
  56. package/dist/esm-node/core/server/stream/shared.mjs +3 -1
  57. package/dist/esm-node/core/server/string/index.mjs +7 -6
  58. package/dist/esm-node/core/server/string/loadable.mjs +33 -7
  59. package/dist/esm-node/exports/head.mjs +189 -4
  60. package/dist/esm-node/router/cli/code/tanstackTypes.mjs +116 -51
  61. package/dist/esm-node/router/cli/code/templates.mjs +1 -8
  62. package/dist/esm-node/router/runtime/tanstack/plugin.mjs +8 -9
  63. package/dist/esm-node/router/runtime/tanstack/plugin.node.mjs +3 -14
  64. package/dist/esm-node/router/runtime/tanstack/routeTree.mjs +47 -4
  65. package/dist/esm-node/rsc/server.worker.mjs +2 -0
  66. package/dist/esm-node/ssr/serverRender/renderToString/entry.mjs +9 -6
  67. package/dist/types/boundary-debugger/index.d.ts +28 -0
  68. package/dist/types/core/context/runtime.d.ts +4 -0
  69. package/dist/types/core/server/federatedCss.d.ts +5 -0
  70. package/dist/types/core/server/helmet.d.ts +5 -3
  71. package/dist/types/core/server/stream/beforeTemplate.d.ts +1 -0
  72. package/dist/types/core/server/stream/beforeTemplate.worker.d.ts +10 -0
  73. package/dist/types/core/server/stream/shared.d.ts +8 -0
  74. package/dist/types/core/server/string/loadable.d.ts +4 -0
  75. package/dist/types/exports/head.d.ts +10 -3
  76. package/dist/types/rsc/server.worker.d.ts +1 -0
  77. package/package.json +25 -19
@@ -1,9 +1,9 @@
1
1
  import "node:module";
2
2
  import { matchRoutes } from "@modern-js/runtime-utils/router";
3
- import react_helmet from "react-helmet";
4
3
  import { getRouterMatchedRouteIds } from "../../../router/runtime/lifecycle.mjs";
5
4
  import { CHUNK_CSS_PLACEHOLDER } from "../constants.mjs";
6
- import { createReplaceHelemt } from "../helmet.mjs";
5
+ import { createFederatedCssLinks } from "../federatedCss.mjs";
6
+ import { createReplaceHelemt, getHelmetData } from "../helmet.mjs";
7
7
  import { buildHtml } from "../shared.mjs";
8
8
  import { checkIsNode, safeReplace } from "../utils.mjs";
9
9
  import { fileURLToPath as __rspack_fileURLToPath } from "node:url";
@@ -21,8 +21,8 @@ const checkIsInline = (chunk, enableInline)=>{
21
21
  return Boolean(enableInline);
22
22
  };
23
23
  async function buildShellBeforeTemplate(beforeAppTemplate, options) {
24
- const { config, runtimeContext, styledComponentsStyleTags, entryName } = options;
25
- const helmetData = react_helmet.renderStatic();
24
+ const { config, runtimeContext, styledComponentsStyleTags, entryName, moduleFederationCssAssets } = options;
25
+ const helmetData = getHelmetData(runtimeContext);
26
26
  const callbacks = [
27
27
  createReplaceHelemt(helmetData),
28
28
  (template)=>injectCss(template, entryName, styledComponentsStyleTags)
@@ -31,33 +31,36 @@ async function buildShellBeforeTemplate(beforeAppTemplate, options) {
31
31
  async function injectCss(template, entryName, styledComponentsStyleTags) {
32
32
  let css = await getCssChunks();
33
33
  if (styledComponentsStyleTags) css += styledComponentsStyleTags;
34
+ css += createFederatedCssLinks(moduleFederationCssAssets, {
35
+ template,
36
+ existingAssets: css.match(/href="([^"]+)"/g)?.map((item)=>item.replace(/^href="/, '').replace(/"$/, ''))
37
+ });
34
38
  return safeReplace(template, CHUNK_CSS_PLACEHOLDER, css);
35
39
  async function getCssChunks() {
36
40
  const { routeManifest, routerContext, routes } = runtimeContext;
37
41
  if (!routeManifest) return '';
38
42
  const { routeAssets } = routeManifest;
39
- let matchedRouteManifests;
43
+ let matchedRouteManifests = [];
40
44
  const matchedRouteIds = getRouterMatchedRouteIds(runtimeContext);
41
45
  if (matchedRouteIds?.length) matchedRouteManifests = matchedRouteIds.map((routeId)=>routeAssets[routeId]).filter(Boolean);
42
- else {
43
- if (!routerContext || !routes) return '';
46
+ else if (routerContext && routes) {
44
47
  const matches = matchRoutes(routes, routerContext.location, routerContext.basename);
45
48
  matchedRouteManifests = matches?.map((match, index)=>{
46
49
  if (!index) return;
47
50
  const routeId = match.route.id;
48
51
  if (routeId) return routeAssets[routeId];
49
- }).filter(Boolean);
52
+ }).filter(Boolean) ?? [];
50
53
  }
51
54
  const asyncEntry = routeAssets[`async-${entryName}`];
52
- if (asyncEntry) matchedRouteManifests?.push(asyncEntry);
53
- const cssChunks = matchedRouteManifests ? matchedRouteManifests.reduce((chunks, routeManifest)=>{
55
+ if (asyncEntry) matchedRouteManifests.push(asyncEntry);
56
+ const cssChunks = matchedRouteManifests.reduce((chunks, routeManifest)=>{
54
57
  const { referenceCssAssets = [] } = routeManifest;
55
58
  const _cssChunks = referenceCssAssets.filter((asset)=>asset?.endsWith('.css') && !template.includes(asset));
56
59
  return [
57
60
  ...chunks,
58
61
  ..._cssChunks
59
62
  ];
60
- }, []) : [];
63
+ }, []);
61
64
  const { inlineStyles } = config;
62
65
  const styles = await Promise.all(cssChunks.map(async (chunk)=>{
63
66
  const link = `<link href="${chunk}" rel="stylesheet" />`;
@@ -0,0 +1,65 @@
1
+ import "node:module";
2
+ import { matchRoutes } from "@modern-js/runtime-utils/router";
3
+ import { getRouterMatchedRouteIds } from "../../../router/runtime/lifecycle.mjs";
4
+ 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 { safeReplace } from "../utils.mjs";
9
+ const checkIsInline = (chunk, enableInline)=>{
10
+ if ('production' !== process.env.NODE_ENV) return false;
11
+ if (enableInline instanceof RegExp) return enableInline.test(chunk);
12
+ return Boolean(enableInline);
13
+ };
14
+ async function buildShellBeforeTemplate(beforeAppTemplate, options) {
15
+ const { config, runtimeContext, styledComponentsStyleTags, entryName, moduleFederationCssAssets } = options;
16
+ const helmetData = getHelmetData(runtimeContext);
17
+ const callbacks = [
18
+ createReplaceHelemt(helmetData),
19
+ (template)=>injectCss(template, entryName, styledComponentsStyleTags)
20
+ ];
21
+ return buildHtml(beforeAppTemplate, callbacks);
22
+ async function injectCss(template, entryName, styledComponentsStyleTags) {
23
+ let css = await getCssChunks();
24
+ if (styledComponentsStyleTags) css += styledComponentsStyleTags;
25
+ css += createFederatedCssLinks(moduleFederationCssAssets, {
26
+ template,
27
+ existingAssets: css.match(/href="([^"]+)"/g)?.map((item)=>item.replace(/^href="/, '').replace(/"$/, ''))
28
+ });
29
+ return safeReplace(template, CHUNK_CSS_PLACEHOLDER, css);
30
+ async function getCssChunks() {
31
+ const { routeManifest, routerContext, routes } = runtimeContext;
32
+ if (!routeManifest) return '';
33
+ const { routeAssets } = routeManifest;
34
+ let matchedRouteManifests = [];
35
+ const matchedRouteIds = getRouterMatchedRouteIds(runtimeContext);
36
+ if (matchedRouteIds?.length) matchedRouteManifests = matchedRouteIds.map((routeId)=>routeAssets[routeId]).filter(Boolean);
37
+ else if (routerContext && routes) {
38
+ const matches = matchRoutes(routes, routerContext.location, routerContext.basename);
39
+ matchedRouteManifests = matches?.map((match, index)=>{
40
+ if (!index) return;
41
+ const routeId = match.route.id;
42
+ if (routeId) return routeAssets[routeId];
43
+ }).filter(Boolean) ?? [];
44
+ }
45
+ const asyncEntry = routeAssets[`async-${entryName}`];
46
+ if (asyncEntry) matchedRouteManifests.push(asyncEntry);
47
+ const cssChunks = matchedRouteManifests.reduce((chunks, routeManifest)=>{
48
+ const { referenceCssAssets = [] } = routeManifest;
49
+ const _cssChunks = referenceCssAssets.filter((asset)=>asset?.endsWith('.css') && !template.includes(asset));
50
+ return [
51
+ ...chunks,
52
+ ..._cssChunks
53
+ ];
54
+ }, []);
55
+ const { inlineStyles } = config;
56
+ const styles = cssChunks.map((chunk)=>{
57
+ const link = `<link href="${chunk}" rel="stylesheet" />`;
58
+ checkIsInline(chunk, inlineStyles);
59
+ return link;
60
+ });
61
+ return `${styles.join('')}`;
62
+ }
63
+ }
64
+ }
65
+ export { buildShellBeforeTemplate };
@@ -16,7 +16,7 @@ const defaultExtender = {
16
16
  };
17
17
  const createReadableStreamFromElement = async (request, rootElement, options)=>{
18
18
  const { renderToPipeableStream } = await import("react-dom/server");
19
- const { runtimeContext, htmlTemplate, config, ssrConfig, entryName } = options;
19
+ const { runtimeContext, htmlTemplate, config, ssrConfig, entryName, moduleFederationCssAssets } = options;
20
20
  let shellChunkStatus = ShellChunkStatus.START;
21
21
  let renderLevel = RenderLevel.SERVER_RENDER;
22
22
  const forceStream2String = Boolean(process.env.MODERN_JS_STREAM_TO_STRING);
@@ -36,11 +36,14 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
36
36
  if (extender.modifyRootElement) processedRootElement = extender.modifyRootElement(processedRootElement);
37
37
  });
38
38
  const chunkVec = [];
39
+ let hasStartedPipe = false;
39
40
  return new Promise((resolve)=>{
40
41
  const { pipe: reactStreamingPipe } = renderToPipeableStream(processedRootElement, {
41
42
  nonce: config.nonce,
42
43
  identifierPrefix: SSR_HYDRATION_ID_PREFIX,
43
44
  [onReady] () {
45
+ if (hasStartedPipe) return;
46
+ hasStartedPipe = true;
44
47
  let styledComponentsStyleTags = '';
45
48
  extenders.forEach((extender)=>{
46
49
  if (extender.getStyleTags) styledComponentsStyleTags += extender.getStyleTags();
@@ -53,6 +56,7 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
53
56
  runtimeContext,
54
57
  config,
55
58
  entryName,
59
+ moduleFederationCssAssets,
56
60
  styledComponentsStyleTags
57
61
  }).then(({ shellAfter, shellBefore })=>{
58
62
  const pendingScripts = [];
@@ -110,7 +114,8 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
110
114
  renderLevel,
111
115
  runtimeContext,
112
116
  entryName,
113
- config
117
+ config,
118
+ moduleFederationCssAssets
114
119
  }).then(({ shellAfter, shellBefore })=>{
115
120
  const fallbackHtml = `${shellBefore}${shellAfter}`;
116
121
  const readableStream = getReadableStreamFromString(fallbackHtml);
@@ -9,19 +9,21 @@ import { getTemplates } from "./template.mjs";
9
9
  const createReadableStreamFromElement = async (request, rootElement, options)=>{
10
10
  let shellChunkStatus = ShellChunkStatus.START;
11
11
  const chunkVec = [];
12
- const { htmlTemplate, runtimeContext, config, ssrConfig, entryName, rscRoot } = options;
12
+ const { htmlTemplate, runtimeContext, config, ssrConfig, entryName, moduleFederationCssAssets, rscManifest, rscRoot } = options;
13
13
  const { shellBefore, shellAfter } = await getTemplates(htmlTemplate, {
14
14
  renderLevel: RenderLevel.SERVER_RENDER,
15
15
  runtimeContext,
16
16
  ssrConfig,
17
17
  request,
18
18
  config,
19
- entryName
19
+ entryName,
20
+ moduleFederationCssAssets
20
21
  });
21
22
  try {
22
23
  const readableOriginal = await renderSSRStream(rootElement, {
23
24
  request,
24
25
  nonce: config.nonce,
26
+ rscManifest,
25
27
  rscRoot: rscRoot,
26
28
  routes: runtimeContext.routes,
27
29
  onError (error) {
@@ -64,7 +64,7 @@ function createRenderStreaming(createReadableStreamPromise) {
64
64
  const end = time();
65
65
  const { runtimeContext, config, resource } = options;
66
66
  const { onError, onTiming } = options;
67
- const { htmlTemplate, entryName } = resource;
67
+ const { htmlTemplate, entryName, moduleFederationCssAssets } = resource;
68
68
  const ssrConfig = getSSRConfigByEntry(entryName, config.ssr, config.ssrByEntries);
69
69
  const StreamServerRootWrapper = ({ children })=>/*#__PURE__*/ jsxs(Fragment, {
70
70
  children: [
@@ -84,9 +84,11 @@ function createRenderStreaming(createReadableStreamPromise) {
84
84
  runtimeContext,
85
85
  ssrConfig,
86
86
  entryName,
87
+ moduleFederationCssAssets,
87
88
  rscClientManifest: options.rscClientManifest,
88
89
  rscSSRManifest: options.rscSSRManifest,
89
90
  rscServerManifest: options.rscServerManifest,
91
+ rscManifest: options.rscManifest,
90
92
  rscRoot: options.rscRoot,
91
93
  onShellReady () {
92
94
  const cost = end();
@@ -2,12 +2,11 @@ import "node:module";
2
2
  import { time } from "@modern-js/runtime-utils/time";
3
3
  import { SSR_HYDRATION_ID_PREFIX } from "@modern-js/utils/universal/constants";
4
4
  import server from "react-dom/server";
5
- import react_helmet from "react-helmet";
6
5
  import { RenderLevel } from "../../constants.mjs";
7
6
  import { getGlobalInternalRuntimeContext } from "../../context/index.mjs";
8
7
  import { wrapRuntimeContextProvider } from "../../react/wrapper.mjs";
9
8
  import { CHUNK_CSS_PLACEHOLDER, CHUNK_JS_PLACEHOLDER, HTML_PLACEHOLDER, SSR_DATA_PLACEHOLDER } from "../constants.mjs";
10
- import { createReplaceHelemt } from "../helmet.mjs";
9
+ import { createReplaceHelemt, getHelmetData } from "../helmet.mjs";
11
10
  import { buildHtml } from "../shared.mjs";
12
11
  import { SSRErrors, SSRTimings } from "../tracer.mjs";
13
12
  import { getSSRConfigByEntry, safeReplace } from "../utils.mjs";
@@ -20,7 +19,7 @@ const renderString = async (request, serverRoot, options)=>{
20
19
  onTiming
21
20
  };
22
21
  const routerContext = runtimeContext.routerContext;
23
- const { htmlTemplate, entryName, loadableStats, routeManifest } = resource;
22
+ const { htmlTemplate, entryName, loadableStats, routeManifest, moduleFederationCssAssets } = resource;
24
23
  const ssrConfig = getSSRConfigByEntry(entryName, config.ssr, config.ssrByEntries);
25
24
  const chunkSet = {
26
25
  renderLevel: RenderLevel.CLIENT_RENDER,
@@ -33,8 +32,10 @@ const renderString = async (request, serverRoot, options)=>{
33
32
  stats: loadableStats,
34
33
  nonce: config.nonce,
35
34
  routeManifest,
35
+ runtimeContext,
36
36
  template: htmlTemplate,
37
37
  entryName,
38
+ moduleFederationCssAssets,
38
39
  chunkSet,
39
40
  config
40
41
  }),
@@ -58,10 +59,10 @@ const renderString = async (request, serverRoot, options)=>{
58
59
  const rootElement = wrapRuntimeContextProvider(serverRoot, Object.assign(runtimeContext, {
59
60
  ssr: true
60
61
  }));
61
- const html = await generateHtml(rootElement, htmlTemplate, chunkSet, collectors, runtimeContext.ssrContext?.htmlModifiers || [], tracer);
62
+ const html = await generateHtml(rootElement, htmlTemplate, chunkSet, collectors, runtimeContext.ssrContext?.htmlModifiers || [], runtimeContext, tracer);
62
63
  return html;
63
64
  };
64
- async function generateHtml(App, htmlTemplate, chunkSet, collectors, htmlModifiers, { onError, onTiming }) {
65
+ async function generateHtml(App, htmlTemplate, chunkSet, collectors, htmlModifiers, runtimeContext, { onError, onTiming }) {
65
66
  let html = '';
66
67
  let helmetData;
67
68
  const finalApp = collectors.reduce((pre, creator)=>creator.collect?.(pre) || pre, App);
@@ -71,7 +72,7 @@ async function generateHtml(App, htmlTemplate, chunkSet, collectors, htmlModifie
71
72
  identifierPrefix: SSR_HYDRATION_ID_PREFIX
72
73
  });
73
74
  chunkSet.renderLevel = RenderLevel.SERVER_RENDER;
74
- helmetData = react_helmet.renderStatic();
75
+ helmetData = getHelmetData(runtimeContext);
75
76
  const cost = end();
76
77
  onTiming(SSRTimings.RENDER_HTML, cost);
77
78
  } catch (e) {
@@ -1,5 +1,7 @@
1
1
  import "node:module";
2
2
  import { ChunkExtractor } from "@loadable/server";
3
+ import { getRouterMatchedRouteIds } from "../../../router/runtime/lifecycle.mjs";
4
+ import { createFederatedCssLinks } from "../federatedCss.mjs";
3
5
  import { attributesToString, checkIsNode } from "../utils.mjs";
4
6
  import { fileURLToPath as __rspack_fileURLToPath } from "node:url";
5
7
  import { dirname as __rspack_dirname } from "node:path";
@@ -25,6 +27,20 @@ class LoadableCollector {
25
27
  const { routeManifest, entryName } = this.options;
26
28
  return routeManifest?.routeAssets?.[entryName]?.assets;
27
29
  }
30
+ getMatchedRouteChunks() {
31
+ const { routeManifest, runtimeContext } = this.options;
32
+ const routeAssets = routeManifest?.routeAssets;
33
+ if (!routeAssets) return [];
34
+ const matchedRouteIds = getRouterMatchedRouteIds(runtimeContext) ?? [];
35
+ return matchedRouteIds.flatMap((routeId)=>{
36
+ const routeAsset = routeAssets[routeId];
37
+ return (routeAsset?.assets ?? []).map((asset)=>({
38
+ filename: asset.replace(/^\//, ''),
39
+ path: asset,
40
+ url: asset
41
+ }));
42
+ });
43
+ }
28
44
  collect(comopnent) {
29
45
  const { stats, entryName } = this.options;
30
46
  if (!stats) return comopnent;
@@ -37,20 +53,21 @@ class LoadableCollector {
37
53
  return this.extractor.collectChunks(comopnent);
38
54
  }
39
55
  async effect() {
40
- if (!this.extractor) return;
41
56
  const { extractor, options } = this;
42
57
  const { entryName, config } = options;
43
58
  const asyncChunks = [];
44
- if (config.enableAsyncEntry) try {
59
+ if (extractor && config.enableAsyncEntry) try {
45
60
  asyncChunks.push(...extractor.getChunkAssets([
46
61
  `async-${entryName}`
47
62
  ]));
48
63
  } catch (e) {}
49
- const chunks = [].concat(asyncChunks).concat(extractor.getChunkAssets(extractor.chunks));
64
+ const chunks = [].concat(asyncChunks).concat(extractor ? extractor.getChunkAssets(extractor.chunks) : []).concat(this.getMatchedRouteChunks());
50
65
  const scriptChunks = generateChunks(chunks, 'js');
51
66
  const styleChunks = generateChunks(chunks, 'css');
52
- this.emitLoadableScripts(extractor);
53
- await this.emitScriptAssets(scriptChunks);
67
+ if (extractor) {
68
+ this.emitLoadableScripts(extractor);
69
+ await this.emitScriptAssets(scriptChunks);
70
+ }
54
71
  await this.emitStyleAssets(styleChunks);
55
72
  }
56
73
  emitLoadableScripts(extractor) {
@@ -85,19 +102,28 @@ class LoadableCollector {
85
102
  chunkSet.jsChunk += scripts.filter((script)=>Boolean(script)).join('');
86
103
  }
87
104
  async emitStyleAssets(chunks) {
88
- const { template, chunkSet, config, entryName } = this.options;
105
+ const { template, chunkSet, config, moduleFederationCssAssets } = this.options;
89
106
  const { inlineStyles } = config;
90
107
  const atrributes = attributesToString(this.generateAttributes());
91
108
  const linkRegExp = /<link .*?href="([^"]+)".*?>/g;
92
109
  const matchs = template.matchAll(linkRegExp);
93
110
  const existedLinks = [];
94
111
  for (const match of matchs)existedLinks.push(match[1]);
95
- const css = await Promise.all(chunks.filter((chunk)=>!existedLinks.includes(chunk.url) && !this.existsAssets?.includes(chunk.path)).map(async (chunk)=>{
112
+ const emittedChunks = chunks.filter((chunk)=>!existedLinks.includes(chunk.url) && !this.existsAssets?.includes(chunk.path));
113
+ const css = await Promise.all(emittedChunks.map(async (chunk)=>{
96
114
  const link = `<link${atrributes} href="${chunk.url}" rel="stylesheet" />`;
97
115
  if (checkIsNode() && checkIsInline(chunk, inlineStyles)) return readAsset(chunk).then((content)=>`<style>${content}</style>`).catch((_)=>link);
98
116
  return link;
99
117
  }));
100
118
  chunkSet.cssChunk += css.filter((css)=>Boolean(css)).join('');
119
+ chunkSet.cssChunk += createFederatedCssLinks(moduleFederationCssAssets, {
120
+ template,
121
+ attributes: this.generateAttributes(),
122
+ existingAssets: [
123
+ ...existedLinks,
124
+ ...emittedChunks.map((chunk)=>chunk.url)
125
+ ]
126
+ });
101
127
  }
102
128
  generateAttributes(extraAtr = {}) {
103
129
  const { config } = this.options;
@@ -1,6 +1,191 @@
1
1
  import "node:module";
2
2
  "use client";
3
- import react_helmet, { Helmet } from "react-helmet";
4
- const head = react_helmet;
5
- export default head;
6
- export { Helmet };
3
+ import react from "react";
4
+ import { Helmet, HelmetData, HelmetProvider } from "react-helmet-async";
5
+ import { InternalRuntimeContext } from "../core/context/index.mjs";
6
+ const ATTRIBUTE_NAME_MAP = {
7
+ charSet: 'charset',
8
+ className: 'class',
9
+ contentEditable: 'contenteditable',
10
+ httpEquiv: 'http-equiv',
11
+ itemProp: 'itemprop',
12
+ tabIndex: 'tabindex'
13
+ };
14
+ const escapeHtml = (value)=>String(value).replaceAll('&', '&amp;').replaceAll('"', '&quot;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
15
+ const toHtmlAttributeName = (name)=>ATTRIBUTE_NAME_MAP[name] ?? name;
16
+ const attributesToString = (attributes, includeHelmetAttribute = false)=>{
17
+ const pairs = [];
18
+ if (includeHelmetAttribute) pairs.push('data-rh="true"');
19
+ for (const [name, value] of Object.entries(attributes ?? {})){
20
+ if (false === value || null == value) continue;
21
+ const htmlName = toHtmlAttributeName(name);
22
+ if (true === value) pairs.push(htmlName);
23
+ else pairs.push(`${htmlName}="${escapeHtml(value)}"`);
24
+ }
25
+ return pairs.join(' ');
26
+ };
27
+ const createDatum = (tagName, tags)=>({
28
+ toComponent: ()=>[],
29
+ toString: ()=>tags.map((tag)=>{
30
+ const attrs = attributesToString(tag, true);
31
+ if ("script" === tagName && 'string' == typeof tag.innerHTML) return `<script ${attrs}>${tag.innerHTML}</script>`;
32
+ if ('style' === tagName && 'string' == typeof tag.cssText) return `<style ${attrs}>${tag.cssText}</style>`;
33
+ if ("noscript" === tagName && 'string' == typeof tag.innerHTML) return `<noscript ${attrs}>${tag.innerHTML}</noscript>`;
34
+ return `<${tagName} ${attrs}>`;
35
+ }).join('')
36
+ });
37
+ const createAttributeDatum = (attributes)=>({
38
+ toComponent: ()=>attributes,
39
+ toString: ()=>attributesToString(attributes)
40
+ });
41
+ const createTitleDatum = (title, attributes)=>({
42
+ toComponent: ()=>[],
43
+ toString: ()=>{
44
+ if (!title) return '';
45
+ const attrs = attributesToString(attributes, true);
46
+ return `<title ${attrs}>${escapeHtml(title)}</title>`;
47
+ }
48
+ });
49
+ const createEmptyHelmetState = ()=>({
50
+ base: createDatum('base', []),
51
+ bodyAttributes: createAttributeDatum({}),
52
+ htmlAttributes: createAttributeDatum({}),
53
+ link: createDatum('link', []),
54
+ meta: createDatum('meta', []),
55
+ noscript: createDatum("noscript", []),
56
+ priority: createDatum('meta', []),
57
+ script: createDatum("script", []),
58
+ style: createDatum('style', []),
59
+ title: createTitleDatum(void 0, {})
60
+ });
61
+ const mergeAttributes = (current, next)=>({
62
+ ...current,
63
+ ...next ?? {}
64
+ });
65
+ const collectChildren = (children, draft)=>{
66
+ react.Children.forEach(children, (child)=>{
67
+ if (!react.isValidElement(child)) return;
68
+ if (child.type === react.Fragment) return void collectChildren(child.props.children, draft);
69
+ if ('string' != typeof child.type) return;
70
+ const { children: nestedChildren, ...props } = child.props;
71
+ if ('title' === child.type) {
72
+ draft.title = react.Children.toArray(nestedChildren).join('');
73
+ draft.titleAttributes = mergeAttributes(draft.titleAttributes, props);
74
+ return;
75
+ }
76
+ if ('html' === child.type || 'body' === child.type) return;
77
+ if ('base' === child.type || 'link' === child.type || 'meta' === child.type || "noscript" === child.type || "script" === child.type || 'style' === child.type) {
78
+ const tag = {
79
+ ...props
80
+ };
81
+ if (("script" === child.type || 'style' === child.type || "noscript" === child.type) && 'string' == typeof nestedChildren) tag['style' === child.type ? 'cssText' : 'innerHTML'] = nestedChildren;
82
+ draft[child.type].push(tag);
83
+ }
84
+ });
85
+ };
86
+ const collectHelmetProps = (current, props)=>{
87
+ const baseState = current ?? createEmptyHelmetState();
88
+ const draft = {
89
+ base: [
90
+ ...props.base ? [
91
+ props.base
92
+ ] : []
93
+ ],
94
+ bodyAttributes: props.bodyAttributes,
95
+ htmlAttributes: props.htmlAttributes,
96
+ link: [
97
+ ...props.link ?? []
98
+ ],
99
+ meta: [
100
+ ...props.meta ?? []
101
+ ],
102
+ noscript: [
103
+ ...props.noscript ?? []
104
+ ],
105
+ script: [
106
+ ...props.script ?? []
107
+ ],
108
+ style: [
109
+ ...props.style ?? []
110
+ ],
111
+ title: 'string' == typeof props.title ? props.title : Array.isArray(props.title) ? props.title.join('') : void 0,
112
+ titleAttributes: props.titleAttributes ?? {}
113
+ };
114
+ collectChildren(props.children, draft);
115
+ const title = draft.title && props.titleTemplate ? props.titleTemplate.replaceAll('%s', draft.title) : draft.title ?? props.defaultTitle;
116
+ return {
117
+ base: createDatum('base', [
118
+ ...baseState.__baseTags ?? [],
119
+ ...draft.base
120
+ ]),
121
+ bodyAttributes: createAttributeDatum(mergeAttributes(baseState.__bodyAttributes ?? {}, draft.bodyAttributes)),
122
+ htmlAttributes: createAttributeDatum(mergeAttributes(baseState.__htmlAttributes ?? {}, draft.htmlAttributes)),
123
+ link: createDatum('link', [
124
+ ...baseState.__linkTags ?? [],
125
+ ...draft.link
126
+ ]),
127
+ meta: createDatum('meta', [
128
+ ...baseState.__metaTags ?? [],
129
+ ...draft.meta
130
+ ]),
131
+ noscript: createDatum("noscript", [
132
+ ...baseState.__noscriptTags ?? [],
133
+ ...draft.noscript
134
+ ]),
135
+ priority: createDatum('meta', []),
136
+ script: createDatum("script", [
137
+ ...baseState.__scriptTags ?? [],
138
+ ...draft.script
139
+ ]),
140
+ style: createDatum('style', [
141
+ ...baseState.__styleTags ?? [],
142
+ ...draft.style
143
+ ]),
144
+ title: createTitleDatum(title ?? baseState.__title, mergeAttributes(baseState.__titleAttributes ?? {}, draft.titleAttributes)),
145
+ __baseTags: [
146
+ ...baseState.__baseTags ?? [],
147
+ ...draft.base
148
+ ],
149
+ __bodyAttributes: mergeAttributes(baseState.__bodyAttributes ?? {}, draft.bodyAttributes),
150
+ __htmlAttributes: mergeAttributes(baseState.__htmlAttributes ?? {}, draft.htmlAttributes),
151
+ __linkTags: [
152
+ ...baseState.__linkTags ?? [],
153
+ ...draft.link
154
+ ],
155
+ __metaTags: [
156
+ ...baseState.__metaTags ?? [],
157
+ ...draft.meta
158
+ ],
159
+ __noscriptTags: [
160
+ ...baseState.__noscriptTags ?? [],
161
+ ...draft.noscript
162
+ ],
163
+ __scriptTags: [
164
+ ...baseState.__scriptTags ?? [],
165
+ ...draft.script
166
+ ],
167
+ __styleTags: [
168
+ ...baseState.__styleTags ?? [],
169
+ ...draft.style
170
+ ],
171
+ __title: title ?? baseState.__title,
172
+ __titleAttributes: mergeAttributes(baseState.__titleAttributes ?? {}, draft.titleAttributes)
173
+ };
174
+ };
175
+ const head_Helmet = (props)=>{
176
+ const runtimeContext = react.useContext(InternalRuntimeContext);
177
+ if (runtimeContext && !runtimeContext.isBrowser) {
178
+ runtimeContext._helmetContext ??= {};
179
+ runtimeContext._helmetContext.helmet = collectHelmetProps(runtimeContext._helmetContext.helmet ?? void 0, props);
180
+ return null;
181
+ }
182
+ return react.createElement(Helmet, props);
183
+ };
184
+ const head = {
185
+ Helmet: head_Helmet,
186
+ HelmetData: HelmetData,
187
+ HelmetProvider: HelmetProvider
188
+ };
189
+ const exports_head = head;
190
+ export default exports_head;
191
+ export { HelmetData, HelmetProvider, head_Helmet as Helmet };