@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,35 @@
1
+ export {
2
+ createRequestLanguageRedirect,
3
+ detectRequestLanguage,
4
+ languageFromPathname,
5
+ type RequestLanguageDetection,
6
+ type ResolvedRequestLanguage,
7
+ type ResolveRequestLanguageOptions,
8
+ resolveRequestLanguage,
9
+ } from '@modern-js/i18n-runtime-extensions/request-language';
10
+ export {
11
+ createI18nSsrHandoffInlineData,
12
+ I18N_SSR_HANDOFF_ELEMENT_ID,
13
+ type I18nSsrHandoffPayload,
14
+ type I18nSsrHandoffResources,
15
+ readI18nSsrHandoff,
16
+ } from '@modern-js/i18n-runtime-extensions/ssrLanguageHandoff';
17
+ // Renderer-neutral pieces an application entry composes with these bindings.
18
+ export {
19
+ type CreateI18nUrlRewriteOptions,
20
+ createI18nUrlRewrite,
21
+ type I18nLocationRewrite,
22
+ } from '@modern-js/i18n-runtime-extensions/urlRewrite';
23
+ export { I18nContext } from './context';
24
+ export { I18nProvider, type I18nProviderProps } from './I18nProvider';
25
+ export {
26
+ LocalizedLink,
27
+ type LocalizedLinkProps,
28
+ useIsLocalizedActive,
29
+ } from './LocalizedLink';
30
+ export {
31
+ createLatestLanguageSync,
32
+ type SolidLatestLanguageSyncOptions,
33
+ } from './languageSync';
34
+ export type { I18nContextValue, I18nInstanceLike } from './types';
35
+ export { type UseI18nReturn, useI18n } from './useI18n';
@@ -0,0 +1,113 @@
1
+ import {
2
+ createLatestLanguageSyncBinding,
3
+ type LanguageSyncFailure,
4
+ type LanguageSyncPolicy,
5
+ } from '@modern-js/i18n-runtime-extensions/language-sync/controller';
6
+ import * as Solid from 'solid-js';
7
+
8
+ export interface SolidLatestLanguageSyncOptions<TTarget extends object> {
9
+ /** The sync target, e.g. the per-request i18next instance. Reactive. */
10
+ target: Solid.Accessor<TTarget | undefined>;
11
+ /** The language the URL (or other source of truth) currently wants. Reactive. */
12
+ desiredLanguage: Solid.Accessor<string | undefined>;
13
+ changeLanguage: (
14
+ target: TTarget,
15
+ language: string,
16
+ ) => PromiseLike<unknown> | unknown;
17
+ commitLanguage: (target: TTarget, language: string) => void;
18
+ /** @default () => true */
19
+ enabled?: Solid.Accessor<boolean>;
20
+ policy?: LanguageSyncPolicy;
21
+ readLanguage?: (target: TTarget) => string | undefined;
22
+ reportFailure?: (failure: LanguageSyncFailure) => void;
23
+ }
24
+
25
+ /**
26
+ * Solid binding over the fork's renderer-neutral `Coordinator`/`Binding` pair
27
+ * (`@modern-js/i18n-runtime-extensions`'s `createLatestLanguageSyncBinding`):
28
+ * the same "latest intent wins, retry with backoff, report terminal failure"
29
+ * policy the React `useLatestLanguageSync` hook drives.
30
+ *
31
+ * Solid 2's `createEffect` has a compute/effect split (`compute(prev)` is
32
+ * tracked, `effect(next)` runs imperatively and may return a cleanup run
33
+ * before the next invocation or on disposal) rather than Solid 1's
34
+ * single-phase `createEffect(fn)` — reactive reads happen in `compute`,
35
+ * subscriptions happen in `effect`, which returns their cleanup.
36
+ *
37
+ * Returns a function to request a language change (e.g. from a language
38
+ * switcher), independent of the reactive `desiredLanguage` source.
39
+ */
40
+ export function createLatestLanguageSync<TTarget extends object>(
41
+ options: SolidLatestLanguageSyncOptions<TTarget>,
42
+ ): (language: string) => void {
43
+ const binding = createLatestLanguageSyncBinding<TTarget>(options.policy);
44
+ const enabled = () => options.enabled?.() ?? true;
45
+
46
+ Solid.createEffect(
47
+ () => undefined,
48
+ () => {
49
+ binding.updateCallbacks({
50
+ changeLanguage: options.changeLanguage,
51
+ commitLanguage: options.commitLanguage,
52
+ readLanguage: options.readLanguage,
53
+ reportFailure: options.reportFailure,
54
+ });
55
+ },
56
+ );
57
+
58
+ Solid.createEffect(
59
+ () => (enabled() ? options.target() : undefined),
60
+ target => {
61
+ if (!target) {
62
+ return;
63
+ }
64
+ binding.activate(target);
65
+ return () => binding.deactivate();
66
+ },
67
+ );
68
+
69
+ type SyncTargetTuple<TTarget> = readonly [
70
+ target: TTarget | undefined,
71
+ desiredLanguage: string | undefined,
72
+ ];
73
+
74
+ Solid.createEffect(
75
+ (): SyncTargetTuple<TTarget> =>
76
+ enabled()
77
+ ? [options.target(), options.desiredLanguage()]
78
+ : [undefined, undefined],
79
+ ([target, desiredLanguage]) => {
80
+ if (!target || !desiredLanguage) {
81
+ binding.clearRequest();
82
+ return;
83
+ }
84
+ binding.request(desiredLanguage);
85
+ },
86
+ );
87
+
88
+ Solid.createEffect(
89
+ (): SyncTargetTuple<TTarget> =>
90
+ enabled() && typeof window !== 'undefined'
91
+ ? [options.target(), options.desiredLanguage()]
92
+ : [undefined, undefined],
93
+ ([target, desiredLanguage]) => {
94
+ if (!target || !desiredLanguage) {
95
+ return;
96
+ }
97
+ const retryCurrentIntent = () => binding.request(desiredLanguage);
98
+ const retryVisibleIntent = () => {
99
+ if (document.visibilityState === 'visible') {
100
+ retryCurrentIntent();
101
+ }
102
+ };
103
+ window.addEventListener('online', retryCurrentIntent);
104
+ document.addEventListener('visibilitychange', retryVisibleIntent);
105
+ return () => {
106
+ window.removeEventListener('online', retryCurrentIntent);
107
+ document.removeEventListener('visibilitychange', retryVisibleIntent);
108
+ };
109
+ },
110
+ );
111
+
112
+ return (language: string) => binding.request(language);
113
+ }
@@ -0,0 +1,35 @@
1
+ import type { LocalisedUrlsOption } from '@modern-js/i18n-runtime-extensions/paths';
2
+
3
+ /**
4
+ * The slice of an i18next-shaped instance this binding needs. Deliberately
5
+ * not `@modern-js/plugin-i18n`'s `I18nInstance`: that type is intentionally
6
+ * narrowed (no index signature, non-overloaded methods — see plugin-i18n's
7
+ * fork notes) and, more importantly, the plugin package's peer dependencies
8
+ * require React/ReactDOM, which a Solid-only application must never need to
9
+ * install. `on`/`off` are real i18next `EventEmitter` methods this interface
10
+ * adds back for the reactive `languageChanged` subscription.
11
+ */
12
+ export interface I18nInstanceLike {
13
+ language: string;
14
+ t(key: string | string[], options?: Record<string, unknown>): string;
15
+ changeLanguage?(lng?: string): Promise<unknown> | unknown;
16
+ on?(event: 'languageChanged', callback: (lng: string) => void): void;
17
+ off?(event: 'languageChanged', callback: (lng: string) => void): void;
18
+ /** SSR per-request isolation: the server clones a base instance per request. */
19
+ cloneInstance?(options?: Record<string, unknown>): I18nInstanceLike;
20
+ }
21
+
22
+ export interface I18nContextValue {
23
+ /** Per-request instance. Never a module-level singleton. */
24
+ instance: I18nInstanceLike;
25
+ languages: readonly string[];
26
+ localisedUrls?: LocalisedUrlsOption;
27
+ /**
28
+ * Reactive current language, owned by `I18nProvider`. Every consumer
29
+ * (`useI18n`, `LocalizedLink`) reads this single shared signal rather than
30
+ * each subscribing to `instance`'s `languageChanged` event independently —
31
+ * `instance.language` is a plain mutable field, not itself reactive, so
32
+ * reading it directly would silently never update a Solid computation.
33
+ */
34
+ language: () => string;
35
+ }
@@ -0,0 +1,121 @@
1
+ import {
2
+ configuredLanguage,
3
+ localizePath,
4
+ } from '@modern-js/i18n-runtime-extensions/paths';
5
+ import { languageFromPathname } from '@modern-js/i18n-runtime-extensions/request-language';
6
+ import * as Solid from 'solid-js';
7
+ import { useRouter } from '../router';
8
+ import { I18nContext } from './context';
9
+ import type { I18nInstanceLike } from './types';
10
+
11
+ export interface UseI18nReturn {
12
+ /** Reactive: re-evaluates when i18next fires `languageChanged`. */
13
+ t: I18nInstanceLike['t'];
14
+ /** Reactive current language accessor, shared with every other consumer. */
15
+ language: Solid.Accessor<string>;
16
+ languages: readonly string[];
17
+ /** The raw per-request instance, for advanced use (namespaces, `exists`, ...). */
18
+ instance: I18nInstanceLike;
19
+ /**
20
+ * Switches the i18next instance's language. Inside a router, also replaces
21
+ * the current URL with the localized path for the new language.
22
+ */
23
+ changeLanguage: (language: string) => Promise<void>;
24
+ }
25
+
26
+ /**
27
+ * Language switches per instance run one at a time: a switch that a newer
28
+ * one has superseded before it starts is skipped, so no two switches ever
29
+ * interleave their language loads and navigations.
30
+ */
31
+ const languageSwitches = new WeakMap<
32
+ object,
33
+ { latest: number; queue: Promise<void> }
34
+ >();
35
+
36
+ /**
37
+ * Must render under `I18nProvider`; `useContext` on the default-less
38
+ * `I18nContext` throws `ContextNotFoundError` otherwise, rather than
39
+ * silently reading a fallback instance. The reactive `language` signal lives
40
+ * on the provider (one `languageChanged` subscription per request, shared by
41
+ * every consumer) — this hook just reads it.
42
+ */
43
+ export function useI18n(): UseI18nReturn {
44
+ const context = Solid.useContext(I18nContext);
45
+ const { instance, languages, localisedUrls, language } = context;
46
+
47
+ const t: I18nInstanceLike['t'] = (key, options) => {
48
+ // Read the signal so every consumer of `t` re-renders on language change,
49
+ // even though `instance.t` itself is not reactive.
50
+ language();
51
+ return instance.t(key, options);
52
+ };
53
+
54
+ const router = useRouter({ warn: false });
55
+
56
+ const changeLanguage = (requested: string): Promise<void> => {
57
+ // i18next would fall back silently and the URL would gain an unknown
58
+ // segment. An unsupported request is rejected before it supersedes any
59
+ // queued switch.
60
+ let nextLanguage: string;
61
+ try {
62
+ nextLanguage = configuredLanguage(requested, languages);
63
+ } catch (error) {
64
+ return Promise.reject(error);
65
+ }
66
+ const state = languageSwitches.get(instance) ?? {
67
+ latest: 0,
68
+ queue: Promise.resolve(),
69
+ };
70
+ languageSwitches.set(instance, state);
71
+ const generation = ++state.latest;
72
+ const run = state.queue.then(async () => {
73
+ if (state.latest !== generation) return;
74
+ // Earlier switches have settled, so this is the language the current
75
+ // URL was committed with.
76
+ const committed = instance.language;
77
+ // The public URL carries the locale. Under the i18n URL rewrite the
78
+ // router's own location is canonical and has none.
79
+ const publicLanguage = () =>
80
+ router
81
+ ? languageFromPathname(
82
+ new URL(router.state.location.publicHref, 'http://localhost')
83
+ .pathname,
84
+ languages,
85
+ router.options.basepath,
86
+ )
87
+ : undefined;
88
+ // A failed, blocked or redirected switch leaves the page on some URL;
89
+ // the language follows that URL's locale, or the committed language on
90
+ // a URL without one.
91
+ const reconcile = async () => {
92
+ const language = publicLanguage() ?? committed;
93
+ if (instance.language !== language)
94
+ await instance.changeLanguage?.(language);
95
+ };
96
+ try {
97
+ await instance.changeLanguage?.(nextLanguage);
98
+ } catch (error) {
99
+ await reconcile();
100
+ throw error;
101
+ }
102
+ if (!router) return;
103
+ // The full href keeps the query and fragment across the language switch.
104
+ const href = localizePath(router.state.location.href, nextLanguage, {
105
+ languages: [...languages],
106
+ localisedUrls,
107
+ });
108
+ try {
109
+ await router.navigate({ to: '.', href, replace: true });
110
+ } catch (error) {
111
+ await reconcile();
112
+ throw error;
113
+ }
114
+ if (publicLanguage() !== nextLanguage) await reconcile();
115
+ });
116
+ state.queue = run.catch(() => {});
117
+ return run;
118
+ };
119
+
120
+ return { t, language, languages, instance, changeLanguage };
121
+ }
package/src/index.ts ADDED
@@ -0,0 +1,41 @@
1
+ /** This entry contains identity only. Select client, router or server explicitly. */
2
+ export const SOLID_RENDERER_PROFILE = {
3
+ renderer: 'solid',
4
+ solid: '2.0.0-rc.13',
5
+ web: '2.0.0-rc.13',
6
+ router: '@modern-js/renderer-solid/router@3.8.3',
7
+ routerSource: '4fbd65c5c3b40be87a6248867c22021c1c19bba8',
8
+ routerCore: '1.171.34',
9
+ history: '1.162.4',
10
+ publicRouteData: {
11
+ containers: ['plain-record', 'null-record', 'array'],
12
+ values: 'canonical-scalars',
13
+ deferred: 'checked-top-level-native-promises',
14
+ mutation: 'immutable-public-snapshot',
15
+ unsupported: ['Date', 'RegExp', 'Map', 'Set', 'AsyncIterable'],
16
+ },
17
+ publicContext: {
18
+ source: 'managed-constructor-route-context-and-before-load',
19
+ providerOverrides: 'unsupported',
20
+ },
21
+ nativePromiseMetadata: {
22
+ runtime: '2.0.0-rc.13',
23
+ fields: ['s', 'v'],
24
+ status: [0, 1, 2],
25
+ handle: 'sealed-with-checked-metadata',
26
+ },
27
+ nativeRegistry: {
28
+ source: 'managed-router-match-transfer',
29
+ rawRejectedMatchInjection: 'unsupported',
30
+ },
31
+ nativeRequestContext: {
32
+ scope: 'complete-native-request-handler',
33
+ event: 'one-per-request-session',
34
+ },
35
+ nativeResponseHead: {
36
+ statusText: 'fetch-response-status-text',
37
+ terminalOutcome: 'authoritative',
38
+ cache: 'intersect-with-session-policy',
39
+ },
40
+ dataHttpTransport: 'renderer-core-rich-public-data',
41
+ } as const;
@@ -0,0 +1,337 @@
1
+ import type { NativeCompilerArtifacts } from '@modern-js/renderer-core/adapter';
2
+ import {
3
+ nativeModuleManifestFilename,
4
+ type RendererIdentity,
5
+ readRendererIdentity,
6
+ } from '@modern-js/renderer-core/identity';
7
+
8
+ export const SOLID_COMPILER_VERSION = '2.0.0-rc.13';
9
+ export function solidModuleManifestFilename(entryName: string): string {
10
+ return nativeModuleManifestFilename('solid', entryName);
11
+ }
12
+
13
+ /** The compiler's structural JSON boundary matches native Solid AssetManifest. */
14
+ interface SolidPreloadAttributes {
15
+ type?: string;
16
+ crossorigin?: '' | true | 'anonymous' | 'use-credentials';
17
+ integrity?: string;
18
+ referrerpolicy?:
19
+ | 'no-referrer'
20
+ | 'no-referrer-when-downgrade'
21
+ | 'origin'
22
+ | 'origin-when-cross-origin'
23
+ | 'same-origin'
24
+ | 'strict-origin'
25
+ | 'strict-origin-when-cross-origin'
26
+ | 'unsafe-url';
27
+ fetchpriority?: 'high' | 'low' | 'auto';
28
+ media?: string;
29
+ }
30
+
31
+ export type SolidPreloadLink = SolidPreloadAttributes &
32
+ (
33
+ | {
34
+ href: string;
35
+ as: 'fetch' | 'font' | 'script' | 'style' | 'track';
36
+ imagesrcset?: never;
37
+ imagesizes?: never;
38
+ }
39
+ | { href: string; as: 'image'; imagesrcset?: string; imagesizes?: string }
40
+ | { href?: never; as: 'image'; imagesrcset: string; imagesizes?: string }
41
+ );
42
+
43
+ export interface SolidAssetChunk {
44
+ file: string;
45
+ css?: string[];
46
+ imports?: string[];
47
+ isEntry?: boolean;
48
+ preloads?: SolidPreloadLink[];
49
+ }
50
+
51
+ export type SolidAssetManifest = Record<string, SolidAssetChunk> & {
52
+ _base?: string;
53
+ };
54
+
55
+ export interface SolidModuleManifest {
56
+ schemaVersion: 1;
57
+ renderer: 'solid';
58
+ compilerVersion: typeof SOLID_COMPILER_VERSION;
59
+ /** The shared source-build identity owner stamps this before runtime use. */
60
+ rendererIdentity: RendererIdentity;
61
+ modules: SolidAssetManifest;
62
+ }
63
+
64
+ function requireRecord(
65
+ value: unknown,
66
+ description: string,
67
+ ): Record<string, unknown> {
68
+ if (
69
+ !value ||
70
+ typeof value !== 'object' ||
71
+ ![Object.prototype, null].includes(Object.getPrototypeOf(value))
72
+ ) {
73
+ throw new Error(`Invalid Solid module manifest ${description}`);
74
+ }
75
+ for (const key of Reflect.ownKeys(value)) {
76
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
77
+ if (typeof key !== 'string' || !descriptor || !('value' in descriptor)) {
78
+ throw new Error(
79
+ `Invalid Solid module manifest ${description}: data properties are required`,
80
+ );
81
+ }
82
+ }
83
+ return value as Record<string, unknown>;
84
+ }
85
+
86
+ function requireArray(
87
+ value: unknown,
88
+ description: string,
89
+ ): asserts value is unknown[] {
90
+ if (
91
+ !Array.isArray(value) ||
92
+ Object.getPrototypeOf(value) !== Array.prototype
93
+ ) {
94
+ throw new Error(
95
+ `Invalid Solid module manifest ${description}: an array is required`,
96
+ );
97
+ }
98
+ for (const key of Reflect.ownKeys(value)) {
99
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
100
+ if (
101
+ typeof key !== 'string' ||
102
+ (key !== 'length' && !/^(0|[1-9]\d*)$/.test(key)) ||
103
+ !descriptor ||
104
+ !('value' in descriptor)
105
+ ) {
106
+ throw new Error(
107
+ `Invalid Solid module manifest ${description}: array data properties are required`,
108
+ );
109
+ }
110
+ }
111
+ }
112
+
113
+ function requireStrings(
114
+ value: unknown,
115
+ description: string,
116
+ ): asserts value is string[] {
117
+ requireArray(value, description);
118
+ for (let index = 0; index < value.length; index++) {
119
+ if (typeof value[index] !== 'string' || !value[index]) {
120
+ throw new Error(
121
+ `Invalid Solid module manifest ${description}: nonempty strings are required`,
122
+ );
123
+ }
124
+ }
125
+ }
126
+
127
+ /** Validate the same path concatenation used by the native rc13 asset resolver. */
128
+ function validateAssetUrl(base: string | undefined, file: string): void {
129
+ const prefix = base || '/';
130
+ const joined = /^(?:[a-z][a-z0-9+.-]*:)?\/\//i.test(file)
131
+ ? file
132
+ : `${prefix.endsWith('/') ? prefix : `${prefix}/`}${file.startsWith('/') ? file.slice(1) : file}`;
133
+ try {
134
+ new URL(joined, 'https://manifest.invalid/');
135
+ } catch (cause) {
136
+ throw new Error(`Invalid Solid module manifest asset URL: ${file}`, {
137
+ cause,
138
+ });
139
+ }
140
+ }
141
+
142
+ function validatePreload(value: unknown, base: string | undefined): void {
143
+ const link = requireRecord(value, 'preload');
144
+ if (
145
+ !['fetch', 'font', 'image', 'script', 'style', 'track'].includes(
146
+ link.as as string,
147
+ )
148
+ ) {
149
+ throw new Error('Invalid Solid module manifest preload destination');
150
+ }
151
+ if (link.href !== undefined) {
152
+ if (typeof link.href !== 'string' || !link.href) {
153
+ throw new Error('Invalid Solid module manifest preload href');
154
+ }
155
+ validateAssetUrl(base, link.href);
156
+ }
157
+ if (link.as === 'image') {
158
+ if (
159
+ link.imagesrcset !== undefined &&
160
+ (typeof link.imagesrcset !== 'string' || !link.imagesrcset)
161
+ ) {
162
+ throw new Error('Invalid Solid module manifest image preload source set');
163
+ }
164
+ if (link.href === undefined && link.imagesrcset === undefined) {
165
+ throw new Error('Invalid Solid module manifest image preload source');
166
+ }
167
+ if (link.imagesizes !== undefined && typeof link.imagesizes !== 'string') {
168
+ throw new Error('Invalid Solid module manifest image preload sizes');
169
+ }
170
+ } else if (
171
+ link.href === undefined ||
172
+ link.imagesrcset !== undefined ||
173
+ link.imagesizes !== undefined
174
+ ) {
175
+ throw new Error('Invalid Solid module manifest preload source');
176
+ }
177
+ for (const key of ['type', 'integrity', 'media']) {
178
+ if (link[key] !== undefined && typeof link[key] !== 'string') {
179
+ throw new Error(`Invalid Solid module manifest preload ${key}`);
180
+ }
181
+ }
182
+ const enumerated: Record<string, readonly unknown[]> = {
183
+ crossorigin: ['', true, 'anonymous', 'use-credentials'],
184
+ fetchpriority: ['high', 'low', 'auto'],
185
+ referrerpolicy: [
186
+ 'no-referrer',
187
+ 'no-referrer-when-downgrade',
188
+ 'origin',
189
+ 'origin-when-cross-origin',
190
+ 'same-origin',
191
+ 'strict-origin',
192
+ 'strict-origin-when-cross-origin',
193
+ 'unsafe-url',
194
+ ],
195
+ };
196
+ for (const [key, values] of Object.entries(enumerated)) {
197
+ if (link[key] !== undefined && !values.includes(link[key])) {
198
+ throw new Error(`Invalid Solid module manifest preload ${key}`);
199
+ }
200
+ }
201
+ }
202
+
203
+ function assertCurrentIdentity(
204
+ manifest: Record<string, unknown>,
205
+ expectedIdentity: RendererIdentity,
206
+ ): void {
207
+ if (!manifest.rendererIdentity || expectedIdentity.renderer !== 'solid') {
208
+ throw new Error(
209
+ 'Solid module manifest requires the current Solid application build identity.',
210
+ );
211
+ }
212
+ try {
213
+ readRendererIdentity(manifest.rendererIdentity, expectedIdentity);
214
+ } catch (cause) {
215
+ throw new Error(
216
+ 'Stale Solid module manifest identity. Serve the manifest and hydration assets from the same application build.',
217
+ { cause },
218
+ );
219
+ }
220
+ }
221
+
222
+ function readSolidModuleManifest(
223
+ value: object,
224
+ expectedIdentity: RendererIdentity,
225
+ ): SolidModuleManifest {
226
+ const manifest = requireRecord(value, 'envelope');
227
+ if (
228
+ manifest.schemaVersion !== 1 ||
229
+ manifest.renderer !== 'solid' ||
230
+ manifest.compilerVersion !== SOLID_COMPILER_VERSION
231
+ ) {
232
+ throw new Error(
233
+ `Solid module manifest compiler ABI mismatch. Rebuild with @solidjs/compiler ${SOLID_COMPILER_VERSION}.`,
234
+ );
235
+ }
236
+ assertCurrentIdentity(manifest, expectedIdentity);
237
+ if (
238
+ !manifest.modules ||
239
+ typeof manifest.modules !== 'object' ||
240
+ Array.isArray(manifest.modules)
241
+ ) {
242
+ throw new Error(
243
+ 'Solid module manifest has no module inventory. Rebuild the application.',
244
+ );
245
+ }
246
+ const modules = requireRecord(manifest.modules, 'module inventory');
247
+ if (modules._base !== undefined && typeof modules._base !== 'string') {
248
+ throw new Error('Invalid Solid module manifest asset base');
249
+ }
250
+ const base = modules._base as string | undefined;
251
+ for (const [key, value] of Object.entries(modules)) {
252
+ if (key === '_base') continue;
253
+ const chunk = requireRecord(value, `chunk ${key}`);
254
+ if (typeof chunk.file !== 'string' || !chunk.file) {
255
+ throw new Error(
256
+ `Invalid Solid module manifest chunk ${key}: a nonempty file is required`,
257
+ );
258
+ }
259
+ validateAssetUrl(base, chunk.file);
260
+ for (const field of ['css', 'imports']) {
261
+ if (chunk[field] !== undefined)
262
+ requireStrings(chunk[field], `${key}.${field}`);
263
+ }
264
+ for (const file of (chunk.css as string[] | undefined) ?? [])
265
+ validateAssetUrl(base, file);
266
+ if (chunk.isEntry !== undefined && typeof chunk.isEntry !== 'boolean') {
267
+ throw new Error(
268
+ `Invalid Solid module manifest chunk ${key}: isEntry must be boolean`,
269
+ );
270
+ }
271
+ if (chunk.preloads !== undefined) {
272
+ requireArray(chunk.preloads, `${key}.preloads`);
273
+ for (const preload of chunk.preloads) validatePreload(preload, base);
274
+ }
275
+ }
276
+ for (const [key, value] of Object.entries(modules)) {
277
+ if (key === '_base') continue;
278
+ for (const dependency of (value as SolidAssetChunk).imports ?? []) {
279
+ if (dependency === '_base' || !Object.hasOwn(modules, dependency)) {
280
+ throw new Error(
281
+ `Solid module manifest chunk ${key} imports missing own chunk ${dependency}`,
282
+ );
283
+ }
284
+ }
285
+ }
286
+ return manifest as unknown as SolidModuleManifest;
287
+ }
288
+
289
+ /** A build's manifest is an immutable artifact: its shape is checked once. */
290
+ const validatedManifests = new WeakMap<object, SolidModuleManifest>();
291
+
292
+ export function validateSolidModuleManifest(
293
+ value: unknown,
294
+ expectedIdentity: RendererIdentity,
295
+ requiredModuleKeys: readonly string[] = [],
296
+ ): SolidModuleManifest {
297
+ if (!value || typeof value !== 'object') {
298
+ throw new Error(
299
+ 'Missing Solid module manifest. Rebuild the application before SSR or hydration.',
300
+ );
301
+ }
302
+ let manifest = validatedManifests.get(value);
303
+ if (manifest) {
304
+ assertCurrentIdentity(
305
+ manifest as unknown as Record<string, unknown>,
306
+ expectedIdentity,
307
+ );
308
+ } else {
309
+ manifest = readSolidModuleManifest(value, expectedIdentity);
310
+ validatedManifests.set(value, manifest);
311
+ }
312
+ for (const key of requiredModuleKeys) {
313
+ if (key === '_base' || !Object.hasOwn(manifest.modules, key)) {
314
+ throw new Error(
315
+ `Solid module manifest is missing ${key}. Rebuild the application before SSR or hydration.`,
316
+ );
317
+ }
318
+ }
319
+ return manifest;
320
+ }
321
+
322
+ /**
323
+ * The compiler artifact ABI. It lives in this runtime module, not in the
324
+ * build-only `./plugin`, because the production server validates manifests
325
+ * with it and deployments trace it from there.
326
+ */
327
+ export const compilerArtifacts: NativeCompilerArtifacts = {
328
+ clientManifestFile: solidModuleManifestFilename,
329
+ async validateClientManifest(value, identity) {
330
+ return { nativeManifest: validateSolidModuleManifest(value, identity) };
331
+ },
332
+ isMutableDevelopmentAsset(filename, entryNames) {
333
+ return entryNames.some(
334
+ entry => filename === solidModuleManifestFilename(entry),
335
+ );
336
+ },
337
+ };