@docspack/sheaf-astro 0.0.1 → 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.
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { createPageRenderer, type PageRenderer, type RenderedPage, type RendererOptions, } from "./render.js";
1
+ export { createMarkdownRenderer, createPageRenderer, type PageRenderer, type RenderedPage, type RendererOptions, type SourceRenderer, } from "./render.js";
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,eAAe,GACrB,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,cAAc,GACpB,MAAM,aAAa,CAAC"}
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export { createPageRenderer, } from "./render.js";
1
+ export { createMarkdownRenderer, createPageRenderer, } from "./render.js";
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,GAInB,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,kBAAkB,GAKnB,MAAM,aAAa,CAAC"}
package/dist/render.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { type RehypePlugins, type RemarkPlugins, type ShikiConfig } from "@astrojs/markdown-remark";
1
2
  import type { Heading, Page } from "@docspack/sheaf";
2
3
  /**
3
4
  * Rendering a graph's pages with Astro's own Markdown pipeline.
@@ -26,18 +27,57 @@ export interface RenderedPage {
26
27
  /** Options passed through to Astro's processor. */
27
28
  export interface RendererOptions {
28
29
  /**
29
- * Astro highlights code by default. A site that paints its own code-block style should turn
30
- * this off, or it ships a second theme's colours over the top of its own.
30
+ * Astro highlights code by default, in its own theme's colours.
31
+ *
32
+ * A site with a code-block palette of its own has a better option than turning this off:
33
+ * Shiki takes a theme whose colours are arbitrary CSS strings, `var(--…)` included, so the
34
+ * host can hand it the same custom properties the rest of the page reads. See `shikiConfig`.
31
35
  */
32
36
  readonly syntaxHighlight?: false | "shiki" | "prism";
37
+ /** Shiki's configuration, including the theme. Only read when `syntaxHighlight` is Shiki. */
38
+ readonly shikiConfig?: ShikiConfig;
33
39
  readonly gfm?: boolean;
40
+ /**
41
+ * Rehype plugins to run over the rendered tree.
42
+ *
43
+ * Passed through rather than curated here, because what a document's HTML needs is the
44
+ * host's decision — an anchor on every heading, a `tabindex` on a code block the host's CSS
45
+ * made scrollable. This package binds to the host's renderer; it does not have opinions
46
+ * about the host's markup.
47
+ *
48
+ * These run **after** heading ids exist. Astro stamps ids last, so a host plugin that wanted
49
+ * to work with one — the anchor case, which is most of why a docs site reaches for this —
50
+ * saw `undefined` and silently did nothing. This package exists for that seam, so it runs
51
+ * Astro's own `rehypeHeadingIds` first. Astro runs it again at the end and it is idempotent:
52
+ * an id already set is left alone.
53
+ */
54
+ readonly rehypePlugins?: RehypePlugins;
55
+ /**
56
+ * Remark plugins to run over the parsed document, before it becomes HTML.
57
+ *
58
+ * This is the seam a `:::` block needs, and the reason it is here rather than left to the
59
+ * host's `astro.config`: the graph's pages are rendered by *this* processor, so a plugin
60
+ * configured for `src/pages` never sees them. `remark-directive` belongs on both sides —
61
+ * `@docspack/sheaf-emit` parses with it too — because a block that one half understands and
62
+ * the other reads as literal text is a page and a chunk describing different documentation.
63
+ */
64
+ readonly remarkPlugins?: RemarkPlugins;
34
65
  }
35
66
  export type PageRenderer = (page: Page) => Promise<RenderedPage>;
67
+ /** Renders Markdown that is not a page. */
68
+ export type SourceRenderer = (markdown: string) => Promise<RenderedPage>;
36
69
  /**
37
- * Builds one processor and returns a function that renders pages with it.
70
+ * Builds one processor and returns a function that renders Markdown with it.
71
+ *
72
+ * Not every piece of Markdown a documentation site renders is a page. An OpenAPI operation's
73
+ * `description` is Markdown, and so is a frontmatter lede; both have to go through *this*
74
+ * processor rather than a second one, or the site renders two dialects — a backtick that becomes a
75
+ * code chip on one route and stays a backtick on another.
38
76
  *
39
- * Building a processor compiles a plugin pipeline, which is slow enough that doing it per page
77
+ * Building a processor compiles a plugin pipeline, which is slow enough that doing it per document
40
78
  * is noticeable on a corpus of any size — so it is built once, lazily, and shared.
41
79
  */
