@wrikka/create-docs 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 (380) hide show
  1. package/README.md +137 -0
  2. package/bin/cli.js +408 -0
  3. package/dist/index.cjs +20 -0
  4. package/dist/index.d.cts +475 -0
  5. package/dist/index.d.ts +475 -0
  6. package/dist/index.js +20 -0
  7. package/docs/_dir.yml +2 -0
  8. package/docs/config.md +75 -0
  9. package/docs/data-sources.md +88 -0
  10. package/docs/deploy.md +38 -0
  11. package/docs/features.md +51 -0
  12. package/docs/getting-started.md +42 -0
  13. package/docs/index.md +34 -0
  14. package/docs/markdown.md +154 -0
  15. package/docs/theming.md +53 -0
  16. package/package.json +142 -0
  17. package/scripts/generate-docs-index.ts +111 -0
  18. package/scripts/github-pull.ts +124 -0
  19. package/src/adapters/config/index.ts +1 -0
  20. package/src/adapters/config/node-config-loader.ts +36 -0
  21. package/src/adapters/fs/fs-docs-loader.ts +31 -0
  22. package/src/adapters/fs/index.ts +12 -0
  23. package/src/adapters/fs/memory-fs.ts +60 -0
  24. package/src/adapters/fs/node-fs.ts +116 -0
  25. package/src/adapters/git/index.ts +164 -0
  26. package/src/adapters/github/github-pull.ts +340 -0
  27. package/src/adapters/index.ts +6 -0
  28. package/src/adapters/velite.ts +112 -0
  29. package/src/adapters/vite/docs-watcher.ts +36 -0
  30. package/src/adapters/vite/index.ts +2 -0
  31. package/src/adapters/vite/polling-watcher.ts +89 -0
  32. package/src/code-block-enhancements/index.ts +3 -0
  33. package/src/config/index.ts +15 -0
  34. package/src/content/index.ts +11 -0
  35. package/src/content-validation/index.ts +5 -0
  36. package/src/index.ts +174 -0
  37. package/src/live-editor/index.ts +7 -0
  38. package/src/modules/analytics/application/plausible-adapter.ts +103 -0
  39. package/src/modules/analytics/application/umami-adapter.ts +109 -0
  40. package/src/modules/analytics/domain/events/index.ts +37 -0
  41. package/src/modules/analytics/domain/models/index.ts +35 -0
  42. package/src/modules/analytics/domain/operations/analytics-operations.ts +67 -0
  43. package/src/modules/analytics/domain/validators/index.ts +50 -0
  44. package/src/modules/analytics/index.ts +9 -0
  45. package/src/modules/analytics/ports/analytics-port.ts +17 -0
  46. package/src/modules/analytics/types/analytics.ts +16 -0
  47. package/src/modules/api-docs/application/type-docs-adapter.ts +34 -0
  48. package/src/modules/api-docs/domain/events/index.ts +42 -0
  49. package/src/modules/api-docs/domain/models/index.ts +40 -0
  50. package/src/modules/api-docs/domain/operations/api-docs-operations.ts +59 -0
  51. package/src/modules/api-docs/domain/validators/index.ts +56 -0
  52. package/src/modules/api-docs/index.ts +8 -0
  53. package/src/modules/api-docs/ports/api-docs-port.ts +21 -0
  54. package/src/modules/api-docs/types/api-docs.ts +15 -0
  55. package/src/modules/api-reference/openapi.ts +166 -0
  56. package/src/modules/api-reference/orpc.ts +94 -0
  57. package/src/modules/code-block-enhancements/application/usecases/index.ts +39 -0
  58. package/src/modules/code-block-enhancements/domain/code-operations.ts +72 -0
  59. package/src/modules/code-block-enhancements/domain/events/index.ts +58 -0
  60. package/src/modules/code-block-enhancements/domain/index.ts +11 -0
  61. package/src/modules/code-block-enhancements/domain/models/index.ts +36 -0
  62. package/src/modules/code-block-enhancements/domain/validators/index.ts +50 -0
  63. package/src/modules/code-block-enhancements/index.ts +10 -0
  64. package/src/modules/code-block-enhancements/ports/index.ts +18 -0
  65. package/src/modules/code-block-enhancements/types/index.ts +37 -0
  66. package/src/modules/code-execution/application/sandbox-adapter.ts +44 -0
  67. package/src/modules/code-execution/domain/events/index.ts +58 -0
  68. package/src/modules/code-execution/domain/models/index.ts +33 -0
  69. package/src/modules/code-execution/domain/operations/execution-operations.ts +60 -0
  70. package/src/modules/code-execution/domain/validators/index.ts +50 -0
  71. package/src/modules/code-execution/index.ts +8 -0
  72. package/src/modules/code-execution/ports/execution-port.ts +15 -0
  73. package/src/modules/code-execution/types/execution.ts +15 -0
  74. package/src/modules/comments/application/giscus-adapter.ts +55 -0
  75. package/src/modules/comments/domain/events/index.ts +55 -0
  76. package/src/modules/comments/domain/models/index.ts +30 -0
  77. package/src/modules/comments/domain/operations/comment-operations.ts +46 -0
  78. package/src/modules/comments/domain/validators/index.ts +50 -0
  79. package/src/modules/comments/index.ts +8 -0
  80. package/src/modules/comments/ports/comment-port.ts +18 -0
  81. package/src/modules/comments/types/comments.ts +19 -0
  82. package/src/modules/config/application/usecases/index.ts +1 -0
  83. package/src/modules/config/application/usecases/loadConfig.ts +38 -0
  84. package/src/modules/config/application/workflows/index.ts +1 -0
  85. package/src/modules/config/application/workflows/validateConfig.ts +14 -0
  86. package/src/modules/config/domain/events/index.ts +50 -0
  87. package/src/modules/config/domain/models/index.ts +9 -0
  88. package/src/modules/config/domain/operations/defaults.ts +74 -0
  89. package/src/modules/config/domain/operations/index.ts +2 -0
  90. package/src/modules/config/domain/operations/resolve.ts +40 -0
  91. package/src/modules/config/domain/validators/index.ts +152 -0
  92. package/src/modules/config/index.ts +23 -0
  93. package/src/modules/config/ports/index.ts +15 -0
  94. package/src/modules/config/types/index.ts +113 -0
  95. package/src/modules/config/types/public.ts +19 -0
  96. package/src/modules/content/application/cache/index.ts +6 -0
  97. package/src/modules/content/application/cache/parse-cache.ts +6 -0
  98. package/src/modules/content/application/cache/parseCache.ts +56 -0
  99. package/src/modules/content/application/usecases/index.ts +2 -0
  100. package/src/modules/content/application/usecases/load-doc-page.ts +1 -0
  101. package/src/modules/content/application/usecases/loadDocPage.ts +111 -0
  102. package/src/modules/content/application/usecases/scanDocsDir.ts +105 -0
  103. package/src/modules/content/application/workflows/index.ts +1 -0
  104. package/src/modules/content/application/workflows/processDocs.ts +51 -0
  105. package/src/modules/content/domain/events/index.ts +61 -0
  106. package/src/modules/content/domain/models/index.ts +13 -0
  107. package/src/modules/content/domain/operations/build-page.ts +146 -0
  108. package/src/modules/content/domain/operations/buildPage.ts +93 -0
  109. package/src/modules/content/domain/operations/index.ts +17 -0
  110. package/src/modules/content/domain/operations/parse.ts +241 -0
  111. package/src/modules/content/domain/validators/frontmatter.ts +82 -0
  112. package/src/modules/content/domain/validators/index.ts +6 -0
  113. package/src/modules/content/index.ts +27 -0
  114. package/src/modules/content/ports/index.ts +45 -0
  115. package/src/modules/content/ports/velite.ts +134 -0
  116. package/src/modules/content/types/index.ts +76 -0
  117. package/src/modules/content/types/public.ts +1 -0
  118. package/src/modules/content-linting/application/builtin-linting-adapter.ts +82 -0
  119. package/src/modules/content-linting/domain/events/index.ts +58 -0
  120. package/src/modules/content-linting/domain/models/index.ts +46 -0
  121. package/src/modules/content-linting/domain/operations/linting-operations.ts +82 -0
  122. package/src/modules/content-linting/domain/validators/index.ts +50 -0
  123. package/src/modules/content-linting/index.ts +8 -0
  124. package/src/modules/content-linting/ports/linting-port.ts +21 -0
  125. package/src/modules/content-linting/types/linting.ts +18 -0
  126. package/src/modules/content-validation/application/usecases/index.ts +18 -0
  127. package/src/modules/content-validation/domain/events/index.ts +7 -0
  128. package/src/modules/content-validation/domain/index.ts +16 -0
  129. package/src/modules/content-validation/domain/models/index.ts +33 -0
  130. package/src/modules/content-validation/domain/operations/index.ts +157 -0
  131. package/src/modules/content-validation/domain/types/index.ts +17 -0
  132. package/src/modules/content-validation/domain/validators/index.ts +8 -0
  133. package/src/modules/content-validation/index.ts +9 -0
  134. package/src/modules/content-validation/ports/index.ts +15 -0
  135. package/src/modules/content-validation/types/index.ts +5 -0
  136. package/src/modules/export/application/puppeteer-export-adapter.ts +63 -0
  137. package/src/modules/export/domain/events/index.ts +55 -0
  138. package/src/modules/export/domain/models/index.ts +38 -0
  139. package/src/modules/export/domain/operations/export-operations.ts +56 -0
  140. package/src/modules/export/domain/validators/index.ts +52 -0
  141. package/src/modules/export/index.ts +8 -0
  142. package/src/modules/export/ports/export-port.ts +15 -0
  143. package/src/modules/export/types/export.ts +15 -0
  144. package/src/modules/external-search/application/algolia-adapter.ts +182 -0
  145. package/src/modules/external-search/application/meilisearch-adapter.ts +161 -0
  146. package/src/modules/external-search/domain/events/index.ts +42 -0
  147. package/src/modules/external-search/domain/models/index.ts +42 -0
  148. package/src/modules/external-search/domain/operations/search-operations.ts +53 -0
  149. package/src/modules/external-search/domain/validators/index.ts +55 -0
  150. package/src/modules/external-search/index.ts +9 -0
  151. package/src/modules/external-search/ports/external-search-port.ts +23 -0
  152. package/src/modules/external-search/types/search.ts +19 -0
  153. package/src/modules/git-history/application/simple-git-adapter.ts +46 -0
  154. package/src/modules/git-history/domain/events/index.ts +34 -0
  155. package/src/modules/git-history/domain/models/index.ts +25 -0
  156. package/src/modules/git-history/domain/operations/git-history-operations.ts +56 -0
  157. package/src/modules/git-history/domain/validators/index.ts +52 -0
  158. package/src/modules/git-history/index.ts +8 -0
  159. package/src/modules/git-history/ports/git-history-port.ts +21 -0
  160. package/src/modules/git-history/types/git-history.ts +15 -0
  161. package/src/modules/i18n/application/fs-locale-loader.ts +39 -0
  162. package/src/modules/i18n/domain/events/index.ts +34 -0
  163. package/src/modules/i18n/domain/models/index.ts +34 -0
  164. package/src/modules/i18n/domain/operations/locale-operations.ts +74 -0
  165. package/src/modules/i18n/domain/validators/index.ts +52 -0
  166. package/src/modules/i18n/index.ts +8 -0
  167. package/src/modules/i18n/ports/locale-loader-port.ts +18 -0
  168. package/src/modules/i18n/types/locale.ts +17 -0
  169. package/src/modules/image-optimization/application/sharp-adapter.ts +58 -0
  170. package/src/modules/image-optimization/domain/events/index.ts +39 -0
  171. package/src/modules/image-optimization/domain/models/index.ts +27 -0
  172. package/src/modules/image-optimization/domain/operations/image-operations.ts +65 -0
  173. package/src/modules/image-optimization/domain/validators/index.ts +52 -0
  174. package/src/modules/image-optimization/index.ts +8 -0
  175. package/src/modules/image-optimization/ports/image-optimizer-port.ts +15 -0
  176. package/src/modules/image-optimization/types/image.ts +17 -0
  177. package/src/modules/live-editor/application/usecases/index.ts +60 -0
  178. package/src/modules/live-editor/domain/events/index.ts +37 -0
  179. package/src/modules/live-editor/domain/gitOperations.ts +110 -0
  180. package/src/modules/live-editor/domain/index.ts +15 -0
  181. package/src/modules/live-editor/domain/models/index.ts +21 -0
  182. package/src/modules/live-editor/domain/validators/index.ts +55 -0
  183. package/src/modules/live-editor/index.ts +14 -0
  184. package/src/modules/live-editor/ports/index.ts +49 -0
  185. package/src/modules/live-editor/types/index.ts +80 -0
  186. package/src/modules/live-preview/application/vite-preview-adapter.ts +20 -0
  187. package/src/modules/live-preview/domain/operations/preview-operations.ts +56 -0
  188. package/src/modules/live-preview/index.ts +8 -0
  189. package/src/modules/live-preview/ports/preview-port.ts +20 -0
  190. package/src/modules/live-preview/types/preview.ts +16 -0
  191. package/src/modules/mdx/application/mdx-js-adapter.ts +38 -0
  192. package/src/modules/mdx/domain/operations/parse-mdx.ts +43 -0
  193. package/src/modules/mdx/index.ts +8 -0
  194. package/src/modules/mdx/ports/mdx-parser-port.ts +15 -0
  195. package/src/modules/mdx/types/mdx.ts +14 -0
  196. package/src/modules/navigation/application/usecases/buildNav.ts +15 -0
  197. package/src/modules/navigation/application/usecases/buildSidebar.ts +45 -0
  198. package/src/modules/navigation/application/usecases/index.ts +2 -0
  199. package/src/modules/navigation/domain/events/index.ts +30 -0
  200. package/src/modules/navigation/domain/models/index.ts +7 -0
  201. package/src/modules/navigation/domain/operations/group.ts +79 -0
  202. package/src/modules/navigation/domain/operations/index.ts +10 -0
  203. package/src/modules/navigation/domain/operations/sort.ts +27 -0
  204. package/src/modules/navigation/domain/shared/utils/array.ts +39 -0
  205. package/src/modules/navigation/domain/validators/index.ts +88 -0
  206. package/src/modules/navigation/index.ts +15 -0
  207. package/src/modules/navigation/ports/index.ts +9 -0
  208. package/src/modules/navigation/types/index.ts +38 -0
  209. package/src/modules/navigation/types/public.ts +9 -0
  210. package/src/modules/openapi/application/swagger-parser-adapter.ts +178 -0
  211. package/src/modules/openapi/domain/operations/parse-openapi.ts +46 -0
  212. package/src/modules/openapi/index.ts +8 -0
  213. package/src/modules/openapi/ports/openapi-parser-port.ts +15 -0
  214. package/src/modules/openapi/types/openapi.ts +37 -0
  215. package/src/modules/plugin/application/usecases/build-virtual-modules.ts +53 -0
  216. package/src/modules/plugin/application/usecases/index.ts +1 -0
  217. package/src/modules/plugin/application/workflows/build-docs.ts +79 -0
  218. package/src/modules/plugin/application/workflows/index.ts +1 -0
  219. package/src/modules/plugin/domain/events/index.ts +62 -0
  220. package/src/modules/plugin/domain/models/index.ts +1 -0
  221. package/src/modules/plugin/domain/operations/build-virtual.ts +76 -0
  222. package/src/modules/plugin/domain/operations/index.ts +11 -0
  223. package/src/modules/plugin/domain/validators/index.ts +30 -0
  224. package/src/modules/plugin/index.ts +14 -0
  225. package/src/modules/plugin/ports/index.ts +21 -0
  226. package/src/modules/plugin/types/index.ts +27 -0
  227. package/src/modules/plugin/types/public.ts +1 -0
  228. package/src/modules/search/application/usecases/index.ts +1 -0
  229. package/src/modules/search/application/usecases/indexPages.ts +15 -0
  230. package/src/modules/search/domain/events/index.ts +32 -0
  231. package/src/modules/search/domain/models/index.ts +1 -0
  232. package/src/modules/search/domain/operations/build-index.ts +6 -0
  233. package/src/modules/search/domain/operations/buildIndex.ts +70 -0
  234. package/src/modules/search/domain/operations/index.ts +7 -0
  235. package/src/modules/search/domain/operations/query.ts +186 -0
  236. package/src/modules/search/domain/validators/index.ts +32 -0
  237. package/src/modules/search/index.ts +9 -0
  238. package/src/modules/search/types/index.ts +22 -0
  239. package/src/modules/search/types/public.ts +1 -0
  240. package/src/modules/search-analytics/application/local-storage-adapter.ts +68 -0
  241. package/src/modules/search-analytics/domain/operations/search-analytics-operations.ts +74 -0
  242. package/src/modules/search-analytics/index.ts +8 -0
  243. package/src/modules/search-analytics/ports/search-analytics-port.ts +20 -0
  244. package/src/modules/search-analytics/types/search-analytics.ts +16 -0
  245. package/src/modules/search-highlighting/application/dom-highlighting-adapter.ts +36 -0
  246. package/src/modules/search-highlighting/domain/operations/highlighting-operations.ts +72 -0
  247. package/src/modules/search-highlighting/index.ts +8 -0
  248. package/src/modules/search-highlighting/ports/highlighting-port.ts +14 -0
  249. package/src/modules/search-highlighting/types/highlighting.ts +15 -0
  250. package/src/modules/seo-optimization/application/usecases/index.ts +18 -0
  251. package/src/modules/seo-optimization/domain/index.ts +12 -0
  252. package/src/modules/seo-optimization/domain/seoOperations.ts +92 -0
  253. package/src/modules/seo-optimization/index.ts +8 -0
  254. package/src/modules/seo-optimization/ports/index.ts +18 -0
  255. package/src/modules/seo-optimization/types/index.ts +41 -0
  256. package/src/modules/theme/application/css-theme-adapter.ts +50 -0
  257. package/src/modules/theme/domain/operations/theme-operations.ts +68 -0
  258. package/src/modules/theme/index.ts +8 -0
  259. package/src/modules/theme/ports/theme-port.ts +8 -0
  260. package/src/modules/theme/types/theme.ts +12 -0
  261. package/src/modules/toc/application/markdown-toc-adapter.ts +106 -0
  262. package/src/modules/toc/domain/operations/toc-operations.ts +92 -0
  263. package/src/modules/toc/index.ts +8 -0
  264. package/src/modules/toc/ports/toc-port.ts +21 -0
  265. package/src/modules/toc/types/toc.ts +17 -0
  266. package/src/modules/toc-generator/application/usecases/index.ts +30 -0
  267. package/src/modules/toc-generator/domain/index.ts +12 -0
  268. package/src/modules/toc-generator/domain/tocOperations.ts +104 -0
  269. package/src/modules/toc-generator/index.ts +9 -0
  270. package/src/modules/toc-generator/ports/index.ts +20 -0
  271. package/src/modules/toc-generator/types/index.ts +50 -0
  272. package/src/modules/version-control/domain/events/index.ts +39 -0
  273. package/src/modules/version-control/domain/models/index.ts +25 -0
  274. package/src/modules/version-control/domain/validators/index.ts +50 -0
  275. package/src/modules/versioning/application/fs-version-loader.ts +44 -0
  276. package/src/modules/versioning/domain/operations/versioning-operations.ts +80 -0
  277. package/src/modules/versioning/index.ts +8 -0
  278. package/src/modules/versioning/ports/version-loader-port.ts +18 -0
  279. package/src/modules/versioning/types/versioning.ts +17 -0
  280. package/src/modules/workflow/domain/events/index.ts +55 -0
  281. package/src/modules/workflow/domain/models/index.ts +40 -0
  282. package/src/modules/workflow/domain/validators/index.ts +50 -0
  283. package/src/modules/yaml-parser/application/js-yaml-adapter.ts +34 -0
  284. package/src/modules/yaml-parser/domain/operations/parse-yaml.ts +58 -0
  285. package/src/modules/yaml-parser/index.ts +7 -0
  286. package/src/modules/yaml-parser/ports/yaml-parser-port.ts +18 -0
  287. package/src/navigation/index.ts +9 -0
  288. package/src/plugin/index.ts +4 -0
  289. package/src/presentation/index.ts +1 -0
  290. package/src/presentation/live-editor/index.ts +22 -0
  291. package/src/presentation/live-editor/live-editor-component.ts +219 -0
  292. package/src/presentation/live-editor/virtual-module.ts +16 -0
  293. package/src/presentation/solid/CodeBlock.tsx +120 -0
  294. package/src/presentation/solid/Toc.tsx +58 -0
  295. package/src/presentation/solid/index.ts +11 -0
  296. package/src/presentation/vite-plugin/docs-plugin.ts +212 -0
  297. package/src/presentation/vite-plugin/index.ts +9 -0
  298. package/src/presentation/vite-plugin/transform-markdown.ts +26 -0
  299. package/src/runtime/analytics.ts +59 -0
  300. package/src/runtime/api-diff.ts +115 -0
  301. package/src/runtime/app.tsx +32 -0
  302. package/src/runtime/components/AccentPicker.tsx +114 -0
  303. package/src/runtime/components/ApiPlayground.tsx +332 -0
  304. package/src/runtime/components/AskAiDialog.tsx +127 -0
  305. package/src/runtime/components/BackToTop.tsx +38 -0
  306. package/src/runtime/components/Banner.tsx +75 -0
  307. package/src/runtime/components/Breadcrumbs.tsx +42 -0
  308. package/src/runtime/components/CollectionDropdown.tsx +85 -0
  309. package/src/runtime/components/CustomizeDrawer.tsx +195 -0
  310. package/src/runtime/components/DocMarkdown.tsx +579 -0
  311. package/src/runtime/components/DocPrevNext.tsx +54 -0
  312. package/src/runtime/components/DocToc.tsx +147 -0
  313. package/src/runtime/components/DocsDropdown.tsx +184 -0
  314. package/src/runtime/components/Footer.tsx +52 -0
  315. package/src/runtime/components/GitHubAuth.tsx +82 -0
  316. package/src/runtime/components/GitHubStats.tsx +73 -0
  317. package/src/runtime/components/Head.tsx +225 -0
  318. package/src/runtime/components/LocaleDropdown.tsx +75 -0
  319. package/src/runtime/components/MobileBottomNav.tsx +111 -0
  320. package/src/runtime/components/NotFound.tsx +139 -0
  321. package/src/runtime/components/PageActions.tsx +144 -0
  322. package/src/runtime/components/RelatedDocs.tsx +50 -0
  323. package/src/runtime/components/ScrollProgress.tsx +32 -0
  324. package/src/runtime/components/SearchPalette.tsx +333 -0
  325. package/src/runtime/components/ShowcaseCard.tsx +87 -0
  326. package/src/runtime/components/SidebarNav.tsx +237 -0
  327. package/src/runtime/components/Skeleton.tsx +39 -0
  328. package/src/runtime/components/ThemeToggle.tsx +26 -0
  329. package/src/runtime/components/TopNav.tsx +270 -0
  330. package/src/runtime/components/VersionDropdown.tsx +189 -0
  331. package/src/runtime/components/markdown/components.ts +280 -0
  332. package/src/runtime/config.ts +225 -0
  333. package/src/runtime/content.ts +451 -0
  334. package/src/runtime/context.tsx +30 -0
  335. package/src/runtime/data.ts +93 -0
  336. package/src/runtime/github.ts +226 -0
  337. package/src/runtime/icons.ts +32 -0
  338. package/src/runtime/index.ts +153 -0
  339. package/src/runtime/layouts/DocsLayout.tsx +144 -0
  340. package/src/runtime/markdown-content.css +996 -0
  341. package/src/runtime/pages/AbTestPage.tsx +88 -0
  342. package/src/runtime/pages/AnalyticsPage.tsx +90 -0
  343. package/src/runtime/pages/ApiDiffPage.tsx +86 -0
  344. package/src/runtime/pages/ApiEndpointPage.tsx +248 -0
  345. package/src/runtime/pages/BuilderPage.tsx +150 -0
  346. package/src/runtime/pages/ChangelogPage.tsx +210 -0
  347. package/src/runtime/pages/CollectionPage.tsx +140 -0
  348. package/src/runtime/pages/CommunityPage.tsx +384 -0
  349. package/src/runtime/pages/CreatePage.tsx +448 -0
  350. package/src/runtime/pages/DocPage.tsx +193 -0
  351. package/src/runtime/pages/EditPage.tsx +201 -0
  352. package/src/runtime/pages/HomePage.tsx +187 -0
  353. package/src/runtime/pages/IssuesPage.tsx +140 -0
  354. package/src/runtime/pages/OAuthCallbackPage.tsx +80 -0
  355. package/src/runtime/pages/PluginsPage.tsx +249 -0
  356. package/src/runtime/pages/ShowcasePage.tsx +202 -0
  357. package/src/runtime/pwa.ts +39 -0
  358. package/src/runtime/router.tsx +192 -0
  359. package/src/runtime/seo.ts +192 -0
  360. package/src/runtime/theme.css +175 -0
  361. package/src/runtime/theme.ts +224 -0
  362. package/src/runtime/types.ts +217 -0
  363. package/src/search/index.ts +6 -0
  364. package/src/seo-optimization/index.ts +4 -0
  365. package/src/shared/constants/index.ts +26 -0
  366. package/src/shared/errors/index.ts +195 -0
  367. package/src/shared/index.ts +27 -0
  368. package/src/shared/types/brand.ts +14 -0
  369. package/src/shared/types/client.d.ts +45 -0
  370. package/src/shared/types/either.ts +38 -0
  371. package/src/shared/types/index.ts +42 -0
  372. package/src/shared/types/option.ts +52 -0
  373. package/src/shared/types/result.ts +81 -0
  374. package/src/shared/utils/array.ts +97 -0
  375. package/src/shared/utils/index.ts +46 -0
  376. package/src/shared/utils/object.ts +47 -0
  377. package/src/shared/utils/string.ts +74 -0
  378. package/src/toc-generator/index.ts +1 -0
  379. package/src/types/js-yaml.d.ts +42 -0
  380. package/src/worker.ts +462 -0
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Use case: load a single doc file by its slug and produce a DocPage.
3
+ * Orchestrates FS read → frontmatter parse → DocPage build.
4
+ */
5
+
6
+ import type { AppError } from "@create-docs/shared/errors";
7
+ import { fileNotFound, parseError } from "@create-docs/shared/errors";
8
+ import { err, ok, type Result } from "@create-docs/shared/types/result";
9
+ import { toPosixPath } from "@create-docs/shared/utils/string";
10
+ import { buildDocPage } from "../../domain/operations/build-page";
11
+ import {
12
+ isMarkdownFile,
13
+ parseFrontmatter,
14
+ } from "../../domain/operations/parse";
15
+ import type { FileReader } from "../../ports";
16
+ import type { DocPage } from "../../types";
17
+ import { FilePath } from "../../types";
18
+ import type { ParseCache } from "../cache";
19
+
20
+ export interface LoadDocPageDeps {
21
+ readonly reader: FileReader;
22
+ readonly docsDir: string;
23
+ readonly slug: string;
24
+ /** Optional edit-link config; if absent, no editUrl is attached. */
25
+ readonly editLink?: {
26
+ readonly enabled?: boolean;
27
+ readonly baseUrl?: string;
28
+ readonly branch?: string;
29
+ };
30
+ /** Optional mtime+size keyed cache. When provided, unchanged files are reused. */
31
+ readonly cache?: ParseCache<DocPage>;
32
+ }
33
+
34
+ export const loadDocPage = async (
35
+ deps: LoadDocPageDeps,
36
+ ): Promise<Result<DocPage, AppError>> => {
37
+ const { reader, docsDir, slug, editLink, cache } = deps;
38
+ const filePath = `${toPosixPath(docsDir).replace(/\/$/, "")}/${slug}.md`;
39
+ const fp = FilePath(filePath);
40
+ const exists = await reader.exists(fp);
41
+ if (!exists) {
42
+ const mdocPath = FilePath(
43
+ `${toPosixPath(docsDir).replace(/\/$/, "")}/${slug}.mdoc`,
44
+ );
45
+ const mdocExists = await reader.exists(mdocPath);
46
+ if (!mdocExists) return err(fileNotFound(filePath));
47
+ return readAndBuild(reader, mdocPath, slug, editLink, cache);
48
+ }
49
+ return readAndBuild(reader, fp, slug, editLink, cache);
50
+ };
51
+
52
+ const readAndBuild = async (
53
+ reader: FileReader,
54
+ filePath: FilePath,
55
+ slug: string,
56
+ editLink: LoadDocPageDeps["editLink"],
57
+ cache: ParseCache<DocPage> | undefined,
58
+ ): Promise<Result<DocPage, AppError>> => {
59
+ const statResult = await reader.stat(filePath);
60
+ if (statResult.ok && cache) {
61
+ const key = `${filePath}@${statResult.value.mtimeMs.toFixed(0)}:${statResult.value.size}`;
62
+ const hit = cache.get(key);
63
+ if (hit) return ok(hit);
64
+ }
65
+ const readResult = await reader.read(filePath);
66
+ if (!readResult.ok) return readResult;
67
+ const raw = readResult.value;
68
+ const fileName = filePath.split(/[/\\]/).pop() ?? "";
69
+ if (!isMarkdownFile(fileName)) {
70
+ return err(parseError(filePath, "Not a markdown file"));
71
+ }
72
+ const parsed = parseFrontmatter(raw);
73
+ const lastModifiedMs = statResult.ok ? statResult.value.mtimeMs : undefined;
74
+ const editUrl = computeEditUrl(String(filePath), editLink);
75
+ const page = buildDocPage({
76
+ slug,
77
+ filePath,
78
+ parsed,
79
+ ...(lastModifiedMs !== undefined ? { lastModifiedMs } : {}),
80
+ ...(editUrl !== undefined ? { editUrl } : {}),
81
+ });
82
+ if (statResult.ok && cache) {
83
+ const key = `${filePath}@${statResult.value.mtimeMs.toFixed(0)}:${statResult.value.size}`;
84
+ cache.set(key, page);
85
+ }
86
+ return ok(page);
87
+ };
88
+
89
+ const computeEditUrl = (
90
+ filePath: string,
91
+ editLink: LoadDocPageDeps["editLink"],
92
+ ): string | undefined => {
93
+ if (!editLink || editLink.enabled === false) return undefined;
94
+ const base = editLink.baseUrl ?? "";
95
+ if (base.length === 0) return undefined;
96
+ const branch = editLink.branch ?? "main";
97
+ const posix = filePath.replace(/\\/g, "/");
98
+ const trimmed = posix.replace(/^\/+/, "");
99
+ const tail = trimmed.startsWith("docs/") ? trimmed.slice(5) : trimmed;
100
+ return `${base.replace(/\/$/, "")}/edit/${branch}/${tail}`;
101
+ };
102
+
103
+ /** Load a doc page from raw content directly (test-friendly, no FS). */
104
+ export const buildDocPageFromRaw = (
105
+ slug: string,
106
+ filePath: string,
107
+ raw: string,
108
+ ): Result<DocPage, AppError> => {
109
+ const parsed = parseFrontmatter(raw);
110
+ return ok(buildDocPage({ slug, filePath, parsed }));
111
+ };
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Use case: scan a docs directory and produce a list of DocFile descriptors.
3
+ *
4
+ * This is the orchestration glue between the FS adapter and the pure domain
5
+ * operations. It does no I/O itself — it only sequences calls through ports.
6
+ */
7
+
8
+ import type { AppError } from "@create-docs/shared/errors";
9
+ import { fileNotFound } from "@create-docs/shared/errors";
10
+ import { err, ok, type Result } from "@create-docs/shared/types/result";
11
+ import { toPosixPath } from "@create-docs/shared/utils/string";
12
+ import {
13
+ extractGroup,
14
+ isMarkdownFile,
15
+ slugToId,
16
+ stripMarkdownExt,
17
+ } from "../../domain/operations/parse";
18
+ import type { DirectoryScanner, FileReader } from "../../ports";
19
+ import type { DocFile } from "../../types";
20
+ import { DocId, DocSlug, FilePath } from "../../types";
21
+
22
+ export interface ScanDocsDirDeps {
23
+ readonly scanner: DirectoryScanner;
24
+ readonly reader: FileReader;
25
+ readonly docsDir: string;
26
+ }
27
+
28
+ export const scanDocsDir = async (
29
+ deps: ScanDocsDirDeps,
30
+ ): Promise<Result<readonly DocFile[], AppError>> => {
31
+ const { scanner, reader, docsDir } = deps;
32
+ const rootExists = await reader.exists(FilePath(docsDir));
33
+ if (!rootExists) return err(fileNotFound(docsDir));
34
+
35
+ const files = await collectFiles(scanner, docsDir);
36
+ const docs: DocFile[] = [];
37
+ for (const filePath of files) {
38
+ const readResult = await reader.read(FilePath(filePath));
39
+ if (!readResult.ok) continue; // skip unreadable files; an event can be raised elsewhere
40
+ const raw = readResult.value;
41
+
42
+ // Only parse .md/.mdoc files; the extension check is also a safety guard.
43
+ const fileName = filePath.split(/[/\\]/).pop() ?? "";
44
+ if (!isMarkdownFile(fileName)) continue;
45
+
46
+ const relPath = toPosixPath(filePath)
47
+ .slice(toPosixPath(docsDir).length)
48
+ .replace(/^\//, "");
49
+ const slug = stripMarkdownExt(relPath);
50
+ const id = DocId(slugToId(slug));
51
+ const group = extractGroup(slug);
52
+
53
+ // Lazy frontmatter extract — only the order field is needed at scan time.
54
+ // Full parse happens in the loadContent use case.
55
+ const order = extractQuickOrder(raw);
56
+
57
+ docs.push({
58
+ id,
59
+ slug: DocSlug(slug),
60
+ frontmatter: {},
61
+ rawContent: raw,
62
+ filePath: FilePath(filePath),
63
+ ...(group !== undefined ? { group } : {}),
64
+ order,
65
+ });
66
+ }
67
+ docs.sort((a, b) => a.order - b.order);
68
+ return ok(docs);
69
+ };
70
+
71
+ const extractQuickOrder = (raw: string): number => {
72
+ const lines = raw.split("\n");
73
+ if (lines[0]?.trim() !== "---") return Number.MAX_SAFE_INTEGER;
74
+ let end = -1;
75
+ for (let i = 1; i < lines.length; i++) {
76
+ if (lines[i]?.trim() === "---") {
77
+ end = i;
78
+ break;
79
+ }
80
+ }
81
+ if (end === -1) return Number.MAX_SAFE_INTEGER;
82
+ for (let i = 1; i < end; i++) {
83
+ const line = lines[i] ?? "";
84
+ const m = line.match(/^order:\s*(\d+)/);
85
+ if (m?.[1]) return Number.parseInt(m[1], 10);
86
+ }
87
+ return Number.MAX_SAFE_INTEGER;
88
+ };
89
+
90
+ const collectFiles = async (
91
+ scanner: DirectoryScanner,
92
+ dir: string,
93
+ ): Promise<string[]> => {
94
+ const result = await scanner.list(dir);
95
+ if (!result.ok) return [];
96
+ const out: string[] = [];
97
+ for (const entry of result.value) {
98
+ if (entry.isDirectory) {
99
+ out.push(...(await collectFiles(scanner, entry.path)));
100
+ } else if (isMarkdownFile(entry.name)) {
101
+ out.push(entry.path);
102
+ }
103
+ }
104
+ return out;
105
+ };
@@ -0,0 +1 @@
1
+ export { processDocsWorkflow } from "./processDocs";
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Workflow: process all docs in a directory into fully-built DocPages.
3
+ * Used by the Vite plugin's `load(id)` virtual modules.
4
+ */
5
+
6
+ import type { AppError } from "@create-docs/shared/errors";
7
+ import { ok, type Result } from "@create-docs/shared/types/result";
8
+ import type { DirectoryScanner, FileReader } from "../../ports";
9
+ import type { DocPage } from "../../types";
10
+ import type { ParseCache } from "../cache";
11
+ import { loadDocPage } from "../usecases/loadDocPage";
12
+ import { scanDocsDir } from "../usecases/scanDocsDir";
13
+
14
+ export interface ProcessDocsWorkflowDeps {
15
+ readonly scanner: DirectoryScanner;
16
+ readonly reader: FileReader;
17
+ readonly docsDir: string;
18
+ /** Optional edit-link config; threaded into per-page editUrl computation. */
19
+ readonly editLink?: {
20
+ readonly enabled?: boolean;
21
+ readonly baseUrl?: string;
22
+ readonly branch?: string;
23
+ };
24
+ /** Optional mtime+size cache, shared across reloads. */
25
+ readonly cache?: ParseCache<DocPage>;
26
+ }
27
+
28
+ export const processDocsWorkflow = async (
29
+ deps: ProcessDocsWorkflowDeps,
30
+ ): Promise<Result<readonly DocPage[], AppError>> => {
31
+ const scanned = await scanDocsDir({
32
+ scanner: deps.scanner,
33
+ reader: deps.reader,
34
+ docsDir: deps.docsDir,
35
+ });
36
+ if (!scanned.ok) return scanned;
37
+
38
+ const pages: DocPage[] = [];
39
+ for (const file of scanned.value) {
40
+ const slug = typeof file.slug === "string" ? file.slug : String(file.slug);
41
+ const pageResult = await loadDocPage({
42
+ reader: deps.reader,
43
+ docsDir: deps.docsDir,
44
+ slug,
45
+ ...(deps.editLink !== undefined ? { editLink: deps.editLink } : {}),
46
+ ...(deps.cache !== undefined ? { cache: deps.cache } : {}),
47
+ });
48
+ if (pageResult.ok) pages.push(pageResult.value);
49
+ }
50
+ return ok(pages);
51
+ };
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Domain events — pure event types. No handlers, no I/O.
3
+ * Handlers live in presentation/events.
4
+ */
5
+ import type { DocPage, FilePath } from "../../types";
6
+
7
+ export type ContentEvent =
8
+ | {
9
+ readonly type: "content.discovered";
10
+ readonly filePath: FilePath;
11
+ readonly slug: string;
12
+ }
13
+ | { readonly type: "content.parsed"; readonly page: DocPage }
14
+ | { readonly type: "content.changed"; readonly filePath: FilePath }
15
+ | {
16
+ readonly type: "content.removed";
17
+ readonly filePath: FilePath;
18
+ readonly slug: string;
19
+ }
20
+ | {
21
+ readonly type: "content.parse_failed";
22
+ readonly filePath: FilePath;
23
+ readonly reason: string;
24
+ };
25
+
26
+ export const contentDiscovered = (
27
+ filePath: FilePath,
28
+ slug: string,
29
+ ): ContentEvent => ({
30
+ type: "content.discovered",
31
+ filePath,
32
+ slug,
33
+ });
34
+
35
+ export const contentParsed = (page: DocPage): ContentEvent => ({
36
+ type: "content.parsed",
37
+ page,
38
+ });
39
+
40
+ export const contentChanged = (filePath: FilePath): ContentEvent => ({
41
+ type: "content.changed",
42
+ filePath,
43
+ });
44
+
45
+ export const contentRemoved = (
46
+ filePath: FilePath,
47
+ slug: string,
48
+ ): ContentEvent => ({
49
+ type: "content.removed",
50
+ filePath,
51
+ slug,
52
+ });
53
+
54
+ export const contentParseFailed = (
55
+ filePath: FilePath,
56
+ reason: string,
57
+ ): ContentEvent => ({
58
+ type: "content.parse_failed",
59
+ filePath,
60
+ reason,
61
+ });
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Domain models — pure data shapes, no behavior.
3
+ * Re-exported for module consumers; the canonical source is `types/`.
4
+ */
5
+ export type {
6
+ DocFile,
7
+ DocId,
8
+ DocPage,
9
+ DocSlug,
10
+ FilePath,
11
+ Frontmatter,
12
+ ParsedContent,
13
+ } from "../../types";
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Pure domain operation for constructing a DocPage from parsed markdown.
3
+ */
4
+
5
+ import { toPosixPath } from "@create-docs/shared/utils/string";
6
+ import type { DocPage, Frontmatter, ParsedContent, TocItem } from "../../types";
7
+ import { DocId, DocSlug, FilePath } from "../../types";
8
+
9
+ export interface BuildDocPageInput {
10
+ slug: string;
11
+ filePath: string;
12
+ parsed: ParsedContent;
13
+ lastModifiedMs?: number;
14
+ editUrl?: string;
15
+ }
16
+
17
+ export interface EditLinkConfig {
18
+ readonly enabled?: boolean;
19
+ readonly baseUrl?: string;
20
+ readonly branch?: string;
21
+ }
22
+
23
+ const parseToc = (content: string): readonly TocItem[] => {
24
+ const headings: TocItem[] = [];
25
+ const seen = new Set<string>();
26
+ for (const line of content.split(/\r?\n/)) {
27
+ const match = line.match(/^(#{1,6})\s+(.+)$/);
28
+ if (!match) continue;
29
+ const depth = match[1].length as TocItem["depth"];
30
+ const rawText = match[2].trim();
31
+ // Strip markdown links, emphasis, and inline code markers.
32
+ const text = rawText
33
+ .replace(/\[([^\]]+)\]\([^)]+\)/g, "$1")
34
+ .replace(/[*_`]/g, "");
35
+ let id = text
36
+ .toLowerCase()
37
+ .replace(/[^a-z0-9\s-]/g, "")
38
+ .replace(/\s+/g, "-")
39
+ .replace(/-+/g, "-")
40
+ .replace(/^-+|-+$/g, "");
41
+ if (!id) id = `heading-${headings.length}`;
42
+ if (seen.has(id)) {
43
+ let suffix = 1;
44
+ let candidate = `${id}-${suffix}`;
45
+ while (seen.has(candidate)) {
46
+ suffix++;
47
+ candidate = `${id}-${suffix}`;
48
+ }
49
+ id = candidate;
50
+ }
51
+ seen.add(id);
52
+ headings.push({ id, text, depth });
53
+ }
54
+ return headings;
55
+ };
56
+
57
+ const deriveTitle = (
58
+ frontmatter: Frontmatter,
59
+ filePath: string,
60
+ slug: string,
61
+ ): string => {
62
+ if (typeof frontmatter.title === "string" && frontmatter.title.trim()) {
63
+ return frontmatter.title.trim();
64
+ }
65
+ const name = filePath.split(/[/\\]/).pop() ?? slug;
66
+ const withoutExt = name.replace(/\.mdx?$/i, "");
67
+ const last = withoutExt.replace(/[-_]/g, " ").split(" ").pop() ?? withoutExt;
68
+ return last.charAt(0).toUpperCase() + last.slice(1);
69
+ };
70
+
71
+ const deriveGroup = (
72
+ frontmatter: Frontmatter,
73
+ slug: string,
74
+ ): string | undefined => {
75
+ if (typeof frontmatter.category === "string" && frontmatter.category.trim()) {
76
+ return frontmatter.category.trim();
77
+ }
78
+ const firstSegment = slug.split("/")[0];
79
+ if (firstSegment && firstSegment !== slug) {
80
+ return firstSegment;
81
+ }
82
+ return undefined;
83
+ };
84
+
85
+ const computeMetadata = (
86
+ content: string,
87
+ lastModifiedMs: number | undefined,
88
+ editUrl: string | undefined,
89
+ ): DocPage["metadata"] => {
90
+ const words = content
91
+ .replace(/[#*_`[\](){}|]/g, " ")
92
+ .split(/\s+/)
93
+ .filter((w) => w.length > 0).length;
94
+ const readingTime = Math.max(1, Math.round(words / 200));
95
+ const metadata: DocPage["metadata"] = {
96
+ readingTime,
97
+ wordCount: words,
98
+ ...(lastModifiedMs !== undefined
99
+ ? { lastModified: new Date(lastModifiedMs).toISOString() }
100
+ : {}),
101
+ ...(editUrl !== undefined ? { editUrl } : {}),
102
+ };
103
+ return metadata;
104
+ };
105
+
106
+ export const buildEditUrl = (
107
+ filePath: string,
108
+ editLink: EditLinkConfig,
109
+ ): string | undefined => {
110
+ if (editLink.enabled === false) return undefined;
111
+ const base = editLink.baseUrl ?? "";
112
+ if (base.length === 0) return undefined;
113
+ const branch = editLink.branch ?? "main";
114
+ const posix = toPosixPath(filePath).replace(/^\/+/, "");
115
+ const tail = posix.startsWith("docs/") ? posix.slice(5) : posix;
116
+ return `${base.replace(/\/$/, "")}/edit/${branch}/${tail}`;
117
+ };
118
+
119
+ export const buildDocPage = (input: BuildDocPageInput): DocPage => {
120
+ const { slug, filePath, parsed, lastModifiedMs, editUrl } = input;
121
+ const title = deriveTitle(parsed.frontmatter, filePath, slug);
122
+ const description =
123
+ typeof parsed.frontmatter.description === "string"
124
+ ? parsed.frontmatter.description
125
+ : undefined;
126
+ const group = deriveGroup(parsed.frontmatter, slug);
127
+ const order =
128
+ typeof parsed.frontmatter.order === "number" ? parsed.frontmatter.order : 0;
129
+ const toc = parseToc(parsed.content);
130
+ const id = slug.replace(/\//g, "-");
131
+
132
+ return {
133
+ id: DocId(id),
134
+ slug: DocSlug(slug),
135
+ title,
136
+ description,
137
+ frontmatter: parsed.frontmatter,
138
+ content: parsed.content,
139
+ rawContent: parsed.rawContent,
140
+ filePath: FilePath(filePath),
141
+ group,
142
+ order,
143
+ toc,
144
+ metadata: computeMetadata(parsed.content, lastModifiedMs, editUrl),
145
+ };
146
+ };
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Pure operations for normalizing a parsed content into a DocPage model.
3
+ * No I/O, no exceptions.
4
+ */
5
+
6
+ import type { DocPage, Frontmatter, ParsedContent } from "../../types";
7
+ import { DocId, DocSlug, FilePath } from "../../types";
8
+ import {
9
+ countWords,
10
+ estimateReadingTime,
11
+ extractDescription,
12
+ extractGroup,
13
+ extractOrder,
14
+ extractTitle,
15
+ extractToc,
16
+ plainText,
17
+ slugToId,
18
+ } from "./parse";
19
+
20
+ export interface BuildDocPageInput {
21
+ readonly slug: string;
22
+ readonly filePath: string;
23
+ readonly parsed: ParsedContent;
24
+ /** Optional mtime (ms since epoch) from the underlying FS. */
25
+ readonly lastModifiedMs?: number;
26
+ /** Optional per-page edit URL (resolved from config.editLink). */
27
+ readonly editUrl?: string;
28
+ }
29
+
30
+ export const buildDocPage = ({
31
+ slug,
32
+ filePath,
33
+ parsed,
34
+ lastModifiedMs,
35
+ editUrl,
36
+ }: BuildDocPageInput): DocPage => {
37
+ const { frontmatter, content, rawContent } = parsed;
38
+ const id = DocId(slugToId(slug));
39
+ const group = extractGroup(slug);
40
+ const text = plainText(content);
41
+ const wordCount = countWords(text);
42
+ const readingTime = estimateReadingTime(wordCount);
43
+ const toc = extractToc(content);
44
+ const metadata: DocPage["metadata"] = {
45
+ readingTime,
46
+ wordCount,
47
+ ...(lastModifiedMs !== undefined
48
+ ? { lastModified: new Date(lastModifiedMs).toISOString() }
49
+ : {}),
50
+ ...(editUrl !== undefined ? { editUrl } : {}),
51
+ };
52
+ return {
53
+ id,
54
+ slug: DocSlug(slug),
55
+ title: extractTitle(frontmatter, slug),
56
+ ...maybeDescription(frontmatter),
57
+ frontmatter,
58
+ content,
59
+ rawContent,
60
+ filePath: FilePath(filePath),
61
+ ...(group !== undefined ? { group } : {}),
62
+ order: extractOrder(frontmatter),
63
+ toc,
64
+ metadata,
65
+ };
66
+ };
67
+
68
+ const maybeDescription = (
69
+ f: Frontmatter,
70
+ ): { description: string } | Record<string, never> => {
71
+ const d = extractDescription(f);
72
+ return d !== undefined ? { description: d } : {};
73
+ };
74
+
75
+ /** Compute an edit URL for a file path from the edit-link config. */
76
+ export const buildEditUrl = (
77
+ filePath: string,
78
+ editLink:
79
+ | { enabled?: boolean; baseUrl?: string; branch?: string }
80
+ | undefined,
81
+ ): string | undefined => {
82
+ if (!editLink || editLink.enabled === false) return undefined;
83
+ const base = editLink.baseUrl ?? "";
84
+ if (base.length === 0) return undefined;
85
+ const branch = editLink.branch ?? "main";
86
+ const posix = filePath.replace(/\\/g, "/");
87
+ const trimmed = posix.replace(/^\/+/, "");
88
+ const tail = trimmed.startsWith("docs/") ? trimmed.slice(5) : trimmed;
89
+ return `${stripTrailingSlash(base)}/edit/${branch}/${tail}`;
90
+ };
91
+
92
+ const stripTrailingSlash = (s: string): string =>
93
+ s.endsWith("/") ? s.slice(0, -1) : s;
@@ -0,0 +1,17 @@
1
+ export { buildDocPage, buildEditUrl } from "./buildPage";
2
+ export {
3
+ countWords,
4
+ estimateReadingTime,
5
+ extractDescription,
6
+ extractGroup,
7
+ extractOrder,
8
+ extractTitle,
9
+ extractToc,
10
+ isMarkdownFile,
11
+ parseFrontmatter,
12
+ parseFrontmatterBlock,
13
+ plainText,
14
+ slugifyHeading,
15
+ slugToId,
16
+ stripMarkdownExt,
17
+ } from "./parse";