@zudojs/docs 0.0.1 → 1.0.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 +32 -9
- package/dist/docsTypes/index.d.ts +1 -1
- package/dist/document/documentBuilder.core.d.ts +15 -5
- package/dist/document/documentBuilder.core.js +27 -15
- package/dist/document/index.d.ts +1 -1
- package/dist/errors/index.d.ts +16 -0
- package/dist/errors/index.js +18 -0
- package/dist/examples/examples.core.d.ts +4 -0
- package/dist/examples/examples.core.js +16 -7
- package/dist/frontmatter/frontmatter.core.d.ts +1 -1
- package/dist/frontmatter/frontmatter.parser.d.ts +15 -2
- package/dist/frontmatter/frontmatter.parser.js +202 -39
- package/dist/frontmatter/frontmatter.serializer.d.ts +13 -1
- package/dist/frontmatter/frontmatter.serializer.js +89 -4
- package/dist/frontmatter/frontmatter.types.d.ts +10 -1
- package/dist/frontmatter/index.d.ts +1 -1
- package/dist/generator/generator.types.d.ts +18 -0
- package/dist/generator/generatorJson.core.d.ts +8 -3
- package/dist/generator/generatorJson.core.js +16 -4
- package/dist/generator/generatorMarkdown.core.d.ts +4 -0
- package/dist/generator/generatorMarkdown.core.js +32 -13
- package/dist/generator/generatorMarkdownNodes.d.ts +20 -0
- package/dist/generator/generatorMarkdownNodes.js +87 -17
- package/dist/generator/index.d.ts +2 -2
- package/dist/generator/index.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/navigation/index.d.ts +1 -1
- package/dist/navigation/index.js +1 -1
- package/dist/navigation/navigation.core.d.ts +25 -6
- package/dist/navigation/navigation.core.js +93 -25
- package/dist/registry/index.d.ts +2 -1
- package/dist/registry/index.js +1 -1
- package/dist/registry/registry.core.d.ts +29 -3
- package/dist/registry/registry.core.js +32 -6
- package/dist/utils/index.d.ts +2 -1
- package/dist/utils/index.js +2 -1
- package/dist/utils/utils.freeze.d.ts +16 -0
- package/dist/utils/utils.freeze.js +53 -0
- package/dist/utils/utils.helper.d.ts +27 -4
- package/dist/utils/utils.helper.js +108 -21
- package/dist/validator/index.d.ts +4 -1
- package/dist/validator/index.js +2 -1
- package/dist/validator/validator.types.d.ts +8 -0
- package/dist/validator/validator.types.js +10 -1
- package/dist/validator/validatorAll/validatorAll.core.d.ts +11 -2
- package/dist/validator/validatorAll/validatorAll.core.js +19 -7
- package/dist/validator/validatorAll/validatorNavigation.core.d.ts +8 -1
- package/dist/validator/validatorAll/validatorNavigation.core.js +64 -10
- package/dist/validator/validatorDocument.core.d.ts +3 -2
- package/dist/validator/validatorDocument.core.js +197 -15
- package/dist/validator/validatorDuplicates.core.d.ts +1 -1
- package/dist/validator/validatorDuplicates.core.js +4 -6
- package/dist/validator/validatorLinks.core.d.ts +22 -4
- package/dist/validator/validatorLinks.core.js +80 -44
- package/package.json +18 -9
- package/dist/docsTypes/documentationContent.d.ts.map +0 -1
- package/dist/docsTypes/documentationContent.js.map +0 -1
- package/dist/docsTypes/documentationDocument.d.ts.map +0 -1
- package/dist/docsTypes/documentationDocument.js.map +0 -1
- package/dist/docsTypes/documentationMetadata.d.ts.map +0 -1
- package/dist/docsTypes/documentationMetadata.js.map +0 -1
- package/dist/docsTypes/documentationNavigation.d.ts.map +0 -1
- package/dist/docsTypes/documentationNavigation.js.map +0 -1
- package/dist/docsTypes/documentationProvider.d.ts.map +0 -1
- package/dist/docsTypes/documentationProvider.js.map +0 -1
- package/dist/docsTypes/index.d.ts.map +0 -1
- package/dist/docsTypes/index.js.map +0 -1
- package/dist/document/documentBuilder.core.d.ts.map +0 -1
- package/dist/document/documentBuilder.core.js.map +0 -1
- package/dist/document/index.d.ts.map +0 -1
- package/dist/document/index.js.map +0 -1
- package/dist/examples/examples.core.d.ts.map +0 -1
- package/dist/examples/examples.core.js.map +0 -1
- package/dist/examples/index.d.ts.map +0 -1
- package/dist/examples/index.js.map +0 -1
- package/dist/frontmatter/frontmatter.core.d.ts.map +0 -1
- package/dist/frontmatter/frontmatter.core.js.map +0 -1
- package/dist/frontmatter/frontmatter.parser.d.ts.map +0 -1
- package/dist/frontmatter/frontmatter.parser.js.map +0 -1
- package/dist/frontmatter/frontmatter.serializer.d.ts.map +0 -1
- package/dist/frontmatter/frontmatter.serializer.js.map +0 -1
- package/dist/frontmatter/frontmatter.types.d.ts.map +0 -1
- package/dist/frontmatter/frontmatter.types.js.map +0 -1
- package/dist/frontmatter/index.d.ts.map +0 -1
- package/dist/frontmatter/index.js.map +0 -1
- package/dist/generator/generator.types.d.ts.map +0 -1
- package/dist/generator/generator.types.js.map +0 -1
- package/dist/generator/generatorJson.core.d.ts.map +0 -1
- package/dist/generator/generatorJson.core.js.map +0 -1
- package/dist/generator/generatorMarkdown.core.d.ts.map +0 -1
- package/dist/generator/generatorMarkdown.core.js.map +0 -1
- package/dist/generator/generatorMarkdownNodes.d.ts.map +0 -1
- package/dist/generator/generatorMarkdownNodes.js.map +0 -1
- package/dist/generator/index.d.ts.map +0 -1
- package/dist/generator/index.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/navigation/index.d.ts.map +0 -1
- package/dist/navigation/index.js.map +0 -1
- package/dist/navigation/navigation.core.d.ts.map +0 -1
- package/dist/navigation/navigation.core.js.map +0 -1
- package/dist/registry/index.d.ts.map +0 -1
- package/dist/registry/index.js.map +0 -1
- package/dist/registry/registry.core.d.ts.map +0 -1
- package/dist/registry/registry.core.js.map +0 -1
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/utils.helper.d.ts.map +0 -1
- package/dist/utils/utils.helper.js.map +0 -1
- package/dist/validator/index.d.ts.map +0 -1
- package/dist/validator/index.js.map +0 -1
- package/dist/validator/validator.types.d.ts.map +0 -1
- package/dist/validator/validator.types.js.map +0 -1
- package/dist/validator/validatorAll/index.d.ts.map +0 -1
- package/dist/validator/validatorAll/index.js.map +0 -1
- package/dist/validator/validatorAll/validatorAll.core.d.ts.map +0 -1
- package/dist/validator/validatorAll/validatorAll.core.js.map +0 -1
- package/dist/validator/validatorAll/validatorNavigation.core.d.ts.map +0 -1
- package/dist/validator/validatorAll/validatorNavigation.core.js.map +0 -1
- package/dist/validator/validatorDocument.core.d.ts.map +0 -1
- package/dist/validator/validatorDocument.core.js.map +0 -1
- package/dist/validator/validatorDuplicates.core.d.ts.map +0 -1
- package/dist/validator/validatorDuplicates.core.js.map +0 -1
- package/dist/validator/validatorLinks.core.d.ts.map +0 -1
- package/dist/validator/validatorLinks.core.js.map +0 -1
|
@@ -1,26 +1,49 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Frontmatter serializer — converts metadata back to YAML-like format.
|
|
3
|
+
*
|
|
4
|
+
* Every value is quoted when it could otherwise be misread by the
|
|
5
|
+
* parser (newlines, `:`/`#`, leading list markers, surrounding
|
|
6
|
+
* whitespace, or text that looks like a number/boolean/null), so
|
|
7
|
+
* `parseFrontmatter(serializeFrontmatter(m, c))` round-trips `m`.
|
|
3
8
|
*/
|
|
4
9
|
const FRONTMATTER_DELIMITER = "---";
|
|
10
|
+
/** Keys that are never written. */
|
|
11
|
+
const FORBIDDEN_KEYS = new Set(["__proto__", "constructor", "prototype"]);
|
|
12
|
+
/** Valid frontmatter key syntax (must match the parser). */
|
|
13
|
+
const KEY_PATTERN = /^[A-Za-z_][\w.-]*$/;
|
|
5
14
|
/**
|
|
6
15
|
* Serializes metadata back into a frontmatter string.
|
|
16
|
+
*
|
|
17
|
+
* Nested plain objects are written as a one-level mapping; other
|
|
18
|
+
* non-scalar values (Dates, class instances) are written as their
|
|
19
|
+
* JSON/ISO representation in quotes. Keys that are not valid
|
|
20
|
+
* frontmatter keys are skipped.
|
|
7
21
|
*/
|
|
8
22
|
export function serializeFrontmatter(metadata, content) {
|
|
9
23
|
const lines = [FRONTMATTER_DELIMITER];
|
|
10
24
|
for (const [key, value] of Object.entries(metadata)) {
|
|
11
25
|
if (value === undefined || value === null)
|
|
12
26
|
continue;
|
|
27
|
+
if (!isSerializableKey(key))
|
|
28
|
+
continue;
|
|
13
29
|
if (Array.isArray(value)) {
|
|
14
30
|
lines.push(`${key}:`);
|
|
15
31
|
for (const item of value) {
|
|
16
|
-
lines.push(` - ${item}`);
|
|
32
|
+
lines.push(` - ${formatScalar(item)}`);
|
|
17
33
|
}
|
|
18
34
|
}
|
|
19
|
-
else if (
|
|
20
|
-
lines.push(`${key}
|
|
35
|
+
else if (isPlainObject(value)) {
|
|
36
|
+
lines.push(`${key}:`);
|
|
37
|
+
for (const [subKey, subValue] of Object.entries(value)) {
|
|
38
|
+
if (subValue === undefined || subValue === null)
|
|
39
|
+
continue;
|
|
40
|
+
if (!isSerializableKey(subKey))
|
|
41
|
+
continue;
|
|
42
|
+
lines.push(` ${subKey}: ${formatScalar(subValue)}`);
|
|
43
|
+
}
|
|
21
44
|
}
|
|
22
45
|
else {
|
|
23
|
-
lines.push(`${key}: ${value}`);
|
|
46
|
+
lines.push(`${key}: ${formatScalar(value)}`);
|
|
24
47
|
}
|
|
25
48
|
}
|
|
26
49
|
lines.push(FRONTMATTER_DELIMITER);
|
|
@@ -28,4 +51,66 @@ export function serializeFrontmatter(metadata, content) {
|
|
|
28
51
|
lines.push(content);
|
|
29
52
|
return lines.join("\n");
|
|
30
53
|
}
|
|
54
|
+
/** Formats a single scalar, quoting whenever the parser could misread it. */
|
|
55
|
+
export function formatScalar(value) {
|
|
56
|
+
if (typeof value === "boolean")
|
|
57
|
+
return value ? "true" : "false";
|
|
58
|
+
if (typeof value === "number") {
|
|
59
|
+
return Number.isFinite(value) ? String(value) : quote(String(value));
|
|
60
|
+
}
|
|
61
|
+
if (typeof value === "string") {
|
|
62
|
+
return needsQuotes(value) ? quote(value) : value;
|
|
63
|
+
}
|
|
64
|
+
if (value instanceof Date) {
|
|
65
|
+
return quote(Number.isNaN(value.getTime()) ? "Invalid Date" : value.toISOString());
|
|
66
|
+
}
|
|
67
|
+
if (value === null || value === undefined)
|
|
68
|
+
return quote("");
|
|
69
|
+
try {
|
|
70
|
+
return quote(JSON.stringify(value) ?? String(value));
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return quote(String(value));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function needsQuotes(value) {
|
|
77
|
+
if (value === "")
|
|
78
|
+
return true;
|
|
79
|
+
if (value !== value.trim())
|
|
80
|
+
return true;
|
|
81
|
+
if (/[\n\r\t"'\\]/.test(value))
|
|
82
|
+
return true;
|
|
83
|
+
if (/(^|\s)#/.test(value))
|
|
84
|
+
return true;
|
|
85
|
+
if (/:(\s|$)/.test(value))
|
|
86
|
+
return true;
|
|
87
|
+
if (/^[-?[\]{}*&!|>%@`,]/.test(value))
|
|
88
|
+
return true;
|
|
89
|
+
if (value === "true" || value === "false")
|
|
90
|
+
return true;
|
|
91
|
+
if (value === "null" || value === "~")
|
|
92
|
+
return true;
|
|
93
|
+
if (/^-?(0|[1-9]\d*)(\.\d+)?$/.test(value))
|
|
94
|
+
return true;
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
function quote(value) {
|
|
98
|
+
return ('"' +
|
|
99
|
+
value
|
|
100
|
+
.replace(/\\/g, "\\\\")
|
|
101
|
+
.replace(/"/g, '\\"')
|
|
102
|
+
.replace(/\n/g, "\\n")
|
|
103
|
+
.replace(/\r/g, "\\r")
|
|
104
|
+
.replace(/\t/g, "\\t") +
|
|
105
|
+
'"');
|
|
106
|
+
}
|
|
107
|
+
function isSerializableKey(key) {
|
|
108
|
+
return !FORBIDDEN_KEYS.has(key) && KEY_PATTERN.test(key);
|
|
109
|
+
}
|
|
110
|
+
function isPlainObject(value) {
|
|
111
|
+
if (typeof value !== "object" || value === null)
|
|
112
|
+
return false;
|
|
113
|
+
const proto = Object.getPrototypeOf(value);
|
|
114
|
+
return proto === Object.prototype || proto === null;
|
|
115
|
+
}
|
|
31
116
|
//# sourceMappingURL=frontmatter.serializer.js.map
|
|
@@ -8,8 +8,17 @@ export interface ParsedFrontmatter {
|
|
|
8
8
|
readonly metadata: FrontmatterMetadata;
|
|
9
9
|
readonly content: string;
|
|
10
10
|
}
|
|
11
|
+
/** Scalar value produced by the frontmatter parser. */
|
|
12
|
+
export type FrontmatterScalar = string | number | boolean | null;
|
|
13
|
+
/** Any value the frontmatter parser can produce for a key. */
|
|
14
|
+
export type FrontmatterValue = FrontmatterScalar | readonly FrontmatterScalar[] | Readonly<Record<string, FrontmatterScalar>>;
|
|
11
15
|
/**
|
|
12
16
|
* Metadata extracted from frontmatter.
|
|
17
|
+
*
|
|
18
|
+
* The named keys below are always returned with the declared type
|
|
19
|
+
* (`version: 1.0` parses to the string `"1.0"`, `tags` items are
|
|
20
|
+
* strings). Unknown keys receive the parser's best-effort scalar,
|
|
21
|
+
* list, or one-level mapping.
|
|
13
22
|
*/
|
|
14
23
|
export interface FrontmatterMetadata {
|
|
15
24
|
readonly title?: string;
|
|
@@ -21,6 +30,6 @@ export interface FrontmatterMetadata {
|
|
|
21
30
|
readonly deprecated?: boolean;
|
|
22
31
|
readonly deprecatedMessage?: string;
|
|
23
32
|
readonly visibility?: string;
|
|
24
|
-
readonly [key: string]:
|
|
33
|
+
readonly [key: string]: FrontmatterValue | undefined;
|
|
25
34
|
}
|
|
26
35
|
//# sourceMappingURL=frontmatter.types.d.ts.map
|
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
* YAML frontmatter parsing and serialization for markdown documentation.
|
|
5
5
|
*/
|
|
6
6
|
export { parseFrontmatter, serializeFrontmatter } from "./frontmatter.core.js";
|
|
7
|
-
export type { ParsedFrontmatter, FrontmatterMetadata, } from "./frontmatter.core.js";
|
|
7
|
+
export type { ParsedFrontmatter, FrontmatterMetadata, FrontmatterScalar, FrontmatterValue, } from "./frontmatter.core.js";
|
|
8
8
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,11 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Type definitions for documentation generators.
|
|
3
3
|
*/
|
|
4
|
+
import type { DocumentationSanitizer } from "../docsTypes/index.js";
|
|
5
|
+
import type { DocumentVisibilityFilter } from "../registry/registry.core.js";
|
|
4
6
|
/**
|
|
5
7
|
* Options for markdown generation.
|
|
6
8
|
*/
|
|
7
9
|
export interface MarkdownGeneratorOptions {
|
|
8
10
|
readonly includeFrontmatter?: boolean;
|
|
9
11
|
readonly includeMeta?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Applied to `html` and `mdx` content before it is emitted. Markdown
|
|
14
|
+
* and structured content are not passed through the sanitizer.
|
|
15
|
+
*/
|
|
16
|
+
readonly sanitizer?: DocumentationSanitizer;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Options for JSON index generation.
|
|
20
|
+
*/
|
|
21
|
+
export interface IndexGeneratorOptions {
|
|
22
|
+
/**
|
|
23
|
+
* Which documents to include. Defaults to `"CLIENT"` so that
|
|
24
|
+
* `visibility: "SERVER"` documents never reach a client-side index
|
|
25
|
+
* unless explicitly requested with `"ALL"` or `"SERVER"`.
|
|
26
|
+
*/
|
|
27
|
+
readonly visibility?: DocumentVisibilityFilter;
|
|
10
28
|
}
|
|
11
29
|
//# sourceMappingURL=generator.types.d.ts.map
|
|
@@ -2,12 +2,17 @@
|
|
|
2
2
|
* JSON output generation for documentation documents.
|
|
3
3
|
*/
|
|
4
4
|
import type { DocumentationDocument } from "../docsTypes/index.js";
|
|
5
|
+
import type { IndexGeneratorOptions } from "./generator.types.js";
|
|
5
6
|
/**
|
|
6
|
-
* Generates a JSON representation of a document
|
|
7
|
+
* Generates a JSON representation of a document, including its
|
|
8
|
+
* `visibility`, `deprecated` and `deprecatedMessage` fields.
|
|
7
9
|
*/
|
|
8
10
|
export declare function generateJSON(document: DocumentationDocument): Record<string, unknown>;
|
|
9
11
|
/**
|
|
10
|
-
* Generates a JSON index for
|
|
12
|
+
* Generates a JSON index for a set of documents.
|
|
13
|
+
*
|
|
14
|
+
* By default only client-visible documents are included (see
|
|
15
|
+
* `IndexGeneratorOptions.visibility`).
|
|
11
16
|
*/
|
|
12
|
-
export declare function generateIndex(documents: readonly DocumentationDocument[]): Record<string, unknown>[];
|
|
17
|
+
export declare function generateIndex(documents: readonly DocumentationDocument[], options?: IndexGeneratorOptions): Record<string, unknown>[];
|
|
13
18
|
//# sourceMappingURL=generatorJson.core.d.ts.map
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* JSON output generation for documentation documents.
|
|
3
3
|
*/
|
|
4
|
+
import { matchesVisibility } from "../registry/registry.core.js";
|
|
4
5
|
/**
|
|
5
|
-
* Generates a JSON representation of a document
|
|
6
|
+
* Generates a JSON representation of a document, including its
|
|
7
|
+
* `visibility`, `deprecated` and `deprecatedMessage` fields.
|
|
6
8
|
*/
|
|
7
9
|
export function generateJSON(document) {
|
|
8
10
|
return {
|
|
@@ -13,6 +15,7 @@ export function generateJSON(document) {
|
|
|
13
15
|
tags: document.tags,
|
|
14
16
|
version: document.version,
|
|
15
17
|
status: document.status,
|
|
18
|
+
visibility: document.visibility,
|
|
16
19
|
deprecated: document.deprecated,
|
|
17
20
|
deprecatedMessage: document.deprecatedMessage,
|
|
18
21
|
content: document.content,
|
|
@@ -20,10 +23,16 @@ export function generateJSON(document) {
|
|
|
20
23
|
};
|
|
21
24
|
}
|
|
22
25
|
/**
|
|
23
|
-
* Generates a JSON index for
|
|
26
|
+
* Generates a JSON index for a set of documents.
|
|
27
|
+
*
|
|
28
|
+
* By default only client-visible documents are included (see
|
|
29
|
+
* `IndexGeneratorOptions.visibility`).
|
|
24
30
|
*/
|
|
25
|
-
export function generateIndex(documents) {
|
|
26
|
-
|
|
31
|
+
export function generateIndex(documents, options = {}) {
|
|
32
|
+
const filter = options.visibility ?? "CLIENT";
|
|
33
|
+
return documents
|
|
34
|
+
.filter((doc) => matchesVisibility(doc, filter))
|
|
35
|
+
.map((doc) => ({
|
|
27
36
|
id: doc.id,
|
|
28
37
|
title: doc.title,
|
|
29
38
|
description: doc.description,
|
|
@@ -31,6 +40,9 @@ export function generateIndex(documents) {
|
|
|
31
40
|
tags: doc.tags,
|
|
32
41
|
version: doc.version,
|
|
33
42
|
status: doc.status,
|
|
43
|
+
visibility: doc.visibility,
|
|
44
|
+
deprecated: doc.deprecated,
|
|
45
|
+
deprecatedMessage: doc.deprecatedMessage,
|
|
34
46
|
}));
|
|
35
47
|
}
|
|
36
48
|
//# sourceMappingURL=generatorJson.core.js.map
|
|
@@ -5,6 +5,10 @@ import type { DocumentationDocument } from "../docsTypes/index.js";
|
|
|
5
5
|
import type { MarkdownGeneratorOptions } from "./generator.types.js";
|
|
6
6
|
/**
|
|
7
7
|
* Generates a markdown string from a document.
|
|
8
|
+
*
|
|
9
|
+
* Frontmatter values are quoted whenever they could be misread
|
|
10
|
+
* (newlines, `:`/`#`, leading `-`), so untrusted titles, descriptions
|
|
11
|
+
* and tags cannot inject metadata keys or terminate the block.
|
|
8
12
|
*/
|
|
9
13
|
export declare function generateMarkdown(document: DocumentationDocument, options?: MarkdownGeneratorOptions): string;
|
|
10
14
|
//# sourceMappingURL=generatorMarkdown.core.d.ts.map
|
|
@@ -1,33 +1,47 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Markdown output generation for documentation documents.
|
|
3
3
|
*/
|
|
4
|
+
import { formatScalar } from "../frontmatter/frontmatter.serializer.js";
|
|
4
5
|
import { nodesToMarkdown } from "./generatorMarkdownNodes.js";
|
|
5
6
|
/**
|
|
6
7
|
* Generates a markdown string from a document.
|
|
8
|
+
*
|
|
9
|
+
* Frontmatter values are quoted whenever they could be misread
|
|
10
|
+
* (newlines, `:`/`#`, leading `-`), so untrusted titles, descriptions
|
|
11
|
+
* and tags cannot inject metadata keys or terminate the block.
|
|
7
12
|
*/
|
|
8
13
|
export function generateMarkdown(document, options = {}) {
|
|
9
|
-
const { includeFrontmatter = true, includeMeta = false } = options;
|
|
14
|
+
const { includeFrontmatter = true, includeMeta = false, sanitizer } = options;
|
|
10
15
|
const lines = [];
|
|
11
16
|
if (includeFrontmatter) {
|
|
12
17
|
lines.push("---");
|
|
13
|
-
lines.push(`title: ${document.title}`);
|
|
18
|
+
lines.push(`title: ${formatScalar(document.title)}`);
|
|
14
19
|
if (document.description) {
|
|
15
|
-
lines.push(`description: ${document.description}`);
|
|
20
|
+
lines.push(`description: ${formatScalar(document.description)}`);
|
|
16
21
|
}
|
|
17
22
|
if (document.category) {
|
|
18
|
-
lines.push(`category: ${document.category}`);
|
|
23
|
+
lines.push(`category: ${formatScalar(document.category)}`);
|
|
19
24
|
}
|
|
20
25
|
if (document.tags && document.tags.length > 0) {
|
|
21
26
|
lines.push("tags:");
|
|
22
27
|
for (const tag of document.tags) {
|
|
23
|
-
lines.push(` - ${tag}`);
|
|
28
|
+
lines.push(` - ${formatScalar(tag)}`);
|
|
24
29
|
}
|
|
25
30
|
}
|
|
26
31
|
if (document.version) {
|
|
27
|
-
lines.push(`version: ${document.version}`);
|
|
32
|
+
lines.push(`version: ${formatScalar(document.version)}`);
|
|
28
33
|
}
|
|
29
34
|
if (document.status) {
|
|
30
|
-
lines.push(`status: ${document.status}`);
|
|
35
|
+
lines.push(`status: ${formatScalar(document.status)}`);
|
|
36
|
+
}
|
|
37
|
+
if (document.visibility) {
|
|
38
|
+
lines.push(`visibility: ${formatScalar(document.visibility)}`);
|
|
39
|
+
}
|
|
40
|
+
if (document.deprecated !== undefined) {
|
|
41
|
+
lines.push(`deprecated: ${formatScalar(document.deprecated)}`);
|
|
42
|
+
}
|
|
43
|
+
if (document.deprecatedMessage) {
|
|
44
|
+
lines.push(`deprecatedMessage: ${formatScalar(document.deprecatedMessage)}`);
|
|
31
45
|
}
|
|
32
46
|
lines.push("---");
|
|
33
47
|
lines.push("");
|
|
@@ -36,17 +50,19 @@ export function generateMarkdown(document, options = {}) {
|
|
|
36
50
|
lines.push("> **DEPRECATED:**");
|
|
37
51
|
if (document.deprecatedMessage) {
|
|
38
52
|
lines.push(">");
|
|
39
|
-
|
|
53
|
+
for (const line of document.deprecatedMessage.split(/\r?\n/)) {
|
|
54
|
+
lines.push(`> ${line}`);
|
|
55
|
+
}
|
|
40
56
|
}
|
|
41
57
|
lines.push("");
|
|
42
58
|
}
|
|
43
|
-
lines.push(contentToMarkdown(document.content));
|
|
59
|
+
lines.push(contentToMarkdown(document.content, sanitizer));
|
|
44
60
|
if (includeMeta && document.metadata) {
|
|
45
61
|
lines.push("");
|
|
46
62
|
lines.push("---");
|
|
47
63
|
lines.push("");
|
|
48
64
|
if (document.metadata.owner) {
|
|
49
|
-
lines.push(`**Owner:** ${document.metadata.owner}`);
|
|
65
|
+
lines.push(`**Owner:** ${escapeInline(document.metadata.owner)}`);
|
|
50
66
|
}
|
|
51
67
|
if (document.metadata.updatedAt) {
|
|
52
68
|
lines.push(`**Updated:** ${document.metadata.updatedAt.toISOString()}`);
|
|
@@ -57,16 +73,19 @@ export function generateMarkdown(document, options = {}) {
|
|
|
57
73
|
/**
|
|
58
74
|
* Converts content to markdown string.
|
|
59
75
|
*/
|
|
60
|
-
function contentToMarkdown(content) {
|
|
76
|
+
function contentToMarkdown(content, sanitizer) {
|
|
61
77
|
switch (content.type) {
|
|
62
78
|
case "markdown":
|
|
63
79
|
return content.value;
|
|
64
80
|
case "html":
|
|
65
|
-
return content.value;
|
|
66
81
|
case "mdx":
|
|
67
|
-
return content.value;
|
|
82
|
+
return sanitizer ? sanitizer.sanitize(content.value) : content.value;
|
|
68
83
|
case "structured":
|
|
69
84
|
return nodesToMarkdown(content.nodes);
|
|
70
85
|
}
|
|
71
86
|
}
|
|
87
|
+
/** Collapses newlines so a value cannot break out of its line. */
|
|
88
|
+
function escapeInline(value) {
|
|
89
|
+
return value.replace(/\r?\n/g, " ");
|
|
90
|
+
}
|
|
72
91
|
//# sourceMappingURL=generatorMarkdown.core.js.map
|
|
@@ -1,9 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Converts structured AST nodes to markdown strings.
|
|
3
|
+
*
|
|
4
|
+
* Every value is escaped for the position it is written to, so
|
|
5
|
+
* untrusted node content cannot break out of a table, code fence,
|
|
6
|
+
* heading or link.
|
|
3
7
|
*/
|
|
4
8
|
import type { DocumentationNode } from "../docsTypes/index.js";
|
|
5
9
|
/**
|
|
6
10
|
* Converts a list of documentation nodes to markdown.
|
|
11
|
+
*
|
|
12
|
+
* @throws {TypeError} for node types that are not part of `DocumentationNode`.
|
|
7
13
|
*/
|
|
8
14
|
export declare function nodesToMarkdown(nodes: readonly DocumentationNode[]): string;
|
|
15
|
+
/** Clamps a heading level to 1–6 (non-integers become 1). */
|
|
16
|
+
export declare function clampHeadingLevel(level: number): number;
|
|
17
|
+
/**
|
|
18
|
+
* Returns a backtick fence longer than any backtick run in `value`,
|
|
19
|
+
* so the code block cannot be closed early by its own content.
|
|
20
|
+
*/
|
|
21
|
+
export declare function fenceFor(value: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Keeps only the first whitespace-delimited token of the language and
|
|
24
|
+
* only characters that are safe on a fence info line.
|
|
25
|
+
*/
|
|
26
|
+
export declare function sanitizeLanguage(language: string | undefined): string;
|
|
27
|
+
/** Escapes `|` and newlines so a value stays inside its table cell. */
|
|
28
|
+
export declare function tableCell(value: string): string;
|
|
9
29
|
//# sourceMappingURL=generatorMarkdownNodes.d.ts.map
|
|
@@ -1,64 +1,134 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Converts structured AST nodes to markdown strings.
|
|
3
|
+
*
|
|
4
|
+
* Every value is escaped for the position it is written to, so
|
|
5
|
+
* untrusted node content cannot break out of a table, code fence,
|
|
6
|
+
* heading or link.
|
|
3
7
|
*/
|
|
8
|
+
const CALLOUT_LABELS = {
|
|
9
|
+
note: "NOTE",
|
|
10
|
+
warning: "WARNING",
|
|
11
|
+
tip: "TIP",
|
|
12
|
+
danger: "DANGER",
|
|
13
|
+
};
|
|
4
14
|
/**
|
|
5
15
|
* Converts a list of documentation nodes to markdown.
|
|
16
|
+
*
|
|
17
|
+
* @throws {TypeError} for node types that are not part of `DocumentationNode`.
|
|
6
18
|
*/
|
|
7
19
|
export function nodesToMarkdown(nodes) {
|
|
8
20
|
const lines = [];
|
|
9
21
|
for (const node of nodes) {
|
|
10
22
|
switch (node.type) {
|
|
11
|
-
case "heading":
|
|
12
|
-
|
|
23
|
+
case "heading": {
|
|
24
|
+
const level = clampHeadingLevel(node.level);
|
|
25
|
+
lines.push(`${"#".repeat(level)} ${singleLine(node.value)}`);
|
|
13
26
|
lines.push("");
|
|
14
27
|
break;
|
|
28
|
+
}
|
|
15
29
|
case "paragraph":
|
|
16
30
|
lines.push(node.value);
|
|
17
31
|
lines.push("");
|
|
18
32
|
break;
|
|
19
|
-
case "code":
|
|
20
|
-
|
|
33
|
+
case "code": {
|
|
34
|
+
const fence = fenceFor(node.value);
|
|
35
|
+
lines.push(fence + sanitizeLanguage(node.language));
|
|
21
36
|
lines.push(node.value);
|
|
22
|
-
lines.push(
|
|
37
|
+
lines.push(fence);
|
|
23
38
|
lines.push("");
|
|
24
39
|
break;
|
|
40
|
+
}
|
|
25
41
|
case "list":
|
|
26
42
|
for (let i = 0; i < node.items.length; i++) {
|
|
27
43
|
const prefix = node.ordered ? `${i + 1}. ` : "- ";
|
|
28
|
-
lines.push(`${prefix}${node.items[i]}`);
|
|
44
|
+
lines.push(`${prefix}${listItem(node.items[i] ?? "")}`);
|
|
29
45
|
}
|
|
30
46
|
lines.push("");
|
|
31
47
|
break;
|
|
32
48
|
case "link":
|
|
33
|
-
lines.push(`[${node.value}](${node.href})`);
|
|
49
|
+
lines.push(`[${escapeLinkText(node.value)}](${escapeLinkHref(node.href)})`);
|
|
34
50
|
lines.push("");
|
|
35
51
|
break;
|
|
36
52
|
case "table": {
|
|
37
|
-
lines.push("| " + node.headers.join(" | ") + " |");
|
|
53
|
+
lines.push("| " + node.headers.map(tableCell).join(" | ") + " |");
|
|
38
54
|
lines.push("| " + node.headers.map(() => "---").join(" | ") + " |");
|
|
39
55
|
for (const row of node.rows) {
|
|
40
|
-
lines.push("| " + row.join(" | ") + " |");
|
|
56
|
+
lines.push("| " + row.map(tableCell).join(" | ") + " |");
|
|
41
57
|
}
|
|
42
58
|
lines.push("");
|
|
43
59
|
break;
|
|
44
60
|
}
|
|
45
61
|
case "quote":
|
|
46
|
-
|
|
62
|
+
for (const line of node.value.split(/\r?\n/)) {
|
|
63
|
+
lines.push(`> ${line}`);
|
|
64
|
+
}
|
|
47
65
|
lines.push("");
|
|
48
66
|
break;
|
|
49
67
|
case "callout": {
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
lines.push(`> **${labels[node.kind] ?? node.kind.toUpperCase()}:** ${node.value}`);
|
|
68
|
+
const label = CALLOUT_LABELS[node.kind] ?? singleLine(String(node.kind)).toUpperCase();
|
|
69
|
+
const [first = "", ...rest] = node.value.split(/\r?\n/);
|
|
70
|
+
lines.push(`> **${label}:** ${first}`);
|
|
71
|
+
for (const line of rest) {
|
|
72
|
+
lines.push(`> ${line}`);
|
|
73
|
+
}
|
|
57
74
|
lines.push("");
|
|
58
75
|
break;
|
|
59
76
|
}
|
|
77
|
+
default: {
|
|
78
|
+
const unknown = node;
|
|
79
|
+
throw new TypeError(`Unknown documentation node type "${String(unknown.type)}".`);
|
|
80
|
+
}
|
|
60
81
|
}
|
|
61
82
|
}
|
|
62
83
|
return lines.join("\n");
|
|
63
84
|
}
|
|
85
|
+
/** Clamps a heading level to 1–6 (non-integers become 1). */
|
|
86
|
+
export function clampHeadingLevel(level) {
|
|
87
|
+
if (!Number.isInteger(level))
|
|
88
|
+
return 1;
|
|
89
|
+
return Math.min(6, Math.max(1, level));
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Returns a backtick fence longer than any backtick run in `value`,
|
|
93
|
+
* so the code block cannot be closed early by its own content.
|
|
94
|
+
*/
|
|
95
|
+
export function fenceFor(value) {
|
|
96
|
+
let longest = 0;
|
|
97
|
+
for (const run of value.match(/`+/g) ?? []) {
|
|
98
|
+
longest = Math.max(longest, run.length);
|
|
99
|
+
}
|
|
100
|
+
return "`".repeat(Math.max(3, longest + 1));
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Keeps only the first whitespace-delimited token of the language and
|
|
104
|
+
* only characters that are safe on a fence info line.
|
|
105
|
+
*/
|
|
106
|
+
export function sanitizeLanguage(language) {
|
|
107
|
+
if (!language)
|
|
108
|
+
return "";
|
|
109
|
+
const first = language.trim().split(/\s+/)[0] ?? "";
|
|
110
|
+
return first.replace(/[^\w+#.-]/g, "");
|
|
111
|
+
}
|
|
112
|
+
/** Escapes `|` and newlines so a value stays inside its table cell. */
|
|
113
|
+
export function tableCell(value) {
|
|
114
|
+
return value.replace(/\\/g, "\\\\").replace(/\|/g, "\\|").replace(/\r?\n/g, "<br>");
|
|
115
|
+
}
|
|
116
|
+
function singleLine(value) {
|
|
117
|
+
return value.replace(/\r?\n/g, " ");
|
|
118
|
+
}
|
|
119
|
+
function listItem(value) {
|
|
120
|
+
return value.replace(/\r?\n/g, "\n ");
|
|
121
|
+
}
|
|
122
|
+
function escapeLinkText(value) {
|
|
123
|
+
// Backslash first: escaping it after the brackets would turn the escapes we
|
|
124
|
+
// just added back into a literal backslash plus an unescaped bracket.
|
|
125
|
+
return singleLine(value)
|
|
126
|
+
.replace(/\\/g, "\\\\")
|
|
127
|
+
.replace(/\[/g, "\\[")
|
|
128
|
+
.replace(/\]/g, "\\]");
|
|
129
|
+
}
|
|
130
|
+
function escapeLinkHref(href) {
|
|
131
|
+
const clean = href.replace(/[\r\n]/g, "");
|
|
132
|
+
return /[\s()]/.test(clean) ? `<${clean.replace(/[<>]/g, "")}>` : clean;
|
|
133
|
+
}
|
|
64
134
|
//# sourceMappingURL=generatorMarkdownNodes.js.map
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Output generators — markdown and JSON for documentation.
|
|
5
5
|
*/
|
|
6
6
|
export { generateMarkdown } from "./generatorMarkdown.core.js";
|
|
7
|
-
export { nodesToMarkdown } from "./generatorMarkdownNodes.js";
|
|
7
|
+
export { nodesToMarkdown, clampHeadingLevel, fenceFor, sanitizeLanguage, tableCell, } from "./generatorMarkdownNodes.js";
|
|
8
8
|
export { generateJSON, generateIndex } from "./generatorJson.core.js";
|
|
9
|
-
export type { MarkdownGeneratorOptions } from "./generator.types.js";
|
|
9
|
+
export type { MarkdownGeneratorOptions, IndexGeneratorOptions, } from "./generator.types.js";
|
|
10
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/generator/index.js
CHANGED
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
* Output generators — markdown and JSON for documentation.
|
|
5
5
|
*/
|
|
6
6
|
export { generateMarkdown } from "./generatorMarkdown.core.js";
|
|
7
|
-
export { nodesToMarkdown } from "./generatorMarkdownNodes.js";
|
|
7
|
+
export { nodesToMarkdown, clampHeadingLevel, fenceFor, sanitizeLanguage, tableCell, } from "./generatorMarkdownNodes.js";
|
|
8
8
|
export { generateJSON, generateIndex } from "./generatorJson.core.js";
|
|
9
9
|
//# sourceMappingURL=index.js.map
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Navigation tree utilities — breadcrumbs, flattening, sibling resolution.
|
|
5
5
|
*/
|
|
6
|
-
export { getBreadcrumbs, flattenNavigation, findNavigationItem, getSiblings, } from "./navigation.core.js";
|
|
6
|
+
export { getBreadcrumbs, flattenNavigation, findNavigationItem, getSiblings, getAdjacent, } from "./navigation.core.js";
|
|
7
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/navigation/index.js
CHANGED
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Navigation tree utilities — breadcrumbs, flattening, sibling resolution.
|
|
5
5
|
*/
|
|
6
|
-
export { getBreadcrumbs, flattenNavigation, findNavigationItem, getSiblings, } from "./navigation.core.js";
|
|
6
|
+
export { getBreadcrumbs, flattenNavigation, findNavigationItem, getSiblings, getAdjacent, } from "./navigation.core.js";
|
|
7
7
|
//# sourceMappingURL=index.js.map
|
|
@@ -3,23 +3,42 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Provides breadcrumb generation, tree flattening, and
|
|
5
5
|
* link resolution for documentation navigation structures.
|
|
6
|
+
*
|
|
7
|
+
* Every walker tolerates shared or cyclic nodes (a node is visited at
|
|
8
|
+
* most once) and stops at `MAX_NAVIGATION_DEPTH`, so malformed trees
|
|
9
|
+
* never overflow the stack.
|
|
6
10
|
*/
|
|
7
11
|
import type { DocumentationBreadcrumb, DocumentationNavigationItem } from "../docsTypes/index.js";
|
|
12
|
+
/** Maximum nesting depth any walker will descend. */
|
|
13
|
+
export declare const MAX_NAVIGATION_DEPTH = 64;
|
|
14
|
+
type Nodes = readonly DocumentationNavigationItem[];
|
|
8
15
|
/**
|
|
9
16
|
* Generates breadcrumbs for a given document ID
|
|
10
|
-
* by walking the navigation tree.
|
|
17
|
+
* by walking the navigation tree. Intermediate section nodes keep
|
|
18
|
+
* their `documentId` when they have one, so section landing pages
|
|
19
|
+
* stay linkable.
|
|
11
20
|
*/
|
|
12
|
-
export declare function getBreadcrumbs(documentId: string, items:
|
|
21
|
+
export declare function getBreadcrumbs(documentId: string, items: Nodes): readonly DocumentationBreadcrumb[];
|
|
13
22
|
/**
|
|
14
23
|
* Flattens a navigation tree into a list of all document IDs in order.
|
|
24
|
+
* A document referenced more than once appears once, at its first position.
|
|
15
25
|
*/
|
|
16
|
-
export declare function flattenNavigation(items:
|
|
26
|
+
export declare function flattenNavigation(items: Nodes): readonly string[];
|
|
17
27
|
/**
|
|
18
28
|
* Finds a navigation item by document ID.
|
|
19
29
|
*/
|
|
20
|
-
export declare function findNavigationItem(documentId: string, items:
|
|
30
|
+
export declare function findNavigationItem(documentId: string, items: Nodes): DocumentationNavigationItem | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Gets sibling document IDs for a given document (the other documents
|
|
33
|
+
* at the same navigation level, excluding the document itself).
|
|
34
|
+
*/
|
|
35
|
+
export declare function getSiblings(documentId: string, items: Nodes): readonly string[];
|
|
21
36
|
/**
|
|
22
|
-
* Gets
|
|
37
|
+
* Gets the previous and next document IDs at the same navigation level.
|
|
23
38
|
*/
|
|
24
|
-
export declare function
|
|
39
|
+
export declare function getAdjacent(documentId: string, items: Nodes): {
|
|
40
|
+
readonly previous?: string;
|
|
41
|
+
readonly next?: string;
|
|
42
|
+
};
|
|
43
|
+
export {};
|
|
25
44
|
//# sourceMappingURL=navigation.core.d.ts.map
|