@bleedingdev/modern-js-renderer-solid 0.0.0-stage → 3.9.3-ultramodern.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (297) hide show
  1. package/dist/esm/actions.js +283 -0
  2. package/dist/esm/application-router.js +25 -0
  3. package/dist/esm/client.js +49 -0
  4. package/dist/esm/entry-application.js +60 -0
  5. package/dist/esm/entry-client.js +23 -0
  6. package/dist/esm/federation-context.js +25 -0
  7. package/dist/esm/federation-ssr.js +84 -0
  8. package/dist/esm/federation.js +83 -0
  9. package/dist/esm/i18n/I18nProvider.js +33 -0
  10. package/dist/esm/i18n/LocalizedLink.js +74 -0
  11. package/dist/esm/i18n/context.js +3 -0
  12. package/dist/esm/i18n/index.js +8 -0
  13. package/dist/esm/i18n/languageSync.js +50 -0
  14. package/dist/esm/i18n/types.js +0 -0
  15. package/dist/esm/i18n/useI18n.js +74 -0
  16. package/dist/esm/index.js +58 -0
  17. package/dist/esm/manifest.js +150 -0
  18. package/dist/esm/route-completion.js +150 -0
  19. package/dist/esm/route-data-error.js +108 -0
  20. package/dist/esm/router-binding/Asset.js +201 -0
  21. package/dist/esm/router-binding/CatchBoundary.js +94 -0
  22. package/dist/esm/router-binding/ClientOnly.js +26 -0
  23. package/dist/esm/router-binding/HeadContent.js +8 -0
  24. package/dist/esm/router-binding/LICENSE +21 -0
  25. package/dist/esm/router-binding/Match.js +319 -0
  26. package/dist/esm/router-binding/Matches.js +117 -0
  27. package/dist/esm/router-binding/PROVENANCE.json +94 -0
  28. package/dist/esm/router-binding/RouterProvider.js +51 -0
  29. package/dist/esm/router-binding/SafeFragment.js +5 -0
  30. package/dist/esm/router-binding/ScriptOnce.js +15 -0
  31. package/dist/esm/router-binding/Scripts.js +30 -0
  32. package/dist/esm/router-binding/ScrollRestoration.js +16 -0
  33. package/dist/esm/router-binding/Transitioner.js +103 -0
  34. package/dist/esm/router-binding/awaited.js +48 -0
  35. package/dist/esm/router-binding/fileRoute.js +83 -0
  36. package/dist/esm/router-binding/flightData.js +23 -0
  37. package/dist/esm/router-binding/headContentUtils.js +162 -0
  38. package/dist/esm/router-binding/history.js +0 -0
  39. package/dist/esm/router-binding/index.js +35 -0
  40. package/dist/esm/router-binding/lazyRouteComponent.js +30 -0
  41. package/dist/esm/router-binding/link.js +389 -0
  42. package/dist/esm/router-binding/matchContext.js +7 -0
  43. package/dist/esm/router-binding/nonRouteComponentContext.js +13 -0
  44. package/dist/esm/router-binding/not-found.js +35 -0
  45. package/dist/esm/router-binding/publicMatchData.js +447 -0
  46. package/dist/esm/router-binding/registryTransfer.js +210 -0
  47. package/dist/esm/router-binding/renderRouteNotFound.js +23 -0
  48. package/dist/esm/router-binding/route.js +143 -0
  49. package/dist/esm/router-binding/router.js +13 -0
  50. package/dist/esm/router-binding/routerContext.js +3 -0
  51. package/dist/esm/router-binding/routerStores.js +50 -0
  52. package/dist/esm/router-binding/scroll-restoration.js +13 -0
  53. package/dist/esm/router-binding/typePrimitives.js +0 -0
  54. package/dist/esm/router-binding/useBlocker.js +152 -0
  55. package/dist/esm/router-binding/useCanGoBack.js +7 -0
  56. package/dist/esm/router-binding/useLoaderData.js +9 -0
  57. package/dist/esm/router-binding/useLoaderDeps.js +8 -0
  58. package/dist/esm/router-binding/useLocation.js +14 -0
  59. package/dist/esm/router-binding/useMatch.js +28 -0
  60. package/dist/esm/router-binding/useNavigate.js +19 -0
  61. package/dist/esm/router-binding/useParams.js +13 -0
  62. package/dist/esm/router-binding/useRouteContext.js +8 -0
  63. package/dist/esm/router-binding/useRouter.js +10 -0
  64. package/dist/esm/router-binding/useRouterState.js +19 -0
  65. package/dist/esm/router-binding/useSearch.js +13 -0
  66. package/dist/esm/router-binding/utils.js +23 -0
  67. package/dist/esm/router.js +187 -0
  68. package/dist/esm-node/actions.js +263 -0
  69. package/dist/esm-node/application-router.js +25 -0
  70. package/dist/esm-node/client.js +49 -0
  71. package/dist/esm-node/entry-application.js +110 -0
  72. package/dist/esm-node/entry-client.js +23 -0
  73. package/dist/esm-node/entry-server.js +47 -0
  74. package/dist/esm-node/federation-context.js +25 -0
  75. package/dist/esm-node/federation-ssr.js +84 -0
  76. package/dist/esm-node/federation.js +83 -0
  77. package/dist/esm-node/i18n/I18nProvider.js +42 -0
  78. package/dist/esm-node/i18n/LocalizedLink.js +142 -0
  79. package/dist/esm-node/i18n/context.js +3 -0
  80. package/dist/esm-node/i18n/index.js +8 -0
  81. package/dist/esm-node/i18n/languageSync.js +50 -0
  82. package/dist/esm-node/i18n/types.js +1 -0
  83. package/dist/esm-node/i18n/useI18n.js +74 -0
  84. package/dist/esm-node/index.js +58 -0
  85. package/dist/esm-node/manifest.js +150 -0
  86. package/dist/esm-node/native-promise-serialization.js +113 -0
  87. package/dist/esm-node/plugin/compiler.js +209 -0
  88. package/dist/esm-node/plugin/create.js +235 -0
  89. package/dist/esm-node/plugin/index.js +73 -0
  90. package/dist/esm-node/plugin/profile.js +52 -0
  91. package/dist/esm-node/route-completion.js +160 -0
  92. package/dist/esm-node/route-data-error.js +108 -0
  93. package/dist/esm-node/router-binding/Asset.js +171 -0
  94. package/dist/esm-node/router-binding/CatchBoundary.js +136 -0
  95. package/dist/esm-node/router-binding/ClientOnly.js +46 -0
  96. package/dist/esm-node/router-binding/HeadContent.js +8 -0
  97. package/dist/esm-node/router-binding/LICENSE +21 -0
  98. package/dist/esm-node/router-binding/Match.js +660 -0
  99. package/dist/esm-node/router-binding/Matches.js +161 -0
  100. package/dist/esm-node/router-binding/PROVENANCE.json +94 -0
  101. package/dist/esm-node/router-binding/RouterProvider.js +87 -0
  102. package/dist/esm-node/router-binding/SafeFragment.js +5 -0
  103. package/dist/esm-node/router-binding/ScriptOnce.js +16 -0
  104. package/dist/esm-node/router-binding/Scripts.js +49 -0
  105. package/dist/esm-node/router-binding/ScrollRestoration.js +16 -0
  106. package/dist/esm-node/router-binding/Transitioner.js +103 -0
  107. package/dist/esm-node/router-binding/awaited.js +75 -0
  108. package/dist/esm-node/router-binding/fileRoute.js +83 -0
  109. package/dist/esm-node/router-binding/flightData.js +23 -0
  110. package/dist/esm-node/router-binding/headContentUtils.js +162 -0
  111. package/dist/esm-node/router-binding/history.js +1 -0
  112. package/dist/esm-node/router-binding/index.js +35 -0
  113. package/dist/esm-node/router-binding/lazyRouteComponent.js +30 -0
  114. package/dist/esm-node/router-binding/link.js +399 -0
  115. package/dist/esm-node/router-binding/matchContext.js +7 -0
  116. package/dist/esm-node/router-binding/nonRouteComponentContext.js +22 -0
  117. package/dist/esm-node/router-binding/not-found.js +49 -0
  118. package/dist/esm-node/router-binding/publicMatchData.js +447 -0
  119. package/dist/esm-node/router-binding/registryTransfer.js +210 -0
  120. package/dist/esm-node/router-binding/renderRouteNotFound.js +22 -0
  121. package/dist/esm-node/router-binding/route.js +162 -0
  122. package/dist/esm-node/router-binding/router.js +13 -0
  123. package/dist/esm-node/router-binding/routerContext.js +3 -0
  124. package/dist/esm-node/router-binding/routerStores.js +50 -0
  125. package/dist/esm-node/router-binding/scroll-restoration.js +12 -0
  126. package/dist/esm-node/router-binding/typePrimitives.js +1 -0
  127. package/dist/esm-node/router-binding/useBlocker.js +152 -0
  128. package/dist/esm-node/router-binding/useCanGoBack.js +7 -0
  129. package/dist/esm-node/router-binding/useLoaderData.js +9 -0
  130. package/dist/esm-node/router-binding/useLoaderDeps.js +8 -0
  131. package/dist/esm-node/router-binding/useLocation.js +14 -0
  132. package/dist/esm-node/router-binding/useMatch.js +28 -0
  133. package/dist/esm-node/router-binding/useNavigate.js +19 -0
  134. package/dist/esm-node/router-binding/useParams.js +13 -0
  135. package/dist/esm-node/router-binding/useRouteContext.js +8 -0
  136. package/dist/esm-node/router-binding/useRouter.js +10 -0
  137. package/dist/esm-node/router-binding/useRouterState.js +19 -0
  138. package/dist/esm-node/router-binding/useSearch.js +13 -0
  139. package/dist/esm-node/router-binding/utils.js +23 -0
  140. package/dist/esm-node/router.js +187 -0
  141. package/dist/esm-node/server.js +396 -0
  142. package/dist/types/actions.d.ts +45 -0
  143. package/dist/types/application-router.d.ts +5 -0
  144. package/dist/types/client.d.ts +23 -0
  145. package/dist/types/entry-application.d.ts +10 -0
  146. package/dist/types/entry-client.d.ts +5 -0
  147. package/dist/types/entry-server.d.ts +5 -0
  148. package/dist/types/federation-context.d.ts +16 -0
  149. package/dist/types/federation-ssr.d.ts +21 -0
  150. package/dist/types/federation.d.ts +30 -0
  151. package/dist/types/i18n/I18nProvider.d.ts +21 -0
  152. package/dist/types/i18n/LocalizedLink.d.ts +43 -0
  153. package/dist/types/i18n/context.d.ts +11 -0
  154. package/dist/types/i18n/index.d.ts +9 -0
  155. package/dist/types/i18n/languageSync.d.ts +31 -0
  156. package/dist/types/i18n/types.d.ts +33 -0
  157. package/dist/types/i18n/useI18n.d.ts +24 -0
  158. package/dist/types/index.d.ts +41 -0
  159. package/dist/types/manifest.d.ts +55 -0
  160. package/dist/types/native-promise-serialization.d.ts +9 -0
  161. package/dist/types/plugin/compiler.d.ts +6 -0
  162. package/dist/types/plugin/create.d.ts +21 -0
  163. package/dist/types/plugin/index.d.ts +3 -0
  164. package/dist/types/plugin/lazy-modules.d.cts +15 -0
  165. package/dist/types/plugin/preserve-import-loader.d.cts +3 -0
  166. package/dist/types/plugin/profile.d.ts +2 -0
  167. package/dist/types/plugin/solid-loader.d.cts +2 -0
  168. package/dist/types/plugin/svg-component-template.d.cts +6 -0
  169. package/dist/types/route-completion.d.ts +21 -0
  170. package/dist/types/route-data-error.d.ts +17 -0
  171. package/dist/types/router-binding/Asset.d.ts +3 -0
  172. package/dist/types/router-binding/CatchBoundary.d.ts +13 -0
  173. package/dist/types/router-binding/ClientOnly.d.ts +31 -0
  174. package/dist/types/router-binding/HeadContent.d.ts +13 -0
  175. package/dist/types/router-binding/Match.d.ts +6 -0
  176. package/dist/types/router-binding/Matches.d.ts +26 -0
  177. package/dist/types/router-binding/RouterProvider.d.ts +10 -0
  178. package/dist/types/router-binding/SafeFragment.d.ts +2 -0
  179. package/dist/types/router-binding/ScriptOnce.d.ts +6 -0
  180. package/dist/types/router-binding/Scripts.d.ts +2 -0
  181. package/dist/types/router-binding/ScrollRestoration.d.ts +14 -0
  182. package/dist/types/router-binding/Transitioner.d.ts +2 -0
  183. package/dist/types/router-binding/awaited.d.ts +11 -0
  184. package/dist/types/router-binding/fileRoute.d.ts +54 -0
  185. package/dist/types/router-binding/flightData.d.ts +38 -0
  186. package/dist/types/router-binding/headContentUtils.d.ts +15 -0
  187. package/dist/types/router-binding/history.d.ts +8 -0
  188. package/dist/types/router-binding/index.d.ts +45 -0
  189. package/dist/types/router-binding/lazyRouteComponent.d.ts +2 -0
  190. package/dist/types/router-binding/link.d.ts +43 -0
  191. package/dist/types/router-binding/matchContext.d.ts +7 -0
  192. package/dist/types/router-binding/nonRouteComponentContext.d.ts +5 -0
  193. package/dist/types/router-binding/not-found.d.ts +11 -0
  194. package/dist/types/router-binding/publicMatchData.d.ts +19 -0
  195. package/dist/types/router-binding/registryTransfer.d.ts +47 -0
  196. package/dist/types/router-binding/renderRouteNotFound.d.ts +11 -0
  197. package/dist/types/router-binding/route.d.ts +105 -0
  198. package/dist/types/router-binding/router.d.ts +70 -0
  199. package/dist/types/router-binding/routerContext.d.ts +3 -0
  200. package/dist/types/router-binding/routerStores.d.ts +2 -0
  201. package/dist/types/router-binding/scroll-restoration.d.ts +2 -0
  202. package/dist/types/router-binding/typePrimitives.d.ts +10 -0
  203. package/dist/types/router-binding/useBlocker.d.ts +70 -0
  204. package/dist/types/router-binding/useCanGoBack.d.ts +2 -0
  205. package/dist/types/router-binding/useLoaderData.d.ts +8 -0
  206. package/dist/types/router-binding/useLoaderDeps.d.ts +8 -0
  207. package/dist/types/router-binding/useLocation.d.ts +7 -0
  208. package/dist/types/router-binding/useMatch.d.ts +10 -0
  209. package/dist/types/router-binding/useNavigate.d.ts +5 -0
  210. package/dist/types/router-binding/useParams.d.ts +9 -0
  211. package/dist/types/router-binding/useRouteContext.d.ts +4 -0
  212. package/dist/types/router-binding/useRouter.d.ts +4 -0
  213. package/dist/types/router-binding/useRouterState.d.ts +8 -0
  214. package/dist/types/router-binding/useSearch.d.ts +9 -0
  215. package/dist/types/router-binding/utils.d.ts +25 -0
  216. package/dist/types/router.d.ts +31 -0
  217. package/dist/types/server.d.ts +47 -0
  218. package/package.json +123 -4
  219. package/src/actions.tsx +437 -0
  220. package/src/application-router.tsx +41 -0
  221. package/src/client.ts +106 -0
  222. package/src/entry-application.tsx +73 -0
  223. package/src/entry-client.tsx +48 -0
  224. package/src/entry-server.tsx +85 -0
  225. package/src/federation-context.ts +47 -0
  226. package/src/federation-ssr.ts +117 -0
  227. package/src/federation.tsx +193 -0
  228. package/src/i18n/I18nProvider.tsx +54 -0
  229. package/src/i18n/LocalizedLink.tsx +122 -0
  230. package/src/i18n/context.ts +13 -0
  231. package/src/i18n/index.ts +35 -0
  232. package/src/i18n/languageSync.ts +113 -0
  233. package/src/i18n/types.ts +35 -0
  234. package/src/i18n/useI18n.ts +121 -0
  235. package/src/index.ts +41 -0
  236. package/src/manifest.ts +337 -0
  237. package/src/native-promise-serialization.ts +167 -0
  238. package/src/plugin/compiler.ts +320 -0
  239. package/src/plugin/create.ts +270 -0
  240. package/src/plugin/index.ts +72 -0
  241. package/src/plugin/lazy-modules.cjs +146 -0
  242. package/src/plugin/preserve-import-loader.cjs +46 -0
  243. package/src/plugin/profile.ts +42 -0
  244. package/src/plugin/solid-loader.cjs +83 -0
  245. package/src/plugin/svg-component-template.cjs +19 -0
  246. package/src/route-completion.tsx +222 -0
  247. package/src/route-data-error.ts +175 -0
  248. package/src/router-binding/Asset.tsx +283 -0
  249. package/src/router-binding/CatchBoundary.tsx +130 -0
  250. package/src/router-binding/ClientOnly.tsx +76 -0
  251. package/src/router-binding/HeadContent.tsx +23 -0
  252. package/src/router-binding/LICENSE +21 -0
  253. package/src/router-binding/Match.tsx +397 -0
  254. package/src/router-binding/Matches.tsx +242 -0
  255. package/src/router-binding/PROVENANCE.json +94 -0
  256. package/src/router-binding/RouterProvider.tsx +129 -0
  257. package/src/router-binding/SafeFragment.tsx +5 -0
  258. package/src/router-binding/ScriptOnce.tsx +23 -0
  259. package/src/router-binding/Scripts.tsx +44 -0
  260. package/src/router-binding/ScrollRestoration.tsx +49 -0
  261. package/src/router-binding/Transitioner.tsx +172 -0
  262. package/src/router-binding/awaited.tsx +71 -0
  263. package/src/router-binding/fileRoute.ts +268 -0
  264. package/src/router-binding/flightData.ts +90 -0
  265. package/src/router-binding/headContentUtils.tsx +290 -0
  266. package/src/router-binding/history.ts +9 -0
  267. package/src/router-binding/index.tsx +318 -0
  268. package/src/router-binding/lazyRouteComponent.tsx +70 -0
  269. package/src/router-binding/link.tsx +865 -0
  270. package/src/router-binding/matchContext.tsx +15 -0
  271. package/src/router-binding/nonRouteComponentContext.tsx +23 -0
  272. package/src/router-binding/not-found.tsx +65 -0
  273. package/src/router-binding/publicMatchData.ts +674 -0
  274. package/src/router-binding/registryTransfer.ts +383 -0
  275. package/src/router-binding/renderRouteNotFound.tsx +53 -0
  276. package/src/router-binding/route.tsx +665 -0
  277. package/src/router-binding/router.ts +121 -0
  278. package/src/router-binding/routerContext.tsx +6 -0
  279. package/src/router-binding/routerStores.ts +85 -0
  280. package/src/router-binding/scroll-restoration.tsx +15 -0
  281. package/src/router-binding/typePrimitives.ts +74 -0
  282. package/src/router-binding/useBlocker.tsx +359 -0
  283. package/src/router-binding/useCanGoBack.ts +9 -0
  284. package/src/router-binding/useLoaderData.tsx +50 -0
  285. package/src/router-binding/useLoaderDeps.tsx +46 -0
  286. package/src/router-binding/useLocation.tsx +43 -0
  287. package/src/router-binding/useMatch.tsx +111 -0
  288. package/src/router-binding/useNavigate.tsx +43 -0
  289. package/src/router-binding/useParams.tsx +72 -0
  290. package/src/router-binding/useRouteContext.ts +30 -0
  291. package/src/router-binding/useRouter.tsx +17 -0
  292. package/src/router-binding/useRouterState.tsx +50 -0
  293. package/src/router-binding/useSearch.tsx +72 -0
  294. package/src/router-binding/utils.ts +59 -0
  295. package/src/router.ts +376 -0
  296. package/src/server.ts +645 -0
  297. package/README.md +0 -3
