@waveso/docs 0.4.0 → 0.6.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 +270 -0
- package/README.md +221 -79
- package/dist/anchors.d.ts +44 -0
- package/dist/anchors.js +76 -0
- package/dist/errors.d.ts +4 -0
- package/dist/highlighter.js +2 -1
- package/dist/link-suggestion.d.ts +31 -0
- package/dist/link-suggestion.js +94 -0
- package/dist/meta.js +6 -9
- package/dist/next.d.ts +54 -14
- package/dist/next.js +135 -20
- package/dist/plugins/rehype-code-frame.d.ts +13 -1
- package/dist/plugins/rehype-code-frame.js +2 -1
- package/dist/plugins/rehype-fallback-heading-ids.js +1 -1
- package/dist/plugins/remark-doc-links.d.ts +83 -1
- package/dist/plugins/remark-doc-links.js +50 -23
- package/dist/plugins/remark-youtube.d.ts +18 -3
- package/dist/plugins/remark-youtube.js +57 -9
- package/dist/react/callout.d.ts +13 -1
- package/dist/react/callout.js +2 -2
- package/dist/react/code-runtime.d.ts +12 -2
- package/dist/react/code-runtime.js +28 -4
- package/dist/react/doc-content.d.ts +12 -1
- package/dist/react/doc-content.js +2 -2
- package/dist/react/layout.d.ts +27 -10
- package/dist/react/layout.js +6 -3
- package/dist/react/link-adapter.d.ts +34 -0
- package/dist/react/link-adapter.js +30 -0
- package/dist/react/markdown-components.d.ts +29 -1
- package/dist/react/markdown-components.js +69 -67
- package/dist/react/nav.d.ts +5 -1
- package/dist/react/nav.js +5 -2
- package/dist/react/next-link.d.ts +6 -28
- package/dist/react/next-link.js +45 -24
- package/dist/react/next-nav.d.ts +5 -1
- package/dist/react/next-nav.js +6 -3
- package/dist/react/next-search.js +1 -1
- package/dist/react/search-dialog.d.ts +59 -3
- package/dist/react/search-dialog.js +53 -9
- package/dist/react/shell-labels.d.ts +135 -21
- package/dist/react/shell-labels.js +47 -6
- package/dist/react/sidebar.d.ts +18 -1
- package/dist/react/sidebar.js +59 -23
- package/dist/react/youtube.d.ts +22 -1
- package/dist/react/youtube.js +22 -4
- package/dist/render.d.ts +12 -1
- package/dist/render.js +107 -21
- package/dist/route-path.js +7 -2
- package/dist/safe-href.d.ts +47 -0
- package/dist/safe-href.js +73 -0
- package/dist/search-index.js +1 -1
- package/dist/search-options.d.ts +64 -2
- package/dist/search-options.js +25 -1
- package/dist/semaphore.d.ts +46 -0
- package/dist/semaphore.js +60 -0
- package/dist/source.js +86 -12
- package/dist/types.d.ts +102 -6
- package/package.json +6 -3
package/dist/source.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { docsError } from "./docs-error.js";
|
|
2
2
|
import { parseFrontmatter } from "./frontmatter.js";
|
|
3
3
|
import { orderNavEntries, readDocsMeta } from "./meta.js";
|
|
4
|
+
import { createSemaphore } from "./semaphore.js";
|
|
4
5
|
import { encodeSegments, toAliasRoute } from "./route-path.js";
|
|
5
6
|
import { readFile, readdir, realpath, stat } from "node:fs/promises";
|
|
6
7
|
import path from "node:path";
|
|
@@ -12,6 +13,48 @@ const PAGE_EXTENSION = ".md";
|
|
|
12
13
|
/** A directory whose `index.md` is the directory's own route. */
|
|
13
14
|
const INDEX_NAME = "index";
|
|
14
15
|
/**
|
|
16
|
+
* Filesystem calls in flight across the whole process, at most.
|
|
17
|
+
*
|
|
18
|
+
* ⚠️ THE SCAN USED TO OPEN EVERY MARKDOWN FILE AT ONCE. `scanDir` recursed into
|
|
19
|
+
* its subdirectories in parallel and read that directory's pages with a bare
|
|
20
|
+
* `Promise.all`, so the number of `readFile` calls in flight equalled the number
|
|
21
|
+
* of markdown files in the entire tree. On a 1,200-page corpus and the common
|
|
22
|
+
* 1,024-descriptor soft limit, `next build` died with a bare `EMFILE: too many
|
|
23
|
+
* open files` — no error code, no mention that this was the docs scan, and
|
|
24
|
+
* nothing pointing at the fix. Exactly the large content set this package is
|
|
25
|
+
* for, and the failure got worse as the site grew.
|
|
26
|
+
*
|
|
27
|
+
* 64 is far under every default soft limit — 1,024 on Linux and CI images, 256
|
|
28
|
+
* on an unconfigured macOS shell — so the descriptors stop scaling with the
|
|
29
|
+
* corpus while staying safe on the tightest of those.
|
|
30
|
+
*
|
|
31
|
+
* ⚠️ IT IS NOT FREE, AND THE COST IS NOT THE CONCURRENCY. Measured over 1,201
|
|
32
|
+
* pages in 49 directories, nine runs, medians: **88 ms ungated, 106 ms at a
|
|
33
|
+
* bound of 16, 102 ms at 64, 101 ms at 128 and at 256.** Flat from 64 upwards,
|
|
34
|
+
* which says the ~14 ms is the gate's own per-call overhead — about 3,600
|
|
35
|
+
* `run` closures — and not the reduced parallelism. libuv's filesystem pool is
|
|
36
|
+
* four threads by default, so there was never 1,201-way parallelism to lose.
|
|
37
|
+
*
|
|
38
|
+
* So the number is chosen for the tightest descriptor limit rather than for
|
|
39
|
+
* speed, because above 64 there is no speed left to buy. 14 ms sits against a
|
|
40
|
+
* build that highlights those same 1,201 pages with Shiki, which is three
|
|
41
|
+
* orders of magnitude more.
|
|
42
|
+
*/
|
|
43
|
+
const SCAN_CONCURRENCY = 64;
|
|
44
|
+
/**
|
|
45
|
+
* The one gate, for the whole process.
|
|
46
|
+
*
|
|
47
|
+
* Module scope rather than per-scan, because descriptors are a process resource:
|
|
48
|
+
* two routes scanning two content directories at once would each stay under a
|
|
49
|
+
* per-scan bound and together exceed the only one that matters.
|
|
50
|
+
*
|
|
51
|
+
* ⚠️ LEAF CALLS ONLY — NEVER THE RECURSION. Gating `scanDir` itself deadlocks as
|
|
52
|
+
* soon as every slot is held by a directory waiting for a slot to read its
|
|
53
|
+
* children. Bounding the `readFile`/`readdir`/`stat`/`realpath` calls bounds the
|
|
54
|
+
* descriptors exactly, and leaves the walk as parallel as it was.
|
|
55
|
+
*/
|
|
56
|
+
const fsGate = createSemaphore(SCAN_CONCURRENCY);
|
|
57
|
+
/**
|
|
15
58
|
* Apply {@link DocsConfig} defaults and resolve `contentDir` against
|
|
16
59
|
* `process.cwd()` — the project root under both `next build` and `vite build`.
|
|
17
60
|
*
|
|
@@ -28,7 +71,9 @@ function resolveDocsConfig(config) {
|
|
|
28
71
|
),
|
|
29
72
|
basePath: normalizeBasePath(config.basePath ?? "/docs"),
|
|
30
73
|
includeDrafts: config.includeDrafts ?? false,
|
|
31
|
-
|
|
74
|
+
onBrokenLinks: config.onBrokenLinks ?? "throw",
|
|
75
|
+
onBrokenAnchors: config.onBrokenAnchors ?? "throw",
|
|
76
|
+
externalRoutes: config.externalRoutes ?? [],
|
|
32
77
|
...config.frontmatterSchema === void 0 ? {} : { frontmatterSchema: config.frontmatterSchema }
|
|
33
78
|
};
|
|
34
79
|
}
|
|
@@ -71,7 +116,9 @@ function createDocsSource(config) {
|
|
|
71
116
|
resolved.contentDir,
|
|
72
117
|
resolved.basePath,
|
|
73
118
|
resolved.includeDrafts,
|
|
74
|
-
resolved.
|
|
119
|
+
resolved.onBrokenLinks,
|
|
120
|
+
resolved.onBrokenAnchors,
|
|
121
|
+
resolved.externalRoutes.join(","),
|
|
75
122
|
schemaKey(resolved.frontmatterSchema)
|
|
76
123
|
].join("\0");
|
|
77
124
|
const existing = sources.get(key);
|
|
@@ -85,7 +132,7 @@ function buildSource(config) {
|
|
|
85
132
|
const load = () => {
|
|
86
133
|
cached ??= scan(config).catch((err) => {
|
|
87
134
|
cached = null;
|
|
88
|
-
throw err;
|
|
135
|
+
throw describeScanFailure(err, config.contentDir);
|
|
89
136
|
});
|
|
90
137
|
return cached;
|
|
91
138
|
};
|
|
@@ -143,9 +190,32 @@ function buildSource(config) {
|
|
|
143
190
|
}
|
|
144
191
|
};
|
|
145
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* Say that a descriptor exhaustion happened here, and that it was not us.
|
|
195
|
+
*
|
|
196
|
+
* `EMFILE`/`ENFILE` arrive from Node as a bare `Error` with no hint of what was
|
|
197
|
+
* being read, and the message a reader used to get was
|
|
198
|
+
* `EMFILE: too many open files, open '<contentDir>/p829.md'` and nothing else.
|
|
199
|
+
* That is a filename out of a corpus of a thousand, from a stack of `dist/`
|
|
200
|
+
* frames, during a `next build` that mentions no package.
|
|
201
|
+
*
|
|
202
|
+
* The scan itself is bounded at {@link SCAN_CONCURRENCY} now, so reaching this
|
|
203
|
+
* means the limit is lower than that or something else in the process has the
|
|
204
|
+
* descriptors — which is the useful thing to be told, and the opposite of what a
|
|
205
|
+
* reader concludes from an error naming one of their own markdown files.
|
|
206
|
+
*
|
|
207
|
+
* Only these two codes, and only when the failure is not already ours: a
|
|
208
|
+
* `broken-symlink` or an `invalid-frontmatter` that came out of the scan is
|
|
209
|
+
* already a better message than anything this could add.
|
|
210
|
+
*/
|
|
211
|
+
function describeScanFailure(err, contentDir) {
|
|
212
|
+
const code = err?.code;
|
|
213
|
+
if (code !== "EMFILE" && code !== "ENFILE") return err;
|
|
214
|
+
return docsError("descriptor-limit", `the process ran out of file descriptors while scanning ${contentDir}. The scan holds at most ${String(SCAN_CONCURRENCY)} open at a time, so something else has them — raise the limit (\`ulimit -n\`, or \`LimitNOFILE\` under systemd) rather than shrinking the corpus. If it persists with a limit well above that, it is a bug in this package: https://github.com/wavedotso/wave-docs/issues`, { cause: err });
|
|
215
|
+
}
|
|
146
216
|
async function scan(config) {
|
|
147
217
|
await assertContentDir(config.contentDir);
|
|
148
|
-
const root = await scanDir(config.contentDir, [], "", config, /* @__PURE__ */ new Set([await realpath(config.contentDir)]));
|
|
218
|
+
const root = await scanDir(config.contentDir, [], "", config, /* @__PURE__ */ new Set([await fsGate.run(() => realpath(config.contentDir))]));
|
|
149
219
|
const files = [];
|
|
150
220
|
const bySlug = /* @__PURE__ */ new Map();
|
|
151
221
|
collect(root, files, bySlug);
|
|
@@ -157,13 +227,13 @@ async function scan(config) {
|
|
|
157
227
|
}
|
|
158
228
|
async function assertContentDir(contentDir) {
|
|
159
229
|
try {
|
|
160
|
-
if ((await stat(contentDir)).isDirectory()) return;
|
|
230
|
+
if ((await fsGate.run(() => stat(contentDir))).isDirectory()) return;
|
|
161
231
|
} catch {}
|
|
162
232
|
throw docsError("missing-content-dir", `Docs content directory not found: ${contentDir}\nSet \`contentDir\` to a directory of markdown files; relative paths resolve against the working directory (${process.cwd()}).`);
|
|
163
233
|
}
|
|
164
234
|
const SKIP = { kind: "skip" };
|
|
165
235
|
async function scanDir(absPath, segments, name, config, ancestors) {
|
|
166
|
-
const [meta, entries] = await Promise.all([readDocsMeta(absPath), readdir(absPath, { withFileTypes: true })]);
|
|
236
|
+
const [meta, entries] = await Promise.all([fsGate.run(() => readDocsMeta(absPath)), fsGate.run(() => readdir(absPath, { withFileTypes: true }))]);
|
|
167
237
|
const classified = await Promise.all(entries.map((entry) => ({
|
|
168
238
|
entry,
|
|
169
239
|
name: entry.name.normalize("NFC")
|
|
@@ -206,7 +276,7 @@ async function classifyEntry(dirPath, entry, name) {
|
|
|
206
276
|
if (!entry.isSymbolicLink()) return SKIP;
|
|
207
277
|
let target;
|
|
208
278
|
try {
|
|
209
|
-
target = await stat(absPath);
|
|
279
|
+
target = await fsGate.run(() => stat(absPath));
|
|
210
280
|
} catch {
|
|
211
281
|
if (!isPageFile(name)) return SKIP;
|
|
212
282
|
throw docsError("broken-symlink", `@waveso/docs: ${absPath} is a broken symbolic link, and it names a markdown page — so skipping it would delete a route nobody asked to delete. Point it at an existing file, or remove the link.`);
|
|
@@ -223,20 +293,23 @@ async function classifyEntry(dirPath, entry, name) {
|
|
|
223
293
|
kind: "dir",
|
|
224
294
|
absPath,
|
|
225
295
|
name,
|
|
226
|
-
realPath: await realpath(absPath)
|
|
296
|
+
realPath: await fsGate.run(() => realpath(absPath))
|
|
227
297
|
};
|
|
228
298
|
return SKIP;
|
|
229
299
|
}
|
|
230
300
|
async function readPage(filePath, name, dirSegments, config) {
|
|
231
|
-
const raw = await readFile(filePath, "utf8");
|
|
301
|
+
const raw = await fsGate.run(() => readFile(filePath, "utf8"));
|
|
232
302
|
const relativePath = toPosix(path.relative(config.contentDir, filePath));
|
|
233
303
|
let data;
|
|
234
304
|
let content;
|
|
305
|
+
let frontmatterLines = 0;
|
|
235
306
|
try {
|
|
236
|
-
const
|
|
307
|
+
const source = raw.charCodeAt(0) === 65279 ? raw.slice(1) : raw;
|
|
308
|
+
const file = new VFile({ value: source });
|
|
237
309
|
matter(file, { strip: true });
|
|
238
310
|
data = file.data.matter;
|
|
239
311
|
content = String(file);
|
|
312
|
+
if (source.endsWith(content)) frontmatterLines = source.slice(0, source.length - content.length).split("\n").length - 1;
|
|
240
313
|
} catch (err) {
|
|
241
314
|
const reason = err instanceof Error ? err.message : String(err);
|
|
242
315
|
throw docsError("invalid-frontmatter", `Could not parse the frontmatter block in ${relativePath}: ${reason}`, { cause: err });
|
|
@@ -253,7 +326,8 @@ async function readPage(filePath, name, dirSegments, config) {
|
|
|
253
326
|
filePath,
|
|
254
327
|
relativePath,
|
|
255
328
|
frontmatter,
|
|
256
|
-
content
|
|
329
|
+
content,
|
|
330
|
+
frontmatterLines
|
|
257
331
|
}
|
|
258
332
|
};
|
|
259
333
|
}
|
|
@@ -409,7 +483,7 @@ function toHref(basePath, segments) {
|
|
|
409
483
|
return `${basePath}/${encodeSegments(segments)}`;
|
|
410
484
|
}
|
|
411
485
|
function normalizeBasePath(basePath) {
|
|
412
|
-
const trimmed = basePath.trim().replace(/\/+$/, "");
|
|
486
|
+
const trimmed = basePath.trim().replace(/\/{2,}/g, "/").replace(/\/+$/, "");
|
|
413
487
|
if (trimmed === "") return "";
|
|
414
488
|
return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
415
489
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -67,6 +67,24 @@ interface DocFile<TFrontmatter extends DocFrontmatter = DocFrontmatter> {
|
|
|
67
67
|
frontmatter: TFrontmatter;
|
|
68
68
|
/** Markdown body with the frontmatter block removed. */
|
|
69
69
|
content: string;
|
|
70
|
+
/**
|
|
71
|
+
* How many lines the frontmatter block took up, so an error can name the line
|
|
72
|
+
* in the file rather than in the body.
|
|
73
|
+
*
|
|
74
|
+
* ⚠️ WITHOUT IT, EVERY LINK ERROR POINTED INTO THE FRONTMATTER. `content` has
|
|
75
|
+
* the block removed, so remark counts `node.position.start.line` from the
|
|
76
|
+
* first line of the *body* — and `broken-link`, `draft-link` and `alias-link`
|
|
77
|
+
* all report `relativePath:line`, the exact `file:line` form a terminal and an
|
|
78
|
+
* editor turn into a jump. A page with four frontmatter fields is six lines
|
|
79
|
+
* out: a link on line 10 was reported at line 4, which is the middle of the
|
|
80
|
+
* block that was deleted.
|
|
81
|
+
*
|
|
82
|
+
* Set by `@waveso/docs/source`. Optional and treated as `0` when absent,
|
|
83
|
+
* because a host loading content itself — the documented reason `./render` is
|
|
84
|
+
* an entry point — hands over a body with no block in front of it and has
|
|
85
|
+
* nothing to offset.
|
|
86
|
+
*/
|
|
87
|
+
frontmatterLines?: number | undefined;
|
|
70
88
|
}
|
|
71
89
|
/** A link to a documentation page. */
|
|
72
90
|
interface DocNavPage {
|
|
@@ -226,11 +244,24 @@ interface DocLinkContext {
|
|
|
226
244
|
/** The source path, e.g. `'api/auth.md'`. For error messages. */
|
|
227
245
|
relativePath: string;
|
|
228
246
|
}
|
|
247
|
+
/**
|
|
248
|
+
* What a link problem should do to a build.
|
|
249
|
+
*
|
|
250
|
+
* The three Docusaurus settled on, and for the same reason: the tool cannot
|
|
251
|
+
* know how much a given site cares, and guessing produces either a build that
|
|
252
|
+
* fails on someone's legitimate URL or one that ships a dead link quietly.
|
|
253
|
+
*
|
|
254
|
+
* `'warn'` writes to `console.warn` and continues, which on a docs site is a
|
|
255
|
+
* line in a build log — useful during a migration, not a substitute for
|
|
256
|
+
* `'throw'`.
|
|
257
|
+
*/
|
|
258
|
+
type DocsLinkSeverity = 'throw' | 'warn' | 'ignore';
|
|
229
259
|
/**
|
|
230
260
|
* Resolve an internal markdown link target to a route.
|
|
231
261
|
*
|
|
232
262
|
* Called for every relative link found in the source. Returning `undefined`
|
|
233
|
-
* signals "not a documentation page", which —
|
|
263
|
+
* signals "not a documentation page", which — under the default
|
|
264
|
+
* `onBrokenLinks: 'throw'` — fails
|
|
234
265
|
* the build rather than shipping a 404 that was valid on GitHub.
|
|
235
266
|
*/
|
|
236
267
|
type LinkResolver = (
|
|
@@ -257,6 +288,18 @@ href: string, from: DocLinkContext) => string | undefined;
|
|
|
257
288
|
* blindly prefixes what it is handed produces `/cdn//logo.png` and
|
|
258
289
|
* `/cdn/https://example.com/a.png`. Branch on them, or return `undefined` to
|
|
259
290
|
* leave the src as authored.
|
|
291
|
+
*
|
|
292
|
+
* ⚠️ IT IS A FILE PATH, NOT AN HREF. `src` is percent-decoded, so
|
|
293
|
+
* `./getting%20started.png` — what GitHub's editor writes when you drag in a
|
|
294
|
+
* file whose name has a space — arrives as `getting started.png`, the name that
|
|
295
|
+
* is actually on disk. Any `?query` and `#fragment` are split off before the
|
|
296
|
+
* call and re-attached to whatever you return, so `./diagram.png?v=2` arrives
|
|
297
|
+
* as `diagram.png` and the reader still gets the `?v=2`. Return a src carrying
|
|
298
|
+
* a query or a fragment of your own and yours is kept instead, because two `?`
|
|
299
|
+
* in one URL is not a URL.
|
|
300
|
+
*
|
|
301
|
+
* All three used to be handed over raw, and `readFile(join(dir, src))` — the
|
|
302
|
+
* implementation the README gives — threw `ENOENT` on every one of them.
|
|
260
303
|
*/
|
|
261
304
|
type ImageResolver = (src: string, from: DocLinkContext) => Promise<{
|
|
262
305
|
src: string;
|
|
@@ -291,10 +334,61 @@ interface DocsConfig<TFrontmatter extends DocFrontmatter = DocFrontmatter> {
|
|
|
291
334
|
*/
|
|
292
335
|
includeDrafts?: boolean | undefined;
|
|
293
336
|
/**
|
|
294
|
-
*
|
|
295
|
-
*
|
|
337
|
+
* What to do about an internal link that resolves to no published page.
|
|
338
|
+
* Defaults to `'throw'`.
|
|
339
|
+
*
|
|
340
|
+
* A link that 404s was valid in the editor and on GitHub, so it is the kind
|
|
341
|
+
* of mistake nobody finds by reading. Throwing is the default for that
|
|
342
|
+
* reason, and there is rarely a good reason to lower it — `'warn'` exists
|
|
343
|
+
* for a migration where the corpus is knowingly incomplete for a while.
|
|
344
|
+
*
|
|
345
|
+
* The error names the file and the line, and offers the closest published
|
|
346
|
+
* route when the link looks like a typo of one.
|
|
347
|
+
*/
|
|
348
|
+
onBrokenLinks?: DocsLinkSeverity | undefined;
|
|
349
|
+
/**
|
|
350
|
+
* What to do about a `#fragment` that no heading on the target page owns.
|
|
351
|
+
* Defaults to `'throw'`.
|
|
352
|
+
*
|
|
353
|
+
* ⚠️ THE MORE COMMON OF THE TWO LINK FAILURES, AND IT WENT UNCHECKED. A route
|
|
354
|
+
* was verified and its fragment discarded, so `[setup](./install.md#setup)`
|
|
355
|
+
* built green with no `#setup` anywhere on the page. Headings get renamed
|
|
356
|
+
* constantly and nothing renames the links into them, which is exactly why it
|
|
357
|
+
* is worth checking and exactly why it breaks.
|
|
358
|
+
*
|
|
359
|
+
* Checked against every `id` in the rendered page rather than against the
|
|
360
|
+
* table of contents, which captures `h2`–`h3` only — so a link to an `h4` is
|
|
361
|
+
* fine, and so is one to an id a `rehypePlugins` entry put on something that
|
|
362
|
+
* is not a heading.
|
|
363
|
+
*
|
|
364
|
+
* Lower it to `'warn'` if a plugin of yours adds ids this package cannot see
|
|
365
|
+
* at render time.
|
|
366
|
+
*/
|
|
367
|
+
onBrokenAnchors?: DocsLinkSeverity | undefined;
|
|
368
|
+
/**
|
|
369
|
+
* Route prefixes that belong to your application, not to the documentation.
|
|
370
|
+
*
|
|
371
|
+
* ⚠️ ONLY MEANINGFUL AT A ROOT MOUNT, WHICH IS ALSO THE ONLY PLACE IT IS
|
|
372
|
+
* NEEDED. Under `basePath: '/docs'` an absolute link either carries the
|
|
373
|
+
* prefix — so it is documentation and is checked — or it does not, and this
|
|
374
|
+
* package leaves it alone. Under `basePath: '/'` there is no prefix to test
|
|
375
|
+
* against: `/setup` and `/login` look identical, and both are checked against
|
|
376
|
+
* the published pages.
|
|
377
|
+
*
|
|
378
|
+
* That is the right default, because a root mount is what you choose when the
|
|
379
|
+
* origin serves documentation and nothing else — `docs.example.com` — and
|
|
380
|
+
* there an unknown absolute link is always a typo. If the origin *does* serve
|
|
381
|
+
* something else, name what is yours:
|
|
382
|
+
*
|
|
383
|
+
* ```ts
|
|
384
|
+
* externalRoutes: ['/login', '/dashboard', '/api/']
|
|
385
|
+
* ```
|
|
386
|
+
*
|
|
387
|
+
* A link is skipped when it equals one of these or begins with one followed
|
|
388
|
+
* by `/`. It is a statement about your application, so nothing here can infer
|
|
389
|
+
* it and nothing tries.
|
|
296
390
|
*/
|
|
297
|
-
|
|
391
|
+
externalRoutes?: readonly string[] | undefined;
|
|
298
392
|
/**
|
|
299
393
|
* Validates every page's frontmatter. Defaults to `docFrontmatterSchema`
|
|
300
394
|
* from `@waveso/docs/frontmatter`.
|
|
@@ -345,7 +439,9 @@ interface ResolvedDocsConfig<TFrontmatter extends DocFrontmatter = DocFrontmatte
|
|
|
345
439
|
contentDir: string;
|
|
346
440
|
basePath: string;
|
|
347
441
|
includeDrafts: boolean;
|
|
348
|
-
|
|
442
|
+
onBrokenLinks: DocsLinkSeverity;
|
|
443
|
+
onBrokenAnchors: DocsLinkSeverity;
|
|
444
|
+
externalRoutes: readonly string[];
|
|
349
445
|
/**
|
|
350
446
|
* As supplied. `resolveDocsConfig` omits the key rather than setting it to
|
|
351
447
|
* `undefined` when the built-in `docFrontmatterSchema` applies, so the
|
|
@@ -354,4 +450,4 @@ interface ResolvedDocsConfig<TFrontmatter extends DocFrontmatter = DocFrontmatte
|
|
|
354
450
|
frontmatterSchema?: StandardSchemaV1<unknown, TFrontmatter> | undefined;
|
|
355
451
|
}
|
|
356
452
|
//#endregion
|
|
357
|
-
export { DocFile, DocFrontmatter, DocLinkContext, DocNavGroup, DocNavLink, DocNavNode, DocNavPage, DocNavSeparator, DocsConfig, DocsMeta, ImageResolver, LinkResolver, RenderedDoc, ResolvedDocsConfig, SearchRecord, TocEntry };
|
|
453
|
+
export { DocFile, DocFrontmatter, DocLinkContext, DocNavGroup, DocNavLink, DocNavNode, DocNavPage, DocNavSeparator, DocsConfig, DocsLinkSeverity, DocsMeta, ImageResolver, LinkResolver, RenderedDoc, ResolvedDocsConfig, SearchRecord, TocEntry };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@waveso/docs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Zero parser bytes in the browser: markdown docs for Next.js, built to hast in Node and rendered as your components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -76,6 +76,10 @@
|
|
|
76
76
|
"types": "./dist/react/markdown-components.d.ts",
|
|
77
77
|
"default": "./dist/react/markdown-components.js"
|
|
78
78
|
},
|
|
79
|
+
"./react/next-link": {
|
|
80
|
+
"types": "./dist/react/next-link.d.ts",
|
|
81
|
+
"default": "./dist/react/next-link.js"
|
|
82
|
+
},
|
|
79
83
|
"./react/next-search": {
|
|
80
84
|
"types": "./dist/react/next-search.d.ts",
|
|
81
85
|
"default": "./dist/react/next-search.js"
|
|
@@ -196,7 +200,6 @@
|
|
|
196
200
|
"test:smoke": "node smoke/check.ts",
|
|
197
201
|
"size": "node scripts/size.ts",
|
|
198
202
|
"build:site": "next build site",
|
|
199
|
-
"dev:site": "next dev site"
|
|
200
|
-
"shoot": "node scripts/shoot.ts"
|
|
203
|
+
"dev:site": "next dev site"
|
|
201
204
|
}
|
|
202
205
|
}
|