@rocket/js 0.0.0 → 0.1.0

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 (340) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +224 -2
  3. package/dist-types/exports/MainMenu.d.ts +2 -0
  4. package/dist-types/exports/MainMenu.d.ts.map +1 -0
  5. package/dist-types/exports/PageData.d.ts +2 -0
  6. package/dist-types/exports/PageData.d.ts.map +1 -0
  7. package/dist-types/exports/RocketCodeBlock.d.ts +2 -0
  8. package/dist-types/exports/RocketCodeBlock.d.ts.map +1 -0
  9. package/dist-types/exports/RocketIcon.d.ts +2 -0
  10. package/dist-types/exports/RocketIcon.d.ts.map +1 -0
  11. package/dist-types/exports/RocketJsDemo.d.ts +2 -0
  12. package/dist-types/exports/RocketJsDemo.d.ts.map +1 -0
  13. package/dist-types/exports/RocketRequestDemo.d.ts +2 -0
  14. package/dist-types/exports/RocketRequestDemo.d.ts.map +1 -0
  15. package/dist-types/exports/SocialPreviewPlayground.d.ts +2 -0
  16. package/dist-types/exports/SocialPreviewPlayground.d.ts.map +1 -0
  17. package/dist-types/exports/adapters/netlify.d.ts +2 -0
  18. package/dist-types/exports/adapters/netlify.d.ts.map +1 -0
  19. package/dist-types/exports/asyncMessage.d.ts +2 -0
  20. package/dist-types/exports/asyncMessage.d.ts.map +1 -0
  21. package/dist-types/exports/component-hydration.d.ts +2 -0
  22. package/dist-types/exports/component-hydration.d.ts.map +1 -0
  23. package/dist-types/exports/components/web-awesome.d.ts +3 -0
  24. package/dist-types/exports/components/web-awesome.d.ts.map +1 -0
  25. package/dist-types/exports/components.d.ts +2 -0
  26. package/dist-types/exports/components.d.ts.map +1 -0
  27. package/dist-types/exports/config.d.ts +2 -0
  28. package/dist-types/exports/config.d.ts.map +1 -0
  29. package/dist-types/exports/debounce.d.ts +2 -0
  30. package/dist-types/exports/debounce.d.ts.map +1 -0
  31. package/dist-types/exports/define/RocketCodeBlock.d.ts +2 -0
  32. package/dist-types/exports/define/RocketCodeBlock.d.ts.map +1 -0
  33. package/dist-types/exports/define/RocketIcon.d.ts +2 -0
  34. package/dist-types/exports/define/RocketIcon.d.ts.map +1 -0
  35. package/dist-types/exports/define/RocketJsDemo.d.ts +2 -0
  36. package/dist-types/exports/define/RocketJsDemo.d.ts.map +1 -0
  37. package/dist-types/exports/define/RocketRequestDemo.d.ts +2 -0
  38. package/dist-types/exports/define/RocketRequestDemo.d.ts.map +1 -0
  39. package/dist-types/exports/define/menus.d.ts +2 -0
  40. package/dist-types/exports/define/menus.d.ts.map +1 -0
  41. package/dist-types/exports/extractCode.d.ts +2 -0
  42. package/dist-types/exports/extractCode.d.ts.map +1 -0
  43. package/dist-types/exports/globalData.d.ts +2 -0
  44. package/dist-types/exports/globalData.d.ts.map +1 -0
  45. package/dist-types/exports/hydration/hydrationLoader.d.ts +2 -0
  46. package/dist-types/exports/hydration/hydrationLoader.d.ts.map +1 -0
  47. package/dist-types/exports/icons.d.ts +2 -0
  48. package/dist-types/exports/icons.d.ts.map +1 -0
  49. package/dist-types/exports/layout-helper.d.ts +2 -0
  50. package/dist-types/exports/layout-helper.d.ts.map +1 -0
  51. package/dist-types/exports/layout.d.ts +2 -0
  52. package/dist-types/exports/layout.d.ts.map +1 -0
  53. package/dist-types/exports/layouts/atlasDoc.d.ts +2 -0
  54. package/dist-types/exports/layouts/atlasDoc.d.ts.map +1 -0
  55. package/dist-types/exports/layouts/atlasHero.d.ts +2 -0
  56. package/dist-types/exports/layouts/atlasHero.d.ts.map +1 -0
  57. package/dist-types/exports/layouts/atlasNotFound.d.ts +2 -0
  58. package/dist-types/exports/layouts/atlasNotFound.d.ts.map +1 -0
  59. package/dist-types/exports/loaded-page-module.d.ts +2 -0
  60. package/dist-types/exports/loaded-page-module.d.ts.map +1 -0
  61. package/dist-types/exports/markdownHook.d.ts +2 -0
  62. package/dist-types/exports/markdownHook.d.ts.map +1 -0
  63. package/dist-types/exports/menu.d.ts +2 -0
  64. package/dist-types/exports/menu.d.ts.map +1 -0
  65. package/dist-types/exports/menus.d.ts +6 -0
  66. package/dist-types/exports/menus.d.ts.map +1 -0
  67. package/dist-types/exports/page-runtime.d.ts +2 -0
  68. package/dist-types/exports/page-runtime.d.ts.map +1 -0
  69. package/dist-types/exports/pages.d.ts +2 -0
  70. package/dist-types/exports/pages.d.ts.map +1 -0
  71. package/dist-types/exports/resolve.d.ts +2 -0
  72. package/dist-types/exports/resolve.d.ts.map +1 -0
  73. package/dist-types/exports/ssr.d.ts +2 -0
  74. package/dist-types/exports/ssr.d.ts.map +1 -0
  75. package/dist-types/exports/standalone-demo-url.d.ts +2 -0
  76. package/dist-types/exports/standalone-demo-url.d.ts.map +1 -0
  77. package/dist-types/exports/transform.d.ts +2 -0
  78. package/dist-types/exports/transform.d.ts.map +1 -0
  79. package/dist-types/exports/types/hydration.d.ts +23 -0
  80. package/dist-types/exports/types/hydration.d.ts.map +1 -0
  81. package/dist-types/exports/types/rocket.d.ts +504 -0
  82. package/dist-types/exports/types/rocket.d.ts.map +1 -0
  83. package/dist-types/exports/types.d.ts +3 -0
  84. package/dist-types/exports/types.d.ts.map +1 -0
  85. package/dist-types/exports/wds-plugin.d.ts +2 -0
  86. package/dist-types/exports/wds-plugin.d.ts.map +1 -0
  87. package/dist-types/src/PageData.d.ts +82 -0
  88. package/dist-types/src/PageData.d.ts.map +1 -0
  89. package/dist-types/src/RocketCodeBlock.d.ts +64 -0
  90. package/dist-types/src/RocketCodeBlock.d.ts.map +1 -0
  91. package/dist-types/src/RocketIcon.d.ts +35 -0
  92. package/dist-types/src/RocketIcon.d.ts.map +1 -0
  93. package/dist-types/src/RocketJsDemo.d.ts +59 -0
  94. package/dist-types/src/RocketJsDemo.d.ts.map +1 -0
  95. package/dist-types/src/RocketJsDemo.test-browser.d.ts +3 -0
  96. package/dist-types/src/RocketJsDemo.test-browser.d.ts.map +1 -0
  97. package/dist-types/src/RocketRequestDemo.d.ts +57 -0
  98. package/dist-types/src/RocketRequestDemo.d.ts.map +1 -0
  99. package/dist-types/src/RocketRequestDemo.test-browser.d.ts +3 -0
  100. package/dist-types/src/RocketRequestDemo.test-browser.d.ts.map +1 -0
  101. package/dist-types/src/SocialPreviewPlayground.d.ts +102 -0
  102. package/dist-types/src/SocialPreviewPlayground.d.ts.map +1 -0
  103. package/dist-types/src/adapters/netlify.d.ts +54 -0
  104. package/dist-types/src/adapters/netlify.d.ts.map +1 -0
  105. package/dist-types/src/asyncMessage.d.ts +14 -0
  106. package/dist-types/src/asyncMessage.d.ts.map +1 -0
  107. package/dist-types/src/cli/RocketBuild.d.ts +78 -0
  108. package/dist-types/src/cli/RocketBuild.d.ts.map +1 -0
  109. package/dist-types/src/cli/RocketCli.d.ts +17 -0
  110. package/dist-types/src/cli/RocketCli.d.ts.map +1 -0
  111. package/dist-types/src/cli/RocketInit.d.ts +22 -0
  112. package/dist-types/src/cli/RocketInit.d.ts.map +1 -0
  113. package/dist-types/src/cli/RocketStart.d.ts +13 -0
  114. package/dist-types/src/cli/RocketStart.d.ts.map +1 -0
  115. package/dist-types/src/cli/cli.d.ts +3 -0
  116. package/dist-types/src/cli/cli.d.ts.map +1 -0
  117. package/dist-types/src/component-hydration.d.ts +26 -0
  118. package/dist-types/src/component-hydration.d.ts.map +1 -0
  119. package/dist-types/src/components/FeatureList.d.ts +15 -0
  120. package/dist-types/src/components/FeatureList.d.ts.map +1 -0
  121. package/dist-types/src/components/Footer.d.ts +17 -0
  122. package/dist-types/src/components/Footer.d.ts.map +1 -0
  123. package/dist-types/src/components/Header.d.ts +6 -0
  124. package/dist-types/src/components/Header.d.ts.map +1 -0
  125. package/dist-types/src/components/RocketDrawer.d.ts +20 -0
  126. package/dist-types/src/components/RocketDrawer.d.ts.map +1 -0
  127. package/dist-types/src/components/RocketSocialLink.d.ts +30 -0
  128. package/dist-types/src/components/RocketSocialLink.d.ts.map +1 -0
  129. package/dist-types/src/components.d.ts +5 -0
  130. package/dist-types/src/components.d.ts.map +1 -0
  131. package/dist-types/src/config.d.ts +6 -0
  132. package/dist-types/src/config.d.ts.map +1 -0
  133. package/dist-types/src/debounce.d.ts +8 -0
  134. package/dist-types/src/debounce.d.ts.map +1 -0
  135. package/dist-types/src/defaultSocialPreviewTemplate.d.ts +31 -0
  136. package/dist-types/src/defaultSocialPreviewTemplate.d.ts.map +1 -0
  137. package/dist-types/src/development-page-module-loader.d.ts +15 -0
  138. package/dist-types/src/development-page-module-loader.d.ts.map +1 -0
  139. package/dist-types/src/extractCode.d.ts +5 -0
  140. package/dist-types/src/extractCode.d.ts.map +1 -0
  141. package/dist-types/src/hydration/evaluate.d.ts +20 -0
  142. package/dist-types/src/hydration/evaluate.d.ts.map +1 -0
  143. package/dist-types/src/hydration/extractStrategies.d.ts +5 -0
  144. package/dist-types/src/hydration/extractStrategies.d.ts.map +1 -0
  145. package/dist-types/src/hydration/hydrationLoader.d.ts +64 -0
  146. package/dist-types/src/hydration/hydrationLoader.d.ts.map +1 -0
  147. package/dist-types/src/icons.d.ts +170 -0
  148. package/dist-types/src/icons.d.ts.map +1 -0
  149. package/dist-types/src/layouts/atlas/atlasDocLayout.d.ts +45 -0
  150. package/dist-types/src/layouts/atlas/atlasDocLayout.d.ts.map +1 -0
  151. package/dist-types/src/layouts/atlas/atlasHeroLayout.d.ts +7 -0
  152. package/dist-types/src/layouts/atlas/atlasHeroLayout.d.ts.map +1 -0
  153. package/dist-types/src/layouts/atlas/atlasNotFoundLayout.d.ts +5 -0
  154. package/dist-types/src/layouts/atlas/atlasNotFoundLayout.d.ts.map +1 -0
  155. package/dist-types/src/layouts/layout-helper.d.ts +16 -0
  156. package/dist-types/src/layouts/layout-helper.d.ts.map +1 -0
  157. package/dist-types/src/layouts/layout.d.ts +9 -0
  158. package/dist-types/src/layouts/layout.d.ts.map +1 -0
  159. package/dist-types/src/loaded-page-module.d.ts +51 -0
  160. package/dist-types/src/loaded-page-module.d.ts.map +1 -0
  161. package/dist-types/src/main.d.ts +2 -0
  162. package/dist-types/src/main.d.ts.map +1 -0
  163. package/dist-types/src/markdownCompiler.d.ts +22 -0
  164. package/dist-types/src/markdownCompiler.d.ts.map +1 -0
  165. package/dist-types/src/markdownHook.d.ts +6 -0
  166. package/dist-types/src/markdownHook.d.ts.map +1 -0
  167. package/dist-types/src/menu.d.ts +22 -0
  168. package/dist-types/src/menu.d.ts.map +1 -0
  169. package/dist-types/src/menus/MainMenu.d.ts +23 -0
  170. package/dist-types/src/menus/MainMenu.d.ts.map +1 -0
  171. package/dist-types/src/menus/RocketMenu.d.ts +23 -0
  172. package/dist-types/src/menus/RocketMenu.d.ts.map +1 -0
  173. package/dist-types/src/menus/RocketNextPage.d.ts +18 -0
  174. package/dist-types/src/menus/RocketNextPage.d.ts.map +1 -0
  175. package/dist-types/src/menus/RocketPreviousPage.d.ts +18 -0
  176. package/dist-types/src/menus/RocketPreviousPage.d.ts.map +1 -0
  177. package/dist-types/src/menus/RocketToc.d.ts +54 -0
  178. package/dist-types/src/menus/RocketToc.d.ts.map +1 -0
  179. package/dist-types/src/menus/pageNavigation.d.ts +41 -0
  180. package/dist-types/src/menus/pageNavigation.d.ts.map +1 -0
  181. package/dist-types/src/page-pagination.d.ts +69 -0
  182. package/dist-types/src/page-pagination.d.ts.map +1 -0
  183. package/dist-types/src/page-runtime.d.ts +110 -0
  184. package/dist-types/src/page-runtime.d.ts.map +1 -0
  185. package/dist-types/src/pages.d.ts +10 -0
  186. package/dist-types/src/pages.d.ts.map +1 -0
  187. package/dist-types/src/publicAssets.d.ts +70 -0
  188. package/dist-types/src/publicAssets.d.ts.map +1 -0
  189. package/dist-types/src/requestDemoMetadata.d.ts +19 -0
  190. package/dist-types/src/requestDemoMetadata.d.ts.map +1 -0
  191. package/dist-types/src/resolve.d.ts +7 -0
  192. package/dist-types/src/resolve.d.ts.map +1 -0
  193. package/dist-types/src/siteDiscoverability.d.ts +33 -0
  194. package/dist-types/src/siteDiscoverability.d.ts.map +1 -0
  195. package/dist-types/src/siteHeadMetadata.d.ts +20 -0
  196. package/dist-types/src/siteHeadMetadata.d.ts.map +1 -0
  197. package/dist-types/src/socialPreviewImages.d.ts +186 -0
  198. package/dist-types/src/socialPreviewImages.d.ts.map +1 -0
  199. package/dist-types/src/socialPreviewTemplatePreview.d.ts +22 -0
  200. package/dist-types/src/socialPreviewTemplatePreview.d.ts.map +1 -0
  201. package/dist-types/src/ssr.d.ts +6 -0
  202. package/dist-types/src/ssr.d.ts.map +1 -0
  203. package/dist-types/src/standalone-demo-url.d.ts +60 -0
  204. package/dist-types/src/standalone-demo-url.d.ts.map +1 -0
  205. package/dist-types/src/static-page-module-loader.d.ts +15 -0
  206. package/dist-types/src/static-page-module-loader.d.ts.map +1 -0
  207. package/dist-types/src/transform.d.ts +10 -0
  208. package/dist-types/src/transform.d.ts.map +1 -0
  209. package/dist-types/src/urlLifecycle.d.ts +23 -0
  210. package/dist-types/src/urlLifecycle.d.ts.map +1 -0
  211. package/dist-types/src/wds-plugin.d.ts +14 -0
  212. package/dist-types/src/wds-plugin.d.ts.map +1 -0
  213. package/docs/assets/home-background.svg +1 -0
  214. package/docs/assets/prism-one-light.css +368 -0
  215. package/docs/assets/rocket-logo-dark-with-text-below.svg +8 -0
  216. package/docs/assets/rocket-logo-dark-with-text.svg +7 -0
  217. package/docs/assets/rocket-logo-dark.svg +7 -0
  218. package/docs/assets/rocket-logo-light-with-text-below.svg +14 -0
  219. package/docs/assets/rocket-logo-light-with-text.svg +13 -0
  220. package/docs/assets/rocket-logo-light.svg +12 -0
  221. package/docs/assets/rocket-text-no-logo.svg +3 -0
  222. package/exports/MainMenu.js +1 -0
  223. package/exports/PageData.js +1 -0
  224. package/exports/RocketCodeBlock.js +1 -0
  225. package/exports/RocketIcon.js +1 -0
  226. package/exports/RocketJsDemo.js +1 -0
  227. package/exports/RocketRequestDemo.js +1 -0
  228. package/exports/SocialPreviewPlayground.js +1 -0
  229. package/exports/adapters/netlify.js +1 -0
  230. package/exports/asyncMessage.js +1 -0
  231. package/exports/component-hydration.js +1 -0
  232. package/exports/components/web-awesome.js +63 -0
  233. package/exports/components.js +1 -0
  234. package/exports/config.js +1 -0
  235. package/exports/debounce.js +1 -0
  236. package/exports/define/RocketCodeBlock.js +2 -0
  237. package/exports/define/RocketIcon.js +3 -0
  238. package/exports/define/RocketJsDemo.js +5 -0
  239. package/exports/define/RocketRequestDemo.js +5 -0
  240. package/exports/define/menus.js +14 -0
  241. package/exports/extractCode.js +1 -0
  242. package/exports/globalData.js +1 -0
  243. package/exports/hydration/hydrationLoader.js +1 -0
  244. package/exports/icons.js +8 -0
  245. package/exports/layout-helper.js +1 -0
  246. package/exports/layout.js +1 -0
  247. package/exports/layouts/_atlas.css +3 -0
  248. package/exports/layouts/atlasDoc.js +5 -0
  249. package/exports/layouts/atlasHero.js +1 -0
  250. package/exports/layouts/atlasNotFound.js +4 -0
  251. package/exports/loaded-page-module.js +5 -0
  252. package/exports/markdownHook.js +4 -0
  253. package/exports/menu.js +4 -0
  254. package/exports/menus.js +5 -0
  255. package/exports/page-runtime.js +5 -0
  256. package/exports/pages.js +1 -0
  257. package/exports/resolve.js +1 -0
  258. package/exports/ssr.js +1 -0
  259. package/exports/standalone-demo-url.js +10 -0
  260. package/exports/transform.js +1 -0
  261. package/exports/types/hydration.ts +26 -0
  262. package/exports/types/rocket.ts +598 -0
  263. package/exports/types.ts +71 -0
  264. package/exports/wds-plugin.js +1 -0
  265. package/package.json +192 -9
  266. package/src/PageData.js +244 -0
  267. package/src/RocketCodeBlock.js +516 -0
  268. package/src/RocketIcon.js +291 -0
  269. package/src/RocketJsDemo.js +397 -0
  270. package/src/RocketJsDemo.test-browser.js +228 -0
  271. package/src/RocketRequestDemo.js +439 -0
  272. package/src/RocketRequestDemo.test-browser.js +301 -0
  273. package/src/SocialPreviewPlayground.js +573 -0
  274. package/src/adapters/netlify.js +814 -0
  275. package/src/asyncMessage.js +21 -0
  276. package/src/cli/RocketBuild.js +545 -0
  277. package/src/cli/RocketCli.js +47 -0
  278. package/src/cli/RocketInit.js +323 -0
  279. package/src/cli/RocketStart.js +79 -0
  280. package/src/cli/cli.js +7 -0
  281. package/src/component-hydration.js +86 -0
  282. package/src/components/FeatureList.js +114 -0
  283. package/src/components/Footer.js +116 -0
  284. package/src/components/Header.js +122 -0
  285. package/src/components/RocketDrawer.js +193 -0
  286. package/src/components/RocketSocialLink.js +128 -0
  287. package/src/components/assets/discord.svg +7 -0
  288. package/src/components/assets/github.svg +4 -0
  289. package/src/components/assets/gitlab.svg +1 -0
  290. package/src/components/assets/info.txt +1 -0
  291. package/src/components/assets/license.svg +3 -0
  292. package/src/components/assets/npm.svg +5 -0
  293. package/src/components/assets/slack.svg +5 -0
  294. package/src/components/assets/telegram.svg +4 -0
  295. package/src/components/assets/twitter.svg +1 -0
  296. package/src/components.js +15 -0
  297. package/src/config.js +319 -0
  298. package/src/debounce.js +21 -0
  299. package/src/defaultSocialPreviewTemplate.js +118 -0
  300. package/src/development-page-module-loader.js +29 -0
  301. package/src/extractCode.js +41 -0
  302. package/src/hydration/evaluate.js +54 -0
  303. package/src/hydration/extractStrategies.js +91 -0
  304. package/src/hydration/hydrationLoader.js +330 -0
  305. package/src/icons.js +883 -0
  306. package/src/layouts/atlas/atlasDoc.css +877 -0
  307. package/src/layouts/atlas/atlasDocLayout.js +288 -0
  308. package/src/layouts/atlas/atlasHero.css +774 -0
  309. package/src/layouts/atlas/atlasHeroLayout.js +346 -0
  310. package/src/layouts/atlas/atlasNotFound.css +365 -0
  311. package/src/layouts/atlas/atlasNotFoundLayout.js +69 -0
  312. package/src/layouts/layout-helper.js +92 -0
  313. package/src/layouts/layout.js +62 -0
  314. package/src/loaded-page-module.js +97 -0
  315. package/src/main.js +55 -0
  316. package/src/markdownCompiler.js +303 -0
  317. package/src/markdownHook.js +148 -0
  318. package/src/menu.js +210 -0
  319. package/src/menus/MainMenu.js +58 -0
  320. package/src/menus/RocketMenu.js +191 -0
  321. package/src/menus/RocketNextPage.js +25 -0
  322. package/src/menus/RocketPreviousPage.js +29 -0
  323. package/src/menus/RocketToc.js +309 -0
  324. package/src/menus/pageNavigation.js +285 -0
  325. package/src/page-pagination.js +241 -0
  326. package/src/page-runtime.js +481 -0
  327. package/src/pages.js +537 -0
  328. package/src/publicAssets.js +336 -0
  329. package/src/requestDemoMetadata.js +97 -0
  330. package/src/resolve.js +15 -0
  331. package/src/siteDiscoverability.js +184 -0
  332. package/src/siteHeadMetadata.js +69 -0
  333. package/src/socialPreviewImages.js +482 -0
  334. package/src/socialPreviewTemplatePreview.js +352 -0
  335. package/src/ssr.js +14 -0
  336. package/src/standalone-demo-url.js +134 -0
  337. package/src/static-page-module-loader.js +29 -0
  338. package/src/transform.js +633 -0
  339. package/src/urlLifecycle.js +57 -0
  340. package/src/wds-plugin.js +302 -0
