@bleedingdev/modern-js-runtime 3.5.0-ultramodern.3 → 3.5.0-ultramodern.31

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 (65) hide show
  1. package/dist/cjs/cli/ssr/index.js +1 -2
  2. package/dist/cjs/core/context/public.js +49 -0
  3. package/dist/cjs/core/context/runtime.js +6 -8
  4. package/dist/cjs/core/react/wrapper.js +57 -0
  5. package/dist/cjs/core/server/federatedCss.js +7 -3
  6. package/dist/cjs/core/server/requestHandler.js +15 -45
  7. package/dist/cjs/core/server/requestResponse.js +83 -0
  8. package/dist/cjs/core/server/routerCleanup.js +21 -0
  9. package/dist/cjs/core/server/scriptOrder.js +13 -7
  10. package/dist/cjs/core/server/string/loadable.js +3 -11
  11. package/dist/cjs/index.js +7 -6
  12. package/dist/cjs/router/cli/code/templates.js +3 -2
  13. package/dist/cjs/router/runtime/hooks.js +7 -6
  14. package/dist/cjs/router/runtime/provider.js +1 -1
  15. package/dist/esm/cli/ssr/index.mjs +1 -2
  16. package/dist/esm/core/context/public.mjs +5 -0
  17. package/dist/esm/core/context/runtime.mjs +4 -6
  18. package/dist/esm/core/react/wrapper.mjs +58 -1
  19. package/dist/esm/core/server/federatedCss.mjs +7 -3
  20. package/dist/esm/core/server/requestHandler.mjs +15 -45
  21. package/dist/esm/core/server/requestResponse.mjs +39 -0
  22. package/dist/esm/core/server/routerCleanup.mjs +16 -1
  23. package/dist/esm/core/server/scriptOrder.mjs +11 -8
  24. package/dist/esm/core/server/string/loadable.mjs +3 -8
  25. package/dist/esm/index.mjs +2 -1
  26. package/dist/esm/router/cli/code/templates.mjs +3 -2
  27. package/dist/esm/router/runtime/hooks.mjs +7 -6
  28. package/dist/esm/router/runtime/provider.mjs +1 -1
  29. package/dist/esm-node/cli/ssr/index.mjs +1 -2
  30. package/dist/esm-node/core/context/public.mjs +6 -0
  31. package/dist/esm-node/core/context/runtime.mjs +4 -6
  32. package/dist/esm-node/core/react/wrapper.mjs +58 -1
  33. package/dist/esm-node/core/server/federatedCss.mjs +7 -3
  34. package/dist/esm-node/core/server/requestHandler.mjs +15 -45
  35. package/dist/esm-node/core/server/requestResponse.mjs +40 -0
  36. package/dist/esm-node/core/server/routerCleanup.mjs +16 -1
  37. package/dist/esm-node/core/server/scriptOrder.mjs +11 -8
  38. package/dist/esm-node/core/server/string/loadable.mjs +3 -8
  39. package/dist/esm-node/index.mjs +2 -1
  40. package/dist/esm-node/router/cli/code/templates.mjs +3 -2
  41. package/dist/esm-node/router/runtime/hooks.mjs +7 -6
  42. package/dist/esm-node/router/runtime/provider.mjs +1 -1
  43. package/dist/types/boundary-debugger/index.d.ts +1 -1
  44. package/dist/types/common.d.ts +8 -1
  45. package/dist/types/core/context/index.d.ts +25 -9
  46. package/dist/types/core/context/monitors/default.d.ts +10 -2
  47. package/dist/types/core/context/monitors/index.d.ts +1 -1
  48. package/dist/types/core/context/monitors/index.server.d.ts +2 -2
  49. package/dist/types/core/context/public.d.ts +26 -0
  50. package/dist/types/core/context/runtime.d.ts +12 -22
  51. package/dist/types/core/react/index.d.ts +1 -1
  52. package/dist/types/core/server/requestResponse.d.ts +15 -0
  53. package/dist/types/core/server/routerCleanup.d.ts +2 -0
  54. package/dist/types/core/server/scriptOrder.d.ts +1 -0
  55. package/dist/types/core/server/stream/shared.d.ts +2 -2
  56. package/dist/types/core/server/string/loadable.d.ts +0 -2
  57. package/dist/types/index.d.ts +5 -5
  58. package/dist/types/internal.d.ts +0 -1
  59. package/dist/types/router/runtime/hooks.d.ts +10 -6
  60. package/dist/types/router/runtime/internal.d.ts +1 -1
  61. package/dist/types/router/runtime/plugin.d.ts +1 -1
  62. package/dist/types/router/runtime/plugin.node.d.ts +1 -1
  63. package/dist/types/router/runtime/provider.d.ts +1 -1
  64. package/dist/types/router/runtime/types.d.ts +27 -5
  65. package/package.json +8 -8