80
+ export declare function createMarkdownRenderer(options?: RendererOptions): SourceRenderer;
81
+ /** The same processor, for the graph's pages. */
42
82
  export declare function createPageRenderer(options?: RendererOptions): PageRenderer;
43
83
  //# sourceMappingURL=render.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAErD;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAC;CACvC;AAED,mDAAmD;AACnD,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,OAAO,CAAC;IACrD,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;AAEjE;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,eAAoB,GAAG,YAAY,CAoB9E"}
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,aAAa,EAClB,KAAK,aAAa,EAElB,KAAK,WAAW,EACjB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAErD;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAC;CACvC;AAED,mDAAmD;AACnD,MAAM,WAAW,eAAe;IAC9B;;;;;;OAMG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,OAAO,CAAC;IACrD,6FAA6F;IAC7F,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC;IACnC,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC;IACvC;;;;;;;;OAQG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC;CACxC;AAED,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;AAEjE,2CAA2C;AAC3C,MAAM,MAAM,cAAc,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;AAEzE;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,eAAoB,GAAG,cAAc,CAsBpF;AAED,iDAAiD;AACjD,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,eAAoB,GAAG,YAAY,CAK9E"}
package/dist/render.js CHANGED
@@ -1,11 +1,16 @@
1
- import { createMarkdownProcessor } from "@astrojs/markdown-remark";
1
+ import { createMarkdownProcessor, rehypeHeadingIds, } from "@astrojs/markdown-remark";
2
2
  /**
3
- * Builds one processor and returns a function that renders pages with it.
3
+ * Builds one processor and returns a function that renders Markdown with it.
4
4
  *
5
- * Building a processor compiles a plugin pipeline, which is slow enough that doing it per page
5
+ * Not every piece of Markdown a documentation site renders is a page. An OpenAPI operation's
6
+ * `description` is Markdown, and so is a frontmatter lede; both have to go through *this*
7
+ * processor rather than a second one, or the site renders two dialects — a backtick that becomes a
8
+ * code chip on one route and stays a backtick on another.
9
+ *
10
+ * Building a processor compiles a plugin pipeline, which is slow enough that doing it per document
6
11
  * is noticeable on a corpus of any size — so it is built once, lazily, and shared.
7
12
  */
