@dogsbay/format-dogsbay-md 0.2.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/attributes.d.ts +33 -0
- package/dist/attributes.d.ts.map +1 -0
- package/dist/attributes.js +83 -0
- package/dist/attributes.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +129 -0
- package/dist/cli.js.map +1 -0
- package/dist/directives.d.ts +19 -0
- package/dist/directives.d.ts.map +1 -0
- package/dist/directives.js +76 -0
- package/dist/directives.js.map +1 -0
- package/dist/escape.d.ts +42 -0
- package/dist/escape.d.ts.map +1 -0
- package/dist/escape.js +79 -0
- package/dist/escape.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/inline.d.ts +9 -0
- package/dist/inline.d.ts.map +1 -0
- package/dist/inline.js +122 -0
- package/dist/inline.js.map +1 -0
- package/dist/nav-file.d.ts +38 -0
- package/dist/nav-file.d.ts.map +1 -0
- package/dist/nav-file.js +257 -0
- package/dist/nav-file.js.map +1 -0
- package/dist/nav.d.ts +34 -0
- package/dist/nav.d.ts.map +1 -0
- package/dist/nav.js +169 -0
- package/dist/nav.js.map +1 -0
- package/dist/parse-attrs.d.ts +24 -0
- package/dist/parse-attrs.d.ts.map +1 -0
- package/dist/parse-attrs.js +117 -0
- package/dist/parse-attrs.js.map +1 -0
- package/dist/parse.d.ts +18 -0
- package/dist/parse.d.ts.map +1 -0
- package/dist/parse.js +1076 -0
- package/dist/parse.js.map +1 -0
- package/dist/plugin-block-leaf.d.ts +19 -0
- package/dist/plugin-block-leaf.d.ts.map +1 -0
- package/dist/plugin-block-leaf.js +81 -0
- package/dist/plugin-block-leaf.js.map +1 -0
- package/dist/plugin-containers.d.ts +11 -0
- package/dist/plugin-containers.d.ts.map +1 -0
- package/dist/plugin-containers.js +63 -0
- package/dist/plugin-containers.js.map +1 -0
- package/dist/plugin-inline-directives.d.ts +18 -0
- package/dist/plugin-inline-directives.d.ts.map +1 -0
- package/dist/plugin-inline-directives.js +121 -0
- package/dist/plugin-inline-directives.js.map +1 -0
- package/dist/serialize.d.ts +25 -0
- package/dist/serialize.d.ts.map +1 -0
- package/dist/serialize.js +712 -0
- package/dist/serialize.js.map +1 -0
- package/dist/types.d.ts +40 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -0
- package/dist/yaml.d.ts +22 -0
- package/dist/yaml.d.ts.map +1 -0
- package/dist/yaml.js +113 -0
- package/dist/yaml.js.map +1 -0
- package/package.json +55 -0
package/dist/inline.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { escapeAttrValue } from "./attributes.js";
|
|
2
|
+
/**
|
|
3
|
+
* Serialize a flat array of inline nodes to markdown.
|
|
4
|
+
*/
|
|
5
|
+
export function inlineToDogsbayMd(nodes) {
|
|
6
|
+
if (!nodes || nodes.length === 0)
|
|
7
|
+
return "";
|
|
8
|
+
return nodes.map(renderInline).join("");
|
|
9
|
+
}
|
|
10
|
+
function renderInline(node) {
|
|
11
|
+
switch (node.type) {
|
|
12
|
+
case "text":
|
|
13
|
+
return renderText(node);
|
|
14
|
+
case "link":
|
|
15
|
+
return renderLink(node);
|
|
16
|
+
case "image":
|
|
17
|
+
return renderImage(node);
|
|
18
|
+
case "code":
|
|
19
|
+
return renderInlineCode(node.text);
|
|
20
|
+
case "highlight":
|
|
21
|
+
return `==${inlineToDogsbayMd(node.children)}==`;
|
|
22
|
+
case "kbd":
|
|
23
|
+
return `:kbd[${node.keys.join("+")}]`;
|
|
24
|
+
case "icon":
|
|
25
|
+
return renderIcon(node);
|
|
26
|
+
case "math":
|
|
27
|
+
return `$${node.latex}$`;
|
|
28
|
+
case "footnote-ref":
|
|
29
|
+
return `[^${node.label}]`;
|
|
30
|
+
case "html-inline":
|
|
31
|
+
return node.html;
|
|
32
|
+
case "break":
|
|
33
|
+
return " \n";
|
|
34
|
+
default:
|
|
35
|
+
// exhaustiveness check
|
|
36
|
+
return "";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function renderText(node) {
|
|
40
|
+
let text = escapeInlineText(node.text);
|
|
41
|
+
// Apply annotations from outside in: strikethrough, italic, bold
|
|
42
|
+
// Order produces canonical output: ~~*_**text**_*~~
|
|
43
|
+
if (node.bold)
|
|
44
|
+
text = `**${text}**`;
|
|
45
|
+
if (node.italic)
|
|
46
|
+
text = `*${text}*`;
|
|
47
|
+
if (node.strikethrough)
|
|
48
|
+
text = `~~${text}~~`;
|
|
49
|
+
return text;
|
|
50
|
+
}
|
|
51
|
+
function renderLink(node) {
|
|
52
|
+
const label = inlineToDogsbayMd(node.children);
|
|
53
|
+
const href = escapeLinkDest(node.href);
|
|
54
|
+
const title = node.title ? ` ${escapeAttrValue(node.title)}` : "";
|
|
55
|
+
return `[${label}](${href}${title})`;
|
|
56
|
+
}
|
|
57
|
+
function renderImage(node) {
|
|
58
|
+
const alt = node.alt ? escapeInlineText(node.alt) : "";
|
|
59
|
+
const src = escapeLinkDest(node.src);
|
|
60
|
+
const title = node.title ? ` ${escapeAttrValue(node.title)}` : "";
|
|
61
|
+
return ``;
|
|
62
|
+
}
|
|
63
|
+
function renderInlineCode(text) {
|
|
64
|
+
// Choose a backtick run that doesn't appear in the content
|
|
65
|
+
let maxRun = 0;
|
|
66
|
+
for (const match of text.matchAll(/`+/g)) {
|
|
67
|
+
if (match[0].length > maxRun)
|
|
68
|
+
maxRun = match[0].length;
|
|
69
|
+
}
|
|
70
|
+
const fence = "`".repeat(maxRun + 1);
|
|
71
|
+
// If content starts or ends with backtick, pad with space
|
|
72
|
+
const needsPad = text.startsWith("`") || text.endsWith("`");
|
|
73
|
+
const body = needsPad ? ` ${text} ` : text;
|
|
74
|
+
return `${fence}${body}${fence}`;
|
|
75
|
+
}
|
|
76
|
+
function renderIcon(node) {
|
|
77
|
+
// Simple form: :name[iconid]. Library/variant encoded via attrs if present.
|
|
78
|
+
const parts = [node.name];
|
|
79
|
+
if (node.library)
|
|
80
|
+
parts.unshift(node.library);
|
|
81
|
+
const fullName = parts.join(":");
|
|
82
|
+
// If the icon has additional metadata, switch to directive with attributes
|
|
83
|
+
const extraAttrs = [];
|
|
84
|
+
if (node.size)
|
|
85
|
+
extraAttrs.push(`size=${escapeAttrValue(node.size)}`);
|
|
86
|
+
if (node.variant)
|
|
87
|
+
extraAttrs.push(`variant=${escapeAttrValue(node.variant)}`);
|
|
88
|
+
if (node.attrs) {
|
|
89
|
+
for (const [k, v] of Object.entries(node.attrs)) {
|
|
90
|
+
extraAttrs.push(`${k}=${escapeAttrValue(v)}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (extraAttrs.length === 0) {
|
|
94
|
+
return `:icon[${fullName}]`;
|
|
95
|
+
}
|
|
96
|
+
return `:icon[${fullName}]{${extraAttrs.join(" ")}}`;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Escape markdown-active characters in inline text.
|
|
100
|
+
* Balance: aggressive enough to round-trip, not so aggressive that readable text
|
|
101
|
+
* becomes a thicket of backslashes.
|
|
102
|
+
*/
|
|
103
|
+
function escapeInlineText(text) {
|
|
104
|
+
// CommonMark rule: `_` is emphasis only at word boundaries (not intraword).
|
|
105
|
+
// So `snake_case` needs no escaping. Only escape `_` at word boundaries.
|
|
106
|
+
return text
|
|
107
|
+
.replace(/\\/g, "\\\\")
|
|
108
|
+
.replace(/([*`\[\]])/g, "\\$1")
|
|
109
|
+
.replace(/(^|\W)_/g, "$1\\_")
|
|
110
|
+
.replace(/_(\W|$)/g, "\\_$1");
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Escape a link/image destination. Angle-bracketed form used when URL contains
|
|
114
|
+
* parens or whitespace.
|
|
115
|
+
*/
|
|
116
|
+
function escapeLinkDest(dest) {
|
|
117
|
+
if (/[\s()]/.test(dest)) {
|
|
118
|
+
return `<${dest}>`;
|
|
119
|
+
}
|
|
120
|
+
return dest;
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=inline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inline.js","sourceRoot":"","sources":["../src/inline.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { NavItem } from "@dogsbay/types";
|
|
2
|
+
/**
|
|
3
|
+
* Schema for nav file entries. The same shape works across JSON and YAML
|
|
4
|
+
* after normalization. Each entry has at least one of `file`, `href`,
|
|
5
|
+
* or `children`. `label` is required.
|
|
6
|
+
*/
|
|
7
|
+
export interface NavFileItem {
|
|
8
|
+
/** Display label in the sidebar */
|
|
9
|
+
label: string;
|
|
10
|
+
/** Internal: relative path to a .md file in the content dir */
|
|
11
|
+
file?: string;
|
|
12
|
+
/** External / absolute: passes through to NavItem.href as-is */
|
|
13
|
+
href?: string;
|
|
14
|
+
/** Nested items */
|
|
15
|
+
children?: NavFileItem[];
|
|
16
|
+
}
|
|
17
|
+
export type NavFile = NavFileItem[];
|
|
18
|
+
export interface LoadNavFileOptions {
|
|
19
|
+
/** Prefix for resolved hrefs. Default: "/docs" */
|
|
20
|
+
hrefPrefix?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Whether to fail or warn on file references that don't exist in the
|
|
23
|
+
* content dir. Default: "warn".
|
|
24
|
+
*/
|
|
25
|
+
missingFile?: "warn" | "fail";
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Load + parse a nav file at the given path. Returns a normalized
|
|
29
|
+
* `NavItem[]` ready for downstream consumers. Throws if the path
|
|
30
|
+
* doesn't exist, the file is malformed, or required fields are
|
|
31
|
+
* missing.
|
|
32
|
+
*
|
|
33
|
+
* Pass an absolute path or a path relative to the current working
|
|
34
|
+
* directory. The contentDir is used to verify file references and
|
|
35
|
+
* to compute slugs.
|
|
36
|
+
*/
|
|
37
|
+
export declare function loadNavFile(path: string, contentDir: string, options?: LoadNavFileOptions): NavItem[];
|
|
38
|
+
//# sourceMappingURL=nav-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nav-file.d.ts","sourceRoot":"","sources":["../src/nav-file.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAE9C;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mBAAmB;IACnB,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;CAC1B;AAED,MAAM,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;AAEpC,MAAM,WAAW,kBAAkB;IACjC,kDAAkD;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC/B;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,EAAE,CAyBX"}
|
package/dist/nav-file.js
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Explicit nav file loader for Dogsbay MD content directories.
|
|
3
|
+
*
|
|
4
|
+
* Supports nav.json (canonical) and nav.yml / nav.yaml (terse,
|
|
5
|
+
* MkDocs-compatible). The loader is format-agnostic at the call site —
|
|
6
|
+
* it dispatches by file extension and returns a normalized
|
|
7
|
+
* `NavItem[]` regardless of source format.
|
|
8
|
+
*
|
|
9
|
+
* Resolution order at the call site:
|
|
10
|
+
* 1. Caller passes an explicit path → use that
|
|
11
|
+
* 2. Caller asks for heuristic detection → try nav.json, nav.yml,
|
|
12
|
+
* nav.yaml in the content root in that order
|
|
13
|
+
* 3. Caller falls back to `buildNavFromDirectory()` (in nav.ts)
|
|
14
|
+
*
|
|
15
|
+
* See `plans/nav-file-support.md` for design context.
|
|
16
|
+
*/
|
|
17
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
18
|
+
import { extname, isAbsolute, resolve } from "node:path";
|
|
19
|
+
import yaml from "js-yaml";
|
|
20
|
+
/**
|
|
21
|
+
* Load + parse a nav file at the given path. Returns a normalized
|
|
22
|
+
* `NavItem[]` ready for downstream consumers. Throws if the path
|
|
23
|
+
* doesn't exist, the file is malformed, or required fields are
|
|
24
|
+
* missing.
|
|
25
|
+
*
|
|
26
|
+
* Pass an absolute path or a path relative to the current working
|
|
27
|
+
* directory. The contentDir is used to verify file references and
|
|
28
|
+
* to compute slugs.
|
|
29
|
+
*/
|
|
30
|
+
export function loadNavFile(path, contentDir, options = {}) {
|
|
31
|
+
const absolutePath = isAbsolute(path) ? path : resolve(path);
|
|
32
|
+
if (!existsSync(absolutePath)) {
|
|
33
|
+
throw new Error(`Nav file not found: ${absolutePath}`);
|
|
34
|
+
}
|
|
35
|
+
const ext = extname(absolutePath).toLowerCase();
|
|
36
|
+
const raw = readFileSync(absolutePath, "utf-8");
|
|
37
|
+
let parsed;
|
|
38
|
+
if (ext === ".json") {
|
|
39
|
+
parsed = parseJsonNav(raw, absolutePath);
|
|
40
|
+
}
|
|
41
|
+
else if (ext === ".yml" || ext === ".yaml") {
|
|
42
|
+
parsed = parseYamlNav(raw, absolutePath);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
throw new Error(`Unsupported nav file extension: ${ext} ` +
|
|
46
|
+
`(supported: .json, .yml, .yaml). Path: ${absolutePath}`);
|
|
47
|
+
}
|
|
48
|
+
const normalized = normalizeNavFile(parsed, absolutePath);
|
|
49
|
+
const hrefPrefix = options.hrefPrefix ?? "/docs";
|
|
50
|
+
const missingFileMode = options.missingFile ?? "warn";
|
|
51
|
+
return convertItems(normalized, contentDir, hrefPrefix, missingFileMode);
|
|
52
|
+
}
|
|
53
|
+
// ── JSON parsing ─────────────────────────────────────────────────
|
|
54
|
+
function parseJsonNav(raw, sourcePath) {
|
|
55
|
+
try {
|
|
56
|
+
return JSON.parse(raw);
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
throw new Error(`Failed to parse nav file as JSON: ${sourcePath}\n ${err.message}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// ── YAML parsing (MkDocs-compatible terse syntax) ────────────────
|
|
63
|
+
/**
|
|
64
|
+
* MkDocs nav YAML uses `Label: target` mappings where target is:
|
|
65
|
+
* - A string (leaf):
|
|
66
|
+
* - Welcome: welcome.md
|
|
67
|
+
* - A list (group):
|
|
68
|
+
* - Section:
|
|
69
|
+
* - Page A: a.md
|
|
70
|
+
* - Page B: b.md
|
|
71
|
+
* - A bare string in a group (group's landing page):
|
|
72
|
+
* - Section:
|
|
73
|
+
* - section/index.md # bare string → group's `file`
|
|
74
|
+
* - Page A: a.md
|
|
75
|
+
*
|
|
76
|
+
* This expands the terse YAML form into the canonical `NavFileItem`
|
|
77
|
+
* shape so the rest of the loader is format-agnostic.
|
|
78
|
+
*/
|
|
79
|
+
function parseYamlNav(raw, sourcePath) {
|
|
80
|
+
let yamlParsed;
|
|
81
|
+
try {
|
|
82
|
+
yamlParsed = yaml.load(raw);
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
throw new Error(`Failed to parse nav file as YAML: ${sourcePath}\n ${err.message}`);
|
|
86
|
+
}
|
|
87
|
+
if (!Array.isArray(yamlParsed)) {
|
|
88
|
+
throw new Error(`Nav file must contain a top-level array. Got ${typeof yamlParsed} in ${sourcePath}`);
|
|
89
|
+
}
|
|
90
|
+
return yamlParsed.map((entry, i) => expandYamlEntry(entry, sourcePath, `[${i}]`));
|
|
91
|
+
}
|
|
92
|
+
const ABSOLUTE_HREF_RE = /^(?:https?:\/\/|\/|mailto:|tel:|#)/i;
|
|
93
|
+
function expandYamlEntry(entry, sourcePath, context) {
|
|
94
|
+
// Bare string at a group level → group's landing page.
|
|
95
|
+
// Caller distinguishes this case by position; here we represent it
|
|
96
|
+
// as `{ label: "", file: "..." }` and the parent group merges it.
|
|
97
|
+
if (typeof entry === "string") {
|
|
98
|
+
return ABSOLUTE_HREF_RE.test(entry)
|
|
99
|
+
? { label: "", href: entry }
|
|
100
|
+
: { label: "", file: entry };
|
|
101
|
+
}
|
|
102
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
|
|
103
|
+
throw new Error(`Nav entry must be a string or single-key map at ${context} in ${sourcePath}. ` +
|
|
104
|
+
`Got ${typeof entry}.`);
|
|
105
|
+
}
|
|
106
|
+
const obj = entry;
|
|
107
|
+
const keys = Object.keys(obj);
|
|
108
|
+
if (keys.length !== 1) {
|
|
109
|
+
throw new Error(`Nav entry must have exactly one key at ${context} in ${sourcePath}. ` +
|
|
110
|
+
`Got ${keys.length}: ${JSON.stringify(keys)}`);
|
|
111
|
+
}
|
|
112
|
+
const label = keys[0];
|
|
113
|
+
const target = obj[label];
|
|
114
|
+
// Leaf: value is a string. External URL → href; otherwise file.
|
|
115
|
+
if (typeof target === "string") {
|
|
116
|
+
return ABSOLUTE_HREF_RE.test(target)
|
|
117
|
+
? { label, href: target }
|
|
118
|
+
: { label, file: target };
|
|
119
|
+
}
|
|
120
|
+
// Group: value is a list. Walk children, then merge any leading
|
|
121
|
+
// bare-string entry as the group's `file`/`href`.
|
|
122
|
+
if (Array.isArray(target)) {
|
|
123
|
+
const expandedChildren = target.map((child, i) => expandYamlEntry(child, sourcePath, `${context}.${label}[${i}]`));
|
|
124
|
+
// Detect leading "bare string" child (label-empty entry that has
|
|
125
|
+
// file/href but no label). MkDocs convention: that's the group's
|
|
126
|
+
// own landing page. Merge it onto the group node.
|
|
127
|
+
const result = { label };
|
|
128
|
+
let children = expandedChildren;
|
|
129
|
+
if (expandedChildren.length > 0 &&
|
|
130
|
+
typeof expandedChildren[0] === "object" &&
|
|
131
|
+
expandedChildren[0] !== null) {
|
|
132
|
+
const first = expandedChildren[0];
|
|
133
|
+
if (first.label === "" && (first.file !== undefined || first.href !== undefined)) {
|
|
134
|
+
if (first.file !== undefined)
|
|
135
|
+
result.file = first.file;
|
|
136
|
+
if (first.href !== undefined)
|
|
137
|
+
result.href = first.href;
|
|
138
|
+
children = expandedChildren.slice(1);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
result.children = children;
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
throw new Error(`Nav entry value must be a string or list at ${context}.${label} in ${sourcePath}. ` +
|
|
145
|
+
`Got ${typeof target}.`);
|
|
146
|
+
}
|
|
147
|
+
// ── Schema normalization ─────────────────────────────────────────
|
|
148
|
+
/**
|
|
149
|
+
* Normalize a parsed nav file into `NavFileItem[]`. Validates required
|
|
150
|
+
* fields and types; throws with a clear path on first violation.
|
|
151
|
+
*
|
|
152
|
+
* Both JSON and YAML produce the same shape after parsing
|
|
153
|
+
* (after YAML's terser syntax is expanded in Step 2), so this
|
|
154
|
+
* normalizer is shared.
|
|
155
|
+
*/
|
|
156
|
+
function normalizeNavFile(parsed, sourcePath) {
|
|
157
|
+
if (!Array.isArray(parsed)) {
|
|
158
|
+
throw new Error(`Nav file must contain a top-level array. Got ${typeof parsed} in ${sourcePath}`);
|
|
159
|
+
}
|
|
160
|
+
return parsed.map((item, index) => normalizeItem(item, sourcePath, `[${index}]`));
|
|
161
|
+
}
|
|
162
|
+
function normalizeItem(item, sourcePath, context) {
|
|
163
|
+
if (typeof item !== "object" || item === null || Array.isArray(item)) {
|
|
164
|
+
throw new Error(`Nav item must be an object. Got ${typeof item} at ${context} in ${sourcePath}`);
|
|
165
|
+
}
|
|
166
|
+
const obj = item;
|
|
167
|
+
if (typeof obj.label !== "string" || !obj.label) {
|
|
168
|
+
throw new Error(`Nav item is missing required "label" string at ${context} in ${sourcePath}`);
|
|
169
|
+
}
|
|
170
|
+
const result = { label: obj.label };
|
|
171
|
+
if (obj.file !== undefined) {
|
|
172
|
+
if (typeof obj.file !== "string") {
|
|
173
|
+
throw new Error(`Nav item "file" must be a string at ${context} in ${sourcePath}`);
|
|
174
|
+
}
|
|
175
|
+
result.file = obj.file;
|
|
176
|
+
}
|
|
177
|
+
if (obj.href !== undefined) {
|
|
178
|
+
if (typeof obj.href !== "string") {
|
|
179
|
+
throw new Error(`Nav item "href" must be a string at ${context} in ${sourcePath}`);
|
|
180
|
+
}
|
|
181
|
+
result.href = obj.href;
|
|
182
|
+
}
|
|
183
|
+
if (obj.children !== undefined) {
|
|
184
|
+
if (!Array.isArray(obj.children)) {
|
|
185
|
+
throw new Error(`Nav item "children" must be an array at ${context} in ${sourcePath}`);
|
|
186
|
+
}
|
|
187
|
+
result.children = obj.children.map((child, i) => normalizeItem(child, sourcePath, `${context}.children[${i}]`));
|
|
188
|
+
}
|
|
189
|
+
// Each item must have at least one of file / href / children
|
|
190
|
+
if (result.file === undefined &&
|
|
191
|
+
result.href === undefined &&
|
|
192
|
+
result.children === undefined) {
|
|
193
|
+
throw new Error(`Nav item "${result.label}" must have at least one of "file", "href", ` +
|
|
194
|
+
`or "children" at ${context} in ${sourcePath}`);
|
|
195
|
+
}
|
|
196
|
+
return result;
|
|
197
|
+
}
|
|
198
|
+
// ── NavFileItem → NavItem conversion ─────────────────────────────
|
|
199
|
+
const EXTERNAL_HREF_RE = /^(?:https?:\/\/|\/|mailto:|tel:|#)/i;
|
|
200
|
+
/**
|
|
201
|
+
* Convert validated NavFileItem nodes into NavItem (the universal nav
|
|
202
|
+
* type). Resolves `file` references to slugs, passes external `href`
|
|
203
|
+
* values through, and warns/fails on missing files per options.
|
|
204
|
+
*/
|
|
205
|
+
function convertItems(items, contentDir, hrefPrefix, missingFileMode) {
|
|
206
|
+
return items.map((item) => convertItem(item, contentDir, hrefPrefix, missingFileMode));
|
|
207
|
+
}
|
|
208
|
+
function convertItem(item, contentDir, hrefPrefix, missingFileMode) {
|
|
209
|
+
const result = { label: item.label };
|
|
210
|
+
if (item.href) {
|
|
211
|
+
// External / absolute URL — pass through unchanged
|
|
212
|
+
result.href = item.href;
|
|
213
|
+
}
|
|
214
|
+
else if (item.file) {
|
|
215
|
+
if (EXTERNAL_HREF_RE.test(item.file)) {
|
|
216
|
+
// Defensive: a writer might put an absolute URL in `file`. Treat
|
|
217
|
+
// as external rather than trying to resolve it as a slug.
|
|
218
|
+
result.href = item.file;
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
const fileAbs = resolve(contentDir, item.file);
|
|
222
|
+
if (!existsSync(fileAbs)) {
|
|
223
|
+
const message = `Nav file references missing file: ${item.file} ` +
|
|
224
|
+
`(resolved: ${fileAbs})`;
|
|
225
|
+
if (missingFileMode === "fail") {
|
|
226
|
+
throw new Error(message);
|
|
227
|
+
}
|
|
228
|
+
// Warn and produce a label-only nav entry (no href)
|
|
229
|
+
console.warn(`[dogsbay] ${message}`);
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
result.href = fileToHref(item.file, hrefPrefix);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (item.children) {
|
|
237
|
+
result.children = convertItems(item.children, contentDir, hrefPrefix, missingFileMode);
|
|
238
|
+
}
|
|
239
|
+
return result;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Convert a relative .md file path into a public-facing URL, e.g.
|
|
243
|
+
* "docs-as-code/welcome.md" → "/docs/docs-as-code/welcome".
|
|
244
|
+
*/
|
|
245
|
+
function fileToHref(file, hrefPrefix) {
|
|
246
|
+
// Normalize path separators to forward slashes (Windows compat)
|
|
247
|
+
const normalized = file.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
248
|
+
// Strip .md extension. index.md becomes the directory itself.
|
|
249
|
+
let slug = normalized.replace(/\.md$/i, "");
|
|
250
|
+
if (slug.endsWith("/index"))
|
|
251
|
+
slug = slug.slice(0, -"/index".length);
|
|
252
|
+
if (slug === "index")
|
|
253
|
+
slug = "";
|
|
254
|
+
const prefix = hrefPrefix.endsWith("/") ? hrefPrefix.slice(0, -1) : hrefPrefix;
|
|
255
|
+
return slug ? `${prefix}/${slug}` : prefix;
|
|
256
|
+
}
|
|
257
|
+
//# sourceMappingURL=nav-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nav-file.js","sourceRoot":"","sources":["../src/nav-file.ts"],"names":[],"mappings":"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"}
|
package/dist/nav.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { NavItem } from "@dogsbay/types";
|
|
2
|
+
export interface BuildNavOptions {
|
|
3
|
+
/** Prefix for hrefs; defaults to "/docs" */
|
|
4
|
+
hrefPrefix?: string;
|
|
5
|
+
/**
|
|
6
|
+
* Explicit nav file path. When set, this file is loaded and the
|
|
7
|
+
* directory-scan fallback is skipped. Errors thrown from loading
|
|
8
|
+
* propagate (caller asked for that specific file).
|
|
9
|
+
*/
|
|
10
|
+
navFilePath?: string;
|
|
11
|
+
/**
|
|
12
|
+
* When true, skip heuristic auto-detection and go directly to
|
|
13
|
+
* directory scan. Useful for existing pipelines that haven't
|
|
14
|
+
* adopted nav files but might have a file in the content dir
|
|
15
|
+
* left over from another tool.
|
|
16
|
+
*/
|
|
17
|
+
skipHeuristic?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Build a hierarchical nav for a Dogsbay MD content directory.
|
|
21
|
+
*
|
|
22
|
+
* Resolution order:
|
|
23
|
+
* 1. options.navFilePath set → load that file; throw on error
|
|
24
|
+
* 2. options.skipHeuristic NOT set → try nav.{json,yml,yaml} in
|
|
25
|
+
* content root; on success use it; on parse error warn + fall
|
|
26
|
+
* back to directory scan
|
|
27
|
+
* 3. Otherwise → walk the directory, read frontmatter
|
|
28
|
+
* (sidebar.order / sidebar.label / title), build hierarchy
|
|
29
|
+
*
|
|
30
|
+
* Directory index pages (index.md) become the folder's landing page.
|
|
31
|
+
* Sorting falls back to filename alphabetical when order is not set.
|
|
32
|
+
*/
|
|
33
|
+
export declare function buildNavFromDirectory(contentDir: string, options?: BuildNavOptions): NavItem[];
|
|
34
|
+
//# sourceMappingURL=nav.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nav.d.ts","sourceRoot":"","sources":["../src/nav.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAoB9C,MAAM,WAAW,eAAe;IAC9B,4CAA4C;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,eAAoB,GAC5B,OAAO,EAAE,CA+BX"}
|
package/dist/nav.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nav builder for Dogsbay MD sources.
|
|
3
|
+
*
|
|
4
|
+
* Three modes, tried in order:
|
|
5
|
+
* 1. Explicit `--nav <path>` — caller passes navFilePath (wins)
|
|
6
|
+
* 2. Heuristic auto-detect — try nav.json / nav.yml / nav.yaml in
|
|
7
|
+
* content root; first hit wins
|
|
8
|
+
* 3. Directory scan + frontmatter — fallback when no nav file is
|
|
9
|
+
* present or heuristic detection fails to parse
|
|
10
|
+
*
|
|
11
|
+
* Produces NavItem[].
|
|
12
|
+
*/
|
|
13
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
14
|
+
import { join, relative, basename } from "node:path";
|
|
15
|
+
import matter from "gray-matter";
|
|
16
|
+
import { loadNavFile } from "./nav-file.js";
|
|
17
|
+
/**
|
|
18
|
+
* File names tried, in priority order, when auto-detecting a nav
|
|
19
|
+
* file in a content directory. The first existing file wins.
|
|
20
|
+
*/
|
|
21
|
+
const HEURISTIC_NAV_FILES = ["nav.json", "nav.yml", "nav.yaml"];
|
|
22
|
+
/**
|
|
23
|
+
* Build a hierarchical nav for a Dogsbay MD content directory.
|
|
24
|
+
*
|
|
25
|
+
* Resolution order:
|
|
26
|
+
* 1. options.navFilePath set → load that file; throw on error
|
|
27
|
+
* 2. options.skipHeuristic NOT set → try nav.{json,yml,yaml} in
|
|
28
|
+
* content root; on success use it; on parse error warn + fall
|
|
29
|
+
* back to directory scan
|
|
30
|
+
* 3. Otherwise → walk the directory, read frontmatter
|
|
31
|
+
* (sidebar.order / sidebar.label / title), build hierarchy
|
|
32
|
+
*
|
|
33
|
+
* Directory index pages (index.md) become the folder's landing page.
|
|
34
|
+
* Sorting falls back to filename alphabetical when order is not set.
|
|
35
|
+
*/
|
|
36
|
+
export function buildNavFromDirectory(contentDir, options = {}) {
|
|
37
|
+
const hrefPrefix = options.hrefPrefix ?? "/docs";
|
|
38
|
+
// Mode 1: explicit path (wins over heuristic + dir scan)
|
|
39
|
+
if (options.navFilePath) {
|
|
40
|
+
return loadNavFile(options.navFilePath, contentDir, { hrefPrefix });
|
|
41
|
+
}
|
|
42
|
+
// Mode 2: heuristic auto-detection
|
|
43
|
+
if (!options.skipHeuristic) {
|
|
44
|
+
for (const fileName of HEURISTIC_NAV_FILES) {
|
|
45
|
+
const candidate = join(contentDir, fileName);
|
|
46
|
+
if (!existsSync(candidate))
|
|
47
|
+
continue;
|
|
48
|
+
try {
|
|
49
|
+
return loadNavFile(candidate, contentDir, { hrefPrefix });
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
// Heuristic-detected file is malformed → warn + fall through
|
|
53
|
+
// to dir scan. The writer didn't ask for this specific file,
|
|
54
|
+
// so a hard failure would be unfriendly.
|
|
55
|
+
console.warn(`[dogsbay] auto-detected nav file ${candidate} is malformed; ` +
|
|
56
|
+
`falling back to directory scan. Error: ${err.message}`);
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Mode 3: directory scan + frontmatter
|
|
62
|
+
const pages = collectPages(contentDir, contentDir);
|
|
63
|
+
return buildHierarchy(pages, hrefPrefix);
|
|
64
|
+
}
|
|
65
|
+
function collectPages(dir, rootDir) {
|
|
66
|
+
const pages = [];
|
|
67
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
68
|
+
const fullPath = join(dir, entry.name);
|
|
69
|
+
if (entry.isDirectory()) {
|
|
70
|
+
pages.push(...collectPages(fullPath, rootDir));
|
|
71
|
+
}
|
|
72
|
+
else if (entry.name.endsWith(".md")) {
|
|
73
|
+
const content = readFileSync(fullPath, "utf-8");
|
|
74
|
+
let frontmatter = {};
|
|
75
|
+
try {
|
|
76
|
+
frontmatter = (matter(content).data ?? {});
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Malformed frontmatter — treat as no frontmatter
|
|
80
|
+
}
|
|
81
|
+
const sidebar = frontmatter.sidebar ?? {};
|
|
82
|
+
if (sidebar.hidden === true)
|
|
83
|
+
continue;
|
|
84
|
+
const rel = relative(rootDir, fullPath).replace(/\\/g, "/");
|
|
85
|
+
const slug = rel.replace(/\.md$/, "");
|
|
86
|
+
const nameWithoutExt = basename(entry.name, ".md");
|
|
87
|
+
const isIndex = nameWithoutExt === "index";
|
|
88
|
+
pages.push({
|
|
89
|
+
title: frontmatter.title ?? prettifyName(nameWithoutExt),
|
|
90
|
+
sidebarLabel: sidebar.label,
|
|
91
|
+
sidebarOrder: sidebar.order,
|
|
92
|
+
sidebarHidden: false,
|
|
93
|
+
slug,
|
|
94
|
+
filePath: fullPath,
|
|
95
|
+
dir: relative(rootDir, dir).replace(/\\/g, "/"),
|
|
96
|
+
isIndex,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return pages;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Build a nav tree from a flat page list, grouped by directory.
|
|
104
|
+
* Uses ordering hints: (sidebarOrder ?? Infinity), then title.
|
|
105
|
+
*/
|
|
106
|
+
function buildHierarchy(pages, hrefPrefix) {
|
|
107
|
+
// Group by top-level directory; index files at the top level become root-level nav items
|
|
108
|
+
const rootPages = [];
|
|
109
|
+
const groups = new Map();
|
|
110
|
+
for (const page of pages) {
|
|
111
|
+
const firstSegment = page.slug.includes("/") ? page.slug.split("/")[0] : "";
|
|
112
|
+
if (firstSegment === "") {
|
|
113
|
+
rootPages.push(page);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
if (!groups.has(firstSegment))
|
|
117
|
+
groups.set(firstSegment, []);
|
|
118
|
+
groups.get(firstSegment).push(page);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Build flat items from root pages
|
|
122
|
+
const items = [];
|
|
123
|
+
for (const page of rootPages.sort(comparePages)) {
|
|
124
|
+
if (page.slug === "index")
|
|
125
|
+
continue; // handled as site root
|
|
126
|
+
items.push({
|
|
127
|
+
label: page.sidebarLabel ?? page.title,
|
|
128
|
+
href: `${hrefPrefix}/${page.slug}`,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
// Build group items for each subdirectory
|
|
132
|
+
const groupItems = [];
|
|
133
|
+
for (const [dir, groupPages] of groups.entries()) {
|
|
134
|
+
const groupIndex = groupPages.find((p) => p.isIndex);
|
|
135
|
+
const groupChildren = groupPages
|
|
136
|
+
.filter((p) => !p.isIndex)
|
|
137
|
+
.sort(comparePages)
|
|
138
|
+
.map((p) => ({
|
|
139
|
+
label: p.sidebarLabel ?? p.title,
|
|
140
|
+
href: `${hrefPrefix}/${p.slug}`,
|
|
141
|
+
}));
|
|
142
|
+
const groupLabel = groupIndex?.sidebarLabel
|
|
143
|
+
?? groupIndex?.title
|
|
144
|
+
?? prettifyName(dir);
|
|
145
|
+
groupItems.push({
|
|
146
|
+
label: groupLabel,
|
|
147
|
+
href: groupIndex ? `${hrefPrefix}/${groupIndex.slug}` : undefined,
|
|
148
|
+
children: groupChildren,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
// Sort groups by the first-page order heuristic
|
|
152
|
+
groupItems.sort((a, b) => a.label.localeCompare(b.label));
|
|
153
|
+
return [...items, ...groupItems];
|
|
154
|
+
}
|
|
155
|
+
function comparePages(a, b) {
|
|
156
|
+
const oa = a.sidebarOrder ?? Number.POSITIVE_INFINITY;
|
|
157
|
+
const ob = b.sidebarOrder ?? Number.POSITIVE_INFINITY;
|
|
158
|
+
if (oa !== ob)
|
|
159
|
+
return oa - ob;
|
|
160
|
+
return a.title.localeCompare(b.title);
|
|
161
|
+
}
|
|
162
|
+
function prettifyName(slug) {
|
|
163
|
+
return slug
|
|
164
|
+
.split("-")
|
|
165
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
166
|
+
.join(" ");
|
|
167
|
+
}
|
|
168
|
+
void statSync; // reserved for future use (e.g. mtime-based caching)
|
|
169
|
+
//# sourceMappingURL=nav.js.map
|
package/dist/nav.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nav.js","sourceRoot":"","sources":["../src/nav.ts"],"names":[],"mappings":"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"}
|