livemark 0.2.0 → 0.7.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 (346) hide show
  1. package/LICENSE.md +9 -0
  2. package/README.md +5 -1
  3. package/build/actions/changelog/build.d.ts +14 -0
  4. package/build/actions/changelog/build.js +158 -0
  5. package/build/actions/changelog/build.unit.js +66 -0
  6. package/build/actions/config/define.d.ts +6 -0
  7. package/build/actions/config/define.js +8 -0
  8. package/build/actions/config/define.unit.js +29 -0
  9. package/build/actions/config/load.d.ts +5 -0
  10. package/build/actions/config/load.js +25 -0
  11. package/build/actions/config/load.unit.js +23 -0
  12. package/build/commands/build.d.ts +5 -0
  13. package/build/commands/build.js +14 -0
  14. package/build/commands/escape.d.ts +9 -0
  15. package/build/commands/escape.js +56 -0
  16. package/build/commands/preview.d.ts +5 -0
  17. package/build/commands/preview.js +18 -0
  18. package/build/commands/start.d.ts +5 -0
  19. package/build/commands/start.js +19 -0
  20. package/build/docs/markdown/includes/example.d.ts +58 -0
  21. package/build/docs/markdown/includes/example.js +5 -0
  22. package/build/entrypoints/main.d.ts +2 -0
  23. package/build/entrypoints/main.js +12 -0
  24. package/build/helpers/program.d.ts +2 -0
  25. package/build/helpers/program.js +5 -0
  26. package/build/index.d.ts +1 -4
  27. package/build/index.js +2 -5
  28. package/build/livemark.config.d.ts +58 -0
  29. package/build/livemark.config.js +25 -0
  30. package/build/models/config.d.ts +340 -0
  31. package/build/models/config.js +34 -0
  32. package/build/models/link.d.ts +13 -0
  33. package/build/models/link.js +9 -0
  34. package/build/models/patch.d.ts +21 -0
  35. package/build/models/patch.js +7 -0
  36. package/build/models/section.d.ts +75 -0
  37. package/build/models/section.js +26 -0
  38. package/build/models/theme.d.ts +72 -0
  39. package/build/models/theme.js +73 -0
  40. package/build/program.d.ts +2 -0
  41. package/build/program.js +16 -0
  42. package/build/vite.config.d.ts +2 -0
  43. package/build/vite.config.js +48 -0
  44. package/build/website/assets/logo.svg +117 -0
  45. package/build/website/client.tsx +10 -0
  46. package/build/website/components/Abbr.tsx +17 -0
  47. package/build/website/components/AnsiCode.tsx +15 -0
  48. package/build/website/components/Article.tsx +172 -0
  49. package/build/website/components/BackToTop.tsx +32 -0
  50. package/build/website/components/Banner.tsx +32 -0
  51. package/build/website/components/BlogIndex.tsx +121 -0
  52. package/build/website/components/BlogSidebar.tsx +162 -0
  53. package/build/website/components/Breadcrumbs.tsx +22 -0
  54. package/build/website/components/Callout.tsx +72 -0
  55. package/build/website/components/Card.tsx +48 -0
  56. package/build/website/components/Cards.tsx +8 -0
  57. package/build/website/components/ChangelogIndex.tsx +111 -0
  58. package/build/website/components/ChangelogSidebar.tsx +120 -0
  59. package/build/website/components/CodeBlock.tsx +135 -0
  60. package/build/website/components/CodeTabs.tsx +83 -0
  61. package/build/website/components/Column.tsx +4 -0
  62. package/build/website/components/Columns.tsx +12 -0
  63. package/build/website/components/Content.tsx +7 -0
  64. package/build/website/components/ContentTab.tsx +7 -0
  65. package/build/website/components/ContentTabs.tsx +82 -0
  66. package/build/website/components/DefaultCatchBoundary.tsx +54 -0
  67. package/build/website/components/DefinitionList.tsx +21 -0
  68. package/build/website/components/Details.tsx +30 -0
  69. package/build/website/components/FileTree.tsx +52 -0
  70. package/build/website/components/Footer.tsx +16 -0
  71. package/build/website/components/FootnoteRef.tsx +52 -0
  72. package/build/website/components/Heading.tsx +20 -0
  73. package/build/website/components/InlineBadge.tsx +9 -0
  74. package/build/website/components/InlineIcon.tsx +10 -0
  75. package/build/website/components/InlineToc.tsx +31 -0
  76. package/build/website/components/Layout.tsx +190 -0
  77. package/build/website/components/LinkButton.tsx +31 -0
  78. package/build/website/components/Mermaid.tsx +18 -0
  79. package/build/website/components/MermaidRenderer.tsx +37 -0
  80. package/build/website/components/MiniSidebar.tsx +49 -0
  81. package/build/website/components/NotFound.tsx +27 -0
  82. package/build/website/components/PackageTabs.tsx +74 -0
  83. package/build/website/components/PageToolbar.tsx +75 -0
  84. package/build/website/components/PrevNext.tsx +60 -0
  85. package/build/website/components/Search.tsx +28 -0
  86. package/build/website/components/SearchDialog.tsx +245 -0
  87. package/build/website/components/Section.tsx +24 -0
  88. package/build/website/components/SectionHeader.tsx +35 -0
  89. package/build/website/components/Sidebar.tsx +160 -0
  90. package/build/website/components/SidebarLinks.tsx +70 -0
  91. package/build/website/components/SidebarSections.tsx +71 -0
  92. package/build/website/components/SiteTitle.tsx +21 -0
  93. package/build/website/components/SortableTable.tsx +81 -0
  94. package/build/website/components/SoundCloud.tsx +16 -0
  95. package/build/website/components/TagIndex.tsx +120 -0
  96. package/build/website/components/Theme.tsx +39 -0
  97. package/build/website/components/Toc.tsx +226 -0
  98. package/build/website/components/YouTube.tsx +16 -0
  99. package/build/website/components/ZoomImage.tsx +25 -0
  100. package/build/website/content/article.ts +78 -0
  101. package/build/website/content/tag.ts +29 -0
  102. package/build/website/content-collections.ts +208 -0
  103. package/build/website/elements/accordion.tsx +78 -0
  104. package/build/website/elements/alert-dialog.tsx +185 -0
  105. package/build/website/elements/alert.tsx +74 -0
  106. package/build/website/elements/aspect-ratio.tsx +22 -0
  107. package/build/website/elements/avatar.tsx +107 -0
  108. package/build/website/elements/badge.tsx +51 -0
  109. package/build/website/elements/breadcrumb.tsx +120 -0
  110. package/build/website/elements/button-group.tsx +87 -0
  111. package/build/website/elements/button.tsx +57 -0
  112. package/build/website/elements/calendar.tsx +229 -0
  113. package/build/website/elements/card.tsx +102 -0
  114. package/build/website/elements/carousel.tsx +240 -0
  115. package/build/website/elements/chart.tsx +372 -0
  116. package/build/website/elements/checkbox.tsx +26 -0
  117. package/build/website/elements/collapsible.tsx +19 -0
  118. package/build/website/elements/command.tsx +210 -0
  119. package/build/website/elements/context-menu.tsx +270 -0
  120. package/build/website/elements/dialog.tsx +152 -0
  121. package/build/website/elements/direction.tsx +4 -0
  122. package/build/website/elements/drawer.tsx +127 -0
  123. package/build/website/elements/dropdown-menu.tsx +260 -0
  124. package/build/website/elements/empty.tsx +104 -0
  125. package/build/website/elements/field.tsx +235 -0
  126. package/build/website/elements/hover-card.tsx +49 -0
  127. package/build/website/elements/input-group.tsx +171 -0
  128. package/build/website/elements/input-otp.tsx +84 -0
  129. package/build/website/elements/input.tsx +18 -0
  130. package/build/website/elements/item.tsx +199 -0
  131. package/build/website/elements/kbd.tsx +26 -0
  132. package/build/website/elements/label.tsx +17 -0
  133. package/build/website/elements/menubar.tsx +282 -0
  134. package/build/website/elements/native-select.tsx +56 -0
  135. package/build/website/elements/navigation-menu.tsx +171 -0
  136. package/build/website/elements/pagination.tsx +133 -0
  137. package/build/website/elements/popover.tsx +88 -0
  138. package/build/website/elements/progress.tsx +81 -0
  139. package/build/website/elements/radio-group.tsx +36 -0
  140. package/build/website/elements/resizable.tsx +48 -0
  141. package/build/website/elements/scroll-area.tsx +51 -0
  142. package/build/website/elements/select.tsx +200 -0
  143. package/build/website/elements/separator.tsx +22 -0
  144. package/build/website/elements/sheet.tsx +130 -0
  145. package/build/website/elements/sidebar.tsx +717 -0
  146. package/build/website/elements/skeleton.tsx +13 -0
  147. package/build/website/elements/slider.tsx +57 -0
  148. package/build/website/elements/sonner.tsx +40 -0
  149. package/build/website/elements/spinner.tsx +16 -0
  150. package/build/website/elements/switch.tsx +30 -0
  151. package/build/website/elements/table.tsx +112 -0
  152. package/build/website/elements/tabs.tsx +79 -0
  153. package/build/website/elements/textarea.tsx +16 -0
  154. package/build/website/elements/toggle-group.tsx +87 -0
  155. package/build/website/elements/toggle.tsx +43 -0
  156. package/build/website/elements/tooltip.tsx +67 -0
  157. package/build/website/env.d.ts +14 -0
  158. package/build/website/helpers/article-icon.ts +61 -0
  159. package/build/website/helpers/article.ts +90 -0
  160. package/build/website/helpers/changelog-source.ts +16 -0
  161. package/build/website/helpers/changelog-source.unit.ts +38 -0
  162. package/build/website/helpers/dynamic-icon-resolver.tsx +19 -0
  163. package/build/website/helpers/dynamic-icon.tsx +12 -0
  164. package/build/website/helpers/favicon.ts +7 -0
  165. package/build/website/helpers/model.ts +4 -0
  166. package/build/website/helpers/resolve-asset-path.ts +9 -0
  167. package/build/website/helpers/resolve-includes.ts +131 -0
  168. package/build/website/helpers/resolve-includes.unit.ts +126 -0
  169. package/build/website/helpers/search-text.ts +15 -0
  170. package/build/website/helpers/section.ts +50 -0
  171. package/build/website/helpers/section.unit.ts +71 -0
  172. package/build/website/helpers/style.ts +6 -0
  173. package/build/website/helpers/theme.ts +27 -0
  174. package/build/website/helpers/toc-context.ts +10 -0
  175. package/build/website/helpers/toc.ts +46 -0
  176. package/build/website/helpers/toc.unit.ts +74 -0
  177. package/build/website/hooks/in-view.ts +30 -0
  178. package/build/website/hooks/media.ts +19 -0
  179. package/build/website/models/article.ts +55 -0
  180. package/build/website/models/toc.ts +5 -0
  181. package/build/website/plugins/rehype-inline-code.ts +107 -0
  182. package/build/website/plugins/rehype-task-list-a11y.ts +33 -0
  183. package/build/website/plugins/remark-abbr.ts +46 -0
  184. package/build/website/plugins/remark-abbr.unit.ts +99 -0
  185. package/build/website/plugins/remark-ansi.ts +248 -0
  186. package/build/website/plugins/remark-ansi.unit.ts +197 -0
  187. package/build/website/plugins/remark-badge.ts +50 -0
  188. package/build/website/plugins/remark-badge.unit.ts +96 -0
  189. package/build/website/plugins/remark-button.ts +60 -0
  190. package/build/website/plugins/remark-button.unit.ts +147 -0
  191. package/build/website/plugins/remark-callout.ts +57 -0
  192. package/build/website/plugins/remark-card.ts +80 -0
  193. package/build/website/plugins/remark-code-tabs.ts +87 -0
  194. package/build/website/plugins/remark-columns.ts +66 -0
  195. package/build/website/plugins/remark-custom-heading-id.ts +70 -0
  196. package/build/website/plugins/remark-custom-heading-id.unit.ts +131 -0
  197. package/build/website/plugins/remark-deflist.ts +36 -0
  198. package/build/website/plugins/remark-details.ts +49 -0
  199. package/build/website/plugins/remark-filetree.ts +89 -0
  200. package/build/website/plugins/remark-filetree.unit.ts +137 -0
  201. package/build/website/plugins/remark-github-callout.ts +71 -0
  202. package/build/website/plugins/remark-icon.ts +42 -0
  203. package/build/website/plugins/remark-image.ts +34 -0
  204. package/build/website/plugins/remark-media.ts +62 -0
  205. package/build/website/plugins/remark-media.unit.ts +74 -0
  206. package/build/website/plugins/remark-mermaid.ts +23 -0
  207. package/build/website/plugins/remark-npm.ts +52 -0
  208. package/build/website/plugins/remark-steps.ts +36 -0
  209. package/build/website/plugins/remark-steps.unit.ts +97 -0
  210. package/build/website/plugins/remark-tab.ts +100 -0
  211. package/build/website/plugins/remark-tab.unit.ts +181 -0
  212. package/build/website/plugins/remark-toc.ts +39 -0
  213. package/build/website/plugins/remark-toc.unit.ts +82 -0
  214. package/build/website/plugins/shiki-icon.ts +82 -0
  215. package/build/website/plugins/shiki-line-highlight.ts +174 -0
  216. package/build/website/plugins/vite-livemark.ts +91 -0
  217. package/build/website/routeTree.gen.ts +86 -0
  218. package/build/website/router.tsx +34 -0
  219. package/build/website/routes/$.tsx +92 -0
  220. package/build/website/routes/__root.tsx +97 -0
  221. package/build/website/server.ts +7 -0
  222. package/build/website/styles/general.css +163 -0
  223. package/build/website/styles/markdown.css +214 -0
  224. package/build/website/vite.config.ts +71 -0
  225. package/entrypoints/main.ts +12 -0
  226. package/package.json +120 -12
  227. package/build/descriptor/index.d.ts +0 -3
  228. package/build/descriptor/index.js +0 -4
  229. package/build/descriptor/load.d.ts +0 -8
  230. package/build/descriptor/load.js +0 -40
  231. package/build/descriptor/node.d.ts +0 -125
  232. package/build/descriptor/node.js +0 -9
  233. package/build/descriptor/save.d.ts +0 -8
  234. package/build/descriptor/save.js +0 -17
  235. package/build/descriptor/save.spec.js +0 -80
  236. package/build/descriptor/validate.d.ts +0 -9
  237. package/build/descriptor/validate.js +0 -14
  238. package/build/descriptor/validate.spec.js +0 -124
  239. package/build/dialect/Dialect.d.ts +0 -88
  240. package/build/dialect/Dialect.js +0 -2
  241. package/build/dialect/index.d.ts +0 -3
  242. package/build/dialect/index.js +0 -3
  243. package/build/dialect/load.d.ts +0 -4
  244. package/build/dialect/load.js +0 -7
  245. package/build/dialect/save.d.ts +0 -9
  246. package/build/dialect/save.js +0 -13
  247. package/build/dialect/validate.d.ts +0 -1
  248. package/build/dialect/validate.js +0 -4
  249. package/build/metadata/index.d.ts +0 -3
  250. package/build/metadata/index.js +0 -4
  251. package/build/metadata/load.d.ts +0 -10
  252. package/build/metadata/load.js +0 -55
  253. package/build/metadata/node.d.ts +0 -125
  254. package/build/metadata/node.js +0 -9
  255. package/build/metadata/save.d.ts +0 -8
  256. package/build/metadata/save.js +0 -17
  257. package/build/metadata/save.spec.js +0 -80
  258. package/build/metadata/validate.d.ts +0 -9
  259. package/build/metadata/validate.js +0 -14
  260. package/build/metadata/validate.spec.d.ts +0 -1
  261. package/build/metadata/validate.spec.js +0 -124
  262. package/build/package/Contributor.d.ts +0 -21
  263. package/build/package/Contributor.js +0 -2
  264. package/build/package/Package.d.ts +0 -65
  265. package/build/package/Package.js +0 -2
  266. package/build/package/index.d.ts +0 -4
  267. package/build/package/index.js +0 -3
  268. package/build/package/load.d.ts +0 -4
  269. package/build/package/load.js +0 -7
  270. package/build/package/save.d.ts +0 -9
  271. package/build/package/save.js +0 -13
  272. package/build/package/validate.d.ts +0 -1
  273. package/build/package/validate.js +0 -4
  274. package/build/resource/License.d.ts +0 -18
  275. package/build/resource/License.js +0 -2
  276. package/build/resource/Resource.d.ts +0 -67
  277. package/build/resource/Resource.js +0 -2
  278. package/build/resource/Source.d.ts +0 -17
  279. package/build/resource/Source.js +0 -2
  280. package/build/resource/index.d.ts +0 -5
  281. package/build/resource/index.js +0 -2
  282. package/build/resource/load.d.ts +0 -1
  283. package/build/resource/load.js +0 -2
  284. package/build/resource/save.d.ts +0 -9
  285. package/build/resource/save.js +0 -13
  286. package/build/resource/types/index.d.ts +0 -1
  287. package/build/resource/types/index.js +0 -2
  288. package/build/resource/types/table.d.ts +0 -29
  289. package/build/resource/types/table.js +0 -2
  290. package/build/resource/validate.d.ts +0 -1
  291. package/build/resource/validate.js +0 -4
  292. package/build/schema/Field.d.ts +0 -5
  293. package/build/schema/Field.js +0 -2
  294. package/build/schema/Schema.d.ts +0 -55
  295. package/build/schema/Schema.js +0 -2
  296. package/build/schema/fields/any.d.ts +0 -20
  297. package/build/schema/fields/any.js +0 -2
  298. package/build/schema/fields/array.d.ts +0 -32
  299. package/build/schema/fields/array.js +0 -2
  300. package/build/schema/fields/base.d.ts +0 -42
  301. package/build/schema/fields/base.js +0 -2
  302. package/build/schema/fields/boolean.d.ts +0 -28
  303. package/build/schema/fields/boolean.js +0 -2
  304. package/build/schema/fields/date.d.ts +0 -35
  305. package/build/schema/fields/date.js +0 -2
  306. package/build/schema/fields/datetime.d.ts +0 -35
  307. package/build/schema/fields/datetime.js +0 -2
  308. package/build/schema/fields/duration.d.ts +0 -28
  309. package/build/schema/fields/duration.js +0 -2
  310. package/build/schema/fields/geojson.d.ts +0 -26
  311. package/build/schema/fields/geojson.js +0 -2
  312. package/build/schema/fields/geopoint.d.ts +0 -27
  313. package/build/schema/fields/geopoint.js +0 -2
  314. package/build/schema/fields/index.d.ts +0 -16
  315. package/build/schema/fields/index.js +0 -17
  316. package/build/schema/fields/integer.d.ts +0 -37
  317. package/build/schema/fields/integer.js +0 -2
  318. package/build/schema/fields/list.d.ts +0 -36
  319. package/build/schema/fields/list.js +0 -2
  320. package/build/schema/fields/number.d.ts +0 -40
  321. package/build/schema/fields/number.js +0 -2
  322. package/build/schema/fields/object.d.ts +0 -32
  323. package/build/schema/fields/object.js +0 -2
  324. package/build/schema/fields/string.d.ts +0 -40
  325. package/build/schema/fields/string.js +0 -2
  326. package/build/schema/fields/time.d.ts +0 -35
  327. package/build/schema/fields/time.js +0 -2
  328. package/build/schema/fields/year.d.ts +0 -28
  329. package/build/schema/fields/year.js +0 -2
  330. package/build/schema/fields/yearmonth.d.ts +0 -28
  331. package/build/schema/fields/yearmonth.js +0 -2
  332. package/build/schema/index.d.ts +0 -4
  333. package/build/schema/index.js +0 -2
  334. package/build/schema/load.d.ts +0 -1
  335. package/build/schema/load.js +0 -2
  336. package/build/schema/save.d.ts +0 -9
  337. package/build/schema/save.js +0 -13
  338. package/build/schema/validate.d.ts +0 -1
  339. package/build/schema/validate.js +0 -4
  340. package/build/tsconfig.tsbuildinfo +0 -1
  341. package/index.ts +0 -0
  342. package/tsconfig.json +0 -10
  343. package/typedoc.json +0 -4
  344. /package/build/{descriptor/save.spec.d.ts → actions/changelog/build.unit.d.ts} +0 -0
  345. /package/build/{descriptor/validate.spec.d.ts → actions/config/define.unit.d.ts} +0 -0
  346. /package/build/{metadata/save.spec.d.ts → actions/config/load.unit.d.ts} +0 -0
