@astrojs/starlight 0.41.11 → 0.42.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 (402) hide show
  1. package/dist/_virtual/_rolldown/runtime.js +13 -0
  2. package/dist/components/MobileMenuToggle.astro +65 -0
  3. package/{components → dist/components}/PageFrame.astro +32 -8
  4. package/dist/components-internals/Icons.d.ts +415 -0
  5. package/dist/components-internals/Icons.js +140 -0
  6. package/dist/components-internals/SidebarPersistState.d.ts +1 -0
  7. package/dist/components-internals/SidebarPersistState.js +53 -0
  8. package/dist/components-internals/TableOfContents/starlight-toc.d.ts +18 -0
  9. package/dist/components-internals/TableOfContents/starlight-toc.js +88 -0
  10. package/dist/constants.d.ts +4 -0
  11. package/dist/constants.js +4 -0
  12. package/{expressive-code.d.ts → dist/expressive-code.d.ts} +6 -13
  13. package/dist/expressive-code.js +31 -0
  14. package/dist/global.d.ts +62 -0
  15. package/dist/global.js +2 -0
  16. package/dist/index.d.ts +7 -0
  17. package/dist/index.js +109 -0
  18. package/dist/integrations/aside-utils.d.ts +9 -0
  19. package/dist/integrations/aside-utils.js +35 -0
  20. package/dist/integrations/expressive-code/hast.d.ts +1 -0
  21. package/dist/integrations/expressive-code/hast.js +2 -0
  22. package/dist/integrations/expressive-code/index.d.ts +60 -0
  23. package/dist/integrations/expressive-code/index.js +48 -0
  24. package/dist/integrations/expressive-code/preprocessor.d.ts +17 -0
  25. package/dist/integrations/expressive-code/preprocessor.js +76 -0
  26. package/dist/integrations/expressive-code/themes/night-owl-dark.js +4 -0
  27. package/dist/integrations/expressive-code/themes/night-owl-light.js +4 -0
  28. package/dist/integrations/expressive-code/theming.d.ts +16 -0
  29. package/dist/integrations/expressive-code/theming.js +71 -0
  30. package/dist/integrations/expressive-code/translations.d.ts +8 -0
  31. package/dist/integrations/expressive-code/translations.js +26 -0
  32. package/dist/integrations/markdown-icon.d.ts +8 -0
  33. package/dist/integrations/markdown-icon.js +14 -0
  34. package/dist/integrations/markdown-plugins.d.ts +18 -0
  35. package/dist/integrations/markdown-plugins.js +36 -0
  36. package/dist/integrations/markdown-processor.d.ts +29 -0
  37. package/dist/integrations/markdown-processor.js +44 -0
  38. package/dist/integrations/pagefind.d.ts +7 -0
  39. package/dist/integrations/pagefind.js +34 -0
  40. package/dist/integrations/rehype-code-rtl-support.d.ts +21 -0
  41. package/dist/integrations/rehype-code-rtl-support.js +36 -0
  42. package/dist/integrations/rehype-heading-links.d.ts +10 -0
  43. package/dist/integrations/rehype-heading-links.js +57 -0
  44. package/dist/integrations/remark-asides.d.ts +32 -0
  45. package/dist/integrations/remark-asides.js +166 -0
  46. package/dist/integrations/remark-rehype.d.ts +17 -0
  47. package/dist/integrations/remark-rehype.js +51 -0
  48. package/dist/integrations/satteri.d.ts +13 -0
  49. package/dist/integrations/satteri.js +209 -0
  50. package/dist/integrations/shared/absolutePathToLang.d.ts +10 -0
  51. package/dist/integrations/shared/absolutePathToLang.js +13 -0
  52. package/dist/integrations/shared/localeToLang.d.ts +10 -0
  53. package/dist/integrations/shared/localeToLang.js +13 -0
  54. package/dist/integrations/shared/slugToLocale.d.ts +12 -0
  55. package/dist/integrations/shared/slugToLocale.js +15 -0
  56. package/dist/integrations/sitemap.d.ts +12 -0
  57. package/dist/integrations/sitemap.js +19 -0
  58. package/dist/integrations/vite-layer-order.d.ts +15 -0
  59. package/dist/integrations/vite-layer-order.js +56 -0
  60. package/dist/integrations/vite-lazy-barrel-optimization.d.ts +12 -0
  61. package/dist/integrations/vite-lazy-barrel-optimization.js +33 -0
  62. package/dist/integrations/vite-virtual-modules.d.ts +14 -0
  63. package/dist/integrations/vite-virtual-modules.js +108 -0
  64. package/dist/internal.d.ts +3 -0
  65. package/dist/internal.js +3 -0
  66. package/dist/loaders.d.ts +20 -0
  67. package/dist/loaders.js +49 -0
  68. package/dist/locals.d.ts +15 -0
  69. package/dist/locals.js +33 -0
  70. package/dist/package.js +4 -0
  71. package/{props.ts → dist/props.d.ts} +6 -4
  72. package/dist/props.js +1 -0
  73. package/dist/route-data.d.ts +7 -0
  74. package/dist/route-data.js +6 -0
  75. package/dist/schema.d.ts +426 -0
  76. package/dist/schema.js +103 -0
  77. package/dist/schemas/badge.d.ts +86 -0
  78. package/dist/schemas/badge.js +39 -0
  79. package/dist/schemas/components.d.ts +236 -0
  80. package/dist/schemas/components.js +36 -0
  81. package/dist/schemas/expressiveCode.d.ts +7 -0
  82. package/dist/schemas/expressiveCode.js +5 -0
  83. package/dist/schemas/favicon.d.ts +9 -0
  84. package/dist/schemas/favicon.js +32 -0
  85. package/dist/schemas/head.d.ts +33 -0
  86. package/dist/schemas/head.js +41 -0
  87. package/dist/schemas/hero.d.ts +319 -0
  88. package/dist/schemas/hero.js +73 -0
  89. package/dist/schemas/i18n.d.ts +125 -0
  90. package/dist/schemas/i18n.js +76 -0
  91. package/dist/schemas/icon.d.ts +284 -0
  92. package/dist/schemas/icon.js +7 -0
  93. package/dist/schemas/locale.d.ts +20 -0
  94. package/dist/schemas/locale.js +11 -0
  95. package/dist/schemas/logo.d.ts +32 -0
  96. package/dist/schemas/logo.js +14 -0
  97. package/dist/schemas/pagefind.d.ts +166 -0
  98. package/dist/schemas/pagefind.js +31 -0
  99. package/dist/schemas/prevNextLink.d.ts +10 -0
  100. package/dist/schemas/prevNextLink.js +14 -0
  101. package/dist/schemas/sidebar.d.ts +205 -0
  102. package/dist/schemas/sidebar.js +71 -0
  103. package/dist/schemas/site-title.d.ts +7 -0
  104. package/dist/schemas/site-title.js +17 -0
  105. package/dist/schemas/social.d.ts +292 -0
  106. package/dist/schemas/social.js +18 -0
  107. package/dist/schemas/tableOfContents.d.ts +24 -0
  108. package/dist/schemas/tableOfContents.js +16 -0
  109. package/dist/translations/ar.js +36 -0
  110. package/dist/translations/ca.js +46 -0
  111. package/dist/translations/cs.js +46 -0
  112. package/dist/translations/da.js +33 -0
  113. package/dist/translations/de.js +33 -0
  114. package/dist/translations/el.js +33 -0
  115. package/dist/translations/en.js +33 -0
  116. package/dist/translations/es.js +46 -0
  117. package/dist/translations/fa.js +33 -0
  118. package/dist/translations/fi.js +33 -0
  119. package/dist/translations/fr.js +36 -0
  120. package/dist/translations/gl.js +46 -0
  121. package/dist/translations/he.js +33 -0
  122. package/dist/translations/hi.js +36 -0
  123. package/dist/translations/hu.js +46 -0
  124. package/dist/translations/id.js +33 -0
  125. package/dist/translations/index.d.ts +48 -0
  126. package/dist/translations/index.js +75 -0
  127. package/dist/translations/it.js +36 -0
  128. package/dist/translations/ja.js +36 -0
  129. package/dist/translations/ko.js +36 -0
  130. package/dist/translations/lv.js +33 -0
  131. package/dist/translations/nb.js +33 -0
  132. package/dist/translations/nl.js +33 -0
  133. package/dist/translations/pl.js +36 -0
  134. package/dist/translations/pt.js +36 -0
  135. package/dist/translations/ro.js +33 -0
  136. package/dist/translations/ru.js +36 -0
  137. package/dist/translations/sk.js +33 -0
  138. package/dist/translations/sv.js +33 -0
  139. package/dist/translations/th.js +33 -0
  140. package/dist/translations/tr.js +33 -0
  141. package/dist/translations/uk.js +33 -0
  142. package/dist/translations/vi.js +33 -0
  143. package/dist/translations/zh-CN.js +36 -0
  144. package/dist/translations/zh-TW.js +36 -0
  145. package/dist/types.d.ts +4 -0
  146. package/dist/types.js +1 -0
  147. package/{user-components → dist/user-components}/FileTree.astro +1 -1
  148. package/{user-components → dist/user-components}/Steps.astro +1 -1
  149. package/{user-components → dist/user-components}/TabItem.astro +1 -1
  150. package/{user-components → dist/user-components}/Tabs.astro +1 -1
  151. package/dist/user-components/file-tree-icons.d.ts +178 -0
  152. package/dist/user-components/file-tree-icons.js +632 -0
  153. package/dist/user-components/file-tree-processor.d.ts +16 -0
  154. package/dist/user-components/file-tree-processor.js +132 -0
  155. package/dist/user-components/steps-processor.d.ts +10 -0
  156. package/dist/user-components/steps-processor.js +39 -0
  157. package/dist/user-components/tabs-processor.d.ts +23 -0
  158. package/dist/user-components/tabs-processor.js +70 -0
  159. package/dist/utils/base.d.ts +7 -0
  160. package/dist/utils/base.js +15 -0
  161. package/dist/utils/canonical.d.ts +10 -0
  162. package/dist/utils/canonical.js +14 -0
  163. package/dist/utils/collection-fs.d.ts +13 -0
  164. package/dist/utils/collection-fs.js +19 -0
  165. package/{utils/collection.ts → dist/utils/collection.d.ts} +9 -20
  166. package/dist/utils/collection.js +26 -0
  167. package/dist/utils/createPathFormatter.d.ts +9 -0
  168. package/dist/utils/createPathFormatter.js +35 -0
  169. package/dist/utils/createTranslationSystem.d.ts +23 -0
  170. package/dist/utils/createTranslationSystem.js +84 -0
  171. package/dist/utils/error-map.d.ts +23 -0
  172. package/dist/utils/error-map.js +125 -0
  173. package/dist/utils/format-path.d.ts +4 -0
  174. package/dist/utils/format-path.js +9 -0
  175. package/dist/utils/generateToC.d.ts +14 -0
  176. package/dist/utils/generateToC.js +25 -0
  177. package/dist/utils/git.d.ts +12 -0
  178. package/dist/utils/git.js +71 -0
  179. package/dist/utils/gitInlined.d.ts +6 -0
  180. package/dist/utils/gitInlined.js +11 -0
  181. package/dist/utils/head.d.ts +7 -0
  182. package/dist/utils/head.js +233 -0
  183. package/dist/utils/i18n.d.ts +50 -0
  184. package/dist/utils/i18n.js +141 -0
  185. package/dist/utils/localizedUrl.d.ts +8 -0
  186. package/dist/utils/localizedUrl.js +28 -0
  187. package/dist/utils/navigation.d.ts +19 -0
  188. package/dist/utils/navigation.js +353 -0
  189. package/dist/utils/path.d.ts +21 -0
  190. package/dist/utils/path.js +51 -0
  191. package/dist/utils/plugins.d.ts +721 -0
  192. package/dist/utils/plugins.js +174 -0
  193. package/dist/utils/routing/data.d.ts +20 -0
  194. package/dist/utils/routing/data.js +127 -0
  195. package/dist/utils/routing/index.d.ts +21 -0
  196. package/dist/utils/routing/index.js +106 -0
  197. package/dist/utils/routing/middleware.d.ts +11 -0
  198. package/dist/utils/routing/middleware.js +65 -0
  199. package/dist/utils/routing/types.d.ts +93 -0
  200. package/dist/utils/routing/types.js +1 -0
  201. package/dist/utils/slugs.d.ts +45 -0
  202. package/dist/utils/slugs.js +104 -0
  203. package/dist/utils/starlight-page.d.ts +36 -0
  204. package/dist/utils/starlight-page.js +120 -0
  205. package/dist/utils/translations-fs.d.ts +15 -0
  206. package/dist/utils/translations-fs.js +39 -0
  207. package/dist/utils/translations.d.ts +20 -0
  208. package/dist/utils/translations.js +29 -0
  209. package/dist/utils/types.d.ts +5 -0
  210. package/dist/utils/types.js +1 -0
  211. package/dist/utils/url.d.ts +7 -0
  212. package/{utils/url.ts → dist/utils/url.js} +5 -4
  213. package/dist/utils/user-config.d.ts +2055 -0
  214. package/dist/utils/user-config.js +136 -0
  215. package/dist/utils/validateLogoImports.d.ts +5 -0
  216. package/dist/utils/validateLogoImports.js +16 -0
  217. package/dist/utils/zodDeepMerge.d.ts +16 -0
  218. package/dist/utils/zodDeepMerge.js +53 -0
  219. package/package.json +60 -34
  220. package/CHANGELOG.md +0 -3051
  221. package/components/MobileMenuToggle.astro +0 -114
  222. package/components-internals/Icons.ts +0 -248
  223. package/components-internals/SidebarPersistState.ts +0 -71
  224. package/components-internals/TableOfContents/starlight-toc.ts +0 -127
  225. package/constants.ts +0 -4
  226. package/expressive-code.mjs +0 -23
  227. package/global.d.ts +0 -11
  228. package/i18n.d.ts +0 -18
  229. package/index.ts +0 -200
  230. package/integrations/aside-utils.ts +0 -41
  231. package/integrations/expressive-code/hast.d.ts +0 -5
  232. package/integrations/expressive-code/hast.mjs +0 -9
  233. package/integrations/expressive-code/index.ts +0 -125
  234. package/integrations/expressive-code/preprocessor.ts +0 -126
  235. package/integrations/expressive-code/themes/night-owl-dark.jsonc +0 -1796
  236. package/integrations/expressive-code/themes/night-owl-light.jsonc +0 -1695
  237. package/integrations/expressive-code/theming.ts +0 -105
  238. package/integrations/expressive-code/translations.ts +0 -36
  239. package/integrations/markdown-icon.ts +0 -13
  240. package/integrations/markdown-plugins.ts +0 -71
  241. package/integrations/markdown-processor.ts +0 -64
  242. package/integrations/pagefind.ts +0 -60
  243. package/integrations/rehype-code-rtl-support.ts +0 -37
  244. package/integrations/rehype-heading-links.ts +0 -79
  245. package/integrations/remark-asides.ts +0 -219
  246. package/integrations/remark-rehype.ts +0 -87
  247. package/integrations/satteri.ts +0 -265
  248. package/integrations/shared/absolutePathToLang.ts +0 -26
  249. package/integrations/shared/localeToLang.ts +0 -15
  250. package/integrations/shared/slugToLocale.ts +0 -18
  251. package/integrations/sitemap.ts +0 -23
  252. package/integrations/vite-layer-order.ts +0 -69
  253. package/integrations/vite-lazy-barrel-optimization.ts +0 -40
  254. package/integrations/vite-virtual-modules.ts +0 -175
  255. package/internal.ts +0 -6
  256. package/loaders.ts +0 -63
  257. package/locals.d.ts +0 -43
  258. package/locals.ts +0 -43
  259. package/route-data.ts +0 -11
  260. package/schema.ts +0 -167
  261. package/schemas/badge.ts +0 -40
  262. package/schemas/components.ts +0 -265
  263. package/schemas/expressiveCode.ts +0 -12
  264. package/schemas/favicon.ts +0 -40
  265. package/schemas/head.ts +0 -50
  266. package/schemas/hero.ts +0 -69
  267. package/schemas/i18n.ts +0 -244
  268. package/schemas/icon.ts +0 -7
  269. package/schemas/logo.ts +0 -28
  270. package/schemas/pagefind.ts +0 -128
  271. package/schemas/prevNextLink.ts +0 -18
  272. package/schemas/sidebar.ts +0 -163
  273. package/schemas/site-title.ts +0 -20
  274. package/schemas/social.ts +0 -23
  275. package/schemas/tableOfContents.ts +0 -21
  276. package/translations/README.md +0 -14
  277. package/translations/ar.json +0 -33
  278. package/translations/ca.json +0 -43
  279. package/translations/cs.json +0 -43
  280. package/translations/da.json +0 -30
  281. package/translations/de.json +0 -30
  282. package/translations/el.json +0 -30
  283. package/translations/en.json +0 -30
  284. package/translations/es.json +0 -43
  285. package/translations/fa.json +0 -30
  286. package/translations/fi.json +0 -30
  287. package/translations/fr.json +0 -33
  288. package/translations/gl.json +0 -43
  289. package/translations/he.json +0 -30
  290. package/translations/hi.json +0 -33
  291. package/translations/hu.json +0 -43
  292. package/translations/id.json +0 -30
  293. package/translations/index.ts +0 -76
  294. package/translations/it.json +0 -33
  295. package/translations/ja.json +0 -33
  296. package/translations/ko.json +0 -33
  297. package/translations/lv.json +0 -30
  298. package/translations/nb.json +0 -30
  299. package/translations/nl.json +0 -30
  300. package/translations/pl.json +0 -33
  301. package/translations/pt.json +0 -33
  302. package/translations/ro.json +0 -30
  303. package/translations/ru.json +0 -33
  304. package/translations/sk.json +0 -30
  305. package/translations/sv.json +0 -30
  306. package/translations/th.json +0 -30
  307. package/translations/tr.json +0 -30
  308. package/translations/uk.json +0 -30
  309. package/translations/vi.json +0 -30
  310. package/translations/zh-CN.json +0 -33
  311. package/translations/zh-TW.json +0 -33
  312. package/types.ts +0 -7
  313. package/user-components/file-tree-icons.ts +0 -782
  314. package/user-components/rehype-file-tree.ts +0 -251
  315. package/user-components/rehype-steps.ts +0 -85
  316. package/user-components/rehype-tabs.ts +0 -121
  317. package/utils/base.ts +0 -15
  318. package/utils/canonical.ts +0 -19
  319. package/utils/collection-fs.ts +0 -20
  320. package/utils/createPathFormatter.ts +0 -60
  321. package/utils/createTranslationSystem.ts +0 -137
  322. package/utils/error-map.ts +0 -222
  323. package/utils/format-path.ts +0 -7
  324. package/utils/generateToC.ts +0 -33
  325. package/utils/git.ts +0 -121
  326. package/utils/gitInlined.ts +0 -20
  327. package/utils/head.ts +0 -235
  328. package/utils/i18n.ts +0 -221
  329. package/utils/localizedUrl.ts +0 -51
  330. package/utils/navigation.ts +0 -574
  331. package/utils/path.ts +0 -58
  332. package/utils/plugins.ts +0 -498
  333. package/utils/routing/data.ts +0 -154
  334. package/utils/routing/index.ts +0 -139
  335. package/utils/routing/middleware.ts +0 -81
  336. package/utils/routing/types.ts +0 -101
  337. package/utils/slugs.ts +0 -119
  338. package/utils/starlight-page.ts +0 -205
  339. package/utils/translations-fs.ts +0 -54
  340. package/utils/translations.ts +0 -57
  341. package/utils/types.ts +0 -15
  342. package/utils/user-config.ts +0 -348
  343. package/utils/validateLogoImports.ts +0 -21
  344. package/utils/zodDeepMerge.ts +0 -144
  345. package/virtual-internal.d.ts +0 -149
  346. package/virtual.d.ts +0 -27
  347. /package/{components → dist/components}/AnchorHeading.astro +0 -0
  348. /package/{components → dist/components}/Banner.astro +0 -0
  349. /package/{components → dist/components}/ContentNotice.astro +0 -0
  350. /package/{components → dist/components}/ContentPanel.astro +0 -0
  351. /package/{components → dist/components}/DraftContentNotice.astro +0 -0
  352. /package/{components → dist/components}/EditLink.astro +0 -0
  353. /package/{components → dist/components}/FallbackContentNotice.astro +0 -0
  354. /package/{components → dist/components}/Footer.astro +0 -0
  355. /package/{components → dist/components}/Head.astro +0 -0
  356. /package/{components → dist/components}/Header.astro +0 -0
  357. /package/{components → dist/components}/Hero.astro +0 -0
  358. /package/{components → dist/components}/LanguageSelect.astro +0 -0
  359. /package/{components → dist/components}/LastUpdated.astro +0 -0
  360. /package/{components → dist/components}/MarkdownContent.astro +0 -0
  361. /package/{components → dist/components}/MobileMenuFooter.astro +0 -0
  362. /package/{components → dist/components}/MobileTableOfContents.astro +0 -0
  363. /package/{components → dist/components}/Page.astro +0 -0
  364. /package/{components → dist/components}/PageSidebar.astro +0 -0
  365. /package/{components → dist/components}/PageTitle.astro +0 -0
  366. /package/{components → dist/components}/Pagination.astro +0 -0
  367. /package/{components → dist/components}/Search.astro +0 -0
  368. /package/{components → dist/components}/Select.astro +0 -0
  369. /package/{components → dist/components}/Sidebar.astro +0 -0
  370. /package/{components → dist/components}/SidebarPersister.astro +0 -0
  371. /package/{components → dist/components}/SidebarRestorePoint.astro +0 -0
  372. /package/{components → dist/components}/SidebarSublist.astro +0 -0
  373. /package/{components → dist/components}/SiteTitle.astro +0 -0
  374. /package/{components → dist/components}/SkipLink.astro +0 -0
  375. /package/{components → dist/components}/SocialIcons.astro +0 -0
  376. /package/{components → dist/components}/StarlightPage.astro +0 -0
  377. /package/{components → dist/components}/TableOfContents.astro +0 -0
  378. /package/{components → dist/components}/ThemeProvider.astro +0 -0
  379. /package/{components → dist/components}/ThemeSelect.astro +0 -0
  380. /package/{components → dist/components}/TwoColumnContent.astro +0 -0
  381. /package/{components-internals → dist/components-internals}/TableOfContents/TableOfContentsList.astro +0 -0
  382. /package/{components.ts → dist/components.ts} +0 -0
  383. /package/{routes → dist/routes}/common.astro +0 -0
  384. /package/{routes → dist/routes}/ssr/404.astro +0 -0
  385. /package/{routes → dist/routes}/ssr/index.astro +0 -0
  386. /package/{routes → dist/routes}/static/404.astro +0 -0
  387. /package/{routes → dist/routes}/static/index.astro +0 -0
  388. /package/{style → dist/style}/anchor-links.css +0 -0
  389. /package/{style → dist/style}/asides.css +0 -0
  390. /package/{style → dist/style}/layers.css +0 -0
  391. /package/{style → dist/style}/markdown.css +0 -0
  392. /package/{style → dist/style}/print.css +0 -0
  393. /package/{style → dist/style}/props.css +0 -0
  394. /package/{style → dist/style}/reset.css +0 -0
  395. /package/{style → dist/style}/util.css +0 -0
  396. /package/{user-components → dist/user-components}/Aside.astro +0 -0
  397. /package/{user-components → dist/user-components}/Badge.astro +0 -0
  398. /package/{user-components → dist/user-components}/Card.astro +0 -0
  399. /package/{user-components → dist/user-components}/CardGrid.astro +0 -0
  400. /package/{user-components → dist/user-components}/Icon.astro +0 -0
  401. /package/{user-components → dist/user-components}/LinkButton.astro +0 -0
  402. /package/{user-components → dist/user-components}/LinkCard.astro +0 -0