8
- export function createPageRenderer(options = {}) {
13
+ export function createMarkdownRenderer(options = {}) {
9
14
  let pending;
10
15
  const renderer = () => {
11
16
  pending ??= createMarkdownProcessor({
@@ -13,14 +18,24 @@ export function createPageRenderer(options = {}) {
13
18
  ...(options.syntaxHighlight === undefined
14
19
  ? {}
15
20
  : { syntaxHighlight: options.syntaxHighlight }),
21
+ ...(options.remarkPlugins === undefined ? {} : { remarkPlugins: options.remarkPlugins }),
22
+ ...(options.rehypePlugins === undefined
23
+ ? {}
24
+ : { rehypePlugins: [rehypeHeadingIds, ...options.rehypePlugins] }),
25
+ ...(options.shikiConfig === undefined ? {} : { shikiConfig: options.shikiConfig }),
16
26
  });
17
27
  return pending;
18
28
  };
19
- return async (page) => {
20
- // `page.content` rather than `page.body`: the shell renders the title itself, and a second
21
- // `<h1>` from the body would be a duplicate in the document outline.
22
- const { code, metadata } = await (await renderer()).render(page.content);
29
+ return async (markdown) => {
30
+ const { code, metadata } = await (await renderer()).render(markdown);
23
31
  return { html: code, headings: metadata.headings };
24
32
  };
25
33
  }
34
+ /** The same processor, for the graph's pages. */
35
+ export function createPageRenderer(options = {}) {
36
+ const render = createMarkdownRenderer(options);
37
+ // `page.content` rather than `page.body`: the shell renders the title itself, and a second `<h1>`
38
+ // from the body would be a duplicate in the document outline.
39
+ return (page) => render(page.content);
40
+ }
26
41
  //# sourceMappingURL=render.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAyB,MAAM,0BAA0B,CAAC;AAyC1F;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,UAA2B,EAAE;IAC9D,IAAI,OAA8C,CAAC;IAEnD,MAAM,QAAQ,GAAG,GAA8B,EAAE;QAC/C,OAAO,KAAK,uBAAuB,CAAC;YAClC,GAAG,EAAE,OAAO,CAAC,GAAG,IAAI,IAAI;YACxB,GAAG,CAAC,OAAO,CAAC,eAAe,KAAK,SAAS;gBACvC,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC,EAAE,eAAe,EAAE,OAAO,CAAC,eAAe,EAAE,CAAC;SAClD,CAAC,CAAC;QACH,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC;IAEF,OAAO,KAAK,EAAE,IAAI,EAAE,EAAE;QACpB,2FAA2F;QAC3F,qEAAqE;QACrE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC,MAAM,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAEzE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,CAAC;IACrD,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EAIvB,gBAAgB,GAEjB,MAAM,0BAA0B,CAAC;AA0ElC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,sBAAsB,CAAC,UAA2B,EAAE;IAClE,IAAI,OAA8C,CAAC;IAEnD,MAAM,QAAQ,GAAG,GAA8B,EAAE;QAC/C,OAAO,KAAK,uBAAuB,CAAC;YAClC,GAAG,EAAE,OAAO,CAAC,GAAG,IAAI,IAAI;YACxB,GAAG,CAAC,OAAO,CAAC,eAAe,KAAK,SAAS;gBACvC,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC,EAAE,eAAe,EAAE,OAAO,CAAC,eAAe,EAAE,CAAC;YACjD,GAAG,CAAC,OAAO,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC;YACxF,GAAG,CAAC,OAAO,CAAC,aAAa,KAAK,SAAS;gBACrC,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,gBAAgB,EAAE,GAAG,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;YACpE,GAAG,CAAC,OAAO,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC;SACnF,CAAC,CAAC;QACH,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC;IAEF,OAAO,KAAK,EAAE,QAAQ,EAAE,EAAE;QACxB,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC,MAAM,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACrE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,CAAC;IACrD,CAAC,CAAC;AACJ,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,kBAAkB,CAAC,UAA2B,EAAE;IAC9D,MAAM,MAAM,GAAG,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAC/C,kGAAkG;IAClG,8DAA8D;IAC9D,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AACxC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docspack/sheaf-astro",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "description": "Renders a Sheaf content graph with Astro's own Markdown pipeline, so a docs site and its agent index come from one parse.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -28,13 +28,13 @@
28
28
  },
29
29
  "dependencies": {
30
30
  "@astrojs/markdown-remark": "7.2.0",
31
- "@docspack/sheaf": "^0.0.1"
31
+ "@docspack/sheaf": "^0.1.0"
32
32
  },
33
33
  "devDependencies": {
34
+ "@docspack/config": "0.1.0",
34
35
  "@types/node": "26.2.0",
35
36
  "typescript": "6.0.3",
36
- "vitest": "4.1.10",
37
- "@docspack/config": "0.1.0"
37
+ "vitest": "4.1.10"
38
38
  },
39
39
  "publishConfig": {
40
40
  "access": "public"
package/src/index.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  export {
2
+ createMarkdownRenderer,
2
3
  createPageRenderer,
3
4
  type PageRenderer,
4
5
  type RenderedPage,
5
6
  type RendererOptions,
7
+ type SourceRenderer,
6
8
  } from "./render.js";
package/src/render.ts CHANGED
@@ -1,4 +1,11 @@
1
- import { createMarkdownProcessor, type MarkdownRenderer } from "@astrojs/markdown-remark";
1
+ import {
2
+ createMarkdownProcessor,
3
+ type MarkdownRenderer,
4
+ type RehypePlugins,
5
+ type RemarkPlugins,
6
+ rehypeHeadingIds,
7
+ type ShikiConfig,
8
+ } from "@astrojs/markdown-remark";
2
9
  import type { Heading, Page } from "@docspack/sheaf";
3
10
 
4
11
  /**
@@ -30,22 +37,60 @@ export interface RenderedPage {
30
37
  /** Options passed through to Astro's processor. */
31
38
  export interface RendererOptions {
32
39
  /**
33
- * Astro highlights code by default. A site that paints its own code-block style should turn
34
- * this off, or it ships a second theme's colours over the top of its own.
40
+ * Astro highlights code by default, in its own theme's colours.
41
+ *
42
+ * A site with a code-block palette of its own has a better option than turning this off:
43
+ * Shiki takes a theme whose colours are arbitrary CSS strings, `var(--…)` included, so the
44
+ * host can hand it the same custom properties the rest of the page reads. See `shikiConfig`.
35
45
  */
36
46
  readonly syntaxHighlight?: false | "shiki" | "prism";
47
+ /** Shiki's configuration, including the theme. Only read when `syntaxHighlight` is Shiki. */
48
+ readonly shikiConfig?: ShikiConfig;
37
49
  readonly gfm?: boolean;
50
+ /**
51
+ * Rehype plugins to run over the rendered tree.
52
+ *
53
+ * Passed through rather than curated here, because what a document's HTML needs is the
54
+ * host's decision — an anchor on every heading, a `tabindex` on a code block the host's CSS
55
+ * made scrollable. This package binds to the host's renderer; it does not have opinions
56
+ * about the host's markup.
57
+ *
58
+ * These run **after** heading ids exist. Astro stamps ids last, so a host plugin that wanted
59
+ * to work with one — the anchor case, which is most of why a docs site reaches for this —
60
+ * saw `undefined` and silently did nothing. This package exists for that seam, so it runs
61
+ * Astro's own `rehypeHeadingIds` first. Astro runs it again at the end and it is idempotent:
62
+ * an id already set is left alone.
63
+ */
64
+ readonly rehypePlugins?: RehypePlugins;
65
+ /**
66
+ * Remark plugins to run over the parsed document, before it becomes HTML.
67
+ *
68
+ * This is the seam a `:::` block needs, and the reason it is here rather than left to the
69
+ * host's `astro.config`: the graph's pages are rendered by *this* processor, so a plugin
70
+ * configured for `src/pages` never sees them. `remark-directive` belongs on both sides —
71
+ * `@docspack/sheaf-emit` parses with it too — because a block that one half understands and
72
+ * the other reads as literal text is a page and a chunk describing different documentation.
73
+ */
74
+ readonly remarkPlugins?: RemarkPlugins;
38
75
  }
39
76
 
40
77
  export type PageRenderer = (page: Page) => Promise<RenderedPage>;
41
78
 
79
+ /** Renders Markdown that is not a page. */
80
+ export type SourceRenderer = (markdown: string) => Promise<RenderedPage>;
81
+
42
82
  /**
43
- * Builds one processor and returns a function that renders pages with it.
83
+ * Builds one processor and returns a function that renders Markdown with it.
84
+ *
85
+ * Not every piece of Markdown a documentation site renders is a page. An OpenAPI operation's
86
+ * `description` is Markdown, and so is a frontmatter lede; both have to go through *this*
87
+ * processor rather than a second one, or the site renders two dialects — a backtick that becomes a
88
+ * code chip on one route and stays a backtick on another.
44
89
  *
45
- * Building a processor compiles a plugin pipeline, which is slow enough that doing it per page
90
+ * Building a processor compiles a plugin pipeline, which is slow enough that doing it per document
46
91
  * is noticeable on a corpus of any size — so it is built once, lazily, and shared.
47
92
  */
48
- export function createPageRenderer(options: RendererOptions = {}): PageRenderer {
93
+ export function createMarkdownRenderer(options: RendererOptions = {}): SourceRenderer {
49
94
  let pending: Promise<MarkdownRenderer> | undefined;
50
95
 
51
96
  const renderer = (): Promise<MarkdownRenderer> => {
@@ -54,15 +99,25 @@ export function createPageRenderer(options: RendererOptions = {}): PageRenderer
54
99
  ...(options.syntaxHighlight === undefined
55
100
  ? {}
56
101
  : { syntaxHighlight: options.syntaxHighlight }),
102
+ ...(options.remarkPlugins === undefined ? {} : { remarkPlugins: options.remarkPlugins }),
103
+ ...(options.rehypePlugins === undefined
104
+ ? {}
105
+ : { rehypePlugins: [rehypeHeadingIds, ...options.rehypePlugins] }),
106
+ ...(options.shikiConfig === undefined ? {} : { shikiConfig: options.shikiConfig }),
57
107
  });
58
108
  return pending;
59
109
  };
60
110
 
61
- return async (page) => {
62
- // `page.content` rather than `page.body`: the shell renders the title itself, and a second
63
- // `<h1>` from the body would be a duplicate in the document outline.
64
- const { code, metadata } = await (await renderer()).render(page.content);
65
-
111
+ return async (markdown) => {
112
+ const { code, metadata } = await (await renderer()).render(markdown);
66
113
  return { html: code, headings: metadata.headings };
67
114
  };
68
115
  }
116
+
117
+ /** The same processor, for the graph's pages. */
118
+ export function createPageRenderer(options: RendererOptions = {}): PageRenderer {
119
+ const render = createMarkdownRenderer(options);
120
+ // `page.content` rather than `page.body`: the shell renders the title itself, and a second `<h1>`
121
+ // from the body would be a duplicate in the document outline.
122
+ return (page) => render(page.content);
123
+ }