@umami/shiso 1.10.0 → 1.12.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 (43) hide show
  1. package/bin/shiso.mjs +20 -1
  2. package/dist/chunks/App.js +363 -29
  3. package/dist/chunks/docs.js +33 -41
  4. package/dist/entry-client.js +1 -1
  5. package/dist/entry-server.js +30 -2
  6. package/docs.schema.json +90 -0
  7. package/mdx.config.ts +13 -97
  8. package/package.json +5 -4
  9. package/scripts/build-runtime.mjs +1 -0
  10. package/scripts/check-content.mjs +358 -0
  11. package/scripts/expand-navigation-globs.mjs +208 -0
  12. package/scripts/expand-openapi-navigation.mjs +94 -0
  13. package/scripts/generate-openapi.mjs +117 -0
  14. package/scripts/generate-search-index.mjs +31 -1
  15. package/scripts/lib/openapi.mjs +653 -0
  16. package/scripts/load-docs-config.mjs +52 -6
  17. package/scripts/load-shiso-config.mjs +45 -3
  18. package/scripts/prerender.mjs +83 -3
  19. package/scripts/vite-docs-config.mjs +27 -6
  20. package/src/App.tsx +0 -1
  21. package/src/components/CodeBlock.tsx +73 -9
  22. package/src/components/DocContent.tsx +24 -3
  23. package/src/components/Docs.tsx +6 -1
  24. package/src/components/OpenApiOperation.tsx +197 -0
  25. package/src/components/SideNav.tsx +8 -2
  26. package/src/components/docs/Card.tsx +1 -1
  27. package/src/components/docs/CodeGroup.tsx +6 -2
  28. package/src/components/docs/Expandable.tsx +1 -1
  29. package/src/components/docs/PropertiesTable.tsx +12 -25
  30. package/src/components/docs/styles.ts +7 -4
  31. package/src/entry-server.tsx +50 -0
  32. package/src/lib/code-blocks.ts +18 -0
  33. package/src/lib/code-meta.ts +87 -0
  34. package/src/lib/docs-config.ts +4 -1
  35. package/src/lib/openapi.generated.ts +4 -0
  36. package/src/lib/openapi.ts +63 -0
  37. package/src/lib/rehype-shiki.ts +196 -0
  38. package/src/lib/site-model.ts +2 -0
  39. package/src/lib/types.ts +116 -2
  40. package/src/styles/global.css +75 -76
  41. package/types/config.d.ts +11 -4
  42. package/vite.config.ts +49 -3
  43. package/CHANGELOG.md +0 -171