@@ -0,0 +1,48 @@
1
+ import type { LocalisedUrlsOption } from '@modern-js/i18n-runtime-extensions/paths';
2
+ import {
3
+ type NativeClientEntryOptions,
4
+ type NativeI18nInstance,
5
+ startNativeClientEntry,
6
+ } from '@modern-js/renderer-core/entry-client';
7
+ import { createNativeRouter } from '@modern-js/renderer-core/router';
8
+ import {
9
+ hydrateApplication,
10
+ mountApplication,
11
+ readSolidDocumentBootstrap,
12
+ } from './client';
13
+ import {
14
+ componentView,
15
+ routerView,
16
+ solidRouterFactory,
17
+ } from './entry-application';
18
+
19
+ export type SolidClientEntryOptions = NativeClientEntryOptions<
20
+ NativeI18nInstance,
21
+ LocalisedUrlsOption
22
+ >;
23
+
24
+ /** Start a generated Solid browser entry: mount, or hydrate the server document. */
25
+ export function startNativeClient(options: SolidClientEntryOptions): void {
26
+ startNativeClientEntry(options, {
27
+ readBootstrap: readSolidDocumentBootstrap,
28
+ createRouter: (application, routerOptions) =>
29
+ createNativeRouter(application, routerOptions, solidRouterFactory),
30
+ async prepareRouter(router, hydrating) {
31
+ // Hydration adopts the server's matches; a fresh mount loads its own.
32
+ if (!hydrating) await router.load();
33
+ },
34
+ async start({ root, bootstrap, load }) {
35
+ const view = await load();
36
+ return (bootstrap?.hydrating ? hydrateApplication : mountApplication)(
37
+ view.kind === 'component'
38
+ ? componentView(view.component, view.i18n)
39
+ : routerView(view.router, view.i18n),
40
+ root,
41
+ {
42
+ ...(bootstrap ? { renderId: bootstrap.documentId } : {}),
43
+ federation: options.federation,
44
+ },
45
+ );
46
+ },
47
+ });
48
+ }
@@ -0,0 +1,85 @@
1
+ import type { LocalisedUrlsOption } from '@modern-js/i18n-runtime-extensions/paths';
2
+ import {
3
+ dataMetadataToDocumentPolicy,
4
+ mergeDataResponseIntoResponse,
5
+ mergeDataResponseMetadata,
6
+ } from '@modern-js/renderer-core/data';
7
+ import {
8
+ createNativeServerEntry as createServerEntry,
9
+ type NativeI18nInstance,
10
+ type NativeServerEntry,
11
+ type NativeServerEntryOptions,
12
+ } from '@modern-js/renderer-core/entry-server';
13
+ import { createNativeRouter } from '@modern-js/renderer-core/router';
14
+ import {
15
+ componentView,
16
+ routerView,
17
+ solidRouterFactory,
18
+ } from './entry-application';
19
+ import { validateSolidModuleManifest } from './manifest';
20
+ import { getApplicationStatus, resolveApplicationRedirect } from './router';
21
+ import { prepareRouterMatchTransfer } from './router-binding/index';
22
+ import {
23
+ renderCSRDocument,
24
+ renderDocumentApplication,
25
+ respondApplicationResponse,
26
+ runApplicationRequest,
27
+ } from './server';
28
+
29
+ export type SolidServerEntryOptions = NativeServerEntryOptions<
30
+ NativeI18nInstance,
31
+ LocalisedUrlsOption
32
+ >;
33
+
34
+ /** The native transport handlers of a generated Solid server entry. */
35
+ export function createNativeServerEntry(
36
+ options: SolidServerEntryOptions,
37
+ ): NativeServerEntry {
38
+ return createServerEntry(options, {
39
+ document: (document, nativeManifest, identity) => ({
40
+ ...document,
41
+ renderId: document.documentId,
42
+ manifest: validateSolidModuleManifest(nativeManifest, identity).modules,
43
+ }),
44
+ run: (session, callback) => runApplicationRequest(session, callback),
45
+ respond: respondApplicationResponse,
46
+ createRouter: (application, routerOptions) =>
47
+ createNativeRouter(application, routerOptions, solidRouterFactory),
48
+ renderCSR: ({ session }, document) =>
49
+ renderCSRDocument({ session, document }),
50
+ renderComponent: ({ session }, component, document, i18n) =>
51
+ renderDocumentApplication({
52
+ session,
53
+ view: componentView(component, i18n),
54
+ document,
55
+ federation: options.federation,
56
+ }),
57
+ async renderRoutes({
58
+ context: { session },
59
+ router,
60
+ outcomes,
61
+ document,
62
+ forbiddenValues,
63
+ i18n,
64
+ }) {
65
+ await router.load();
66
+ prepareRouterMatchTransfer(router, session, forbiddenValues);
67
+ const metadata = mergeDataResponseMetadata(outcomes, {
68
+ status: getApplicationStatus(router),
69
+ });
70
+ const redirect = resolveApplicationRedirect(router);
71
+ if (redirect)
72
+ return respondApplicationResponse(
73
+ session,
74
+ mergeDataResponseIntoResponse(redirect, metadata),
75
+ );
76
+ session.resolveResponse(dataMetadataToDocumentPolicy(metadata));
77
+ return renderDocumentApplication({
78
+ session,
79
+ view: routerView(router, i18n),
80
+ document,
81
+ federation: options.federation,
82
+ });
83
+ },
84
+ });
85
+ }
@@ -0,0 +1,47 @@
1
+ import {
2
+ type FederationInstance,
3
+ getFederationHost,
4
+ type NativeFederationBinding,
5
+ } from '@modern-js/renderer-core/federation';
6
+ import type { JSX } from '@solidjs/web';
7
+ import { createComponent, createContext } from 'solid-js';
8
+
9
+ /** One application owns its host runtime; each response owns its render scope. */
10
+ export interface SolidFederationScope {
11
+ readonly instance: FederationInstance | undefined;
12
+ /** Server scopes: the host's client module that hydrates a remote. */
13
+ readonly hydrationModule?: string;
14
+ }
15
+
16
+ /** Outside a generated entry, remote ids have no host; custom loaders work. */
17
+ export const FederationContext = createContext<SolidFederationScope>(
18
+ Object.freeze({ instance: undefined }),
19
+ );
20
+
21
+ export function createFederationScope(
22
+ binding: NativeFederationBinding | undefined,
23
+ ): SolidFederationScope | undefined {
24
+ if (binding === undefined) return undefined;
25
+ const instance = getFederationHost(binding);
26
+ return binding.hydrationModule === undefined
27
+ ? { instance }
28
+ : { instance, hydrationModule: binding.hydrationModule };
29
+ }
30
+
31
+ /**
32
+ * Provide the scope to an application view. The server and the browser wrap
33
+ * the same view, so both render the provider at the same hydration position.
34
+ */
35
+ export function provideFederation(
36
+ scope: SolidFederationScope | undefined,
37
+ view: () => JSX.Element,
38
+ ): () => JSX.Element {
39
+ if (!scope) return view;
40
+ return () =>
41
+ createComponent(FederationContext, {
42
+ value: scope,
43
+ get children() {
44
+ return view();
45
+ },
46
+ });
47
+ }
@@ -0,0 +1,117 @@
1
+ import { remoteBrowserAssets } from '@modern-js/renderer-core/federation';
2
+ import type { SolidFederationScope } from './federation-context';
3
+
4
+ /**
5
+ * The server half of same-renderer Module Federation. federatedComponent()
6
+ * names each remote it server-rendered by an asset key, and the response's
7
+ * document resolves that key through the same host's federation scope.
8
+ */
9
+
10
+ /** Asset keys for server-rendered remotes, e.g. `ultramodern-federation:remote/Widget`. */
11
+ export const FEDERATED_ASSET_PREFIX = 'ultramodern-federation:';
12
+
13
+ /** The host's server state for a remote: its browser assets and hydration module. */
14
+ export function federatedServerAssets(
15
+ scope: SolidFederationScope | undefined,
16
+ id: string,
17
+ ) {
18
+ const instance = scope?.instance;
19
+ const assets = instance && remoteBrowserAssets(instance, id);
20
+ if (!assets || !scope?.hydrationModule || !instance.name) return undefined;
21
+ // The browser module finds this host by name among the realm's instances.
22
+ const hydration = `${scope.hydrationModule}?id=${encodeURIComponent(id)}&host=${encodeURIComponent(instance.name)}`;
23
+ return { hydration, assets };
24
+ }
25
+
26
+ export function federatedAssetKey(id: string): string {
27
+ return `${FEDERATED_ASSET_PREFIX}${id}`;
28
+ }
29
+
30
+ interface ResolvedDocumentAssets {
31
+ js: string[];
32
+ css: string[];
33
+ preloads?: Record<string, unknown>[];
34
+ }
35
+
36
+ interface StaticAssetChunk {
37
+ file: string;
38
+ css?: string[];
39
+ imports?: string[];
40
+ preloads?: Record<string, unknown>[];
41
+ }
42
+
43
+ /** Solid's own asset path join: absolute URLs pass through. */
44
+ function joinAssetPath(base: unknown, file: string): string {
45
+ if (/^(?:[a-z][a-z0-9+.-]*:)?\/\//iu.test(file)) return file;
46
+ let prefix = typeof base === 'string' && base ? base : '/';
47
+ if (!prefix.endsWith('/')) prefix += '/';
48
+ return prefix + (file.startsWith('/') ? file.slice(1) : file);
49
+ }
50
+
51
+ /** Solid's static manifest walk, so a resolver can extend a static manifest. */
52
+ function resolveStaticAssets(
53
+ manifest: Record<string, unknown>,
54
+ key: string,
55
+ ): ResolvedDocumentAssets | null {
56
+ if (key === '_base' || !Object.hasOwn(manifest, key)) return null;
57
+ const base = manifest._base;
58
+ const js: string[] = [];
59
+ const css: string[] = [];
60
+ let preloads: Record<string, unknown>[] | undefined;
61
+ const visited = new Set<string>();
62
+ const walk = (name: string) => {
63
+ if (visited.has(name) || name === '_base') return;
64
+ visited.add(name);
65
+ const chunk = manifest[name] as StaticAssetChunk | undefined;
66
+ if (!chunk) return;
67
+ js.push(joinAssetPath(base, chunk.file));
68
+ for (const file of chunk.css ?? []) css.push(joinAssetPath(base, file));
69
+ for (const link of chunk.preloads ?? []) {
70
+ const href = typeof link.href === 'string' && link.href;
71
+ if (!href && typeof link.imagesrcset !== 'string') continue;
72
+ preloads ??= [];
73
+ if (href) preloads.push({ ...link, href: joinAssetPath(base, href) });
74
+ else {
75
+ const { href: _ignored, ...rest } = link;
76
+ preloads.push(rest);
77
+ }
78
+ }
79
+ for (const dependency of chunk.imports ?? []) walk(dependency);
80
+ };
81
+ walk(key);
82
+ return preloads ? { js, css, preloads } : { js, css };
83
+ }
84
+
85
+ /**
86
+ * Extend a static Solid asset manifest with the remotes this response
87
+ * server-rendered through its host. The first module is the host's hydration
88
+ * module for that remote, which Solid imports before hydrating the boundary
89
+ * that rendered it.
90
+ */
91
+ export function withFederatedAssets<Manifest>(
92
+ manifest: Manifest,
93
+ scope: SolidFederationScope | undefined,
94
+ ): Manifest {
95
+ if (
96
+ !scope?.hydrationModule ||
97
+ manifest === null ||
98
+ typeof manifest !== 'object' ||
99
+ typeof (manifest as { resolve?: unknown }).resolve === 'function'
100
+ )
101
+ return manifest;
102
+ const modules = manifest as Record<string, unknown>;
103
+ const resolve = (key: string): ResolvedDocumentAssets | null => {
104
+ if (!key.startsWith(FEDERATED_ASSET_PREFIX))
105
+ return resolveStaticAssets(modules, key);
106
+ const remote = federatedServerAssets(
107
+ scope,
108
+ key.slice(FEDERATED_ASSET_PREFIX.length),
109
+ );
110
+ if (!remote) return null;
111
+ return {
112
+ js: [joinAssetPath(modules._base, remote.hydration), ...remote.assets.js],
113
+ css: [...remote.assets.css],
114
+ };
115
+ };
116
+ return { resolve, resolveSync: resolve } as Manifest;
117
+ }
@@ -0,0 +1,193 @@
1
+ import { loadFederatedModule } from '@modern-js/renderer-core/federation';
2
+ import { isServer, type JSX } from '@solidjs/web';
3
+ import * as solid from 'solid-js';
4
+ import {
5
+ type Component,
6
+ createComponent,
7
+ createSignal,
8
+ Errored,
9
+ isHydrating,
10
+ Loading,
11
+ lazy,
12
+ onCleanup,
13
+ useContext,
14
+ } from 'solid-js';
15
+ import {
16
+ FederationContext,
17
+ type SolidFederationScope,
18
+ } from './federation-context';
19
+ import { federatedAssetKey, federatedServerAssets } from './federation-ssr';
20
+
21
+ /** The module shape a same-renderer Module Federation remote exposes. */
22
+ export interface FederatedModule<P extends object> {
23
+ readonly default: Component<P>;
24
+ }
25
+
26
+ export interface FederatedComponentOptions {
27
+ /** Rendered while the remote loads, and on the server when it cannot load. */
28
+ readonly fallback?: () => JSX.Element;
29
+ /**
30
+ * Milliseconds a remote load may take before it fails. Defaults to 3000 on
31
+ * the server, so an unavailable remote cannot hold the document; the
32
+ * browser waits without a limit unless one is given.
33
+ */
34
+ readonly timeout?: number;
35
+ }
36
+
37
+ const DEFAULT_SERVER_TIMEOUT = 3000;
38
+
39
+ function withTimeout<T>(
40
+ promise: Promise<T>,
41
+ timeout: number | undefined,
42
+ label: string,
43
+ ): Promise<T> {
44
+ if (timeout === undefined) return promise;
45
+ return new Promise<T>((resolve, reject) => {
46
+ const timer = setTimeout(
47
+ () =>
48
+ reject(
49
+ new Error(`Remote module ${label} did not load within ${timeout}ms`),
50
+ ),
51
+ timeout,
52
+ );
53
+ // A pending remote must not keep a server process alive.
54
+ (timer as { unref?: () => void }).unref?.();
55
+ promise.then(
56
+ value => {
57
+ clearTimeout(timer);
58
+ resolve(value);
59
+ },
60
+ error => {
61
+ clearTimeout(timer);
62
+ reject(error);
63
+ },
64
+ );
65
+ });
66
+ }
67
+
68
+ /**
69
+ * Run once the document has hydrated. A retry inside a hydration pass would
70
+ * look for server data the failed render never produced.
71
+ */
72
+ function afterHydration(callback: () => void): void {
73
+ // The hydration runtime installs this hook on Solid's shared config.
74
+ const { sharedConfig } = solid as unknown as {
75
+ sharedConfig?: { onHydrationEnd?: (callback: () => void) => void };
76
+ };
77
+ const onHydrationEnd = sharedConfig?.onHydrationEnd;
78
+ if (onHydrationEnd) onHydrationEnd(callback);
79
+ else queueMicrotask(callback);
80
+ }
81
+
82
+ /**
83
+ * Render a Solid component exposed by a Module Federation remote, by its
84
+ * `remote/Expose` id or a custom loader.
85
+ *
86
+ * A remote id loads through the federation instance of the application that
87
+ * renders it, so co-located hosts keep their own remotes. On the server it
88
+ * renders into the document with the remote's stylesheets and module
89
+ * preloads; the browser loads the same remote before hydrating it.
90
+ * A remote that fails or exceeds its timeout on the server renders the
91
+ * fallback, and the browser retries it after hydration. Custom loaders render
92
+ * in the browser only. Browser load and renderer-admission failures throw to
93
+ * the nearest `Errored` boundary.
94
+ */
95
+ export function federatedComponent<P extends object = Record<string, never>>(
96
+ remote: string | (() => Promise<FederatedModule<P>>),
97
+ options: FederatedComponentOptions = {},
98
+ ): Component<P> {
99
+ if (
100
+ typeof remote === 'string'
101
+ ? !/^[^/]+\/.+/u.test(remote)
102
+ : typeof remote !== 'function'
103
+ )
104
+ throw new TypeError(
105
+ "federatedComponent requires a remote id such as 'remote/Widget' or a loader",
106
+ );
107
+ if (
108
+ options.timeout !== undefined &&
109
+ (!Number.isFinite(options.timeout) || options.timeout <= 0)
110
+ )
111
+ throw new TypeError(
112
+ 'federatedComponent timeout must be a positive number of milliseconds',
113
+ );
114
+ const id = typeof remote === 'string' ? remote : undefined;
115
+ const timeout =
116
+ options.timeout ?? (isServer ? DEFAULT_SERVER_TIMEOUT : undefined);
117
+ const load = (scope: SolidFederationScope): Component<P> =>
118
+ lazy(async () => {
119
+ if (isServer && !id)
120
+ throw new Error(
121
+ 'A federatedComponent loader renders in the browser only; use a remote id to server-render it',
122
+ );
123
+ const module = await withTimeout(
124
+ Promise.resolve().then(() =>
125
+ id
126
+ ? loadFederatedModule<FederatedModule<P>>(scope.instance, id)
127
+ : (remote as () => Promise<never>)(),
128
+ ),
129
+ timeout,
130
+ id ?? 'loader',
131
+ );
132
+ const component = module?.default;
133
+ if (typeof component !== 'function')
134
+ throw new TypeError(
135
+ 'A federated Solid module must default-export a component',
136
+ );
137
+ if (!isServer || !id) return { default: component };
138
+ if (!federatedServerAssets(scope, id))
139
+ throw new Error(
140
+ `Cannot server-render ${id}: the host has no server federation state for its browser assets`,
141
+ );
142
+ // Solid resolves a loaded module's $$moduleUrl through the response's
143
+ // asset resolver: stylesheets, preloads and the hydration module.
144
+ return { default: component, $$moduleUrl: federatedAssetKey(id) };
145
+ }) as Component<P>;
146
+ // Solid's lazy caches its module. Scope the cache to one application or
147
+ // response, so two hosts rendering this component never share a remote.
148
+ const remotes = new WeakMap<SolidFederationScope, Component<P>>();
149
+ const fallback = (): JSX.Element => options.fallback?.();
150
+
151
+ return (props: P): JSX.Element => {
152
+ const scope = useContext(FederationContext);
153
+ const Remote = remotes.get(scope) ?? load(scope);
154
+ remotes.set(scope, Remote);
155
+ const remoteView = () => createComponent(Remote, props);
156
+ // After a server failure, the browser renders the remote afresh: like a
157
+ // client-only mount, its failures reach the application's boundaries.
158
+ const [retried, retry] = createSignal(false);
159
+ const recover = (error: () => unknown): JSX.Element => {
160
+ // A server failure renders the fallback and serializes the failure;
161
+ // hydration claims that fallback, then the browser retries the remote.
162
+ if (isServer) return fallback();
163
+ if (isHydrating()) {
164
+ let active = true;
165
+ onCleanup(() => {
166
+ active = false;
167
+ });
168
+ afterHydration(() => {
169
+ if (active) retry(true);
170
+ });
171
+ return fallback();
172
+ }
173
+ throw error();
174
+ };
175
+ // Loading owns the pending load. On the first render Errored sits inside
176
+ // it, so a load that fails after suspending still reaches it on the server.
177
+ return createComponent(Loading, {
178
+ get fallback() {
179
+ return fallback();
180
+ },
181
+ get children() {
182
+ return retried()
183
+ ? remoteView()
184
+ : createComponent(Errored, {
185
+ fallback: recover,
186
+ get children() {
187
+ return remoteView();
188
+ },
189
+ });
190
+ },
191
+ });
192
+ };
193
+ }
@@ -0,0 +1,54 @@
1
+ import type { JSX } from '@solidjs/web';
2
+ import * as Solid from 'solid-js';
3
+ import { I18nContext } from './context';
4
+ import type { I18nContextValue } from './types';
5
+
6
+ export interface I18nProviderProps extends Omit<I18nContextValue, 'language'> {
7
+ children: JSX.Element;
8
+ }
9
+
10
+ /**
11
+ * Provides a per-request i18next-shaped instance to the Solid tree. The
12
+ * instance must come from the caller (created with `cloneInstance` per
13
+ * request on the server, and the same instance reused on hydration) — this
14
+ * provider holds no module-level state of its own beyond the one reactive
15
+ * `language` signal it derives from `instance` and shares with every
16
+ * consumer through context (see `I18nContextValue.language`'s doc for why
17
+ * that single shared subscription matters, instead of each consumer
18
+ * subscribing to `languageChanged` independently).
19
+ *
20
+ * In Solid 2 a `Context` doubles as its own provider component: `value` is
21
+ * read reactively off `props` here, so an ancestor re-rendering this
22
+ * provider with a new `instance`/`languages`/`localisedUrls` propagates
23
+ * without needing a `.Provider` subcomponent.
24
+ */
25
+ export function I18nProvider(props: I18nProviderProps): JSX.Element {
26
+ const [language, setLanguage] = Solid.createSignal(props.instance.language);
27
+
28
+ Solid.createEffect(
29
+ () => props.instance,
30
+ instance => {
31
+ setLanguage(instance.language);
32
+ const handleLanguageChanged = (lng: string) => setLanguage(lng);
33
+ instance.on?.('languageChanged', handleLanguageChanged);
34
+ return () => {
35
+ instance.off?.('languageChanged', handleLanguageChanged);
36
+ };
37
+ },
38
+ );
39
+
40
+ const value: I18nContextValue = {
41
+ get instance() {
42
+ return props.instance;
43
+ },
44
+ get languages() {
45
+ return props.languages;
46
+ },
47
+ get localisedUrls() {
48
+ return props.localisedUrls;
49
+ },
50
+ language,
51
+ };
52
+
53
+ return <I18nContext value={value}>{props.children}</I18nContext>;
54
+ }
@@ -0,0 +1,122 @@
1
+ import {
2
+ canonicalPath,
3
+ configuredLanguage,
4
+ localizePath,
5
+ } from '@modern-js/i18n-runtime-extensions/paths';
6
+ import type { JSX } from '@solidjs/web';
7
+ import * as Solid from 'solid-js';
8
+ import {
9
+ Link,
10
+ type LinkComponentProps,
11
+ useLocation,
12
+ useRouter,
13
+ } from '../router';
14
+ import { I18nContext } from './context';
15
+
16
+ export interface LocalizedLinkProps {
17
+ /** A canonical, language-agnostic target, e.g. `/products/red-shoe`. */
18
+ to: string;
19
+ /** Localize for a specific language instead of the current one. */
20
+ language?: string;
21
+ children?: LinkComponentProps<'a'>['children'];
22
+ class?: string;
23
+ activeProps?: LinkComponentProps<'a'>['activeProps'];
24
+ inactiveProps?: LinkComponentProps<'a'>['inactiveProps'];
25
+ activeOptions?: LinkComponentProps<'a'>['activeOptions'];
26
+ replace?: boolean;
27
+ preload?: LinkComponentProps<'a'>['preload'];
28
+ [key: `data-${string}`]: unknown;
29
+ }
30
+
31
+ /**
32
+ * A thin wrapper over the native Solid router `Link` that localizes a
33
+ * canonical `to` for the current (or an explicit) language before handing it
34
+ * to the real link, the same contract as `@modern-js/plugin-i18n`'s React
35
+ * `Link`. It resolves the href itself via the shared pathname helpers, so it
36
+ * works with or without the router's i18n `rewrite`. Every link, including
37
+ * one to another language, is the native `Link`: navigation, preloading and
38
+ * active state stay with the router.
39
+ *
40
+ * Under the i18n `rewrite` the router matches canonical paths and localizes
41
+ * outgoing locations to the current language, so a link to another language
42
+ * routes to the canonical `to` and publishes the target language's URL as a
43
+ * route mask (the rewrite keeps a mask's explicit language). The entry's
44
+ * router language synchronization (`syncWithRouter`) switches the i18next
45
+ * instance from the navigated URL, as it does for history navigation.
46
+ *
47
+ * Props are forwarded explicitly (not via object-rest or a missing
48
+ * `splitProps`/`mergeProps` primitive — Solid 2 only exports `merge`) so
49
+ * each one is read directly from `props` in JSX position and keeps its
50
+ * per-key reactivity.
51
+ */
52
+ export function LocalizedLink(props: LocalizedLinkProps): JSX.Element {
53
+ const context = Solid.useContext(I18nContext);
54
+ const router = useRouter();
55
+ const rewrite = Boolean(router.options.rewrite);
56
+
57
+ // An explicit language must be configured: it becomes the locale prefix.
58
+ const targetLanguage = () =>
59
+ props.language === undefined
60
+ ? context.language()
61
+ : configuredLanguage(props.language, context.languages);
62
+ const href = Solid.createMemo(() =>
63
+ localizePath(props.to, targetLanguage(), {
64
+ languages: [...context.languages],
65
+ localisedUrls: context.localisedUrls,
66
+ }),
67
+ );
68
+ const crossLanguage = () => targetLanguage() !== context.language();
69
+ const masked = () => rewrite && crossLanguage();
70
+ // Declared `data-*` attributes reach the anchor through getters, so each
71
+ // keeps its own reactivity like the explicitly forwarded props.
72
+ const dataAttributes = Object.defineProperties(
73
+ {},
74
+ Object.fromEntries(
75
+ Object.keys(props)
76
+ .filter(key => key.startsWith('data-'))
77
+ .map(key => [
78
+ key,
79
+ {
80
+ enumerable: true,
81
+ get: () => props[key as `data-${string}`],
82
+ },
83
+ ]),
84
+ ),
85
+ );
86
+
87
+ return (
88
+ <Link
89
+ {...dataAttributes}
90
+ // A masked link routes to the canonical `to`. Otherwise `href` wins over
91
+ // `to` in router-core's `buildLocation` (it checks `dest.href` first);
92
+ // `to="."` only satisfies the type-level requirement that a `to` be
93
+ // present and is otherwise inert.
94
+ to={masked() ? props.to : '.'}
95
+ href={masked() ? undefined : href()}
96
+ mask={masked() ? { to: href() } : undefined}
97
+ hreflang={crossLanguage() ? targetLanguage() : undefined}
98
+ class={props.class}
99
+ activeProps={props.activeProps}
100
+ inactiveProps={props.inactiveProps}
101
+ activeOptions={props.activeOptions}
102
+ replace={props.replace}
103
+ preload={props.preload}
104
+ >
105
+ {props.children}
106
+ </Link>
107
+ );
108
+ }
109
+
110
+ /**
111
+ * Language-invariant active check: `to="/products"` is active whether the
112
+ * current location is `/en/products` or `/produkty` under a mapped locale.
113
+ */
114
+ export function useIsLocalizedActive(canonicalTarget: string): () => boolean {
115
+ const context = Solid.useContext(I18nContext);
116
+ const location = useLocation();
117
+ return () =>
118
+ canonicalPath(location().pathname, {
119
+ languages: [...context.languages],
120
+ localisedUrls: context.localisedUrls,
121
+ }) === canonicalTarget;
122
+ }
@@ -0,0 +1,13 @@
1
+ import * as Solid from 'solid-js';
2
+ import type { I18nContextValue } from './types';
3
+
4
+ export type { I18nContextValue, I18nInstanceLike } from './types';
5
+
6
+ /**
7
+ * Default-less form (Solid 2): `useContext` on this throws
8
+ * `ContextNotFoundError` when no `I18nProvider` is mounted, instead of
9
+ * silently reading a shared fallback instance across requests — exactly the
10
+ * failure mode a per-request i18n instance needs. The context object is
11
+ * itself the provider component: render `<I18nContext value={...}>`.
12
+ */
13
+ export const I18nContext = Solid.createContext<I18nContextValue>();