@docspack/sheaf-astro 0.1.0 → 0.3.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/README.md +30 -0
- package/dist/render.d.ts +20 -6
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +29 -4
- package/dist/render.js.map +1 -1
- package/package.json +2 -2
- package/src/render.ts +65 -10
package/README.md
CHANGED
|
@@ -17,17 +17,47 @@ const { html, headings } = await renderPage(page);
|
|
|
17
17
|
`.md` file in `src/pages`. Your Markdown configuration keeps applying, and a document reaches
|
|
18
18
|
a reader through one pipeline whichever route served it.
|
|
19
19
|
|
|
20
|
+
A site that is not Astro should use [`@docspack/sheaf-html`](../sheaf-html) instead: the same
|
|
21
|
+
API on a plain unified pipeline, without Astro's renderer and its dependencies.
|
|
22
|
+
|
|
20
23
|
## The part that matters
|
|
21
24
|
|
|
22
25
|
A table of contents is built from the graph's heading slugs; the anchors it links to are the
|
|
23
26
|
`id`s this renderer stamps. When those disagree the failure is silent — the browser scrolls to
|
|
24
27
|
the top instead of erroring. This package asserts they agree for every page it renders.
|
|
25
28
|
|
|
29
|
+
## Stability
|
|
30
|
+
|
|
31
|
+
Sheaf's packages are `0.x` and release together, as one version (see the repository's
|
|
32
|
+
*Releasing* section). Until 1.0, a breaking change to anything listed as stable below lands only in
|
|
33
|
+
a minor release, and its changeset says **Breaking** and what to change. A patch release never
|
|
34
|
+
breaks.
|
|
35
|
+
|
|
36
|
+
**Stable:** `createPageRenderer`, `createMarkdownRenderer`, the `RenderedPage` shape, and the
|
|
37
|
+
rule that a heading's `id` is the graph's slug for it. **Not yet stable:** the `links` option.
|
|
38
|
+
|
|
26
39
|
## Options
|
|
27
40
|
|
|
28
41
|
| | |
|
|
29
42
|
| --- | --- |
|
|
30
43
|
| `syntaxHighlight` | `false`, `"shiki"` or `"prism"`. Turn it off if your site paints its own code blocks. |
|
|
44
|
+
| `shikiConfig` | Passed to Astro's Shiki integration: a theme, languages, transformers. |
|
|
31
45
|
| `gfm` | GitHub-flavoured Markdown. On by default. |
|
|
46
|
+
| `remarkPlugins` | Run over the parsed page. Pass `sheafDirectives` from `@docspack/sheaf-emit` here for `:::` blocks, in place of `remark-directive`. |
|
|
47
|
+
| `rehypePlugins` | Run over the HTML tree, after heading ids are set. |
|
|
48
|
+
| `links` | `{ graph, target, resolve? }`. Rewrites links between pages to `target(page, "site")`, fragment checked; other relative paths go to `resolve`. A broken link fails the render. |
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
const render = createPageRenderer({
|
|
52
|
+
links: {
|
|
53
|
+
graph,
|
|
54
|
+
target: (page) => `/docs/${page.slug}`,
|
|
55
|
+
resolve: (path) => `https://github.com/acme/repo/blob/main/docs/${path}`,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`createMarkdownRenderer(options)` returns the same processor for Markdown that is not a page;
|
|
61
|
+
pass the page as a second argument when the Markdown is part of one, so its links resolve from it.
|
|
32
62
|
|
|
33
63
|
MIT © docspack
|
package/dist/render.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type RehypePlugins, type RemarkPlugins, type ShikiConfig } from "@astrojs/markdown-remark";
|
|
2
|
-
import type
|
|
2
|
+
import { type ContentGraph, type Heading, type LinkOptions, type Page } from "@docspack/sheaf";
|
|
3
3
|
/**
|
|
4
4
|
* Rendering a graph's pages with Astro's own Markdown pipeline.
|
|
5
5
|
*
|
|
@@ -57,15 +57,29 @@ export interface RendererOptions {
|
|
|
57
57
|
*
|
|
58
58
|
* This is the seam a `:::` block needs, and the reason it is here rather than left to the
|
|
59
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. `
|
|
61
|
-
* `@docspack/sheaf-emit`
|
|
62
|
-
*
|
|
60
|
+
* configured for `src/pages` never sees them. Pass `sheafDirectives` from
|
|
61
|
+
* `@docspack/sheaf-emit` here rather than `remark-directive`: it is the vocabulary that side
|
|
62
|
+
* parses with, so a block that one half understands and the other reads as literal text — or
|
|
63
|
+
* a `15:16:24` one half keeps and the other drops — cannot happen.
|
|
63
64
|
*/
|
|
64
65
|
readonly remarkPlugins?: RemarkPlugins;
|
|
66
|
+
/**
|
|
67
|
+
* Rewrites relative links between pages — `./TOKENS.md#colors` — to `target(page, "site")`,
|
|
68
|
+
* fragment kept and checked against the target's headings, and any other relative path through
|
|
69
|
+
* `resolve`. A broken fragment or an unresolvable path fails the render, naming the page.
|
|
70
|
+
*
|
|
71
|
+
* Applies to pages only: Markdown rendered without a page has nothing to be relative to.
|
|
72
|
+
*/
|
|
73
|
+
readonly links?: LinkOptions & {
|
|
74
|
+
readonly graph: ContentGraph;
|
|
75
|
+
};
|
|
65
76
|
}
|
|
66
77
|
export type PageRenderer = (page: Page) => Promise<RenderedPage>;
|
|
67
|
-
/**
|
|
68
|
-
|
|
78
|
+
/**
|
|
79
|
+
* Renders Markdown. Given the page it belongs to — a page's `content`, or a part of it — its
|
|
80
|
+
* relative links resolve from that page and its frontmatter reaches the plugins.
|
|
81
|
+
*/
|
|
82
|
+
export type SourceRenderer = (markdown: string, page?: Page) => Promise<RenderedPage>;
|
|
69
83
|
/**
|
|
70
84
|
* Builds one processor and returns a function that renders Markdown with it.
|
|
71
85
|
*
|
package/dist/render.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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,
|
|
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,EACL,KAAK,YAAY,EACjB,KAAK,OAAO,EACZ,KAAK,WAAW,EAEhB,KAAK,IAAI,EACV,MAAM,iBAAiB,CAAC;AAEzB;;;;;;;;;;;;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;;;;;;;;;OASG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC;IACvC;;;;;;OAMG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,GAAG;QAAE,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAA;KAAE,CAAC;CACjE;AAiCD,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;AAEjE;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,IAAI,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;AAEtF;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,eAAoB,GAAG,cAAc,CA4BpF;AAED,iDAAiD;AACjD,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,eAAoB,GAAG,YAAY,CAK9E"}
|
package/dist/render.js
CHANGED
|
@@ -1,4 +1,26 @@
|
|
|
1
1
|
import { createMarkdownProcessor, rehypeHeadingIds, } from "@astrojs/markdown-remark";
|
|
2
|
+
import { linkHref, } from "@docspack/sheaf";
|
|
3
|
+
/**
|
|
4
|
+
* The remark side of `links`. The processor is shared by every page, so the page being rendered
|
|
5
|
+
* arrives the way Astro hands any document's metadata to a plugin: in its frontmatter, where
|
|
6
|
+
* `render` puts the page's slug.
|
|
7
|
+
*/
|
|
8
|
+
function rewritePageLinks(links) {
|
|
9
|
+
return () => (tree, file) => {
|
|
10
|
+
const slug = file.data.astro?.frontmatter?.slug;
|
|
11
|
+
const from = links.graph.pages.find((page) => page.slug === slug);
|
|
12
|
+
if (from === undefined)
|
|
13
|
+
return;
|
|
14
|
+
const visit = (node) => {
|
|
15
|
+
if ((node.type === "link" || node.type === "definition") && node.url) {
|
|
16
|
+
node.url = linkHref(links.graph, from, node.url, links, "site");
|
|
17
|
+
}
|
|
18
|
+
for (const child of node.children ?? [])
|
|
19
|
+
visit(child);
|
|
20
|
+
};
|
|
21
|
+
visit(tree);
|
|
22
|
+
};
|
|
23
|
+
}
|
|
2
24
|
/**
|
|
3
25
|
* Builds one processor and returns a function that renders Markdown with it.
|
|
4
26
|
*
|
|
@@ -18,7 +40,10 @@ export function createMarkdownRenderer(options = {}) {
|
|
|
18
40
|
...(options.syntaxHighlight === undefined
|
|
19
41
|
? {}
|
|
20
42
|
: { syntaxHighlight: options.syntaxHighlight }),
|
|
21
|
-
|
|
43
|
+
remarkPlugins: [
|
|
44
|
+
...(options.remarkPlugins ?? []),
|
|
45
|
+
...(options.links === undefined ? [] : [rewritePageLinks(options.links)]),
|
|
46
|
+
],
|
|
22
47
|
...(options.rehypePlugins === undefined
|
|
23
48
|
? {}
|
|
24
49
|
: { rehypePlugins: [rehypeHeadingIds, ...options.rehypePlugins] }),
|
|
@@ -26,8 +51,8 @@ export function createMarkdownRenderer(options = {}) {
|
|
|
26
51
|
});
|
|
27
52
|
return pending;
|
|
28
53
|
};
|
|
29
|
-
return async (markdown) => {
|
|
30
|
-
const { code, metadata } = await (await renderer()).render(markdown);
|
|
54
|
+
return async (markdown, page) => {
|
|
55
|
+
const { code, metadata } = await (await renderer()).render(markdown, page === undefined ? undefined : { frontmatter: { ...page.frontmatter, slug: page.slug } });
|
|
31
56
|
return { html: code, headings: metadata.headings };
|
|
32
57
|
};
|
|
33
58
|
}
|
|
@@ -36,6 +61,6 @@ export function createPageRenderer(options = {}) {
|
|
|
36
61
|
const render = createMarkdownRenderer(options);
|
|
37
62
|
// `page.content` rather than `page.body`: the shell renders the title itself, and a second `<h1>`
|
|
38
63
|
// from the body would be a duplicate in the document outline.
|
|
39
|
-
return (page) => render(page.content);
|
|
64
|
+
return (page) => render(page.content, page);
|
|
40
65
|
}
|
|
41
66
|
//# sourceMappingURL=render.js.map
|
package/dist/render.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EAIvB,gBAAgB,GAEjB,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docspack/sheaf-astro",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.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,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@astrojs/markdown-remark": "7.2.0",
|
|
31
|
-
"@docspack/sheaf": "^0.
|
|
31
|
+
"@docspack/sheaf": "^0.3.0"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@docspack/config": "0.1.0",
|
package/src/render.ts
CHANGED
|
@@ -6,7 +6,13 @@ import {
|
|
|
6
6
|
rehypeHeadingIds,
|
|
7
7
|
type ShikiConfig,
|
|
8
8
|
} from "@astrojs/markdown-remark";
|
|
9
|
-
import
|
|
9
|
+
import {
|
|
10
|
+
type ContentGraph,
|
|
11
|
+
type Heading,
|
|
12
|
+
type LinkOptions,
|
|
13
|
+
linkHref,
|
|
14
|
+
type Page,
|
|
15
|
+
} from "@docspack/sheaf";
|
|
10
16
|
|
|
11
17
|
/**
|
|
12
18
|
* Rendering a graph's pages with Astro's own Markdown pipeline.
|
|
@@ -67,17 +73,60 @@ export interface RendererOptions {
|
|
|
67
73
|
*
|
|
68
74
|
* This is the seam a `:::` block needs, and the reason it is here rather than left to the
|
|
69
75
|
* host's `astro.config`: the graph's pages are rendered by *this* processor, so a plugin
|
|
70
|
-
* configured for `src/pages` never sees them. `
|
|
71
|
-
* `@docspack/sheaf-emit`
|
|
72
|
-
*
|
|
76
|
+
* configured for `src/pages` never sees them. Pass `sheafDirectives` from
|
|
77
|
+
* `@docspack/sheaf-emit` here rather than `remark-directive`: it is the vocabulary that side
|
|
78
|
+
* parses with, so a block that one half understands and the other reads as literal text — or
|
|
79
|
+
* a `15:16:24` one half keeps and the other drops — cannot happen.
|
|
73
80
|
*/
|
|
74
81
|
readonly remarkPlugins?: RemarkPlugins;
|
|
82
|
+
/**
|
|
83
|
+
* Rewrites relative links between pages — `./TOKENS.md#colors` — to `target(page, "site")`,
|
|
84
|
+
* fragment kept and checked against the target's headings, and any other relative path through
|
|
85
|
+
* `resolve`. A broken fragment or an unresolvable path fails the render, naming the page.
|
|
86
|
+
*
|
|
87
|
+
* Applies to pages only: Markdown rendered without a page has nothing to be relative to.
|
|
88
|
+
*/
|
|
89
|
+
readonly links?: LinkOptions & { readonly graph: ContentGraph };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
interface MdastNode {
|
|
93
|
+
type: string;
|
|
94
|
+
url?: string;
|
|
95
|
+
children?: MdastNode[];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
interface AstroFile {
|
|
99
|
+
readonly data: { readonly astro?: { readonly frontmatter?: Record<string, unknown> } };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* The remark side of `links`. The processor is shared by every page, so the page being rendered
|
|
104
|
+
* arrives the way Astro hands any document's metadata to a plugin: in its frontmatter, where
|
|
105
|
+
* `render` puts the page's slug.
|
|
106
|
+
*/
|
|
107
|
+
function rewritePageLinks(links: NonNullable<RendererOptions["links"]>) {
|
|
108
|
+
return () => (tree: MdastNode, file: AstroFile) => {
|
|
109
|
+
const slug = file.data.astro?.frontmatter?.slug;
|
|
110
|
+
const from = links.graph.pages.find((page) => page.slug === slug);
|
|
111
|
+
if (from === undefined) return;
|
|
112
|
+
|
|
113
|
+
const visit = (node: MdastNode) => {
|
|
114
|
+
if ((node.type === "link" || node.type === "definition") && node.url) {
|
|
115
|
+
node.url = linkHref(links.graph, from, node.url, links, "site");
|
|
116
|
+
}
|
|
117
|
+
for (const child of node.children ?? []) visit(child);
|
|
118
|
+
};
|
|
119
|
+
visit(tree);
|
|
120
|
+
};
|
|
75
121
|
}
|
|
76
122
|
|
|
77
123
|
export type PageRenderer = (page: Page) => Promise<RenderedPage>;
|
|
78
124
|
|
|
79
|
-
/**
|
|
80
|
-
|
|
125
|
+
/**
|
|
126
|
+
* Renders Markdown. Given the page it belongs to — a page's `content`, or a part of it — its
|
|
127
|
+
* relative links resolve from that page and its frontmatter reaches the plugins.
|
|
128
|
+
*/
|
|
129
|
+
export type SourceRenderer = (markdown: string, page?: Page) => Promise<RenderedPage>;
|
|
81
130
|
|
|
82
131
|
/**
|
|
83
132
|
* Builds one processor and returns a function that renders Markdown with it.
|
|
@@ -99,7 +148,10 @@ export function createMarkdownRenderer(options: RendererOptions = {}): SourceRen
|
|
|
99
148
|
...(options.syntaxHighlight === undefined
|
|
100
149
|
? {}
|
|
101
150
|
: { syntaxHighlight: options.syntaxHighlight }),
|
|
102
|
-
|
|
151
|
+
remarkPlugins: [
|
|
152
|
+
...(options.remarkPlugins ?? []),
|
|
153
|
+
...(options.links === undefined ? [] : [rewritePageLinks(options.links)]),
|
|
154
|
+
],
|
|
103
155
|
...(options.rehypePlugins === undefined
|
|
104
156
|
? {}
|
|
105
157
|
: { rehypePlugins: [rehypeHeadingIds, ...options.rehypePlugins] }),
|
|
@@ -108,8 +160,11 @@ export function createMarkdownRenderer(options: RendererOptions = {}): SourceRen
|
|
|
108
160
|
return pending;
|
|
109
161
|
};
|
|
110
162
|
|
|
111
|
-
return async (markdown) => {
|
|
112
|
-
const { code, metadata } = await (await renderer()).render(
|
|
163
|
+
return async (markdown, page) => {
|
|
164
|
+
const { code, metadata } = await (await renderer()).render(
|
|
165
|
+
markdown,
|
|
166
|
+
page === undefined ? undefined : { frontmatter: { ...page.frontmatter, slug: page.slug } },
|
|
167
|
+
);
|
|
113
168
|
return { html: code, headings: metadata.headings };
|
|
114
169
|
};
|
|
115
170
|
}
|
|
@@ -119,5 +174,5 @@ export function createPageRenderer(options: RendererOptions = {}): PageRenderer
|
|
|
119
174
|
const render = createMarkdownRenderer(options);
|
|
120
175
|
// `page.content` rather than `page.body`: the shell renders the title itself, and a second `<h1>`
|
|
121
176
|
// from the body would be a duplicate in the document outline.
|
|
122
|
-
return (page) => render(page.content);
|
|
177
|
+
return (page) => render(page.content, page);
|
|
123
178
|
}
|