@@ -9832,7 +9832,8 @@ const styles = {
9832
9832
  accordionItem: "",
9833
9833
  accordionTrigger: "text-foreground",
9834
9834
  accordionContent: "text-muted-foreground [&_p]:mt-0 [&_p:last-child]:mb-0",
9835
- expandableTrigger: "items-center px-4 py-3 text-sm font-normal text-foreground hover:no-underline",
9835
+ expandableTrigger: "items-center py-3 text-sm font-normal text-foreground hover:no-underline",
9836
+ expandableContent: "pl-6",
9836
9837
  callout: "my-4 flex items-start gap-3 rounded-lg border px-3 py-2.5 text-sm leading-6",
9837
9838
  calloutIcon: "flex h-6 w-5 shrink-0 items-center justify-center [&_svg]:block [&_svg]:size-4",
9838
9839
  calloutBody: "flex min-w-0 flex-1 flex-col gap-[0.35rem] [&>:first-child]:mt-0 [&>:last-child]:mb-0 [&_:where(p,ul,ol)]:my-[0.35rem] [&>:where(p,ul,ol):first-child]:mt-0 [&>:where(p,ul,ol):last-child]:mb-0",
@@ -9844,7 +9845,8 @@ const styles = {
9844
9845
  tip: "border-green-300 bg-green-50 text-green-900 dark:border-green-400/40 dark:bg-green-400/10 dark:text-green-300",
9845
9846
  check: "border-emerald-300 bg-emerald-50 text-emerald-900 dark:border-emerald-400/40 dark:bg-emerald-400/10 dark:text-emerald-300",
9846
9847
  danger: "border-destructive/40 bg-destructive/10 text-destructive",
9847
- card: "block h-full gap-0 rounded-lg border border-border bg-transparent p-4 text-base ring-0 hover:border-primary",
9848
+ card: "block h-full gap-0 rounded-lg border border-border p-4 text-sm ring-0 hover:border-primary",
9849
+ cardPlain: "bg-card",
9848
9850
  cardHorizontal: "[&_[data-slot=card-inner]]:items-center [&_[data-slot=card-main]]:flex-row [&_[data-slot=card-main]]:items-center [&_[data-slot=card-main]]:gap-3 [&_[data-slot=card-body]]:m-0 [&_[data-slot=card-header]]:flex-row [&_[data-slot=card-header]]:items-center [&_[data-slot=card-header]]:gap-2",
9849
9851
  cardTyped: "[&_[data-slot=card-title]]:text-inherit [&_[data-slot=card-body]]:text-inherit [&_[data-slot=card-cta]]:text-inherit [&_[data-slot=card-arrow]]:text-inherit",
9850
9852
  cardImageLayout: "p-0 [&_[data-slot=card-inner]]:p-6",
@@ -9853,7 +9855,7 @@ const styles = {
9853
9855
  cardMain: "flex min-w-0 flex-col gap-2",
9854
9856
  cardHeader: "flex flex-col items-start gap-3",
9855
9857
  cardIcon: "inline-flex shrink-0 items-center justify-center [&_img]:size-6 [&_svg]:size-6",
9856
- cardTitle: "text-base font-semibold text-foreground",
9858
+ cardTitle: "text-sm font-semibold text-foreground",
9857
9859
  cardBody: "text-muted-foreground [&>:first-child]:mt-0 [&>:last-child]:mb-0",
9858
9860
  cardCta: "flex shrink-0 items-center gap-[0.35rem] text-[0.9rem] font-medium text-muted-foreground",
9859
9861
  cardArrow: "shrink-0 text-muted-foreground",
@@ -10377,7 +10379,7 @@ function DocsCardContent({ title, icon, color, img, cta, arrow, type, children }
10377
10379
  function Card({ title, href, icon, color, img, cta, horizontal, arrow, type, children }) {
10378
10380
  const external = typeof href === "string" && /^https?:\/\//i.test(href);
10379
10381
  const showArrow = arrow ?? external;
10380
- const className = `${styles.card} ${type ? `${styles.cardTyped} ${styles[type]} ${CARD_TYPE_HOVER[type]}` : ""} ${horizontal ? styles.cardHorizontal : ""} ${img ? styles.cardImageLayout : ""}`;
10382
+ const className = `${styles.card} ${type ? `${styles.cardTyped} ${styles[type]} ${CARD_TYPE_HOVER[type]}` : styles.cardPlain} ${horizontal ? styles.cardHorizontal : ""} ${img ? styles.cardImageLayout : ""}`;
10381
10383
  const content = /* @__PURE__ */ jsx(Card$1, {
10382
10384
  className,
10383
10385
  children: /* @__PURE__ */ jsx(DocsCardContent, {
@@ -11298,13 +11300,13 @@ function CodeGroup({ children }) {
11298
11300
  const selected = blocks.some((block) => block.id === selectedKey) ? selectedKey : blocks[0]?.id;
11299
11301
  if (!blocks.length) return null;
11300
11302
  if (blocks.length === 1) return /* @__PURE__ */ jsx("div", {
11301
- className: styles.tabs,
11303
+ className: `${styles.tabs} [&_[data-slot=code-block-header]]:hidden`,
11302
11304
  children: blocks[0].content
11303
11305
  });
11304
11306
  return /* @__PURE__ */ jsxs(Tabs$1, {
11305
11307
  value: selected,
11306
11308
  onValueChange: setSelectedKey,
11307
- className: "my-4 gap-0 overflow-hidden rounded-lg bg-muted/50 [&_[data-slot=code-block]]:my-0 [&_[data-slot=code-block]]:rounded-none [&_[data-slot=code-block]]:border-0 [&_[data-slot=code-block]]:bg-transparent",
11309
+ className: "my-4 gap-0 overflow-hidden rounded-lg bg-card [&_[data-slot=code-block]]:my-0 [&_[data-slot=code-block]]:rounded-none [&_[data-slot=code-block]]:border-0 [&_[data-slot=code-block]]:bg-transparent [&_[data-slot=code-block-header]]:hidden",
11308
11310
  children: [/* @__PURE__ */ jsx(TabsList, {
11309
11311
  variant: "line",
11310
11312
  className: "h-9 w-full justify-start gap-5 overflow-x-auto overflow-y-hidden rounded-none px-3 py-0 shadow-[inset_0_-1px_0_var(--border)] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
@@ -11541,7 +11543,7 @@ function Expandable({ title, children, defaultOpen = false }) {
11541
11543
  className: `${styles.expandableTrigger} group/collapsible-trigger flex w-full justify-start gap-2 text-left outline-none focus-visible:ring-3 focus-visible:ring-ring/50`,
11542
11544
  children: [/* @__PURE__ */ jsx(ChevronRight, { className: "size-4 shrink-0 text-muted-foreground transition-transform group-aria-expanded/collapsible-trigger:rotate-90" }), title]
11543
11545
  }), /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx("div", {
11544
- className: styles.accordionContent,
11546
+ className: `${styles.accordionContent} ${styles.expandableContent}`,
11545
11547
  children
11546
11548
  }) })]
11547
11549
  })
@@ -11725,53 +11727,43 @@ function PropertiesTable({ children }) {
11725
11727
  const rows = toElementArray(children);
11726
11728
  if (!rows.length) return null;
11727
11729
  return /* @__PURE__ */ jsx("div", {
11728
- className: "my-4 overflow-x-auto rounded-lg border border-border px-6 py-2",
11730
+ className: "my-4 overflow-x-auto",
11729
11731
  children: /* @__PURE__ */ jsxs("table", {
11730
- className: "m-0 min-w-[40rem] table-fixed border-collapse border-0",
11732
+ className: "m-0 min-w-[40rem] table-fixed",
11731
11733
  children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
11732
11734
  /* @__PURE__ */ jsx("th", {
11733
- className: "w-1/5 border-x-0 border-t-0 border-b border-border bg-transparent px-0 py-2 pr-6 text-left text-sm font-semibold text-foreground",
11735
+ className: "w-1/5",
11734
11736
  children: "Name"
11735
11737
  }),
11736
11738
  /* @__PURE__ */ jsx("th", {
11737
- className: "w-1/4 border-x-0 border-t-0 border-b border-border bg-transparent px-0 py-2 pr-6 text-left text-sm font-semibold text-foreground",
11739
+ className: "w-1/4",
11738
11740
  children: "Type"
11739
11741
  }),
11740
- /* @__PURE__ */ jsx("th", {
11741
- className: "border-x-0 border-t-0 border-b border-border bg-transparent px-0 py-2 text-left text-sm font-semibold text-foreground",
11742
- children: "Description"
11743
- })
11744
- ] }) }), /* @__PURE__ */ jsx("tbody", { children: rows.map((row, index) => {
11742
+ /* @__PURE__ */ jsx("th", { children: "Description" })
11743
+ ] }) }), /* @__PURE__ */ jsx("tbody", { children: rows.map((row) => {
11745
11744
  const { name, type, required, deprecated, children: description } = row.props;
11746
11745
  const defaultValue = displayValue(row.props.default);
11747
- const borderClass = index === rows.length - 1 ? "border-b-0" : "border-b";
11748
11746
  return /* @__PURE__ */ jsxs("tr", { children: [
11749
- /* @__PURE__ */ jsx("td", {
11750
- className: `${borderClass} border-x-0 border-t-0 border-border px-0 py-2 pr-6 align-top text-sm text-foreground`,
11751
- children: /* @__PURE__ */ jsxs("span", {
11752
- className: "inline-flex flex-wrap items-center gap-2",
11753
- children: [
11754
- name,
11755
- required ? /* @__PURE__ */ jsx(Badge, {
11756
- size: "xs",
11757
- tone: "primary",
11758
- children: "required"
11759
- }) : null,
11760
- deprecated ? /* @__PURE__ */ jsx(Badge, {
11761
- size: "xs",
11762
- children: "deprecated"
11763
- }) : null
11764
- ]
11765
- })
11766
- }),
11767
- /* @__PURE__ */ jsx("td", {
11768
- className: `${borderClass} border-x-0 border-t-0 border-border px-0 py-2 pr-6 align-top text-sm text-foreground`,
11769
- children: displayValue(type)
11770
- }),
11747
+ /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsxs("span", {
11748
+ className: "inline-flex flex-wrap items-center gap-2",
11749
+ children: [
11750
+ name,
11751
+ required ? /* @__PURE__ */ jsx(Badge, {
11752
+ size: "xs",
11753
+ tone: "primary",
11754
+ children: "required"
11755
+ }) : null,
11756
+ deprecated ? /* @__PURE__ */ jsx(Badge, {
11757
+ size: "xs",
11758
+ children: "deprecated"
11759
+ }) : null
11760
+ ]
11761
+ }) }),
11762
+ /* @__PURE__ */ jsx("td", { children: displayValue(type) }),
11771
11763
  /* @__PURE__ */ jsxs("td", {
11772
- className: `${borderClass} border-x-0 border-t-0 border-border px-0 py-2 align-top text-sm leading-6 text-foreground [&_p]:m-0`,
11764
+ className: "[&_p]:m-0",
11773
11765
  children: [description, defaultValue ? /* @__PURE__ */ jsxs("span", {
11774
- className: "mt-1 block text-sm text-muted-foreground",
11766
+ className: "mt-1 block text-muted-foreground",
11775
11767
  children: ["Default: ", defaultValue]
11776
11768
  }) : null]
11777
11769
  })
@@ -1,5 +1,5 @@
1
1
  import { ar as BrowserRouter } from "./chunks/docs.js";
2
- import { m as BASE_URL, t as App } from "./chunks/App.js";
2
+ import { h as BASE_URL, t as App } from "./chunks/App.js";
3
3
  import { jsx } from "react/jsx-runtime";
4
4
 
5
5
  //#region src/entry-client.tsx
@@ -1,5 +1,5 @@
1
1
  import { hr as ABSOLUTE_URL_REGEX, lr as Router, mr as parsePath, pr as createPath } from "./chunks/docs.js";
2
- import { a as docsSite, c as getSeo, d as getDocModule, f as getLastModified, h as toAbsoluteUrl, i as docsHomeUrl, l as siteName, m as BASE_URL, n as buildHead, o as getLocaleByPathname, p as getScopeForPage, r as renderHeadToString, s as getRedirects, t as App, u as standalonePages } from "./chunks/App.js";
2
+ import { a as docsSite, c as getSeo, d as standalonePages, f as getDocModule, g as toAbsoluteUrl, h as BASE_URL, i as docsHomeUrl, l as siteConfig, m as getScopeForPage, n as buildHead, o as getLocaleByPathname, p as getLastModified, r as renderHeadToString, s as getRedirects, t as App, u as siteName } from "./chunks/App.js";
3
3
  import * as React$1 from "react";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  import { renderToString } from "react-dom/server";
@@ -107,6 +107,33 @@ function getMarkdownPages() {
107
107
  filePath: page.filePath
108
108
  }))];
109
109
  }
110
+ /** Navigable Markdown pages used to generate llms.txt and llms-full.txt. */
111
+ function getLlmsPages() {
112
+ const { indexing } = getSeo();
113
+ const pages = [];
114
+ for (const page of docsSite.pages) {
115
+ const doc = getDocModule(page.filePath);
116
+ if (doc?.frontmatter?.noindex === true || (page.hidden || getScopeForPage(docsSite, page).hidden) && indexing !== "all") continue;
117
+ pages.push({
118
+ route: page.url,
119
+ filePath: page.filePath,
120
+ title: doc?.frontmatter?.title || page.label,
121
+ description: doc?.frontmatter?.description
122
+ });
123
+ }
124
+ for (const page of standalonePages) {
125
+ if (page.filePath.endsWith(".tsx")) continue;
126
+ const doc = getDocModule(page.filePath);
127
+ if (doc?.frontmatter?.noindex === true) continue;
128
+ pages.push({
129
+ route: page.path,
130
+ filePath: page.filePath,
131
+ title: doc?.frontmatter?.title || page.title || page.path,
132
+ description: doc?.frontmatter?.description
133
+ });
134
+ }
135
+ return pages;
136
+ }
110
137
  /**
111
138
  * Absolute URLs for the sitemap, honoring `seo.indexing` and per-page
112
139
  * noindex. Empty when the shiso.config `siteUrl` is not configured, since a sitemap
@@ -145,6 +172,7 @@ function render(url) {
145
172
  htmlAttrs: getLocaleByPathname(url)
146
173
  };
147
174
  }
175
+ const siteDescription = siteConfig.description;
148
176
 
149
177
  //#endregion
150
- export { docsHomeUrl, getMarkdownPages, getRedirects, getRoutes, getSitemapEntries, render, siteName };
178
+ export { docsHomeUrl, getLlmsPages, getMarkdownPages, getRedirects, getRoutes, getSitemapEntries, render, siteDescription, siteName };
package/docs.schema.json CHANGED
@@ -172,6 +172,37 @@
172
172
  ],
173
173
  "description": "Page eyebrow style: the section name (default) or the full navigation path.",
174
174
  "default": "section"
175
+ },
176
+ "codeBlocks": {
177
+ "type": "object",
178
+ "description": "Fenced code block defaults: line numbers and the Shiki light/dark themes.",
179
+ "additionalProperties": false,
180
+ "properties": {
181
+ "$ref": { "$ref": "#/definitions/configRef" },
182
+ "lineNumbers": {
183
+ "allOf": [{ "$ref": "#/definitions/booleanValue" }],
184
+ "description": "Show line numbers on every block. Override per block with showLineNumbers or hideLineNumbers in the fence meta.",
185
+ "default": false
186
+ },
187
+ "theme": {
188
+ "type": "object",
189
+ "description": "Bundled Shiki theme names for each color mode (https://shiki.style/themes).",
190
+ "additionalProperties": false,
191
+ "properties": {
192
+ "$ref": { "$ref": "#/definitions/configRef" },
193
+ "light": {
194
+ "allOf": [{ "$ref": "#/definitions/nonEmptyStringValue" }],
195
+ "description": "Theme used in light mode.",
196
+ "default": "github-light"
197
+ },
198
+ "dark": {
199
+ "allOf": [{ "$ref": "#/definitions/nonEmptyStringValue" }],
200
+ "description": "Theme used in dark mode.",
201
+ "default": "github-dark"
202
+ }
203
+ }
204
+ }
205
+ }
175
206
  }
176
207
  }
177
208
  },
@@ -567,6 +598,24 @@
567
598
  }
568
599
  }
569
600
  },
601
+ "api": {
602
+ "type": "object",
603
+ "description": "API reference settings: the OpenAPI spec powering openapi frontmatter pages.",
604
+ "anyOf": [{ "required": ["$ref"] }, { "required": ["spec"] }],
605
+ "additionalProperties": false,
606
+ "properties": {
607
+ "$ref": { "$ref": "#/definitions/configRef" },
608
+ "spec": {
609
+ "allOf": [{ "$ref": "#/definitions/nonEmptyStringValue" }],
610
+ "description": "Path to a local OpenAPI 3.0 or 3.1 spec (JSON or YAML), relative to the project root."
611
+ },
612
+ "directory": {
613
+ "allOf": [{ "$ref": "#/definitions/nonEmptyStringValue" }],
614
+ "description": "Folder inside the content directory for generated endpoint pages.",
615
+ "default": "api-reference"
616
+ }
617
+ }
618
+ },
570
619
  "errors": {
571
620
  "type": "object",
572
621
  "description": "Error handling settings.",
@@ -712,6 +761,8 @@
712
761
  "description": "Page reference relative to the content directory, without extension (e.g. \"components/tabs\")."
713
762
  },
714
763
  { "$ref": "#/definitions/pageObject" },
764
+ { "$ref": "#/definitions/globItem" },
765
+ { "$ref": "#/definitions/openapiItem" },
715
766
  { "$ref": "#/definitions/linkItem" },
716
767
  { "$ref": "#/definitions/group" }
717
768
  ]
@@ -736,12 +787,51 @@
736
787
  },
737
788
  "icon": { "allOf": [{ "$ref": "#/definitions/stringValue" }], "description": "Icon name shown beside the label." },
738
789
  "tag": { "allOf": [{ "$ref": "#/definitions/stringValue" }], "description": "Short label shown after the page name." },
790
+ "method": {
791
+ "anyOf": [
792
+ { "enum": ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS", "TRACE"] },
793
+ { "$ref": "#/definitions/configRefValue" }
794
+ ],
795
+ "description": "HTTP method badge shown after the page name on API reference pages."
796
+ },
739
797
  "hidden": {
740
798
  "allOf": [{ "$ref": "#/definitions/booleanValue" }],
741
799
  "description": "Hide from the sidebar and search. The page is still built and reachable by URL."
742
800
  }
743
801
  }
744
802
  },
803
+ "globItem": {
804
+ "type": "object",
805
+ "required": ["glob"],
806
+ "additionalProperties": false,
807
+ "properties": {
808
+ "glob": {
809
+ "allOf": [{ "$ref": "#/definitions/nonEmptyStringValue" }],
810
+ "description": "Glob relative to contentDir. Matching Markdown and MDX files are expanded into page entries."
811
+ },
812
+ "exclude": {
813
+ "anyOf": [
814
+ {
815
+ "type": "array",
816
+ "items": { "$ref": "#/definitions/nonEmptyStringValue" }
817
+ },
818
+ { "$ref": "#/definitions/configRefValue" }
819
+ ],
820
+ "description": "Optional glob patterns to omit from the matches."
821
+ }
822
+ }
823
+ },
824
+ "openapiItem": {
825
+ "type": "object",
826
+ "required": ["openapi"],
827
+ "additionalProperties": false,
828
+ "properties": {
829
+ "openapi": {
830
+ "anyOf": [{ "type": "boolean" }, { "$ref": "#/definitions/nonEmptyStringValue" }],
831
+ "description": "Expand API endpoints into pages: true for every tag (grouped), or a tag name for that tag's operations. Requires the api.spec setting."
832
+ }
833
+ }
834
+ },
745
835
  "linkItem": {
746
836
  "type": "object",
747
837
  "description": "An external link in the navigation tree.",
package/mdx.config.ts CHANGED
@@ -1,104 +1,19 @@
1
1
  import mdx from '@mdx-js/rollup';
2
2
  import rehypeAutolinkHeadings from 'rehype-autolink-headings';
3
- import rehypeHighlight from 'rehype-highlight';
4
3
  import rehypeSlug from 'rehype-slug';
5
4
  import remarkFrontmatter from 'remark-frontmatter';
6
5
  import remarkGfm from 'remark-gfm';
7
6
  import remarkMdxFrontmatter from 'remark-mdx-frontmatter';
8
7
  import type { Plugin } from 'vite';
9
- import { type MdNode, toText, walkTree } from './src/lib/mdast.ts';
8
+ import { resolveCodeBlockConfig } from './src/lib/code-blocks.ts';
9
+ import { type MdNode, toText } from './src/lib/mdast.ts';
10
+ import { rehypeShiki } from './src/lib/rehype-shiki.ts';
10
11
  import { remarkToc } from './src/lib/remark-toc.ts';
12
+ import type { MdxConfig, ResolvedCodeBlockConfig } from './src/lib/types.ts';
11
13
 
12
- const SYNTHETIC_FRAGMENT = 'data-shiso-synthetic-fragment';
13
-
14
- function remarkCodeTitles() {
15
- return (tree: MdNode) => {
16
- const visit = (node: MdNode) => {
17
- if (node.type === 'code' && node.meta?.trim()) {
18
- const meta = node.meta.trim();
19
- const titleMatch = meta.match(/(?:^|\s)title=(?:"([^"]+)"|'([^']+)'|([^\s]+))/);
20
- const title = titleMatch ? titleMatch[1] || titleMatch[2] || titleMatch[3] : meta;
21
- const hProperties = (node.data?.hProperties || {}) as Record<string, unknown>;
22
- node.data = {
23
- ...node.data,
24
- hProperties: { ...hProperties, 'data-title': title },
25
- };
26
- }
27
-
28
- node.children?.forEach(visit);
29
- };
30
-
31
- visit(tree);
32
- };
33
- }
34
-
35
- function rehypeWrapJsxForHighlighting() {
36
- return (tree: MdNode) => {
37
- walkTree(tree, node => {
38
- if (node.tagName !== 'code') {
39
- return;
40
- }
41
-
42
- const properties = (node.properties || {}) as Record<string, unknown>;
43
- const className = properties.className;
44
- const classes = Array.isArray(className) ? className.map(String) : [String(className || '')];
45
- const source = toText(node);
46
-
47
- if (!classes.some(value => /\blanguage-(?:jsx|tsx)\b/.test(value))) {
48
- return;
49
- }
50
-
51
- if (!/^\s*<[A-Z][\w.]*(?:\s|>|\/)/.test(source)) {
52
- return;
53
- }
54
-
55
- properties[SYNTHETIC_FRAGMENT] = true;
56
- node.properties = properties;
57
- node.children = [{ type: 'text', value: `<>\n${source}\n</>` }];
58
- });
59
- };
60
- }
61
-
62
- function boundaryText(node: MdNode, fromEnd = false): MdNode | undefined {
63
- if (node.type === 'text' && typeof node.value === 'string') {
64
- return node;
65
- }
66
-
67
- const children = fromEnd ? [...(node.children || [])].reverse() : node.children || [];
68
- for (const child of children) {
69
- const match = boundaryText(child, fromEnd);
70
- if (match) {
71
- return match;
72
- }
73
- }
74
-
75
- return undefined;
76
- }
77
-
78
- function rehypeRemoveHighlightingFragments() {
79
- return (tree: MdNode) => {
80
- walkTree(tree, node => {
81
- if (node.tagName !== 'code') {
82
- return;
83
- }
84
-
85
- const properties = (node.properties || {}) as Record<string, unknown>;
86
- if (!properties[SYNTHETIC_FRAGMENT]) {
87
- return;
88
- }
89
-
90
- const first = boundaryText(node);
91
- const last = boundaryText(node, true);
92
- if (first?.value) {
93
- first.value = first.value.replace(/^<>\r?\n/, '');
94
- }
95
- if (last?.value) {
96
- last.value = last.value.replace(/\r?\n<\/>$/, '');
97
- }
98
-
99
- delete properties[SYNTHETIC_FRAGMENT];
100
- });
101
- };
14
+ export interface ShisoMdxOptions extends MdxConfig {
15
+ /** Resolved docs.json `styling.codeBlocks`; defaults apply when omitted. */
16
+ codeBlocks?: ResolvedCodeBlockConfig;
102
17
  }
103
18
 
104
19
  function rehypeZoomableImages() {
@@ -142,7 +57,9 @@ function rehypeZoomableImages() {
142
57
  * The MDX compilation pipeline, shared by the app build and the test runner so
143
58
  * tests exercise the same transforms the site ships with.
144
59
  */
145
- export function shisoMdx(): Plugin {
60
+ export function shisoMdx(options: ShisoMdxOptions = {}): Plugin {
61
+ const codeBlocks = options.codeBlocks || resolveCodeBlockConfig();
62
+
146
63
  return {
147
64
  // Must run before vite:react-babel so MDX is compiled to JSX first.
148
65
  enforce: 'pre',
@@ -152,13 +69,12 @@ export function shisoMdx(): Plugin {
152
69
  remarkFrontmatter,
153
70
  remarkMdxFrontmatter,
154
71
  remarkGfm,
155
- remarkCodeTitles,
72
+ ...(options.remarkPlugins || []),
156
73
  remarkToc,
157
74
  ],
158
75
  rehypePlugins: [
159
- rehypeWrapJsxForHighlighting,
160
- rehypeHighlight,
161
- rehypeRemoveHighlightingFragments,
76
+ ...(options.rehypePlugins || []),
77
+ [rehypeShiki, codeBlocks],
162
78
  rehypeZoomableImages,
163
79
  rehypeSlug,
164
80
  [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umami/shiso",
3
- "version": "1.10.0",
3
+ "version": "1.12.0",
4
4
  "description": "Open-source documentation framework for Markdown and MDX sites.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -72,6 +72,7 @@
72
72
  "@fontsource/jetbrains-mono": "^5.2.5",
73
73
  "@mdx-js/react": "^3.1.1",
74
74
  "@mdx-js/rollup": "^3.1.1",
75
+ "@shikijs/transformers": "^4.4.3",
75
76
  "@tailwindcss/vite": "^4.3.3",
76
77
  "@vitejs/plugin-react": "^6.0.5",
77
78
  "ajv": "^8.20.0",
@@ -80,24 +81,24 @@
80
81
  "cmdk": "^1.1.1",
81
82
  "estree-util-value-to-estree": "^3.5.0",
82
83
  "github-slugger": "^2.0.0",
83
- "highlight.js": "^11.11.1",
84
84
  "jiti": "^2.4.0",
85
85
  "lucide-react": "^1.28.0",
86
86
  "react-router": "^8.3.0",
87
87
  "rehype-autolink-headings": "^7.1.0",
88
- "rehype-highlight": "^7.0.1",
89
88
  "rehype-slug": "^6.0.0",
90
89
  "remark-frontmatter": "^5.0.0",
91
90
  "remark-gfm": "^4.0.0",
92
91
  "remark-mdx": "^3.1.1",
93
92
  "remark-mdx-frontmatter": "^5.2.0",
94
93
  "remark-parse": "^11.0.0",
94
+ "shiki": "^4.4.3",
95
95
  "simple-icons": "^16.28.0",
96
96
  "tailwind-merge": "^3.6.0",
97
97
  "tailwindcss": "^4.3.3",
98
98
  "tw-animate-css": "^1.4.0",
99
99
  "unified": "^11.0.5",
100
- "vite": "^8.2.0"
100
+ "vite": "^8.2.0",
101
+ "yaml": "^2.9.0"
101
102
  },
102
103
  "optionalDependencies": {
103
104
  "pagefind": "^1.4.0"
@@ -10,6 +10,7 @@ const outputRoot = path.join(packageRoot, 'dist');
10
10
  const projectModules = new Set([
11
11
  '@/generated/last-modified',
12
12
  '@/lib/icon-registry.generated',
13
+ '@/lib/openapi.generated',
13
14
  '@/lib/search-index.generated',
14
15
  'virtual:shiso-docs-config',
15
16
  'virtual:shiso-config',