@@ -0,0 +1,241 @@
1
+ import { URLPattern } from 'urlpattern-polyfill';
2
+ import { PageData } from './PageData.js';
3
+ import { normalizeDocumentPath } from './standalone-demo-url.js';
4
+
5
+ /** @typedef {import('@rocket/js/types.js').Page} Page */
6
+ /** @typedef {import('@rocket/js/types.js').PagePagination} PagePagination */
7
+ /** @typedef {import('@rocket/js/types.js').PagePaginationConfig} PagePaginationConfig */
8
+ /** @typedef {import('@rocket/js/types.js').PagePaginationDeclaration} PagePaginationDeclaration */
9
+ /** @typedef {import('@rocket/js/types.js').PageRegistry} PageRegistry */
10
+
11
+ /**
12
+ * @param {Page} page
13
+ */
14
+ export function hasPagePagination(page) {
15
+ return isJavaScriptPage(page) && page.module.pagination !== undefined;
16
+ }
17
+
18
+ /**
19
+ * @param {{
20
+ * pages: PageRegistry;
21
+ * page: Page;
22
+ * pagePath: string;
23
+ * pathname: string;
24
+ * currentPage: number;
25
+ * siteHeadMetadata?: import('@rocket/js/types.js').SiteHeadMetadataConfig;
26
+ * siteOrigin?: string;
27
+ * defaultSocialPreviewImages?: Map<string, string>;
28
+ * }} options
29
+ * @returns {PageData}
30
+ */
31
+ export function pageDataWithPagination({
32
+ pages,
33
+ page,
34
+ pagePath,
35
+ pathname,
36
+ currentPage,
37
+ siteHeadMetadata,
38
+ siteOrigin,
39
+ defaultSocialPreviewImages,
40
+ }) {
41
+ const pageData = new PageData(pages, page.metadata, pathname, {
42
+ siteHeadMetadata,
43
+ pageSiteHeadMetadata: page.module.config.siteHeadMetadata,
44
+ defaultSocialPreviewImage: defaultSocialPreviewImages?.get(pathname),
45
+ siteOrigin,
46
+ });
47
+ if (page.module.config.iconReferences) {
48
+ pageData.addIconReferences(page.module.config.iconReferences);
49
+ }
50
+ if (!hasPagePagination(page)) {
51
+ return pageData;
52
+ }
53
+ pageData.pagination = createPagePagination({
54
+ declaration: /** @type {PagePaginationDeclaration} */ (page.module.pagination),
55
+ pageData,
56
+ pagePath,
57
+ currentPage,
58
+ });
59
+ return pageData;
60
+ }
61
+
62
+ /**
63
+ * @param {{
64
+ * pages: PageRegistry;
65
+ * page: Page;
66
+ * pagePath: string;
67
+ * }} options
68
+ * @returns {string[]}
69
+ */
70
+ export function paginatedArchivePaths({ pages, page, pagePath }) {
71
+ if (!hasPagePagination(page)) {
72
+ return [];
73
+ }
74
+ const pageCount = pagePaginationPageCount({ pages, page, pagePath });
75
+ const paths = [];
76
+ for (let pageNumber = 2; pageNumber <= pageCount; pageNumber += 1) {
77
+ paths.push(paginatedArchivePath(pagePath, pageNumber));
78
+ }
79
+ return paths;
80
+ }
81
+
82
+ /**
83
+ * @param {string} pagePath
84
+ * @param {number} pageNumber
85
+ */
86
+ export function paginatedArchivePath(pagePath, pageNumber) {
87
+ if (pageNumber === 1) {
88
+ return normalizeDocumentPath(pagePath);
89
+ }
90
+ return `${normalizeDocumentPath(pagePath)}${pageNumber}/`;
91
+ }
92
+
93
+ /**
94
+ * @param {string} pathname
95
+ * @param {string} origin
96
+ * @param {PageRegistry} pages
97
+ * @returns {{ page: Page; routePath: string; params: Record<string, string | undefined>; pageNumber: number } | null}
98
+ */
99
+ export function matchPaginatedArchivePath(pathname, origin, pages) {
100
+ const archivePath = parsePaginatedArchivePath(pathname);
101
+ if (!archivePath) {
102
+ return null;
103
+ }
104
+
105
+ for (const [routePath, page] of pages) {
106
+ if (!hasPagePagination(page)) {
107
+ continue;
108
+ }
109
+ const match =
110
+ matchPagePath(archivePath.parentPathname, origin, routePath) ||
111
+ matchPagePath(normalizeDocumentPath(archivePath.parentPathname), origin, routePath);
112
+ if (!match) {
113
+ continue;
114
+ }
115
+ const pageCount = pagePaginationPageCount({ pages, page, pagePath: routePath });
116
+ if (archivePath.pageNumber > pageCount) {
117
+ continue;
118
+ }
119
+ return {
120
+ page,
121
+ routePath,
122
+ params: match.pathname.groups,
123
+ pageNumber: archivePath.pageNumber,
124
+ };
125
+ }
126
+ return null;
127
+ }
128
+
129
+ /**
130
+ * @param {{
131
+ * pages: PageRegistry;
132
+ * page: Page;
133
+ * pagePath: string;
134
+ * }} options
135
+ */
136
+ function pagePaginationPageCount({ pages, page, pagePath }) {
137
+ const pageData = pageDataWithPagination({
138
+ pages,
139
+ page,
140
+ pagePath,
141
+ pathname: pagePath,
142
+ currentPage: 1,
143
+ });
144
+ return pageData.pagination?.totalPages || 1;
145
+ }
146
+
147
+ /**
148
+ * @param {{
149
+ * declaration: PagePaginationDeclaration;
150
+ * pageData: PageData;
151
+ * pagePath: string;
152
+ * currentPage: number;
153
+ * }} options
154
+ * @returns {PagePagination}
155
+ */
156
+ function createPagePagination({ declaration, pageData, pagePath, currentPage }) {
157
+ const config = normalizePagePaginationConfig(
158
+ typeof declaration === 'function' ? declaration(pageData) : declaration,
159
+ );
160
+ const totalPages = Math.max(1, Math.ceil(config.collection.length / config.pageSize));
161
+ const basePath = normalizeDocumentPath(pagePath);
162
+ const firstItemIndex = (currentPage - 1) * config.pageSize;
163
+ const items = config.collection.slice(firstItemIndex, firstItemIndex + config.pageSize);
164
+
165
+ return {
166
+ items,
167
+ currentPage,
168
+ totalPages,
169
+ basePath,
170
+ ...(currentPage < totalPages
171
+ ? { nextPath: paginatedArchivePath(pagePath, currentPage + 1) }
172
+ : {}),
173
+ ...(currentPage > 1 ? { previousPath: paginatedArchivePath(pagePath, currentPage - 1) } : {}),
174
+ };
175
+ }
176
+
177
+ /**
178
+ * @param {unknown} config
179
+ * @returns {PagePaginationConfig}
180
+ */
181
+ function normalizePagePaginationConfig(config) {
182
+ if (!isPagePaginationConfig(config)) {
183
+ throw new Error('Page pagination must return a collection array and a positive pageSize');
184
+ }
185
+ return config;
186
+ }
187
+
188
+ /**
189
+ * @param {unknown} config
190
+ * @returns {config is PagePaginationConfig}
191
+ */
192
+ function isPagePaginationConfig(config) {
193
+ if (typeof config !== 'object' || config === null) {
194
+ return false;
195
+ }
196
+ const paginationConfig = /** @type {Record<string, unknown>} */ (config);
197
+ return (
198
+ Array.isArray(paginationConfig.collection) &&
199
+ typeof paginationConfig.pageSize === 'number' &&
200
+ Number.isInteger(paginationConfig.pageSize) &&
201
+ paginationConfig.pageSize > 0
202
+ );
203
+ }
204
+
205
+ /**
206
+ * @param {string} pathname
207
+ * @returns {{ parentPathname: string; pageNumber: number } | null}
208
+ */
209
+ function parsePaginatedArchivePath(pathname) {
210
+ if (!pathname.endsWith('/')) {
211
+ return null;
212
+ }
213
+ const segments = pathname.split('/');
214
+ const pageNumberSegment = segments[segments.length - 2];
215
+ if (!/^[1-9]\d*$/.test(pageNumberSegment)) {
216
+ return null;
217
+ }
218
+ const pageNumber = Number(pageNumberSegment);
219
+ if (pageNumber === 1) {
220
+ return null;
221
+ }
222
+ const parentPathname = segments.slice(0, -2).join('/') || '/';
223
+ return { parentPathname, pageNumber };
224
+ }
225
+
226
+ /**
227
+ * @param {string} pathname
228
+ * @param {string} origin
229
+ * @param {string} routePath
230
+ */
231
+ function matchPagePath(pathname, origin, routePath) {
232
+ const pattern = new URLPattern({ pathname: routePath });
233
+ return pattern.exec(pathname, origin);
234
+ }
235
+
236
+ /**
237
+ * @param {Page} page
238
+ */
239
+ function isJavaScriptPage(page) {
240
+ return page.file.endsWith('.js');
241
+ }
@@ -0,0 +1,481 @@
1
+ import { collectResult } from '@lit-labs/ssr/lib/render-result.js';
2
+ import { URLPattern } from 'urlpattern-polyfill';
3
+ import { layout, singleDemoLayout } from './layouts/layout.js';
4
+ import { PageData } from './PageData.js';
5
+ import { finalizeRocketIcons } from './icons.js';
6
+ import { matchPaginatedArchivePath, pageDataWithPagination } from './page-pagination.js';
7
+ import { matchStandaloneDemoUrl } from './standalone-demo-url.js';
8
+
9
+ /** @typedef {import('@rocket/js/types.js').PageRegistry} PageRegistry */
10
+ /** @typedef {{ file: string; module: import('@rocket/js/types.js').Module; metadata: import('@rocket/js/types.js').PageMetadata; demoNames?: string[] }} Page */
11
+ /** @typedef {'default' | { kind: 'standalone-demo'; demoName: string } | { kind: 'paginated-archive'; pageNumber: number }} PageVariant */
12
+ /** @typedef {{ kind: 'javascript'; body: unknown }} LoadedJavaScriptPageModule */
13
+ /** @typedef {{ kind: 'markdown'; contentFn: unknown }} LoadedMarkdownPageModule */
14
+ /** @typedef {LoadedJavaScriptPageModule | LoadedMarkdownPageModule} LoadedPageModule */
15
+ /** @typedef {{ page: Page; routePath: string; request: Request; variant: PageVariant }} PageModuleLoaderOptions */
16
+ /** @typedef {{ load(options: PageModuleLoaderOptions): LoadedPageModule | Promise<LoadedPageModule> }} PageModuleLoader */
17
+ /** @typedef {import('@rocket/js/types.js').UrlLifecycleConfig} UrlLifecycleConfig */
18
+ /** @typedef {import('@rocket/js/types.js').RedirectConfig} RedirectConfig */
19
+ /** @typedef {'INVALID_PAGE_MODULE' | 'PAGE_RENDER_FAILED'} PageRuntimeErrorCode */
20
+
21
+ export class PageRuntimeError extends Error {
22
+ /**
23
+ * @param {PageRuntimeErrorCode} code
24
+ * @param {string} message
25
+ * @param {{ cause?: unknown; page?: Page; routePath?: string }} [options]
26
+ */
27
+ constructor(code, message, options = {}) {
28
+ super(message, { cause: options.cause });
29
+ this.name = 'PageRuntimeError';
30
+ this.code = code;
31
+ this.page = options.page;
32
+ this.routePath = options.routePath;
33
+ }
34
+ }
35
+
36
+ export class PageRuntime {
37
+ /**
38
+ * @param {{
39
+ * pages: PageRegistry;
40
+ * pageModuleLoader: PageModuleLoader;
41
+ * urlLifecycle?: UrlLifecycleConfig;
42
+ * siteHeadMetadata?: import('@rocket/js/types.js').SiteHeadMetadataConfig;
43
+ * siteOrigin?: string;
44
+ * defaultSocialPreviewImages?: Map<string, string>;
45
+ * iconLibraries?: import('@rocket/js/types.js').IconLibrariesConfig;
46
+ * defaultIconLibrary?: string;
47
+ * iconAssetStore?: import('./icons.js').IconAssetStore;
48
+ * }} options
49
+ */
50
+ constructor({
51
+ pages,
52
+ pageModuleLoader,
53
+ urlLifecycle,
54
+ siteHeadMetadata,
55
+ siteOrigin,
56
+ defaultSocialPreviewImages,
57
+ iconLibraries,
58
+ defaultIconLibrary,
59
+ iconAssetStore,
60
+ }) {
61
+ this.pages = pages;
62
+ this.pageModuleLoader = pageModuleLoader;
63
+ this.urlLifecycle = urlLifecycle;
64
+ this.siteHeadMetadata = siteHeadMetadata;
65
+ this.siteOrigin = siteOrigin;
66
+ this.defaultSocialPreviewImages = defaultSocialPreviewImages;
67
+ this.iconLibraries = iconLibraries;
68
+ this.defaultIconLibrary = defaultIconLibrary;
69
+ this.iconAssetStore = iconAssetStore;
70
+ }
71
+
72
+ /**
73
+ * @param {Request} request
74
+ * @param {{ adapterContext?: unknown }} [options]
75
+ * @returns {Promise<Response>}
76
+ */
77
+ async render(request, { adapterContext } = {}) {
78
+ const url = new URL(request.url);
79
+ const redirect = findRedirect(url.pathname, this.urlLifecycle?.redirects);
80
+ if (redirect) {
81
+ return new Response(null, {
82
+ status: redirect.status || 308,
83
+ headers: {
84
+ location: redirect.target,
85
+ },
86
+ });
87
+ }
88
+
89
+ const pageMatch = findPage(url.pathname, url.origin, this.pages);
90
+ if (!pageMatch) {
91
+ return new Response('Page not found', { status: 404 });
92
+ }
93
+
94
+ const variant = pageMatch.variant;
95
+ const loadedPageModule = await this.pageModuleLoader.load({
96
+ page: pageMatch.page,
97
+ routePath: pageMatch.routePath,
98
+ request,
99
+ variant,
100
+ });
101
+ if (loadedPageModule.kind === 'javascript') {
102
+ return renderJavaScriptPage({
103
+ loadedPageModule,
104
+ request,
105
+ pages: this.pages,
106
+ page: pageMatch.page,
107
+ routePath: pageMatch.routePath,
108
+ params: pageMatch.params,
109
+ pathname: url.pathname,
110
+ variant,
111
+ siteHeadMetadata: this.siteHeadMetadata,
112
+ siteOrigin: this.siteOrigin,
113
+ defaultSocialPreviewImages: this.defaultSocialPreviewImages,
114
+ iconLibraries: this.iconLibraries,
115
+ defaultIconLibrary: this.defaultIconLibrary,
116
+ iconAssetStore: this.iconAssetStore,
117
+ adapterContext,
118
+ });
119
+ }
120
+ if (loadedPageModule.kind === 'markdown') {
121
+ return renderMarkdownPage({
122
+ loadedPageModule,
123
+ pages: this.pages,
124
+ page: pageMatch.page,
125
+ routePath: pageMatch.routePath,
126
+ pathname: url.pathname,
127
+ variant,
128
+ siteHeadMetadata: this.siteHeadMetadata,
129
+ siteOrigin: this.siteOrigin,
130
+ defaultSocialPreviewImages: this.defaultSocialPreviewImages,
131
+ iconLibraries: this.iconLibraries,
132
+ defaultIconLibrary: this.defaultIconLibrary,
133
+ iconAssetStore: this.iconAssetStore,
134
+ });
135
+ }
136
+
137
+ throw new Error('Unsupported Page module kind');
138
+ }
139
+ }
140
+
141
+ /**
142
+ * @param {string} pathname
143
+ * @param {RedirectConfig[] | undefined} redirects
144
+ */
145
+ function findRedirect(pathname, redirects = []) {
146
+ return redirects.find(redirect => redirect.source === pathname);
147
+ }
148
+
149
+ /**
150
+ * @param {{
151
+ * loadedPageModule: LoadedJavaScriptPageModule;
152
+ * request: Request;
153
+ * pages: PageRegistry;
154
+ * page: Page;
155
+ * routePath: string;
156
+ * params: Record<string, string | undefined>;
157
+ * pathname: string;
158
+ * variant: PageVariant;
159
+ * siteHeadMetadata?: import('@rocket/js/types.js').SiteHeadMetadataConfig;
160
+ * siteOrigin?: string;
161
+ * defaultSocialPreviewImages?: Map<string, string>;
162
+ * iconLibraries?: import('@rocket/js/types.js').IconLibrariesConfig;
163
+ * defaultIconLibrary?: string;
164
+ * iconAssetStore?: import('./icons.js').IconAssetStore;
165
+ * adapterContext: unknown;
166
+ * }} options
167
+ */
168
+ async function renderJavaScriptPage({
169
+ loadedPageModule,
170
+ request,
171
+ pages,
172
+ page,
173
+ routePath,
174
+ params,
175
+ pathname,
176
+ variant,
177
+ siteHeadMetadata,
178
+ siteOrigin,
179
+ defaultSocialPreviewImages,
180
+ iconLibraries,
181
+ defaultIconLibrary,
182
+ iconAssetStore,
183
+ adapterContext,
184
+ }) {
185
+ const body = loadedPageModule.body;
186
+ if (typeof body === 'function') {
187
+ const pageData = pageDataWithPagination({
188
+ pages,
189
+ page,
190
+ pagePath: routePath,
191
+ pathname,
192
+ currentPage: archivePageNumber(variant),
193
+ siteHeadMetadata,
194
+ siteOrigin,
195
+ defaultSocialPreviewImages,
196
+ });
197
+ /** @type {unknown} */
198
+ let result;
199
+ try {
200
+ result = await body(request, { params, pageData, adapterContext });
201
+ } catch (error) {
202
+ if (error instanceof PageRuntimeError) {
203
+ throw error;
204
+ }
205
+ throw new PageRuntimeError(
206
+ 'PAGE_RENDER_FAILED',
207
+ `Failed to render JavaScript Page ${page.file}`,
208
+ {
209
+ cause: error,
210
+ page,
211
+ routePath,
212
+ },
213
+ );
214
+ }
215
+ return finalizeHtmlResponse(normalizeJavaScriptPageResult(result), pageData, {
216
+ iconLibraries,
217
+ defaultIconLibrary,
218
+ iconAssetStore,
219
+ });
220
+ }
221
+ if (body instanceof Response) {
222
+ return body;
223
+ }
224
+ throw new PageRuntimeError(
225
+ 'INVALID_PAGE_MODULE',
226
+ `Invalid JavaScript Page module ${page.file}: default or content export must be a function or Response`,
227
+ { page, routePath },
228
+ );
229
+ }
230
+
231
+ /**
232
+ * @param {{
233
+ * loadedPageModule: LoadedMarkdownPageModule;
234
+ * pages: PageRegistry;
235
+ * page: Page;
236
+ * routePath: string;
237
+ * pathname: string;
238
+ * variant: PageVariant;
239
+ * siteHeadMetadata?: import('@rocket/js/types.js').SiteHeadMetadataConfig;
240
+ * siteOrigin?: string;
241
+ * defaultSocialPreviewImages?: Map<string, string>;
242
+ * iconLibraries?: import('@rocket/js/types.js').IconLibrariesConfig;
243
+ * defaultIconLibrary?: string;
244
+ * iconAssetStore?: import('./icons.js').IconAssetStore;
245
+ * }} options
246
+ */
247
+ async function renderMarkdownPage({
248
+ loadedPageModule,
249
+ pages,
250
+ page,
251
+ routePath,
252
+ pathname,
253
+ variant,
254
+ siteHeadMetadata,
255
+ siteOrigin,
256
+ defaultSocialPreviewImages,
257
+ iconLibraries,
258
+ defaultIconLibrary,
259
+ iconAssetStore,
260
+ }) {
261
+ const contentFn = loadedPageModule.contentFn;
262
+ if (typeof contentFn !== 'function') {
263
+ throw new PageRuntimeError(
264
+ 'INVALID_PAGE_MODULE',
265
+ `Invalid Markdown Page module ${page.file}: contentFn export must be a function`,
266
+ { page, routePath },
267
+ );
268
+ }
269
+ const pageData = new PageData(pages, page.metadata, pathname, {
270
+ siteHeadMetadata,
271
+ pageSiteHeadMetadata: page.module.config.siteHeadMetadata,
272
+ defaultSocialPreviewImage: defaultSocialPreviewImages?.get(pathname),
273
+ siteOrigin,
274
+ });
275
+ if (page.module.config.iconReferences) {
276
+ pageData.addIconReferences(page.module.config.iconReferences);
277
+ }
278
+ /** @type {string} */
279
+ let body;
280
+ try {
281
+ const content = await contentFn(pageData, layoutForPageVariant(variant));
282
+ body = await collectResult(content);
283
+ } catch (error) {
284
+ if (error instanceof PageRuntimeError) {
285
+ throw error;
286
+ }
287
+ throw new PageRuntimeError(
288
+ 'PAGE_RENDER_FAILED',
289
+ `Failed to render Markdown Page ${page.file}`,
290
+ {
291
+ cause: error,
292
+ page,
293
+ routePath,
294
+ },
295
+ );
296
+ }
297
+ const finalizedBody = await finalizeRocketIcons(body, {
298
+ pageData,
299
+ iconLibraries,
300
+ defaultIconLibrary,
301
+ iconAssetStore,
302
+ });
303
+ return new Response(finalizedBody, {
304
+ headers: { 'content-type': 'text/html; charset=utf-8' },
305
+ });
306
+ }
307
+
308
+ /**
309
+ * @param {unknown} result
310
+ * @returns {Response}
311
+ */
312
+ export function normalizeJavaScriptPageResult(result) {
313
+ if (result instanceof Response) {
314
+ return result;
315
+ }
316
+ if (result === null || result === undefined) {
317
+ return new Response(null);
318
+ }
319
+ if (typeof result === 'string') {
320
+ return new Response(result, {
321
+ headers: { 'content-type': 'text/html; charset=utf-8' },
322
+ });
323
+ }
324
+ return Response.json(result);
325
+ }
326
+
327
+ /**
328
+ * @param {Response} response
329
+ * @param {PageData} pageData
330
+ * @param {{
331
+ * iconLibraries?: import('@rocket/js/types.js').IconLibrariesConfig;
332
+ * defaultIconLibrary?: string;
333
+ * iconAssetStore?: import('./icons.js').IconAssetStore;
334
+ * }} options
335
+ */
336
+ async function finalizeHtmlResponse(
337
+ response,
338
+ pageData,
339
+ { iconLibraries, defaultIconLibrary, iconAssetStore },
340
+ ) {
341
+ const contentType = response.headers.get('content-type') || '';
342
+ if (!contentType.toLowerCase().startsWith('text/html')) {
343
+ return response;
344
+ }
345
+
346
+ const body = await response.text();
347
+ const finalizedBody = await finalizeRocketIcons(body, {
348
+ pageData,
349
+ iconLibraries,
350
+ defaultIconLibrary,
351
+ iconAssetStore,
352
+ });
353
+ const headers = new Headers(response.headers);
354
+ headers.delete('content-length');
355
+ return new Response(finalizedBody, {
356
+ status: response.status,
357
+ statusText: response.statusText,
358
+ headers,
359
+ });
360
+ }
361
+
362
+ /**
363
+ * @param {string} pathname
364
+ * @param {string} origin
365
+ * @param {PageRegistry} pages
366
+ * @returns {{ page: Page; routePath: string; params: Record<string, string | undefined>; variant: PageVariant } | null}
367
+ */
368
+ function findPage(pathname, origin, pages) {
369
+ const exactPage = findExactPage(pathname, pages);
370
+ if (exactPage) {
371
+ return { ...exactPage, params: {}, variant: 'default' };
372
+ }
373
+
374
+ const standaloneDemoMatch = matchStandaloneDemoUrl(pathname, origin, pages);
375
+ if (standaloneDemoMatch) {
376
+ return standaloneDemoMatch;
377
+ }
378
+
379
+ const paginatedArchiveMatch = matchPaginatedArchivePath(pathname, origin, pages);
380
+ if (paginatedArchiveMatch) {
381
+ return {
382
+ page: paginatedArchiveMatch.page,
383
+ routePath: paginatedArchiveMatch.routePath,
384
+ params: paginatedArchiveMatch.params,
385
+ variant: {
386
+ kind: 'paginated-archive',
387
+ pageNumber: paginatedArchiveMatch.pageNumber,
388
+ },
389
+ };
390
+ }
391
+
392
+ const matchPathnames = pageMatchPathnames(pathname);
393
+ for (const [routePath, page] of pages) {
394
+ for (const matchPathname of matchPathnames) {
395
+ const match = matchPagePath(matchPathname, origin, routePath);
396
+ if (match) {
397
+ return { page, routePath, params: match.pathname.groups, variant: 'default' };
398
+ }
399
+ }
400
+ }
401
+ return null;
402
+ }
403
+
404
+ /**
405
+ * @param {string} pathname
406
+ * @param {PageRegistry} pages
407
+ * @returns {{ page: Page; routePath: string } | null}
408
+ */
409
+ function findExactPage(pathname, pages) {
410
+ const exactPage = pages.get(pathname);
411
+ if (exactPage) {
412
+ return { page: exactPage, routePath: pathname };
413
+ }
414
+ const configuredDocumentPath = configuredDocumentPathFromRequest(pathname);
415
+ if (configuredDocumentPath === pathname) {
416
+ return null;
417
+ }
418
+ const documentPage = pages.get(configuredDocumentPath);
419
+ return documentPage ? { page: documentPage, routePath: configuredDocumentPath } : null;
420
+ }
421
+
422
+ /**
423
+ * @param {string} pathname
424
+ * @returns {string[]}
425
+ */
426
+ function pageMatchPathnames(pathname) {
427
+ const configuredDocumentPath = configuredDocumentPathFromRequest(pathname);
428
+ return configuredDocumentPath === pathname ? [pathname] : [pathname, configuredDocumentPath];
429
+ }
430
+
431
+ /**
432
+ * @param {string} pathname
433
+ */
434
+ function configuredDocumentPathFromRequest(pathname) {
435
+ if (pathname === '/' || !pathname.endsWith('/')) {
436
+ return pathname;
437
+ }
438
+ const withoutTrailingSlash = pathname.slice(0, -1);
439
+ if (isFilePath(withoutTrailingSlash)) {
440
+ return pathname;
441
+ }
442
+ return withoutTrailingSlash || '/';
443
+ }
444
+
445
+ /**
446
+ * @param {string} pathname
447
+ */
448
+ function isFilePath(pathname) {
449
+ const segment = pathname.split('/').at(-1) || '';
450
+ return /\.[^/.]+$/.test(segment);
451
+ }
452
+
453
+ /**
454
+ * @param {PageVariant} variant
455
+ */
456
+ function archivePageNumber(variant) {
457
+ if (typeof variant === 'object' && variant.kind === 'paginated-archive') {
458
+ return variant.pageNumber;
459
+ }
460
+ return 1;
461
+ }
462
+
463
+ /**
464
+ * @param {string} pathname
465
+ * @param {string} origin
466
+ * @param {string} routePath
467
+ */
468
+ function matchPagePath(pathname, origin, routePath) {
469
+ const pattern = new URLPattern({ pathname: routePath });
470
+ return pattern.exec(pathname, origin);
471
+ }
472
+
473
+ /**
474
+ * @param {PageVariant} variant
475
+ */
476
+ function layoutForPageVariant(variant) {
477
+ if (typeof variant === 'object' && variant.kind === 'standalone-demo') {
478
+ return singleDemoLayout;
479
+ }
480
+ return layout;
481
+ }