@@ -0,0 +1,49 @@
1
+ import type { Paragraph, Root } from "mdast"
2
+ import type { ContainerDirective } from "mdast-util-directive"
3
+ import type { Plugin } from "unified"
4
+ import { visit } from "unist-util-visit"
5
+
6
+ /** Remark plugin that converts :::details directives into collapsible details/summary elements */
7
+ export const remarkDetails: Plugin<[], Root> = () => {
8
+ return tree => {
9
+ visit(
10
+ tree,
11
+ "containerDirective",
12
+ (node: ContainerDirective, index, parent) => {
13
+ if (node.name !== "details" || index === undefined || !parent) return
14
+
15
+ const labelParagraph = node.children.find(
16
+ (child): child is Paragraph =>
17
+ child.type === "paragraph" && !!child.data?.directiveLabel,
18
+ )
19
+
20
+ const summary = labelParagraph
21
+ ? labelParagraph.children
22
+ .map(c => ("value" in c ? String(c.value) : ""))
23
+ .join("")
24
+ : "Details"
25
+
26
+ const body = node.children.filter(
27
+ child => !(child.type === "paragraph" && child.data?.directiveLabel),
28
+ )
29
+
30
+ const jsxNode = {
31
+ type: "mdxJsxFlowElement" as const,
32
+ name: "Details",
33
+ attributes: [
34
+ {
35
+ type: "mdxJsxAttribute" as const,
36
+ name: "summary",
37
+ value: summary,
38
+ },
39
+ ],
40
+ children: body,
41
+ data: { _mdxExplicitJsx: true },
42
+ }
43
+
44
+ // @ts-expect-error mdxJsxFlowElement is not in mdast types
45
+ parent.children.splice(index, 1, jsxNode)
46
+ },
47
+ )
48
+ }
49
+ }
@@ -0,0 +1,89 @@
1
+ import type { List, ListItem, Paragraph, Root, Text } from "mdast"
2
+ import type { ContainerDirective } from "mdast-util-directive"
3
+ import type { Plugin } from "unified"
4
+ import { visit, SKIP } from "unist-util-visit"
5
+
6
+ interface TreeItem {
7
+ name: string
8
+ isDir: boolean
9
+ children?: TreeItem[]
10
+ }
11
+
12
+ /** Extract tree items from a mdast list node */
13
+ export function parseList(list: List): TreeItem[] {
14
+ const items: TreeItem[] = []
15
+
16
+ for (const listItem of list.children) {
17
+ if (listItem.type !== "listItem") continue
18
+ const item = parseListItem(listItem)
19
+ if (item) items.push(item)
20
+ }
21
+
22
+ return items
23
+ }
24
+
25
+ function parseListItem(listItem: ListItem): TreeItem | undefined {
26
+ const paragraph = listItem.children.find(
27
+ (child): child is Paragraph => child.type === "paragraph",
28
+ )
29
+ if (!paragraph) return undefined
30
+
31
+ const textContent = paragraph.children
32
+ .filter((c): c is Text => c.type === "text")
33
+ .map(c => c.value)
34
+ .join("")
35
+ .trim()
36
+
37
+ if (!textContent) return undefined
38
+
39
+ const isDir = textContent.endsWith("/")
40
+ const name = isDir ? textContent.slice(0, -1) : textContent
41
+
42
+ const nestedList = listItem.children.find(
43
+ (child): child is List => child.type === "list",
44
+ )
45
+
46
+ const children = nestedList ? parseList(nestedList) : undefined
47
+
48
+ return { name, isDir, children }
49
+ }
50
+
51
+ /** Remark plugin that converts :::filetree directives into FileTree JSX elements */
52
+ export const remarkFiletree: Plugin<[], Root> = () => {
53
+ return tree => {
54
+ visit(
55
+ tree,
56
+ "containerDirective",
57
+ (node: ContainerDirective, index, parent) => {
58
+ if (node.name !== "filetree" || index === undefined || !parent) return
59
+
60
+ const lists = node.children.filter(
61
+ (child): child is List => child.type === "list",
62
+ )
63
+
64
+ const treeItems: TreeItem[] = []
65
+ for (const list of lists) {
66
+ treeItems.push(...parseList(list))
67
+ }
68
+
69
+ const jsxNode = {
70
+ type: "mdxJsxFlowElement" as const,
71
+ name: "FileTree",
72
+ attributes: [
73
+ {
74
+ type: "mdxJsxAttribute" as const,
75
+ name: "tree",
76
+ value: JSON.stringify(treeItems),
77
+ },
78
+ ],
79
+ children: [],
80
+ data: { _mdxExplicitJsx: true },
81
+ }
82
+
83
+ // @ts-expect-error mdxJsxFlowElement is not in mdast types
84
+ parent.children[index] = jsxNode
85
+ return SKIP
86
+ },
87
+ )
88
+ }
89
+ }
@@ -0,0 +1,137 @@
1
+ import type { List, ListItem, Paragraph, Text } from "mdast"
2
+ import { describe, expect, it } from "vite-plus/test"
3
+ import { parseList } from "./remark-filetree.ts"
4
+
5
+ function text(value: string): Text {
6
+ return { type: "text", value }
7
+ }
8
+
9
+ function paragraph(...children: Text[]): Paragraph {
10
+ return { type: "paragraph", children }
11
+ }
12
+
13
+ function listItem(...children: (Paragraph | List)[]): ListItem {
14
+ return { type: "listItem", children, spread: false }
15
+ }
16
+
17
+ function list(...children: ListItem[]): List {
18
+ return { type: "list", children, ordered: false, spread: false }
19
+ }
20
+
21
+ describe("parseList", () => {
22
+ it("should parse flat file entries", () => {
23
+ const input = list(
24
+ listItem(paragraph(text("package.json"))),
25
+ listItem(paragraph(text("tsconfig.json"))),
26
+ )
27
+
28
+ expect(parseList(input)).toEqual([
29
+ { name: "package.json", isDir: false },
30
+ { name: "tsconfig.json", isDir: false },
31
+ ])
32
+ })
33
+
34
+ it("should detect directories by trailing slash", () => {
35
+ const input = list(
36
+ listItem(paragraph(text("src/"))),
37
+ listItem(paragraph(text("README.md"))),
38
+ )
39
+
40
+ expect(parseList(input)).toEqual([
41
+ { name: "src", isDir: true },
42
+ { name: "README.md", isDir: false },
43
+ ])
44
+ })
45
+
46
+ it("should parse nested directory structures", () => {
47
+ const input = list(
48
+ listItem(
49
+ paragraph(text("src/")),
50
+ list(
51
+ listItem(paragraph(text("index.ts"))),
52
+ listItem(
53
+ paragraph(text("components/")),
54
+ list(listItem(paragraph(text("Button.tsx")))),
55
+ ),
56
+ ),
57
+ ),
58
+ )
59
+
60
+ expect(parseList(input)).toEqual([
61
+ {
62
+ name: "src",
63
+ isDir: true,
64
+ children: [
65
+ { name: "index.ts", isDir: false },
66
+ {
67
+ name: "components",
68
+ isDir: true,
69
+ children: [{ name: "Button.tsx", isDir: false }],
70
+ },
71
+ ],
72
+ },
73
+ ])
74
+ })
75
+
76
+ it("should skip list items without text content", () => {
77
+ const emptyItem: ListItem = {
78
+ type: "listItem",
79
+ children: [],
80
+ spread: false,
81
+ }
82
+ const input = list(emptyItem, listItem(paragraph(text("file.ts"))))
83
+
84
+ expect(parseList(input)).toEqual([{ name: "file.ts", isDir: false }])
85
+ })
86
+
87
+ it("should trim whitespace from names", () => {
88
+ const input = list(listItem(paragraph(text(" file.ts "))))
89
+
90
+ expect(parseList(input)).toEqual([{ name: "file.ts", isDir: false }])
91
+ })
92
+
93
+ it("should handle deeply nested structures", () => {
94
+ const input = list(
95
+ listItem(
96
+ paragraph(text("a/")),
97
+ list(
98
+ listItem(
99
+ paragraph(text("b/")),
100
+ list(
101
+ listItem(
102
+ paragraph(text("c/")),
103
+ list(listItem(paragraph(text("deep.ts")))),
104
+ ),
105
+ ),
106
+ ),
107
+ ),
108
+ ),
109
+ )
110
+
111
+ expect(parseList(input)).toEqual([
112
+ {
113
+ name: "a",
114
+ isDir: true,
115
+ children: [
116
+ {
117
+ name: "b",
118
+ isDir: true,
119
+ children: [
120
+ {
121
+ name: "c",
122
+ isDir: true,
123
+ children: [{ name: "deep.ts", isDir: false }],
124
+ },
125
+ ],
126
+ },
127
+ ],
128
+ },
129
+ ])
130
+ })
131
+
132
+ it("should return undefined children for directories without nested list", () => {
133
+ const input = list(listItem(paragraph(text("empty-dir/"))))
134
+
135
+ expect(parseList(input)).toEqual([{ name: "empty-dir", isDir: true }])
136
+ })
137
+ })
@@ -0,0 +1,71 @@
1
+ import type { Blockquote, Paragraph, Root, Text } from "mdast"
2
+ import type { Plugin } from "unified"
3
+ import { visit } from "unist-util-visit"
4
+
5
+ const typeMap: Record<string, string> = {
6
+ NOTE: "note",
7
+ TIP: "tip",
8
+ WARNING: "warning",
9
+ CAUTION: "danger",
10
+ IMPORTANT: "info",
11
+ }
12
+
13
+ const calloutPattern = /^\[!(\w+)\]\s*/
14
+
15
+ /** Remark plugin that converts GitHub-style blockquote callouts into Callout JSX elements */
16
+ export const remarkGithubCallout: Plugin<[], Root> = () => {
17
+ return tree => {
18
+ visit(tree, "blockquote", (node: Blockquote, index, parent) => {
19
+ if (!parent || index === undefined || node.children.length === 0) return
20
+
21
+ const first = node.children[0]
22
+ if (first?.type !== "paragraph" || first.children.length === 0) return
23
+
24
+ const firstInline = first.children[0]
25
+ if (firstInline?.type !== "text") return
26
+
27
+ const match = calloutPattern.exec(firstInline.value)
28
+ if (!match) return
29
+
30
+ const rawType = match[1]
31
+ if (!rawType) return
32
+
33
+ const calloutType = typeMap[rawType]
34
+ if (!calloutType) return
35
+
36
+ const remainingText = firstInline.value.slice(match[0].length)
37
+
38
+ const updatedFirstChildren = [
39
+ ...(remainingText
40
+ ? [{ type: "text" as const, value: remainingText } satisfies Text]
41
+ : []),
42
+ ...first.children.slice(1),
43
+ ]
44
+
45
+ const body =
46
+ updatedFirstChildren.length > 0
47
+ ? [
48
+ { ...first, children: updatedFirstChildren } satisfies Paragraph,
49
+ ...node.children.slice(1),
50
+ ]
51
+ : node.children.slice(1)
52
+
53
+ const jsxNode = {
54
+ type: "mdxJsxFlowElement" as const,
55
+ name: "Callout",
56
+ attributes: [
57
+ {
58
+ type: "mdxJsxAttribute" as const,
59
+ name: "type",
60
+ value: calloutType,
61
+ },
62
+ ],
63
+ children: body,
64
+ data: { _mdxExplicitJsx: true },
65
+ }
66
+
67
+ // @ts-expect-error mdxJsxFlowElement is not in mdast types
68
+ parent.children.splice(index, 1, jsxNode)
69
+ })
70
+ }
71
+ }
@@ -0,0 +1,42 @@
1
+ import type { Root } from "mdast"
2
+ import type { TextDirective } from "mdast-util-directive"
3
+ import type { Plugin } from "unified"
4
+ import { visit } from "unist-util-visit"
5
+
6
+ /** Remark plugin that converts :icon text directives into InlineIcon JSX elements */
7
+ export const remarkIcon: Plugin<[], Root> = () => {
8
+ return tree => {
9
+ visit(tree, "textDirective", (node: TextDirective, index, parent) => {
10
+ if (node.name !== "icon" || index === undefined || !parent) return
11
+
12
+ const name = node.attributes?.name
13
+ if (!name) return
14
+
15
+ const className = node.attributes?.className
16
+
17
+ const attributes = [
18
+ { type: "mdxJsxAttribute" as const, name: "name", value: name },
19
+ ...(className
20
+ ? [
21
+ {
22
+ type: "mdxJsxAttribute" as const,
23
+ name: "className",
24
+ value: className,
25
+ },
26
+ ]
27
+ : []),
28
+ ]
29
+
30
+ const jsxNode = {
31
+ type: "mdxJsxTextElement" as const,
32
+ name: "InlineIcon",
33
+ attributes,
34
+ children: [],
35
+ data: { _mdxExplicitJsx: true },
36
+ }
37
+
38
+ // @ts-expect-error mdxJsxTextElement is not in mdast types
39
+ parent.children[index] = jsxNode
40
+ })
41
+ }
42
+ }
@@ -0,0 +1,34 @@
1
+ import type { Root } from "mdast"
2
+ import type { Plugin } from "unified"
3
+ import { visit } from "unist-util-visit"
4
+ import { resolveAssetPath } from "../helpers/resolve-asset-path.ts"
5
+
6
+ /** Remark plugin that resolves relative image paths and unwraps image-only paragraphs */
7
+ export const remarkImage: Plugin<
8
+ [{ filePath: string; root: string }],
9
+ Root
10
+ > = ({ filePath, root }) => {
11
+ return tree => {
12
+ visit(tree, "image", node => {
13
+ node.url = resolveAssetPath(node.url, filePath, root)
14
+ })
15
+
16
+ visit(tree, "paragraph", (node, index, parent) => {
17
+ if (!parent || typeof index !== "number") return
18
+ const isImageOnly = node.children.every(
19
+ child =>
20
+ child.type === "image" ||
21
+ child.type === "imageReference" ||
22
+ child.type === "break" ||
23
+ (child.type === "text" && child.value.trim() === ""),
24
+ )
25
+ if (!isImageOnly) return
26
+ const images = node.children.filter(
27
+ child => child.type === "image" || child.type === "imageReference",
28
+ )
29
+ if (images.length === 0) return
30
+ parent.children.splice(index, 1, ...images)
31
+ return index + images.length
32
+ })
33
+ }
34
+ }
@@ -0,0 +1,62 @@
1
+ import type { Root } from "mdast"
2
+ import type { LeafDirective } from "mdast-util-directive"
3
+ import type { Plugin } from "unified"
4
+ import { visit } from "unist-util-visit"
5
+
6
+ /** Walk a mdast tree and convert media leaf directives into JSX embed elements */
7
+ export function transformMediaDirectives(tree: Root) {
8
+ visit(tree, "leafDirective", (node: LeafDirective, index, parent) => {
9
+ if (
10
+ (node.name !== "video" && node.name !== "audio") ||
11
+ index === undefined ||
12
+ !parent
13
+ )
14
+ return
15
+
16
+ const type = node.attributes?.type
17
+ if (!type) return
18
+
19
+ if (node.name === "video" && type === "youtube") {
20
+ const id = node.attributes?.id
21
+ if (!id) return
22
+
23
+ const jsxNode = {
24
+ type: "mdxJsxFlowElement" as const,
25
+ name: "YouTube",
26
+ attributes: [
27
+ { type: "mdxJsxAttribute" as const, name: "id", value: id },
28
+ ],
29
+ children: [],
30
+ data: { _mdxExplicitJsx: true },
31
+ }
32
+
33
+ // @ts-expect-error mdxJsxFlowElement is not in mdast types
34
+ parent.children.splice(index, 1, jsxNode)
35
+ }
36
+
37
+ if (node.name === "audio" && type === "soundcloud") {
38
+ const url = node.attributes?.url
39
+ if (!url) return
40
+
41
+ const jsxNode = {
42
+ type: "mdxJsxFlowElement" as const,
43
+ name: "SoundCloud",
44
+ attributes: [
45
+ { type: "mdxJsxAttribute" as const, name: "url", value: url },
46
+ ],
47
+ children: [],
48
+ data: { _mdxExplicitJsx: true },
49
+ }
50
+
51
+ // @ts-expect-error mdxJsxFlowElement is not in mdast types
52
+ parent.children.splice(index, 1, jsxNode)
53
+ }
54
+ })
55
+ }
56
+
57
+ /** Remark plugin that converts leaf directives into media embed JSX elements */
58
+ export const remarkMedia: Plugin<[], Root> = () => {
59
+ return tree => {
60
+ transformMediaDirectives(tree)
61
+ }
62
+ }
@@ -0,0 +1,74 @@
1
+ import type { Root } from "mdast"
2
+ import type { LeafDirective } from "mdast-util-directive"
3
+ import { describe, expect, it } from "vite-plus/test"
4
+ import { transformMediaDirectives } from "./remark-media.ts"
5
+
6
+ function makeLeafDirective(
7
+ name: string,
8
+ attributes: Record<string, string>,
9
+ ): LeafDirective {
10
+ return {
11
+ type: "leafDirective" as LeafDirective["type"],
12
+ name,
13
+ attributes,
14
+ children: [],
15
+ }
16
+ }
17
+
18
+ function makeTree(...children: Root["children"]): Root {
19
+ return { type: "root", children }
20
+ }
21
+
22
+ function process(tree: Root) {
23
+ transformMediaDirectives(tree)
24
+ return tree
25
+ }
26
+
27
+ describe("transformMediaDirectives", () => {
28
+ it("should convert video youtube directive to YouTube JSX element", () => {
29
+ const tree = process(
30
+ makeTree(makeLeafDirective("video", { type: "youtube", id: "abc123" })),
31
+ )
32
+ const node = tree.children[0]
33
+ expect(node).toMatchObject({
34
+ type: "mdxJsxFlowElement",
35
+ name: "YouTube",
36
+ attributes: [{ type: "mdxJsxAttribute", name: "id", value: "abc123" }],
37
+ })
38
+ })
39
+
40
+ it("should convert audio soundcloud directive to SoundCloud JSX element", () => {
41
+ const url = "https://soundcloud.com/artist/track"
42
+ const tree = process(
43
+ makeTree(makeLeafDirective("audio", { type: "soundcloud", url })),
44
+ )
45
+ const node = tree.children[0]
46
+ expect(node).toMatchObject({
47
+ type: "mdxJsxFlowElement",
48
+ name: "SoundCloud",
49
+ attributes: [{ type: "mdxJsxAttribute", name: "url", value: url }],
50
+ })
51
+ })
52
+
53
+ it("should skip video directive without type", () => {
54
+ const tree = process(makeTree(makeLeafDirective("video", { id: "abc123" })))
55
+ const node = tree.children[0]
56
+ expect(node).toMatchObject({ type: "leafDirective", name: "video" })
57
+ })
58
+
59
+ it("should skip audio directive without url", () => {
60
+ const tree = process(
61
+ makeTree(makeLeafDirective("audio", { type: "soundcloud" })),
62
+ )
63
+ const node = tree.children[0]
64
+ expect(node).toMatchObject({ type: "leafDirective", name: "audio" })
65
+ })
66
+
67
+ it("should ignore unrelated leaf directives", () => {
68
+ const tree = process(
69
+ makeTree(makeLeafDirective("include", { file: "test.md" })),
70
+ )
71
+ const node = tree.children[0]
72
+ expect(node).toMatchObject({ type: "leafDirective", name: "include" })
73
+ })
74
+ })
@@ -0,0 +1,23 @@
1
+ import type { Root } from "mdast"
2
+ import type { Plugin } from "unified"
3
+
4
+ /** Remark plugin that converts mermaid code blocks into Mermaid components */
5
+ export const remarkMermaid: Plugin<[], Root> = () => {
6
+ return tree => {
7
+ const children = tree.children
8
+ for (let i = 0; i < children.length; i++) {
9
+ const node = children[i]
10
+ if (node?.type !== "code" || node.lang !== "mermaid") continue
11
+
12
+ const jsxNode = {
13
+ type: "mdxJsxFlowElement" as const,
14
+ name: "Mermaid",
15
+ attributes: [],
16
+ children: [{ type: "text" as const, value: node.value }],
17
+ }
18
+
19
+ // @ts-expect-error mdxJsxFlowElement is not in mdast types
20
+ children[i] = jsxNode
21
+ }
22
+ }
23
+ }
@@ -0,0 +1,52 @@
1
+ import type { Root } from "mdast"
2
+ import type { Plugin } from "unified"
3
+ import convert from "npm-to-yarn"
4
+
5
+ type Manager = "npm" | "pnpm" | "yarn" | "bun"
6
+
7
+ const managers: Manager[] = ["npm", "pnpm", "yarn", "bun"]
8
+
9
+ interface MdxJsxAttribute {
10
+ type: "mdxJsxAttribute"
11
+ name: string
12
+ value: string
13
+ }
14
+
15
+ interface MdxJsxFlowElement {
16
+ type: "mdxJsxFlowElement"
17
+ name: string
18
+ attributes: MdxJsxAttribute[]
19
+ children: []
20
+ }
21
+
22
+ // @ts-expect-error tsgo does not resolve npm-to-yarn default export correctly
23
+ const convertCommand: (s: string, to: string) => string = convert
24
+
25
+ /** Remark plugin that converts npm code blocks into PackageTabs components */
26
+ export const remarkNpm: Plugin<[], Root> = () => {
27
+ return tree => {
28
+ const children = tree.children
29
+ for (let i = 0; i < children.length; i++) {
30
+ const node = children[i]
31
+ if (node?.type !== "code") continue
32
+ if (node.lang !== "npm" && node.lang !== "package-install") continue
33
+
34
+ const command = node.value.trim()
35
+ const attributes: MdxJsxAttribute[] = managers.map(pm => ({
36
+ type: "mdxJsxAttribute" as const,
37
+ name: pm,
38
+ value: convertCommand(command, pm),
39
+ }))
40
+
41
+ const jsxNode: MdxJsxFlowElement = {
42
+ type: "mdxJsxFlowElement",
43
+ name: "PackageTabs",
44
+ attributes,
45
+ children: [],
46
+ }
47
+
48
+ // @ts-expect-error mdxJsxFlowElement is not in mdast types
49
+ children[i] = jsxNode
50
+ }
51
+ }
52
+ }
@@ -0,0 +1,36 @@
1
+ import type { Heading, Root } from "mdast"
2
+ import type { Plugin } from "unified"
3
+ import { visit } from "unist-util-visit"
4
+
5
+ const STEP_PATTERN = /\s*\[step\]\s*$/
6
+
7
+ /** Walk a mdast tree, strip `[step]` suffixes from headings, and add a `data-step` attribute */
8
+ export function transformStepHeadings(tree: Root) {
9
+ let stepNumber = 0
10
+
11
+ visit(tree, "heading", (node: Heading) => {
12
+ const lastChild = node.children[node.children.length - 1]
13
+ if (!lastChild || lastChild.type !== "text") return
14
+ if (!STEP_PATTERN.test(lastChild.value)) return
15
+
16
+ stepNumber += 1
17
+ lastChild.value = lastChild.value.replace(STEP_PATTERN, "")
18
+
19
+ if (lastChild.value === "" && node.children.length > 1) {
20
+ node.children.pop()
21
+ }
22
+
23
+ node.data = node.data ?? {}
24
+ node.data.hProperties = {
25
+ ...node.data.hProperties,
26
+ "data-step": stepNumber,
27
+ }
28
+ })
29
+ }
30
+
31
+ /** Remark plugin that converts `[step]` heading suffixes into numbered step markers */
32
+ export const remarkSteps: Plugin<[], Root> = () => {
33
+ return tree => {
34
+ transformStepHeadings(tree)
35
+ }
36
+ }