@@ -1,574 +0,0 @@
1
- import { AstroError } from 'astro/errors';
2
- import project from 'virtual:starlight/project-context';
3
- import config from 'virtual:starlight/user-config';
4
- import type { Badge, I18nBadge, I18nBadgeConfig } from '../schemas/badge';
5
- import type { PrevNextLinkConfig } from '../schemas/prevNextLink';
6
- import type {
7
- AutoSidebarEntries,
8
- InternalSidebarLinkItem,
9
- LinkHTMLAttributes,
10
- SidebarItem,
11
- SidebarLinkItem,
12
- } from '../schemas/sidebar';
13
- import { getCollectionPathFromRoot } from './collection';
14
- import { createPathFormatter } from './createPathFormatter';
15
- import { formatPath } from './format-path';
16
- import { BuiltInDefaultLocale, pickLang } from './i18n';
17
- import {
18
- ensureLeadingSlash,
19
- ensureTrailingSlash,
20
- stripExtension,
21
- stripLeadingAndTrailingSlashes,
22
- } from './path';
23
- import { getLocaleRoutes, getRouteById } from './routing';
24
- import type {
25
- SidebarGroup,
26
- SidebarLink,
27
- SidebarManualLink,
28
- PaginationLinks,
29
- Route,
30
- SidebarEntry,
31
- SidebarAutoLink,
32
- SidebarAutoGroup,
33
- SidebarAutogenerateRouteData,
34
- } from './routing/types';
35
- import { localeToLang, localizedFilePath, slugToPathname } from './slugs';
36
- import { hasProtocol } from './url';
37
- import type { StarlightConfig } from './user-config';
38
-
39
- const DirKey = Symbol('DirKey');
40
- const SlugKey = Symbol('SlugKey');
41
-
42
- const rootAutogenerate: SidebarAutogenerateRouteData = { directory: '' };
43
-
44
- const neverPathFormatter = createPathFormatter({ trailingSlash: 'never' });
45
- const sidebarCollator = new Intl.Collator(localeToLang(undefined));
46
-
47
- const docsCollectionPathFromRoot = getCollectionPathFromRoot('docs', project);
48
-
49
- /**
50
- * A representation of the route structure. For each object entry:
51
- * if it’s a folder, the key is the directory name, and value is the directory
52
- * content; if it’s a route entry, the key is the last segment of the route, and value
53
- * is the full entry.
54
- */
55
- interface Dir {
56
- [DirKey]: undefined;
57
- [SlugKey]: string;
58
- [item: string]: Dir | Route;
59
- }
60
-
61
- /** Create a new directory object. */
62
- function makeDir(slug: string): Dir {
63
- const dir = {} as Dir;
64
- // Add DirKey and SlugKey as non-enumerable properties so that `Object.entries(dir)` ignores them.
65
- Object.defineProperty(dir, DirKey, { enumerable: false });
66
- Object.defineProperty(dir, SlugKey, { value: slug, enumerable: false });
67
- return dir;
68
- }
69
-
70
- /** Test if the passed object is a directory record. */
71
- function isDir(data: Record<string, unknown>): data is Dir {
72
- return DirKey in data;
73
- }
74
-
75
- /** Convert an item in a user’s sidebar config to a sidebar entry. */
76
- function configItemToEntry(
77
- item: SidebarItem,
78
- locale: string | undefined,
79
- routes: Route[]
80
- ): SidebarEntry | SidebarEntry[] {
81
- if ('link' in item) {
82
- return linkFromSidebarLinkItem(item, locale);
83
- } else if ('autogenerate' in item) {
84
- return entriesFromAutogenerateConfig(item, locale, routes);
85
- } else if ('slug' in item) {
86
- return linkFromInternalSidebarLinkItem(item, locale);
87
- } else {
88
- const label = pickLang(item.translations, localeToLang(locale)) || item.label;
89
- return {
90
- type: 'group',
91
- label,
92
- entries: item.items.flatMap((i) => configItemToEntry(i, locale, routes)),
93
- collapsed: item.collapsed,
94
- badge: getSidebarBadge(item.badge, locale, label),
95
- };
96
- }
97
- }
98
-
99
- /** Autogenerate links and groups from a user’s sidebar config. */
100
- function entriesFromAutogenerateConfig(
101
- item: AutoSidebarEntries,
102
- locale: string | undefined,
103
- routes: Route[]
104
- ): (SidebarAutoLink | SidebarGroup)[] {
105
- const { attrs, collapsed, directory } = item.autogenerate;
106
- const localeDir = locale ? locale + '/' + directory : directory;
107
- const autogenerate = { directory };
108
- const dirDocs = routes.filter((doc) => {
109
- const filePathFromContentDir = getRoutePathRelativeToCollectionRoot(doc, locale);
110
- return (
111
- // Match against `foo.md` or `foo/index.md`.
112
- stripExtension(filePathFromContentDir) === localeDir ||
113
- // Match against `foo/anything/else.md`.
114
- filePathFromContentDir.startsWith(localeDir + '/')
115
- );
116
- });
117
- const tree = treeify(dirDocs, locale, localeDir);
118
- return sidebarFromDir(tree, { collapsed: collapsed ?? false, attrs }, autogenerate);
119
- }
120
-
121
- /** Create a link entry from a manual link item in user config. */
122
- function linkFromSidebarLinkItem(item: SidebarLinkItem, locale: string | undefined) {
123
- let href = item.link;
124
- if (!hasProtocol(href)) {
125
- href = ensureLeadingSlash(href);
126
- // Inject current locale into link.
127
- if (locale) href = '/' + locale + href;
128
- }
129
- const label = pickLang(item.translations, localeToLang(locale)) || item.label;
130
- return makeSidebarLink({
131
- href,
132
- label,
133
- badge: getSidebarBadge(item.badge, locale, label),
134
- attrs: item.attrs,
135
- });
136
- }
137
-
138
- /** Create a link entry from an automatic internal link item in user config. */
139
- function linkFromInternalSidebarLinkItem(
140
- item: InternalSidebarLinkItem,
141
- locale: string | undefined
142
- ) {
143
- // Astro passes root `index.[md|mdx]` entries with a slug of `index`
144
- const slug = item.slug === 'index' ? '' : item.slug;
145
- const localizedSlug = locale ? (slug ? locale + '/' + slug : locale) : slug;
146
- const route = getRouteById(localizedSlug);
147
- if (!route) {
148
- const hasExternalSlashes = item.slug.at(0) === '/' || item.slug.at(-1) === '/';
149
- if (hasExternalSlashes) {
150
- throw new AstroError(
151
- `The slug \`"${item.slug}"\` specified in the Starlight sidebar config must not start or end with a slash.`,
152
- `Please try updating \`"${item.slug}"\` to \`"${stripLeadingAndTrailingSlashes(item.slug)}"\`.`
153
- );
154
- } else {
155
- throw new AstroError(
156
- `The slug \`"${item.slug}"\` specified in the Starlight sidebar config does not exist.`,
157
- 'Update the Starlight config to reference a valid entry slug in the docs content collection.\n' +
158
- 'Learn more about Astro content collection slugs at https://docs.astro.build/en/reference/modules/astro-content/#getentry'
159
- );
160
- }
161
- }
162
- const frontmatter = route.entry.data;
163
- const label =
164
- pickLang(item.translations, localeToLang(locale)) ||
165
- item.label ||
166
- frontmatter.sidebar?.label ||
167
- frontmatter.title;
168
- const badge = item.badge ?? frontmatter.sidebar?.badge;
169
- const attrs = { ...frontmatter.sidebar?.attrs, ...item.attrs };
170
- return makeSidebarLink({
171
- href: slugToPathname(route.id),
172
- label,
173
- badge: getSidebarBadge(badge, locale, label),
174
- attrs,
175
- });
176
- }
177
-
178
- interface MakeLinkOptions {
179
- autogenerate?: SidebarAutogenerateRouteData | undefined;
180
- href: string;
181
- label: string;
182
- badge?: Badge | undefined;
183
- attrs?: LinkHTMLAttributes | undefined;
184
- }
185
-
186
- /** Process sidebar link options to create a link entry. */
187
- function makeSidebarLink(opts: MakeLinkOptions & { autogenerate?: undefined }): SidebarManualLink;
188
- function makeSidebarLink(
189
- opts: MakeLinkOptions & { autogenerate: SidebarAutogenerateRouteData }
190
- ): SidebarAutoLink;
191
- function makeSidebarLink({ attrs, badge, href, label, autogenerate }: MakeLinkOptions) {
192
- if (!hasProtocol(href)) {
193
- href = formatPath(href);
194
- }
195
- return makeLink({ label, href, badge, attrs, autogenerate });
196
- }
197
-
198
- /** Create a link entry */
199
- function makeLink({ attrs = {}, badge, autogenerate, ...opts }: MakeLinkOptions): SidebarLink {
200
- return {
201
- type: 'link',
202
- ...opts,
203
- badge,
204
- isCurrent: false,
205
- attrs,
206
- ...(autogenerate ? { autogenerate } : {}),
207
- };
208
- }
209
-
210
- /** Get the segments leading to a page. */
211
- function getBreadcrumbs(path: string, baseDir: string): string[] {
212
- // Strip extension from path.
213
- const pathWithoutExt = stripExtension(path);
214
- // Index paths will match `baseDir` and don’t include breadcrumbs.
215
- if (pathWithoutExt === baseDir) return [];
216
- // Ensure base directory ends in a trailing slash.
217
- baseDir = ensureTrailingSlash(baseDir);
218
- // Strip base directory from path if present.
219
- const relativePath = pathWithoutExt.startsWith(baseDir)
220
- ? pathWithoutExt.replace(baseDir, '')
221
- : pathWithoutExt;
222
-
223
- return relativePath.split('/');
224
- }
225
-
226
- /** Return the path of a route relative to the root of the collection. */
227
- function getRoutePathRelativeToCollectionRoot(route: Route, locale: string | undefined) {
228
- // Use a localized filePath relative to the collection
229
- return localizedFilePath(
230
- route.entry.filePath.replace(`${docsCollectionPathFromRoot}/`, ''),
231
- locale
232
- );
233
- }
234
-
235
- /** Turn a flat array of routes into a tree structure. */
236
- function treeify(routes: Route[], locale: string | undefined, baseDir: string): Dir {
237
- const treeRoot: Dir = makeDir(baseDir);
238
- routes
239
- // Remove any entries that should be hidden
240
- .filter((doc) => !doc.entry.data.sidebar.hidden)
241
- // Compute the path of each entry from the root of the collection ahead of time.
242
- .map((doc) => [getRoutePathRelativeToCollectionRoot(doc, locale), doc] as const)
243
- // Sort by depth, to build the tree depth first.
244
- .sort(([a], [b]) => b.split('/').length - a.split('/').length)
245
- // Build the tree
246
- .forEach(([filePathFromContentDir, doc]) => {
247
- const parts = getBreadcrumbs(filePathFromContentDir, baseDir);
248
- let currentNode = treeRoot;
249
-
250
- parts.forEach((part, index) => {
251
- const isLeaf = index === parts.length - 1;
252
-
253
- // Handle directory index pages by renaming them to `index`
254
- if (isLeaf && Object.hasOwn(currentNode, part)) {
255
- currentNode = currentNode[part] as Dir;
256
- part = 'index';
257
- }
258
-
259
- // Recurse down the tree if this isn’t the leaf node.
260
- if (!isLeaf) {
261
- const path = currentNode[SlugKey];
262
- currentNode[part] ||= makeDir(stripLeadingAndTrailingSlashes(path + '/' + part));
263
- currentNode = currentNode[part] as Dir;
264
- } else {
265
- currentNode[part] = doc;
266
- }
267
- });
268
- });
269
-
270
- return treeRoot;
271
- }
272
-
273
- /** Create a link entry for a given content collection entry. */
274
- function linkFromRoute(
275
- route: Route,
276
- attrs: LinkHTMLAttributes | undefined,
277
- autogenerate: SidebarAutogenerateRouteData
278
- ): SidebarAutoLink {
279
- return makeSidebarLink({
280
- href: slugToPathname(route.id),
281
- label: route.entry.data.sidebar.label || route.entry.data.title,
282
- badge: route.entry.data.sidebar.badge,
283
- attrs: { ...attrs, ...route.entry.data.sidebar.attrs },
284
- autogenerate,
285
- });
286
- }
287
-
288
- /**
289
- * Get the sort weight for a given route or directory. Lower numbers rank higher.
290
- * Directories have the weight of the lowest weighted route they contain.
291
- */
292
- function getOrder(routeOrDir: Route | Dir): number {
293
- const cachedOrder = orderByEntry.get(routeOrDir);
294
- if (cachedOrder !== undefined) return cachedOrder;
295
- const order = isDir(routeOrDir)
296
- ? Math.min(...Object.values(routeOrDir).flatMap(getOrder))
297
- : // If no order value is found, set it to the largest number possible.
298
- (routeOrDir.entry.data.sidebar.order ?? Number.MAX_VALUE);
299
- orderByEntry.set(routeOrDir, order);
300
- return order;
301
- }
302
-
303
- const orderByEntry = new WeakMap<Dir | Route, number>();
304
-
305
- /** Sort a directory’s entries by user-specified order or alphabetically if no order specified. */
306
- function sortDirEntries(dir: [string, Dir | Route][]): [string, Dir | Route][] {
307
- return dir.sort(([_keyA, a], [_keyB, b]) => {
308
- const [aOrder, bOrder] = [getOrder(a), getOrder(b)];
309
- // Pages are sorted by order in ascending order.
310
- if (aOrder !== bOrder) return aOrder < bOrder ? -1 : 1;
311
- // If two pages have the same order value they will be sorted by their slug.
312
- return sidebarCollator.compare(isDir(a) ? a[SlugKey] : a.id, isDir(b) ? b[SlugKey] : b.id);
313
- });
314
- }
315
-
316
- interface SidebarDirOptions {
317
- collapsed: boolean;
318
- attrs: LinkHTMLAttributes | undefined;
319
- }
320
-
321
- interface SidebarDirContext extends SidebarDirOptions {
322
- fullPath: string;
323
- dirName: string;
324
- autogenerate: SidebarAutogenerateRouteData;
325
- }
326
-
327
- /** Create a group entry for a given content collection directory. */
328
- function groupFromDir(dir: Dir, context: SidebarDirContext): SidebarAutoGroup {
329
- const { fullPath, dirName, collapsed, autogenerate } = context;
330
- const entries = sortDirEntries(Object.entries(dir)).map(([key, dirOrRoute]) =>
331
- dirToItem(dirOrRoute, { ...context, fullPath: `${fullPath}/${key}`, dirName: key })
332
- );
333
- return {
334
- type: 'group',
335
- label: dirName,
336
- entries,
337
- collapsed,
338
- badge: undefined,
339
- autogenerate,
340
- };
341
- }
342
-
343
- /** Create a sidebar entry for a directory or content entry. */
344
- function dirToItem(
345
- dirOrRoute: Dir[string],
346
- context: SidebarDirContext
347
- ): SidebarAutoGroup | SidebarAutoLink {
348
- const { attrs, autogenerate } = context;
349
- return isDir(dirOrRoute)
350
- ? groupFromDir(dirOrRoute, context)
351
- : linkFromRoute(dirOrRoute, attrs, autogenerate);
352
- }
353
-
354
- /** Create a sidebar entry for a given content directory. */
355
- function sidebarFromDir(
356
- tree: Dir,
357
- options: SidebarDirOptions,
358
- autogenerate: SidebarAutogenerateRouteData = rootAutogenerate
359
- ) {
360
- return sortDirEntries(Object.entries(tree)).map(([key, dirOrRoute]) =>
361
- dirToItem(dirOrRoute, { ...options, fullPath: key, dirName: key, autogenerate })
362
- );
363
- }
364
-
365
- /**
366
- * Intermediate sidebar represents sidebar entries generated from the user config for a specific
367
- * locale. These representations are cached per locale to avoid regenerating them for each page.
368
- * When generating the final sidebar for a page, the current page entry in the sidebar is marked
369
- * with `isCurrent` and cached. Subsequent runs then reset the previous current entry before marking
370
- * the new current page.
371
- *
372
- * Sidebars, like all route data, are deep cloned before the data is passed to users for mutation,
373
- * so optimising with a single mutable object per locale is safe.
374
- *
375
- * @see getSidebarFromIntermediateSidebar
376
- */
377
- const intermediateSidebars = new Map<string | undefined, SidebarEntry[]>();
378
- const lastCurrentEntryByLocale = new Map<string | undefined, SidebarLink>();
379
-
380
- /** Get the sidebar for the current page using the global config. */
381
- export function getSidebar(pathname: string, locale: string | undefined): SidebarEntry[] {
382
- let intermediateSidebar = intermediateSidebars.get(locale);
383
- if (!intermediateSidebar) {
384
- intermediateSidebar = getIntermediateSidebarFromConfig(config.sidebar, locale);
385
- intermediateSidebars.set(locale, intermediateSidebar);
386
- }
387
- setIntermediateSidebarCurrentEntry(intermediateSidebar, pathname, locale);
388
- return intermediateSidebar;
389
- }
390
-
391
- /** Get the sidebar for the current page using the specified sidebar config. */
392
- export function getSidebarFromConfig(
393
- sidebarConfig: StarlightConfig['sidebar'],
394
- pathname: string,
395
- locale: string | undefined
396
- ): SidebarEntry[] {
397
- const sidebar = getIntermediateSidebarFromConfig(sidebarConfig, locale);
398
- const currentEntry = getSidebarCurrentEntry(sidebar, pathname);
399
- if (currentEntry) currentEntry.isCurrent = true;
400
- return sidebar;
401
- }
402
-
403
- /** Get the intermediate sidebar for a locale using the specified sidebar config. */
404
- function getIntermediateSidebarFromConfig(
405
- sidebarConfig: StarlightConfig['sidebar'],
406
- locale: string | undefined
407
- ): SidebarEntry[] {
408
- const routes = getLocaleRoutes(locale);
409
- if (sidebarConfig) {
410
- return sidebarConfig.flatMap((group) => configItemToEntry(group, locale, routes));
411
- } else {
412
- const tree = treeify(routes, locale, locale || '');
413
- return sidebarFromDir(tree, { collapsed: false, attrs: undefined });
414
- }
415
- }
416
-
417
- /** Marks the current page in an intermediate sidebar. */
418
- function setIntermediateSidebarCurrentEntry(
419
- intermediateSidebar: SidebarEntry[],
420
- pathname: string,
421
- locale: string | undefined
422
- ): void {
423
- // Reset the `isCurrent` flag in this sidebar if it was previously set.
424
- const lastCurrentEntry = lastCurrentEntryByLocale.get(locale);
425
- if (lastCurrentEntry) {
426
- lastCurrentEntry.isCurrent = false;
427
- }
428
- // Find the new current entry.
429
- const entry = getSidebarCurrentEntry(intermediateSidebar, pathname);
430
- // Mark it as current and store it to be reset later.
431
- if (entry) {
432
- entry.isCurrent = true;
433
- lastCurrentEntryByLocale.set(locale, entry);
434
- }
435
- }
436
-
437
- /** Finds the current page in a sidebar. */
438
- function getSidebarCurrentEntry(sidebar: SidebarEntry[], pathname: string): SidebarLink | null {
439
- let entriesByPath = sidebarEntriesByPath.get(sidebar);
440
- if (!entriesByPath) {
441
- entriesByPath = new Map();
442
- for (const entry of flattenSidebar(sidebar)) {
443
- const path = neverPathFormatter(encodeURI(entry.href));
444
- if (!entriesByPath.has(path)) entriesByPath.set(path, entry);
445
- }
446
- sidebarEntriesByPath.set(sidebar, entriesByPath);
447
- }
448
- return entriesByPath.get(neverPathFormatter(pathname)) ?? null;
449
- }
450
-
451
- const sidebarEntriesByPath = new WeakMap<SidebarEntry[], Map<string, SidebarLink>>();
452
-
453
- /** Generates a deterministic string based on the content of the passed sidebar. */
454
- export function getSidebarHash(sidebar: SidebarEntry[]): string {
455
- let hash = 0;
456
- const sidebarIdentity = recursivelyBuildSidebarIdentity(sidebar);
457
- for (let i = 0; i < sidebarIdentity.length; i++) {
458
- const char = sidebarIdentity.charCodeAt(i);
459
- hash = (hash << 5) - hash + char;
460
- }
461
- return (hash >>> 0).toString(36).padStart(7, '0');
462
- }
463
-
464
- /** Recurses through a sidebar tree to generate a string concatenating labels and link hrefs. */
465
- function recursivelyBuildSidebarIdentity(sidebar: SidebarEntry[]): string {
466
- return sidebar
467
- .flatMap((entry) =>
468
- entry.type === 'group'
469
- ? entry.label + recursivelyBuildSidebarIdentity(entry.entries)
470
- : entry.label + entry.href
471
- )
472
- .join('');
473
- }
474
-
475
- /** Turn the nested tree structure of a sidebar into a flat list of all the links. */
476
- export function flattenSidebar(sidebar: SidebarEntry[]): SidebarLink[] {
477
- const cachedSidebar = flattenedSidebars.get(sidebar);
478
- if (cachedSidebar) return cachedSidebar;
479
- const flattenedSidebar = sidebar.flatMap((entry) =>
480
- entry.type === 'group' ? flattenSidebar(entry.entries) : entry
481
- );
482
- flattenedSidebars.set(sidebar, flattenedSidebar);
483
- return flattenedSidebar;
484
- }
485
-
486
- const flattenedSidebars = new WeakMap<SidebarEntry[], SidebarLink[]>();
487
-
488
- /** Get previous/next pages in the sidebar or the ones from the frontmatter if any. */
489
- export function getPrevNextLinks(
490
- sidebar: SidebarEntry[],
491
- paginationEnabled: boolean,
492
- config: {
493
- prev?: PrevNextLinkConfig;
494
- next?: PrevNextLinkConfig;
495
- }
496
- ): PaginationLinks {
497
- const entries = flattenSidebar(sidebar);
498
- const currentIndex = entries.findIndex((entry) => entry.isCurrent);
499
- const prev = applyPrevNextLinkConfig(entries[currentIndex - 1], paginationEnabled, config.prev);
500
- const next = applyPrevNextLinkConfig(
501
- currentIndex > -1 ? entries[currentIndex + 1] : undefined,
502
- paginationEnabled,
503
- config.next
504
- );
505
- return { prev, next };
506
- }
507
-
508
- /** Apply a prev/next link config to a navigation link. */
509
- function applyPrevNextLinkConfig(
510
- link: SidebarLink | undefined,
511
- paginationEnabled: boolean,
512
- config: PrevNextLinkConfig | undefined
513
- ): SidebarLink | undefined {
514
- // Explicitly remove the link.
515
- if (config === false) return undefined;
516
- // Use the generated link if any.
517
- else if (config === true) return link;
518
- // If a link exists, update its label if needed.
519
- else if (typeof config === 'string' && link) {
520
- return { ...link, label: config };
521
- } else if (typeof config === 'object') {
522
- if (link) {
523
- // If a link exists, update both its label and href if needed.
524
- return {
525
- ...link,
526
- label: config.label ?? link.label,
527
- href: config.link ?? link.href,
528
- // Explicitly remove sidebar link attributes for prev/next links.
529
- attrs: {},
530
- };
531
- } else if (config.link && config.label) {
532
- // If there is no link and the frontmatter contains both a URL and a label,
533
- // create a new link.
534
- return makeLink({ href: config.link, label: config.label });
535
- }
536
- }
537
- // Otherwise, if the global config is enabled, return the generated link if any.
538
- return paginationEnabled ? link : undefined;
539
- }
540
-
541
- /** Get a sidebar badge for a given item. */
542
- function getSidebarBadge(
543
- config: I18nBadgeConfig,
544
- locale: string | undefined,
545
- itemLabel: string
546
- ): Badge | undefined {
547
- if (!config) return;
548
- if (typeof config === 'string') {
549
- return { variant: 'default', text: config };
550
- }
551
- return { ...config, text: getSidebarBadgeText(config.text, locale, itemLabel) };
552
- }
553
-
554
- /** Get the badge text for a sidebar item. */
555
- function getSidebarBadgeText(
556
- text: I18nBadge['text'],
557
- locale: string | undefined,
558
- itemLabel: string
559
- ): string {
560
- if (typeof text === 'string') return text;
561
- const defaultLang =
562
- config.defaultLocale?.lang || config.defaultLocale?.locale || BuiltInDefaultLocale.lang;
563
- const defaultText = text[defaultLang];
564
-
565
- if (!defaultText) {
566
- throw new AstroError(
567
- `The badge text for "${itemLabel}" must have a key for the default language "${defaultLang}".`,
568
- 'Update the Starlight config to include a badge text for the default language.\n' +
569
- 'Learn more about sidebar badges internationalization at https://starlight.astro.build/guides/sidebar/#internationalization-with-badges'
570
- );
571
- }
572
-
573
- return pickLang(text, localeToLang(locale)) || defaultText;
574
- }
package/utils/path.ts DELETED
@@ -1,58 +0,0 @@
1
- /** Ensure the passed path starts with a leading slash. */
2
- export function ensureLeadingSlash(href: string): string {
3
- if (href[0] !== '/') href = '/' + href;
4
- return href;
5
- }
6
-
7
- /** Ensure the passed path ends with a trailing slash. */
8
- export function ensureTrailingSlash(href: string): string {
9
- if (href[href.length - 1] !== '/') href += '/';
10
- return href;
11
- }
12
-
13
- /** Ensure the passed path starts and ends with slashes. */
14
- export function ensureLeadingAndTrailingSlashes(href: string): string {
15
- href = ensureLeadingSlash(href);
16
- href = ensureTrailingSlash(href);
17
- return href;
18
- }
19
-
20
- /** Ensure the passed path does not start with a leading slash. */
21
- export function stripLeadingSlash(href: string) {
22
- if (href[0] === '/') href = href.slice(1);
23
- return href;
24
- }
25
-
26
- /** Ensure the passed path does not end with a trailing slash. */
27
- export function stripTrailingSlash(href: string) {
28
- if (href[href.length - 1] === '/') href = href.slice(0, -1);
29
- return href;
30
- }
31
-
32
- /** Ensure the passed path does not start and end with slashes. */
33
- export function stripLeadingAndTrailingSlashes(href: string): string {
34
- href = stripLeadingSlash(href);
35
- href = stripTrailingSlash(href);
36
- return href;
37
- }
38
-
39
- /** Remove the extension from a path. */
40
- export function stripHtmlExtension(path: string) {
41
- const pathWithoutTrailingSlash = stripTrailingSlash(path);
42
- return pathWithoutTrailingSlash.endsWith('.html') ? pathWithoutTrailingSlash.slice(0, -5) : path;
43
- }
44
-
45
- /** Add '.html' extension to a path. */
46
- export function ensureHtmlExtension(path: string) {
47
- path = stripLeadingAndTrailingSlashes(path);
48
- if (!path.endsWith('.html')) {
49
- path = path ? path + '.html' : '/index.html';
50
- }
51
- return ensureLeadingSlash(path);
52
- }
53
-
54
- /** Remove the extension from a path. */
55
- export function stripExtension(path: string) {
56
- const periodIndex = path.lastIndexOf('.');
57
- return path.slice(0, periodIndex > -1 ? periodIndex : undefined);
58
- }