@@ -1,8 +1,6 @@
1
- import { ROUTE_MANIFEST } from "@modern-js/utils/universal/constants";
2
- import { createContext, useContext } from "react";
1
+ import { createContext } from "react";
2
+ const ROUTE_MANIFEST = '_MODERNJS_ROUTE_MANIFEST';
3
3
  const InternalRuntimeContext = createContext({});
4
- const RuntimeContext = createContext({});
5
- const ReactRuntimeContext = RuntimeContext;
6
4
  const getInitialContext = (isBrowser = true, routeManifest)=>{
7
5
  const requestContext = {
8
6
  request: {},
@@ -15,5 +13,5 @@ const getInitialContext = (isBrowser = true, routeManifest)=>{
15
13
  context: requestContext
16
14
  };
17
15
  };
18
- const useRuntimeContext = ()=>useContext(RuntimeContext);
19
- export { InternalRuntimeContext, ReactRuntimeContext, RuntimeContext, getInitialContext, useRuntimeContext };
16
+ export { ReactRuntimeContext, RuntimeContext, useRuntimeContext } from "./public.mjs";
17
+ export { InternalRuntimeContext, getInitialContext };
@@ -1,8 +1,44 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { HelmetProvider } from "react-helmet-async";
3
- import { InternalRuntimeContext, RuntimeContext } from "../context/index.mjs";
3
+ import { InternalRuntimeContext, RuntimeContext, getGlobalEnableRsc } from "../context/index.mjs";
4
4
  import { stripRuntimeContextExtensions } from "../context/extensions.mjs";
5
5
  import { ensureHelmetContext } from "../context/helmetContext.mjs";
6
+ function toRscSafeRecord(value) {
7
+ if (!value || 'object' != typeof value) return value;
8
+ return Object.fromEntries(Object.entries(value));
9
+ }
10
+ function createRscSafeSsrContext(ssrContext) {
11
+ if (!ssrContext) return ssrContext;
12
+ const { request } = ssrContext;
13
+ return {
14
+ nonce: ssrContext.nonce,
15
+ useJsonScript: ssrContext.useJsonScript,
16
+ htmlModifiers: [],
17
+ baseUrl: ssrContext.baseUrl,
18
+ request: request ? {
19
+ url: request.url,
20
+ userAgent: request.userAgent,
21
+ cookie: request.cookie,
22
+ pathname: request.pathname,
23
+ query: toRscSafeRecord(request.query),
24
+ params: toRscSafeRecord(request.params),
25
+ headers: toRscSafeRecord(request.headers),
26
+ host: request.host,
27
+ referer: request.referer
28
+ } : request,
29
+ mode: ssrContext.mode,
30
+ loaderFailureMode: ssrContext.loaderFailureMode
31
+ };
32
+ }
33
+ function createRscSafeRequestContext(ssrContext) {
34
+ const safeSsrContext = createRscSafeSsrContext(ssrContext);
35
+ return {
36
+ request: safeSsrContext?.request || {},
37
+ response: {
38
+ locals: ssrContext?.response?.locals || {}
39
+ }
40
+ };
41
+ }
6
42
  function wrapRuntimeContextProvider(App, contextValue) {
7
43
  const { isBrowser, initialData, routes, context, routeManifest, routerContext, unstable_getBlockNavState, ssrContext, _internalContext, _internalRouterBaseName, ...rest } = contextValue;
8
44
  const internalContextValue = contextValue;
@@ -15,6 +51,27 @@ function wrapRuntimeContextProvider(App, contextValue) {
15
51
  ...rest
16
52
  };
17
53
  stripRuntimeContextExtensions(runtimeContextValue);
54
+ if (getGlobalEnableRsc() && !isBrowser) {
55
+ const rscSafeRequestContext = createRscSafeRequestContext(ssrContext);
56
+ const rscInternalContextValue = {
57
+ ...internalContextValue,
58
+ context: rscSafeRequestContext,
59
+ requestContext: rscSafeRequestContext,
60
+ ssrContext: createRscSafeSsrContext(ssrContext)
61
+ };
62
+ const rscRuntimeContextValue = {
63
+ ...runtimeContextValue,
64
+ context: rscSafeRequestContext,
65
+ requestContext: rscSafeRequestContext
66
+ };
67
+ return /*#__PURE__*/ jsx(InternalRuntimeContext.Provider, {
68
+ value: rscInternalContextValue,
69
+ children: /*#__PURE__*/ jsx(RuntimeContext.Provider, {
70
+ value: rscRuntimeContextValue,
71
+ children: App
72
+ })
73
+ });
74
+ }
18
75
  return /*#__PURE__*/ jsx(InternalRuntimeContext.Provider, {
19
76
  value: internalContextValue,
20
77
  children: /*#__PURE__*/ jsx(RuntimeContext.Provider, {
@@ -1,12 +1,16 @@
1
1
  import { attributesToString } from "./utils.mjs";
2
+ const escapeHtmlAttribute = (value)=>value.replaceAll('&', '&amp;').replaceAll('"', '&quot;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
2
3
  const createFederatedCssLinks = (assets, options)=>{
3
4
  if (void 0 === assets || 0 === assets.length) return '';
4
5
  const seen = new Set(options.existingAssets || []);
5
6
  const attributes = attributesToString(options.attributes || {});
6
7
  const links = [];
7
- for (const asset of assets)if (!('' === asset || seen.has(asset) || options.template.includes(asset))) {
8
- seen.add(asset);
9
- links.push(`<link${attributes} href="${asset}" rel="stylesheet" />`);
8
+ for (const asset of assets){
9
+ const href = escapeHtmlAttribute(asset);
10
+ if (!('' === asset || seen.has(asset) || options.template.includes(asset) || options.template.includes(href))) {
11
+ seen.add(asset);
12
+ links.push(`<link${attributes} href="${href}" rel="stylesheet" />`);
13
+ }
10
14
  }
11
15
  return links.join('');
12
16
  };
@@ -2,15 +2,13 @@ import { jsx } from "react/jsx-runtime";
2
2
  import { storage } from "@modern-js/runtime-utils/node";
3
3
  import { getPathname, parseCookie, parseHeaders, parseQuery } from "@modern-js/runtime-utils/universal/request";
4
4
  import react, { Fragment } from "react";
5
- import { getRouterServerSnapshot } from "../../router/runtime/lifecycle.mjs";
6
- import { handleRSCRedirect } from "../../router/runtime/rsc-router.mjs";
7
5
  import { getGlobalInternalRuntimeContext, getGlobalRSCRoot } from "../context/index.mjs";
8
6
  import { getInitialContext } from "../context/runtime.mjs";
9
7
  import { getServerPayload } from "../context/serverPayload/index.mjs";
10
8
  import { createRoot } from "../react/index.mjs";
11
9
  import { CHUNK_CSS_PLACEHOLDER } from "./constants.mjs";
12
- import { withRouterCleanup } from "./routerCleanup.mjs";
13
- import { SSRErrors } from "./tracer.mjs";
10
+ import { applyRouterSnapshotResult, createLoaderRedirectResponse, finalizeRenderResponse } from "./requestResponse.mjs";
11
+ import { createRouterCleanup, finishWithRouterCleanup, runWithRouterCleanupOnError } from "./routerCleanup.mjs";
14
12
  import { getSSRConfigByEntry, getSSRMode } from "./utils.mjs";
15
13
  async function handleRSCRequest(request, Root, context, options, handleRequest) {
16
14
  const serverPayload = getServerPayload();
@@ -30,29 +28,6 @@ async function handleRSCRequest(request, Root, context, options, handleRequest)
30
28
  runtimeContext: context
31
29
  });
32
30
  }
33
- const isRedirectStatus = (status)=>status >= 300 && status <= 399;
34
- const processRedirect = (headers, status, ctx)=>{
35
- if (ctx.enableRsc && ctx.isRSCNavigation) return handleRSCRedirect(headers, ctx.basename, status);
36
- return new Response(null, {
37
- status,
38
- headers
39
- });
40
- };
41
- const applyRouterSnapshotResult = (context, onError)=>{
42
- const routerServerSnapshot = getRouterServerSnapshot(context);
43
- const routerStatusCode = routerServerSnapshot?.statusCode ?? context.routerContext?.statusCode;
44
- if (routerStatusCode && 200 !== routerStatusCode) context.ssrContext?.response.status(routerStatusCode);
45
- const errors = Object.values(routerServerSnapshot?.errors || context.routerContext?.errors || {});
46
- if (errors.length > 0) onError(errors[0], SSRErrors.LOADER_ERROR);
47
- };
48
- const createLoaderRedirectResponse = (beforeRenderResult, redirectCtx)=>{
49
- if (!beforeRenderResult || !isRedirectStatus(beforeRenderResult.status)) return;
50
- if (beforeRenderResult.headers.has('X-Modernjs-Redirect')) return beforeRenderResult;
51
- const redirectUrl = beforeRenderResult.headers.get('Location') || '/';
52
- return processRedirect(new Headers({
53
- Location: redirectUrl
54
- }), beforeRenderResult.status, redirectCtx);
55
- };
56
31
  const renderRequest = async (request, Root, context, options, handleRequest, enableRsc)=>{
57
32
  if (enableRsc) return handleRSCRequest(request, Root, context, options, handleRequest);
58
33
  return handleRequest(request, Root, {
@@ -60,17 +35,6 @@ const renderRequest = async (request, Root, context, options, handleRequest, ena
60
35
  runtimeContext: context
61
36
  });
62
37
  };
63
- const finalizeRenderResponse = (response, responseProxy, redirectCtx, routerCleanup)=>{
64
- if (-1 !== responseProxy.status && isRedirectStatus(responseProxy.status) && responseProxy.headers.Location) return processRedirect(new Headers(responseProxy.headers), responseProxy.status, redirectCtx);
65
- Object.entries(responseProxy.headers).forEach(([key, value])=>{
66
- response.headers.set(key, value);
67
- });
68
- if (-1 !== responseProxy.status) return routerCleanup.deferUntilBodyDone(new Response(response.body, {
69
- status: responseProxy.status,
70
- headers: response.headers
71
- }));
72
- return routerCleanup.deferUntilBodyDone(response);
73
- };
74
38
  function createSSRContext(request, options) {
75
39
  const { config, loaderContext, onError, onTiming, locals, resource, params, responseProxy, reporter } = options;
76
40
  const { nonce, useJsonScript } = config;
@@ -166,20 +130,26 @@ const createRequestHandler = async (handleRequest, createRequestOptions)=>{
166
130
  isRSCNavigation: 'true' === request.headers.get('x-rsc-tree'),
167
131
  basename: ssrContext.baseUrl || '/'
168
132
  };
169
- return withRouterCleanup(context, options.onError, async (routerCleanup)=>{
170
- const beforeRenderResult = await runBeforeRender(context);
133
+ const routerCleanup = createRouterCleanup(context, options.onError);
134
+ const beforeRenderResult = await runWithRouterCleanupOnError(routerCleanup, ()=>runBeforeRender(context));
135
+ await runWithRouterCleanupOnError(routerCleanup, ()=>{
171
136
  applyRouterSnapshotResult(context, options.onError);
172
- if ("u" > typeof Response) {
173
- const redirectResponse = createLoaderRedirectResponse(beforeRenderResult, redirectCtx);
174
- if (redirectResponse) return redirectResponse;
137
+ });
138
+ if ("u" > typeof Response) {
139
+ const redirectResponse = await runWithRouterCleanupOnError(routerCleanup, ()=>createLoaderRedirectResponse(beforeRenderResult, redirectCtx));
140
+ if (redirectResponse) {
141
+ await routerCleanup.run();
142
+ return redirectResponse;
175
143
  }
144
+ }
145
+ await runWithRouterCleanupOnError(routerCleanup, ()=>{
176
146
  if (!createRequestOptions?.enableRsc) {
177
147
  const { htmlTemplate } = options.resource;
178
148
  options.resource.htmlTemplate = htmlTemplate.replace('</head>', `${CHUNK_CSS_PLACEHOLDER}</head>`);
179
149
  }
180
- const response = await renderRequest(request, Root, context, options, handleRequest, !!createRequestOptions?.enableRsc);
181
- return finalizeRenderResponse(response, responseProxy, redirectCtx, routerCleanup);
182
150
  });
151
+ const response = await runWithRouterCleanupOnError(routerCleanup, ()=>renderRequest(request, Root, context, options, handleRequest, !!createRequestOptions?.enableRsc));
152
+ return finishWithRouterCleanup(routerCleanup, ()=>finalizeRenderResponse(response, responseProxy, redirectCtx, routerCleanup));
183
153
  });
184
154
  };
185
155
  return requestHandler;
@@ -0,0 +1,39 @@
1
+ import { getRouterServerSnapshot } from "../../router/runtime/lifecycle.mjs";
2
+ import { handleRSCRedirect } from "../../router/runtime/rsc-router.mjs";
3
+ import { SSRErrors } from "./tracer.mjs";
4
+ const isRedirectStatus = (status)=>status >= 300 && status <= 399;
5
+ const isNullBodyStatus = (status)=>204 === status || 205 === status || 304 === status;
6
+ const processRedirect = (headers, status, ctx)=>{
7
+ if (ctx.enableRsc && ctx.isRSCNavigation) return handleRSCRedirect(headers, ctx.basename, status);
8
+ return new Response(null, {
9
+ status,
10
+ headers
11
+ });
12
+ };
13
+ const applyRouterSnapshotResult = (context, onError)=>{
14
+ const routerServerSnapshot = getRouterServerSnapshot(context);
15
+ const routerStatusCode = routerServerSnapshot?.statusCode ?? context.routerContext?.statusCode;
16
+ if (routerStatusCode && 200 !== routerStatusCode) context.ssrContext?.response.status(routerStatusCode);
17
+ const errors = Object.values(routerServerSnapshot?.errors || context.routerContext?.errors || {});
18
+ if (errors.length > 0) onError(errors[0], SSRErrors.LOADER_ERROR);
19
+ };
20
+ const createLoaderRedirectResponse = (beforeRenderResult, redirectCtx)=>{
21
+ if (!beforeRenderResult || !isRedirectStatus(beforeRenderResult.status)) return;
22
+ 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);
27
+ };
28
+ const finalizeRenderResponse = (response, responseProxy, redirectCtx, routerCleanup)=>{
29
+ if (-1 !== responseProxy.status && isRedirectStatus(responseProxy.status) && responseProxy.headers.Location) return processRedirect(new Headers(responseProxy.headers), responseProxy.status, redirectCtx);
30
+ Object.entries(responseProxy.headers).forEach(([key, value])=>{
31
+ response.headers.set(key, value);
32
+ });
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
+ return routerCleanup.deferUntilBodyDone(response);
38
+ };
39
+ export { applyRouterSnapshotResult, createLoaderRedirectResponse, finalizeRenderResponse };
@@ -8,6 +8,21 @@ async function withRouterCleanup(runtimeContext, onError, callback) {
8
8
  if (!routerCleanup.deferred) await routerCleanup.run();
9
9
  }
10
10
  }
11
+ async function runWithRouterCleanupOnError(routerCleanup, callback) {
12
+ try {
13
+ return await callback();
14
+ } catch (error) {
15
+ await routerCleanup.run();
16
+ throw error;
17
+ }
18
+ }
19
+ async function finishWithRouterCleanup(routerCleanup, callback) {
20
+ try {
21
+ return await callback();
22
+ } finally{
23
+ if (!routerCleanup.deferred) await routerCleanup.run();
24
+ }
25
+ }
11
26
  function createRouterCleanup(runtimeContext, onError) {
12
27
  let deferred = false;
13
28
  let finished = false;
@@ -63,4 +78,4 @@ function createRouterCleanup(runtimeContext, onError) {
63
78
  deferUntilBodyDone
64
79
  };
65
80
  }
66
- export { ROUTER_CLEANUP_ERROR, createRouterCleanup, withRouterCleanup };
81
+ export { ROUTER_CLEANUP_ERROR, createRouterCleanup, finishWithRouterCleanup, runWithRouterCleanupOnError, withRouterCleanup };
@@ -42,6 +42,10 @@ function getMatchedRouteAssets(runtimeContext, routeManifest) {
42
42
  const matchedRouteIds = getRouterMatchedRouteIds(runtimeContext) ?? [];
43
43
  return matchedRouteIds.flatMap((routeId)=>routeAssets[routeId]?.assets ?? []);
44
44
  }
45
+ function getMatchedRouteChunks(runtimeContext, routeManifest, routeAssetToChunk) {
46
+ if (!routeManifest) return [];
47
+ return getMatchedRouteAssets(runtimeContext, routeManifest).map(routeAssetToChunk);
48
+ }
45
49
  const orderHydrationScriptChunks = ({ asyncEntryChunks, collectedChunks, matchedRouteChunks, entryName })=>{
46
50
  const asyncEntryScriptChunks = [];
47
51
  const asyncEntryDependencyChunks = [];
@@ -72,13 +76,12 @@ function createRouteHydrationScriptTags(runtimeContext, entryName, options = {})
72
76
  const { nonce, template } = options;
73
77
  const routeAssets = getRouteAssets(runtimeContext);
74
78
  if (void 0 === routeAssets) return '';
75
- const matchedRouteIds = getRouterMatchedRouteIds(runtimeContext) ?? [];
76
- const assetEntries = [
77
- ...matchedRouteIds.map((routeId)=>routeAssets[routeId]),
78
- routeAssets[`async-${entryName}`]
79
- ].filter((entry)=>void 0 !== entry);
80
- const jsAssets = Array.from(new Set(assetEntries.flatMap((entry)=>(entry.assets ?? []).filter((asset)=>asset.endsWith('.js')))));
79
+ const existingScriptSrcs = void 0 === template ? new Set() : new Set(getScriptTags(template).map(({ src })=>src));
80
+ const jsAssets = Array.from(new Set([
81
+ ...getMatchedRouteAssets(runtimeContext),
82
+ ...routeAssets[`async-${entryName}`]?.assets ?? []
83
+ ].filter((asset)=>asset.endsWith('.js'))));
81
84
  const nonceAttr = void 0 === nonce || '' === nonce ? '' : ` nonce="${nonce}"`;
82
- return jsAssets.filter((asset)=>template?.includes(asset) !== true).map((asset)=>`<script src=${asset}${nonceAttr}></script>`).join(' ');
85
+ return jsAssets.filter((asset)=>!existingScriptSrcs.has(asset)).map((asset)=>`<script src=${asset}${nonceAttr}></script>`).join(' ');
83
86
  }
84
- export { createRouteHydrationScriptTags, getMatchedRouteAssets, injectBeforeHydrationEntryScript, orderHydrationScriptChunks, replaceChunkJsPlaceholder };
87
+ export { createRouteHydrationScriptTags, getMatchedRouteAssets, getMatchedRouteChunks, injectBeforeHydrationEntryScript, orderHydrationScriptChunks, replaceChunkJsPlaceholder };
@@ -1,6 +1,6 @@
1
1
  import { ChunkExtractor } from "@loadable/server";
2
2
  import { createFederatedCssLinks } from "../federatedCss.mjs";
3
- import { getMatchedRouteAssets, orderHydrationScriptChunks } from "../scriptOrder.mjs";
3
+ import { getMatchedRouteChunks, orderHydrationScriptChunks } from "../scriptOrder.mjs";
4
4
  import { attributesToString, checkIsNode, hasStylesheetLink } from "../utils.mjs";
5
5
  const extname = (uri)=>{
6
6
  if ('string' != typeof uri || !uri.includes('.')) return '';
@@ -28,11 +28,6 @@ const readAsset = async (chunk)=>{
28
28
  return fs.readFile(filepath, 'utf-8');
29
29
  };
30
30
  class LoadableCollector {
31
- getMatchedRouteChunks() {
32
- const { routeManifest, runtimeContext } = this.options;
33
- if (!routeManifest) return [];
34
- return getMatchedRouteAssets(runtimeContext, routeManifest).map(routeAssetToChunk);
35
- }
36
31
  collect(comopnent) {
37
32
  const { stats, entryName } = this.options;
38
33
  if (!stats) return comopnent;
@@ -54,7 +49,7 @@ class LoadableCollector {
54
49
  ]));
55
50
  } catch (e) {}
56
51
  const collectedChunks = extractor ? extractor.getChunkAssets(extractor.chunks) : [];
57
- const matchedRouteChunks = this.getMatchedRouteChunks();
52
+ const matchedRouteChunks = getMatchedRouteChunks(options.runtimeContext, options.routeManifest, routeAssetToChunk);
58
53
  const orderedScriptChunks = orderHydrationScriptChunks({
59
54
  asyncEntryChunks: asyncChunks,
60
55
  collectedChunks,
@@ -132,4 +127,4 @@ class LoadableCollector {
132
127
  this.options = options;
133
128
  }
134
129
  }
135
- export { LoadableCollector, orderHydrationScriptChunks };
130
+ export { LoadableCollector };
@@ -1,5 +1,6 @@
1
1
  export { isBrowser } from "./common.mjs";
2
- export { RuntimeContext, defineRuntimeConfig, useRuntimeContext } from "./core/index.mjs";
2
+ export { defineRuntimeConfig } from "./core/config.mjs";
3
3
  export { getMonitors } from "./core/context/monitors/index.mjs";
4
+ export { RuntimeContext, useRuntimeContext } from "./core/context/public.mjs";
4
5
  export { getRequest } from "./core/context/request/index.mjs";
5
6
  export { redirect, setHeaders, setStatus } from "./core/context/response/index.mjs";
@@ -182,7 +182,8 @@ const fileSystemRoutes = async ({ metaName, routes, ssrMode, nestedRoutesEntry,
182
182
  component = `component_${components.length - 1}`;
183
183
  }
184
184
  const isClientComponent = 'nested' === route.type && Boolean(route._component) && Boolean(srcDirectory) && Boolean(internalSrcAlias) && await hasUseClientDirective(route._component, srcDirectory, internalSrcAlias);
185
- const shouldIncludeClientBundle = !isRscClientBundle || isClientComponent;
185
+ const shouldHydrateRscRoute = isRscClientBundle && !isClientComponent && !route.loader && !route.data;
186
+ const shouldIncludeClientBundle = !isRscClientBundle || isClientComponent || shouldHydrateRscRoute;
186
187
  const finalRoute = {
187
188
  ...route,
188
189
  loading,
@@ -285,7 +286,7 @@ const fileSystemRoutes = async ({ metaName, routes, ssrMode, nestedRoutesEntry,
285
286
  ${importLazyCode}
286
287
  ${!isRscClientBundle ? importComponentsCode : ''}
287
288
  ${importRuntimeRouterCode}
288
- ${!isRscClientBundle ? rootLayoutCode : ''}
289
+ ${rootLayoutCode}
289
290
  ${importLoadingCode}
290
291
  ${importErrorComponentsCode}
291
292
  ${importLoadersCode}
@@ -1,8 +1,9 @@
1
1
  import { createSyncHook } from "@modern-js/plugin";
2
- const modifyRoutes = createSyncHook();
3
- const onBeforeCreateRoutes = createSyncHook();
4
- const onBeforeCreateRouter = createSyncHook();
5
- const onAfterCreateRouter = createSyncHook();
6
- const onBeforeHydrateRouter = createSyncHook();
7
- const onAfterHydrateRouter = createSyncHook();
2
+ const createRouterSyncHook = ()=>createSyncHook();
3
+ const modifyRoutes = createRouterSyncHook();
4
+ const onBeforeCreateRoutes = createRouterSyncHook();
5
+ const onBeforeCreateRouter = createRouterSyncHook();
6
+ const onAfterCreateRouter = createRouterSyncHook();
7
+ const onBeforeHydrateRouter = createRouterSyncHook();
8
+ const onAfterHydrateRouter = createRouterSyncHook();
8
9
  export { modifyRoutes, onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreateRouter, onBeforeCreateRoutes, onBeforeHydrateRouter };
@@ -40,7 +40,7 @@ function registerRouterProvider(name, factory, options = {}) {
40
40
  }
41
41
  function resolveRouterProvider(framework, options = {}) {
42
42
  const registry = getRegistry();
43
- const name = framework || registry.defaultProvider || options.localDefault?.name;
43
+ const name = framework || options.localDefault?.name || registry.defaultProvider;
44
44
  if (void 0 === name) throw new Error('[@modern-js/runtime] No default router provider is registered. This is a bug in the runtime setup.');
45
45
  if (void 0 !== options.localDefault && name === options.localDefault.name) return options.localDefault.factory;
46
46
  const factory = registry.providers.get(name);
@@ -86,14 +86,13 @@ const ssrBuilderPlugin = (modernAPI, outputModule, exportLoadablePath)=>({
86
86
  const hasServerRendering = hasServerRenderingConfig(userConfig);
87
87
  const hasModuleFederationRuntimeMarker = hasServerRendering && shouldUseModuleFederationNodeOutput(config);
88
88
  const hasExplicitMfSsrFlag = isModuleFederationAppSSREnabled(userConfig);
89
- const isCloudflareWorkerSSR = 'workerSSR' === name && userConfig.deploy?.target === 'cloudflare';
90
89
  const requireExplicitMfSsrFlag = 'true' === process.env.MODERN_MF_APP_SSR_REQUIRE_EXPLICIT;
91
90
  if (hasServerRendering && hasModuleFederationRuntimeMarker && !hasExplicitMfSsrFlag) {
92
91
  const warningMessage = '[modernjs][mf-ssr] Module Federation SSR was auto-detected from runtime markers. Set server.ssr.moduleFederationAppSSR=true explicitly in host and remotes to avoid heuristic drift.';
93
92
  if (requireExplicitMfSsrFlag) throw new Error(`${warningMessage} (enforced by MODERN_MF_APP_SSR_REQUIRE_EXPLICIT=true)`);
94
93
  console.warn(warningMessage);
95
94
  }
96
- const isModuleFederationAppSSR = hasServerRendering && hasExplicitMfSsrFlag && !isCloudflareWorkerSSR;
95
+ const isModuleFederationAppSSR = hasServerRendering && hasExplicitMfSsrFlag;
97
96
  const ssrEnv = userConfig.deploy?.worker?.ssr || userConfig.server?.rsc ? 'edge' : 'node';
98
97
  const appContext = modernAPI.getAppContext();
99
98
  const { appDirectory, entrypoints } = appContext;
@@ -0,0 +1,6 @@
1
+ import "node:module";
2
+ import { createContext, useContext } from "react";
3
+ const RuntimeContext = createContext({});
4
+ const ReactRuntimeContext = RuntimeContext;
5
+ const useRuntimeContext = ()=>useContext(RuntimeContext);
6
+ export { ReactRuntimeContext, RuntimeContext, useRuntimeContext };
@@ -1,9 +1,7 @@
1
1
  import "node:module";
2
- import { ROUTE_MANIFEST } from "@modern-js/utils/universal/constants";
3
- import { createContext, useContext } from "react";
2
+ import { createContext } from "react";
3
+ const ROUTE_MANIFEST = '_MODERNJS_ROUTE_MANIFEST';
4
4
  const InternalRuntimeContext = createContext({});
5
- const RuntimeContext = createContext({});
6
- const ReactRuntimeContext = RuntimeContext;
7
5
  const getInitialContext = (isBrowser = true, routeManifest)=>{
8
6
  const requestContext = {
9
7
  request: {},
@@ -16,5 +14,5 @@ const getInitialContext = (isBrowser = true, routeManifest)=>{
16
14
  context: requestContext
17
15
  };
18
16
  };
19
- const useRuntimeContext = ()=>useContext(RuntimeContext);
20
- export { InternalRuntimeContext, ReactRuntimeContext, RuntimeContext, getInitialContext, useRuntimeContext };
17
+ export { ReactRuntimeContext, RuntimeContext, useRuntimeContext } from "./public.mjs";
18
+ export { InternalRuntimeContext, getInitialContext };
@@ -1,9 +1,45 @@
1
1
  import "node:module";
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import { HelmetProvider } from "react-helmet-async";
4
- import { InternalRuntimeContext, RuntimeContext } from "../context/index.mjs";
4
+ import { InternalRuntimeContext, RuntimeContext, getGlobalEnableRsc } from "../context/index.mjs";
5
5
  import { stripRuntimeContextExtensions } from "../context/extensions.mjs";
6
6
  import { ensureHelmetContext } from "../context/helmetContext.mjs";
7
+ function toRscSafeRecord(value) {
8
+ if (!value || 'object' != typeof value) return value;
9
+ return Object.fromEntries(Object.entries(value));
10
+ }
11
+ function createRscSafeSsrContext(ssrContext) {
12
+ if (!ssrContext) return ssrContext;
13
+ const { request } = ssrContext;
14
+ return {
15
+ nonce: ssrContext.nonce,
16
+ useJsonScript: ssrContext.useJsonScript,
17
+ htmlModifiers: [],
18
+ baseUrl: ssrContext.baseUrl,
19
+ request: request ? {
20
+ url: request.url,
21
+ userAgent: request.userAgent,
22
+ cookie: request.cookie,
23
+ pathname: request.pathname,
24
+ query: toRscSafeRecord(request.query),
25
+ params: toRscSafeRecord(request.params),
26
+ headers: toRscSafeRecord(request.headers),
27
+ host: request.host,
28
+ referer: request.referer
29
+ } : request,
30
+ mode: ssrContext.mode,
31
+ loaderFailureMode: ssrContext.loaderFailureMode
32
+ };
33
+ }
34
+ function createRscSafeRequestContext(ssrContext) {
35
+ const safeSsrContext = createRscSafeSsrContext(ssrContext);
36
+ return {
37
+ request: safeSsrContext?.request || {},
38
+ response: {
39
+ locals: ssrContext?.response?.locals || {}
40
+ }
41
+ };
42
+ }
7
43
  function wrapRuntimeContextProvider(App, contextValue) {
8
44
  const { isBrowser, initialData, routes, context, routeManifest, routerContext, unstable_getBlockNavState, ssrContext, _internalContext, _internalRouterBaseName, ...rest } = contextValue;
9
45
  const internalContextValue = contextValue;
@@ -16,6 +52,27 @@ function wrapRuntimeContextProvider(App, contextValue) {
16
52
  ...rest
17
53
  };
18
54
  stripRuntimeContextExtensions(runtimeContextValue);
55
+ if (getGlobalEnableRsc() && !isBrowser) {
56
+ const rscSafeRequestContext = createRscSafeRequestContext(ssrContext);
57
+ const rscInternalContextValue = {
58
+ ...internalContextValue,
59
+ context: rscSafeRequestContext,
60
+ requestContext: rscSafeRequestContext,
61
+ ssrContext: createRscSafeSsrContext(ssrContext)
62
+ };
63
+ const rscRuntimeContextValue = {
64
+ ...runtimeContextValue,
65
+ context: rscSafeRequestContext,
66
+ requestContext: rscSafeRequestContext
67
+ };
68
+ return /*#__PURE__*/ jsx(InternalRuntimeContext.Provider, {
69
+ value: rscInternalContextValue,
70
+ children: /*#__PURE__*/ jsx(RuntimeContext.Provider, {
71
+ value: rscRuntimeContextValue,
72
+ children: App
73
+ })
74
+ });
75
+ }
19
76
  return /*#__PURE__*/ jsx(InternalRuntimeContext.Provider, {
20
77
  value: internalContextValue,
21
78
  children: /*#__PURE__*/ jsx(RuntimeContext.Provider, {
@@ -1,13 +1,17 @@
1
1
  import "node:module";
2
2
  import { attributesToString } from "./utils.mjs";
3
+ const escapeHtmlAttribute = (value)=>value.replaceAll('&', '&amp;').replaceAll('"', '&quot;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
3
4
  const createFederatedCssLinks = (assets, options)=>{
4
5
  if (void 0 === assets || 0 === assets.length) return '';
5
6
  const seen = new Set(options.existingAssets || []);
6
7
  const attributes = attributesToString(options.attributes || {});
7
8
  const links = [];
8
- for (const asset of assets)if (!('' === asset || seen.has(asset) || options.template.includes(asset))) {
9
- seen.add(asset);
10
- links.push(`<link${attributes} href="${asset}" rel="stylesheet" />`);
9
+ for (const asset of assets){
10
+ const href = escapeHtmlAttribute(asset);
11
+ if (!('' === asset || seen.has(asset) || options.template.includes(asset) || options.template.includes(href))) {
12
+ seen.add(asset);
13
+ links.push(`<link${attributes} href="${href}" rel="stylesheet" />`);
14
+ }
11
15
  }
12
16
  return links.join('');
13
17
  };