@iyulab/canopy-page 0.5.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.
- package/CHANGELOG.md +39 -0
- package/README.md +35 -2
- package/dist/build.js +2 -0
- package/dist/check.js +36 -5
- package/dist/settings.d.ts +29 -0
- package/dist/settings.js +51 -6
- package/package.json +6 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,45 @@ Notable changes to canopy-page. The format follows
|
|
|
7
7
|
The `settings.json` contract is what consuming projects plan their upgrades around, so changes
|
|
8
8
|
to it — its fields, its validation, and what the checks reject — are what this file is about.
|
|
9
9
|
|
|
10
|
+
## [0.7.0] — 2026-08-09
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **A `strings` field** overriding the reader chrome's own built-in text — search, the theme
|
|
15
|
+
toggle, and the navigation landmarks. `lang` only ever changed what `<html lang>` declares;
|
|
16
|
+
this text is canopy's own UI, not vault content, so it stayed English regardless. No built-in
|
|
17
|
+
translation table, the same reasoning `home.label` already follows. Threaded to canopy as a
|
|
18
|
+
JSON `--strings` flag.
|
|
19
|
+
- **`check` warns about a root-absolute reference that resolves today but would only be correct
|
|
20
|
+
if the site is served from the domain root**, whenever `siteUrl` already declares a non-root
|
|
21
|
+
mount path — reusing the one signal a settings file already carries about where the site is
|
|
22
|
+
served from, rather than requiring a new field.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- **`home.url` no longer requires an absolute http(s) URL.** A relative one (a sibling of the
|
|
27
|
+
published site, at the same origin) is now resolved against each page's depth, the same as
|
|
28
|
+
every other internal link canopy writes — useful when the site is mounted at a sub-path whose
|
|
29
|
+
absolute origin differs between environments.
|
|
30
|
+
- **Upgraded to canopy 0.8.0**, the release the relative `home.url` and `strings` support build on.
|
|
31
|
+
|
|
32
|
+
## [0.6.0] — 2026-08-09
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- **A `rehypePlugins` field**, naming installed rehype plugin packages to run on every page —
|
|
37
|
+
canopy's fixed extension point (after sanitize, before syntax highlighting) for markdown that
|
|
38
|
+
needs more than CommonMark and GFM, a diagram fence rendered to SVG being the case this exists
|
|
39
|
+
for. This site's own [guide to diagrams](https://iyulab.github.io/canopy-page/guide/writing/diagrams.html)
|
|
40
|
+
demonstrates it end to end, with [declart](https://github.com/iyulab/declart) rendered at build
|
|
41
|
+
time through `rehype-declart`.
|
|
42
|
+
|
|
43
|
+
### Changed
|
|
44
|
+
|
|
45
|
+
- **Upgraded to canopy 0.7.0**, the release `rehypePlugins` builds on. See
|
|
46
|
+
[canopy's changelog](https://github.com/iyulab/canopy/blob/main/CHANGELOG.md#070--2026-08-09)
|
|
47
|
+
for the plugin's fixed position and how it interacts with sanitizing and highlighting.
|
|
48
|
+
|
|
10
49
|
## [0.5.0] — 2026-08-09
|
|
11
50
|
|
|
12
51
|
### Added
|
package/README.md
CHANGED
|
@@ -7,6 +7,9 @@ authoring pipeline around the rendering: the settings a site is configured by, t
|
|
|
7
7
|
keep broken references from shipping, and the build that ties them together. The rendering
|
|
8
8
|
itself is [canopy](https://github.com/iyulab/canopy)'s job, and canopy-page drives it.
|
|
9
9
|
|
|
10
|
+
**Live docs**: <https://iyulab.github.io/canopy-page> — built with canopy-page itself, from the
|
|
11
|
+
[`examples/site`](examples/site) in this repository, republished on every push to `main`.
|
|
12
|
+
|
|
10
13
|
---
|
|
11
14
|
|
|
12
15
|
## Why
|
|
@@ -23,6 +26,26 @@ the ones this package holds.
|
|
|
23
26
|
- **One site, built in one pass.** Links and backlinks resolve across the whole of it, so a guide
|
|
24
27
|
and the release notes it refers to stay connected.
|
|
25
28
|
|
|
29
|
+
## What ships in every site
|
|
30
|
+
|
|
31
|
+
No `settings.json` field turns these on — they are just there, on the next build. Reading a
|
|
32
|
+
page never depends on any of the scripted ones: block scripts, or print the page, and only they
|
|
33
|
+
go away.
|
|
34
|
+
|
|
35
|
+
- **Search**, matching a query against every page's title, headings, and body — `Ctrl+K` /
|
|
36
|
+
`Cmd+K` jumps to it from anywhere
|
|
37
|
+
- **The current page and section, marked** in the sidebar and the on-page outline, updating as
|
|
38
|
+
you scroll
|
|
39
|
+
- **A dark/light toggle** that remembers a reader's choice; without one, pages follow the
|
|
40
|
+
system setting
|
|
41
|
+
- **Prev/next cards** linking to a page's neighbors in the sidebar's own order, and **backlinks**
|
|
42
|
+
listing every page that points to it
|
|
43
|
+
- **A full-screen menu on narrow screens**, rather than one that pushes the page's content down
|
|
44
|
+
- **Sitemap and `robots.txt`**, once `siteUrl` is set
|
|
45
|
+
|
|
46
|
+
See it live at <https://iyulab.github.io/canopy-page>, or read
|
|
47
|
+
[What a reader gets](examples/site/guide/reading.md) for how each one behaves.
|
|
48
|
+
|
|
26
49
|
## Install
|
|
27
50
|
|
|
28
51
|
```sh
|
|
@@ -69,6 +92,7 @@ unpublished.
|
|
|
69
92
|
"lang": "en-GB",
|
|
70
93
|
"icon": "assets/favicon.png",
|
|
71
94
|
"exclude": ["_drafts", "*.tmp"],
|
|
95
|
+
"rehypePlugins": ["rehype-declart"],
|
|
72
96
|
"sections": [
|
|
73
97
|
{ "path": "guide", "label": "Guide", "items": [
|
|
74
98
|
{ "label": "Orders", "items": ["guide/orders/list", "guide/orders/detail"] },
|
|
@@ -84,12 +108,14 @@ unpublished.
|
|
|
84
108
|
| `title` | Site name. Defaults to the folder's name |
|
|
85
109
|
| `description` | Fills `<meta name="description">`, which is what link previews show |
|
|
86
110
|
| `lang` | BCP 47 tag for `<html lang>`. Worth setting for any non-English site: assistive technology reads pronunciation from it |
|
|
111
|
+
| `strings` | Overrides for the reader chrome's own text — `search`, `toggleTheme`, `siteNav`, `pageNav`, `onThisPage`. `lang` only changes what `<html lang>` declares; this text is canopy's own UI, not vault content, so it stays English otherwise. No built-in translation table — the same reasoning `home.label` already follows: link text has to be written in the site's own language. Keys left out keep their English default |
|
|
87
112
|
| `icon` | Favicon, relative to the settings file. Must be a published file |
|
|
88
113
|
| `tokens` | CSS of design-token overrides, relative to the settings file. Appended *after* canopy's own tokens, so a file naming one value keeps the rest. It is configuration rather than content, so — unlike `icon` and `logo` — it is excluded from the published site automatically. Absent: canopy's default palette |
|
|
89
114
|
| `logo` | Image shown beside the site title, relative to the settings file. Must be a published file — the opposite direction from `tokens`, because this one is content. Rendered with an empty `alt`, deliberately: the site title beside it already names the site, so there is no separate text to give it. Absent: the sidebar header shows the title text alone |
|
|
90
|
-
| `home` | A link back to the site this documentation sits beside: `{ url, label }`. Both are required together — naming half of it is not a valid setting. `url`
|
|
115
|
+
| `home` | A link back to the site this documentation sits beside: `{ url, label }`. Both are required together — naming half of it is not a valid setting. `url` is absolute when the target is a different origin, relative when it is a sibling of the published site (each page resolves it against its own depth, the same as every other internal link); there is no default `label`, because link text has to be written in the site's own language. Absent: no link back to a surrounding site is rendered |
|
|
91
116
|
| `siteUrl` | Absolute URL naming where the built site will stand. Every link canopy writes is relative, which is what lets a site be served from any sub-path — and exactly why a sitemap, whose entries must be absolute, needs this separately. **Only** when it is set does `build` write `sitemap.xml` and a `robots.txt` pointing at it. Absent: neither file is written |
|
|
92
117
|
| `exclude` | Paths to leave unpublished: a directory (`_drafts` or `_drafts/**`), an extension at any depth (`*.tmp`), or one exact path. Patterns are relative to the settings file, and a shape outside that list — `images/*.md` — is refused rather than left to match nothing |
|
|
118
|
+
| `rehypePlugins` | Package names of rehype plugins to run on every page, after canopy's own sanitize step and before syntax highlighting — canopy's fixed extension point for markdown that needs more than CommonMark and GFM, a diagram fence rendered to SVG being the case this exists for. Each entry is an installed package name (`"rehype-declart"`), never a filesystem path — a relative-looking entry is refused, since the directory it would resolve against is wherever the build happens to run from, not this file |
|
|
93
119
|
| `sections` | Ordered regions of the site — see below |
|
|
94
120
|
|
|
95
121
|
`tokens` is two blocks in practice, not one — a bare `:root` and a `prefers-color-scheme: dark`
|
|
@@ -183,7 +209,9 @@ Warnings — reported, and the build continues:
|
|
|
183
209
|
path resolves depends on what the site is served from, which is not a checker's to know — but a
|
|
184
210
|
site served from its own root is the ordinary case, and a `public/`-style folder that other
|
|
185
211
|
generators map onto the root does not exist here, so these silently 404. A warning rather than
|
|
186
|
-
an error, because mounting the site elsewhere would make it right
|
|
212
|
+
an error, because mounting the site elsewhere would make it right. When `siteUrl` already
|
|
213
|
+
declares a sub-path mount, a root-absolute reference warns even if it resolves today, since that
|
|
214
|
+
is the one case the checker can actually judge
|
|
187
215
|
- An `exclude` pattern that matched nothing, which usually means a path written from the wrong
|
|
188
216
|
place. Extension patterns are left alone: `*.tmp` in a site with no scratch files is a rule
|
|
189
217
|
about what may never ship, not a claim that something is there
|
|
@@ -215,6 +243,11 @@ door every other consumer uses, and a door only stays wide enough if the people
|
|
|
215
243
|
gone around it do not. Where the command line cannot express something, that is worth raising
|
|
216
244
|
with canopy rather than working around here.
|
|
217
245
|
|
|
246
|
+
## Contributing
|
|
247
|
+
|
|
248
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md), including what a release has to update besides the
|
|
249
|
+
version number.
|
|
250
|
+
|
|
218
251
|
## License
|
|
219
252
|
|
|
220
253
|
MIT
|
package/dist/build.js
CHANGED
|
@@ -34,6 +34,7 @@ export function canopyArgs(site, out, navPath, searchAssets) {
|
|
|
34
34
|
...(settings.home === undefined
|
|
35
35
|
? []
|
|
36
36
|
: ["--home-url", settings.home.url, "--home-label", settings.home.label]),
|
|
37
|
+
...(settings.strings === undefined ? [] : ["--strings", JSON.stringify(settings.strings)]),
|
|
37
38
|
...(navPath === undefined ? [] : ["--nav", navPath]),
|
|
38
39
|
// Always on, same reasoning as --tokens-css above: a search index and the
|
|
39
40
|
// script that searches it are canopy-page's own contribution, not a site
|
|
@@ -50,6 +51,7 @@ export function canopyArgs(site, out, navPath, searchAssets) {
|
|
|
50
51
|
// assembleTokensCss, once copied as a plain asset.
|
|
51
52
|
...(settings.tokens === undefined ? [] : ["--exclude", settings.tokens]),
|
|
52
53
|
...(settings.exclude ?? []).flatMap((pattern) => ["--exclude", pattern]),
|
|
54
|
+
...(settings.rehypePlugins ?? []).flatMap((specifier) => ["--rehype-plugin", specifier]),
|
|
53
55
|
];
|
|
54
56
|
}
|
|
55
57
|
/** Build the site in `dir` into `out`, returning the exit code to leave with. */
|
package/dist/check.js
CHANGED
|
@@ -28,6 +28,31 @@ import { toPageKey } from "./vault.js";
|
|
|
28
28
|
function isRootAbsolute(url) {
|
|
29
29
|
return url.startsWith("/") && !url.startsWith("//");
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* The mount path `siteUrl` declares, when it says the site stands under a
|
|
33
|
+
* sub-path rather than a domain root.
|
|
34
|
+
*
|
|
35
|
+
* `siteUrl` is the one place settings already say where the site is served
|
|
36
|
+
* from — set today only to address a sitemap, but its path component answers
|
|
37
|
+
* exactly the question a root-absolute reference otherwise leaves open.
|
|
38
|
+
*/
|
|
39
|
+
function siteBasePath(site) {
|
|
40
|
+
const { siteUrl } = site.settings;
|
|
41
|
+
if (siteUrl === undefined)
|
|
42
|
+
return undefined;
|
|
43
|
+
// settings.ts only checks the "http(s)://" prefix, which "http://" itself
|
|
44
|
+
// satisfies without naming a host — malformed enough that `new URL` throws.
|
|
45
|
+
// That is a settings mistake for `sitemapXml` to report, not a reason for
|
|
46
|
+
// this unrelated check to crash the whole run.
|
|
47
|
+
let pathname;
|
|
48
|
+
try {
|
|
49
|
+
({ pathname } = new URL(siteUrl));
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
return pathname === "/" ? undefined : pathname;
|
|
55
|
+
}
|
|
31
56
|
/**
|
|
32
57
|
* Does anything published sit at this path — a page, a copied file, or the index
|
|
33
58
|
* page a directory is entered by?
|
|
@@ -81,14 +106,20 @@ export async function referenceFindings(site) {
|
|
|
81
106
|
const url = targetPath(reference.target);
|
|
82
107
|
if (isRootAbsolute(url)) {
|
|
83
108
|
const atRoot = decodeTarget(url.replace(/^\/+/, "")) ?? url.replace(/^\/+/, "");
|
|
84
|
-
|
|
109
|
+
const resolves = atRoot === "" || existsInSite(site, atRoot);
|
|
110
|
+
const basePath = siteBasePath(site);
|
|
111
|
+
if (resolves && basePath === undefined)
|
|
85
112
|
continue;
|
|
86
113
|
findings.push({
|
|
87
114
|
level: "warning",
|
|
88
|
-
message:
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
115
|
+
message: resolves
|
|
116
|
+
? `${where}: ${reference.kind} "${reference.target}" resolves only when the ` +
|
|
117
|
+
`site is served from the domain root, but settings.siteUrl declares it is ` +
|
|
118
|
+
`mounted under "${basePath}"`
|
|
119
|
+
: `${where}: ${reference.kind} "${reference.target}" — ` +
|
|
120
|
+
`nothing is published at "${atRoot}". A root-absolute path resolves ` +
|
|
121
|
+
"against wherever the site is served from, so this is right only if " +
|
|
122
|
+
"something else answers it there",
|
|
92
123
|
});
|
|
93
124
|
continue;
|
|
94
125
|
}
|
package/dist/settings.d.ts
CHANGED
|
@@ -99,6 +99,35 @@ export interface Settings {
|
|
|
99
99
|
* points at it. Absent, neither is written.
|
|
100
100
|
*/
|
|
101
101
|
siteUrl?: string;
|
|
102
|
+
/**
|
|
103
|
+
* Rehype plugins to run on every page, after canopy's own sanitize step and
|
|
104
|
+
* before syntax highlighting — canopy's fixed extension point for markdown
|
|
105
|
+
* that needs more than CommonMark and GFM, a diagram fence rendered to SVG
|
|
106
|
+
* being the case this exists for.
|
|
107
|
+
*
|
|
108
|
+
* Each entry is an installed package name (`"rehype-declart"`), never a
|
|
109
|
+
* filesystem path: this is a JSON settings file naming a dependency the site
|
|
110
|
+
* author already declared, not a script with a place of its own to resolve a
|
|
111
|
+
* relative path against.
|
|
112
|
+
*/
|
|
113
|
+
rehypePlugins?: string[];
|
|
114
|
+
/**
|
|
115
|
+
* Overrides for the reader chrome's own text — search, the theme toggle,
|
|
116
|
+
* and the navigation landmarks.
|
|
117
|
+
*
|
|
118
|
+
* `lang` changes what `<html lang>` declares, but that text is canopy's own
|
|
119
|
+
* UI, not vault content, so `lang` alone leaves it English. There is no
|
|
120
|
+
* built-in translation table: like `home.label`, link text has to be
|
|
121
|
+
* written in the site's own language, and canopy cannot know what that
|
|
122
|
+
* language calls "Search". Keys left out keep their English default.
|
|
123
|
+
*/
|
|
124
|
+
strings?: {
|
|
125
|
+
search?: string;
|
|
126
|
+
toggleTheme?: string;
|
|
127
|
+
siteNav?: string;
|
|
128
|
+
pageNav?: string;
|
|
129
|
+
onThisPage?: string;
|
|
130
|
+
};
|
|
102
131
|
}
|
|
103
132
|
/**
|
|
104
133
|
* Parse and validate a settings file from JSON text.
|
package/dist/settings.js
CHANGED
|
@@ -49,9 +49,12 @@ const SETTINGS_KEYS = new Set([
|
|
|
49
49
|
"logo",
|
|
50
50
|
"home",
|
|
51
51
|
"siteUrl",
|
|
52
|
+
"rehypePlugins",
|
|
53
|
+
"strings",
|
|
52
54
|
]);
|
|
53
55
|
const SECTION_KEYS = new Set(["path", "label", "order", "items"]);
|
|
54
56
|
const HOME_KEYS = new Set(["url", "label"]);
|
|
57
|
+
const STRINGS_KEYS = new Set(["search", "toggleTheme", "siteNav", "pageNav", "onThisPage"]);
|
|
55
58
|
const NAV_ITEM_KEYS = new Set(["label", "path", "items"]);
|
|
56
59
|
/**
|
|
57
60
|
* Unknown keys are rejected rather than ignored.
|
|
@@ -130,6 +133,31 @@ function asExclusionPattern(value, where) {
|
|
|
130
133
|
}
|
|
131
134
|
return pattern;
|
|
132
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Check a `rehypePlugins` entry names a package rather than a file.
|
|
138
|
+
*
|
|
139
|
+
* canopy itself accepts either shape on `--rehype-plugin`, resolving a
|
|
140
|
+
* filesystem-looking specifier against its own process's working directory.
|
|
141
|
+
* That directory is canopy-page's spawning process, not the settings file —
|
|
142
|
+
* the same ambiguity every other path setting here avoids by resolving
|
|
143
|
+
* relative to the settings file instead. Rather than resolve it a second way
|
|
144
|
+
* here, a path-looking entry is refused with a message saying why, matching
|
|
145
|
+
* every other setting whose value must stay inside a stated shape.
|
|
146
|
+
*/
|
|
147
|
+
function asModuleSpecifier(value, where) {
|
|
148
|
+
const specifier = asString(value, where);
|
|
149
|
+
const looksLikeAPath = specifier.startsWith("./") ||
|
|
150
|
+
specifier.startsWith("../") ||
|
|
151
|
+
specifier.startsWith("/") ||
|
|
152
|
+
specifier.includes("\\") ||
|
|
153
|
+
/^[A-Za-z]:/.test(specifier);
|
|
154
|
+
if (looksLikeAPath) {
|
|
155
|
+
fail(`${where}: "${specifier}" looks like a file path, not a package name. ` +
|
|
156
|
+
"Install the plugin as a dependency and name it here the way its package.json does " +
|
|
157
|
+
'(e.g. "rehype-declart"), so resolution does not depend on the directory the build runs from.');
|
|
158
|
+
}
|
|
159
|
+
return specifier;
|
|
160
|
+
}
|
|
133
161
|
function parseNavItem(value, where) {
|
|
134
162
|
// A bare string is the common case — a page in the order it should appear.
|
|
135
163
|
if (typeof value === "string") {
|
|
@@ -201,7 +229,7 @@ export function parseSettings(json) {
|
|
|
201
229
|
}
|
|
202
230
|
const value = asObject(raw, "settings", "expected a JSON object");
|
|
203
231
|
rejectUnknownKeys(value, SETTINGS_KEYS, "settings");
|
|
204
|
-
const { title, description, lang, icon, tokens, exclude, sections, logo, home, siteUrl } = value;
|
|
232
|
+
const { title, description, lang, icon, tokens, exclude, sections, logo, home, siteUrl, rehypePlugins, strings, } = value;
|
|
205
233
|
if (title !== undefined)
|
|
206
234
|
asString(title, "settings.title");
|
|
207
235
|
if (description !== undefined)
|
|
@@ -219,6 +247,9 @@ export function parseSettings(json) {
|
|
|
219
247
|
fail("settings.exclude: must be an array");
|
|
220
248
|
if (sections !== undefined && !Array.isArray(sections))
|
|
221
249
|
fail("settings.sections: must be an array");
|
|
250
|
+
if (rehypePlugins !== undefined && !Array.isArray(rehypePlugins)) {
|
|
251
|
+
fail("settings.rehypePlugins: must be an array");
|
|
252
|
+
}
|
|
222
253
|
let parsedHome;
|
|
223
254
|
if (home !== undefined) {
|
|
224
255
|
const object = asObject(home, "settings.home", 'expected an object with "url" and "label"');
|
|
@@ -227,12 +258,11 @@ export function parseSettings(json) {
|
|
|
227
258
|
fail('settings.home.url: needed alongside "label"');
|
|
228
259
|
if (object.label === undefined)
|
|
229
260
|
fail('settings.home.label: needed alongside "url"');
|
|
261
|
+
// Absolute when the target is a different origin, relative when it is a
|
|
262
|
+
// sibling of the published site (a product this documentation is mounted
|
|
263
|
+
// beside) — canopy resolves a relative one against each page's depth, the
|
|
264
|
+
// same way it resolves every other internal link.
|
|
230
265
|
const url = asString(object.url, "settings.home.url");
|
|
231
|
-
// The target is normally outside the published site, so a relative path has
|
|
232
|
-
// nothing here to resolve against.
|
|
233
|
-
if (!/^https?:\/\//i.test(url)) {
|
|
234
|
-
fail(`settings.home.url: "${url}" must be an absolute http(s) URL`);
|
|
235
|
-
}
|
|
236
266
|
parsedHome = { url, label: asString(object.label, "settings.home.label") };
|
|
237
267
|
}
|
|
238
268
|
if (siteUrl !== undefined) {
|
|
@@ -241,6 +271,15 @@ export function parseSettings(json) {
|
|
|
241
271
|
fail(`settings.siteUrl: "${url}" must be an absolute http(s) URL`);
|
|
242
272
|
}
|
|
243
273
|
}
|
|
274
|
+
let parsedStrings;
|
|
275
|
+
if (strings !== undefined) {
|
|
276
|
+
const object = asObject(strings, "settings.strings", "expected an object");
|
|
277
|
+
rejectUnknownKeys(object, STRINGS_KEYS, "settings.strings");
|
|
278
|
+
parsedStrings = {};
|
|
279
|
+
for (const key of Object.keys(object)) {
|
|
280
|
+
parsedStrings[key] = asString(object[key], `settings.strings.${key}`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
244
283
|
return {
|
|
245
284
|
...(title === undefined ? {} : { title: title }),
|
|
246
285
|
...(description === undefined ? {} : { description: description }),
|
|
@@ -263,5 +302,11 @@ export function parseSettings(json) {
|
|
|
263
302
|
...(logo === undefined ? {} : { logo: asRelativePath(logo, "settings.logo") }),
|
|
264
303
|
...(parsedHome === undefined ? {} : { home: parsedHome }),
|
|
265
304
|
...(siteUrl === undefined ? {} : { siteUrl: siteUrl }),
|
|
305
|
+
...(parsedStrings === undefined ? {} : { strings: parsedStrings }),
|
|
306
|
+
...(rehypePlugins === undefined
|
|
307
|
+
? {}
|
|
308
|
+
: {
|
|
309
|
+
rehypePlugins: rehypePlugins.map((specifier, i) => asModuleSpecifier(specifier, `settings.rehypePlugins[${i}]`)),
|
|
310
|
+
}),
|
|
266
311
|
};
|
|
267
312
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iyulab/canopy-page",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Authoring pipeline for documentation sites: one settings file, integrity checks, and a build.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -44,11 +44,15 @@
|
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@biomejs/biome": "^2.5.0",
|
|
47
|
+
"@iyulab/declart": "^0.20.0",
|
|
47
48
|
"@types/node": "^25.9.3",
|
|
49
|
+
"playwright": "^1.62.1",
|
|
50
|
+
"rehype-declart": "^0.20.0",
|
|
51
|
+
"rehype-mermaid": "^3.0.0",
|
|
48
52
|
"typescript": "^6.0.3",
|
|
49
53
|
"vitest": "^4.1.9"
|
|
50
54
|
},
|
|
51
55
|
"dependencies": {
|
|
52
|
-
"@iyulab/canopy": "^0.
|
|
56
|
+
"@iyulab/canopy": "^0.8.0"
|
|
53
57
|
}
|
|
54
58
|
}
|