@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.
Files changed (65) hide show
  1. package/dist/attributes.d.ts +33 -0
  2. package/dist/attributes.d.ts.map +1 -0
  3. package/dist/attributes.js +83 -0
  4. package/dist/attributes.js.map +1 -0
  5. package/dist/cli.d.ts +3 -0
  6. package/dist/cli.d.ts.map +1 -0
  7. package/dist/cli.js +129 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/directives.d.ts +19 -0
  10. package/dist/directives.d.ts.map +1 -0
  11. package/dist/directives.js +76 -0
  12. package/dist/directives.js.map +1 -0
  13. package/dist/escape.d.ts +42 -0
  14. package/dist/escape.d.ts.map +1 -0
  15. package/dist/escape.js +79 -0
  16. package/dist/escape.js.map +1 -0
  17. package/dist/index.d.ts +10 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +10 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/inline.d.ts +9 -0
  22. package/dist/inline.d.ts.map +1 -0
  23. package/dist/inline.js +122 -0
  24. package/dist/inline.js.map +1 -0
  25. package/dist/nav-file.d.ts +38 -0
  26. package/dist/nav-file.d.ts.map +1 -0
  27. package/dist/nav-file.js +257 -0
  28. package/dist/nav-file.js.map +1 -0
  29. package/dist/nav.d.ts +34 -0
  30. package/dist/nav.d.ts.map +1 -0
  31. package/dist/nav.js +169 -0
  32. package/dist/nav.js.map +1 -0
  33. package/dist/parse-attrs.d.ts +24 -0
  34. package/dist/parse-attrs.d.ts.map +1 -0
  35. package/dist/parse-attrs.js +117 -0
  36. package/dist/parse-attrs.js.map +1 -0
  37. package/dist/parse.d.ts +18 -0
  38. package/dist/parse.d.ts.map +1 -0
  39. package/dist/parse.js +1076 -0
  40. package/dist/parse.js.map +1 -0
  41. package/dist/plugin-block-leaf.d.ts +19 -0
  42. package/dist/plugin-block-leaf.d.ts.map +1 -0
  43. package/dist/plugin-block-leaf.js +81 -0
  44. package/dist/plugin-block-leaf.js.map +1 -0
  45. package/dist/plugin-containers.d.ts +11 -0
  46. package/dist/plugin-containers.d.ts.map +1 -0
  47. package/dist/plugin-containers.js +63 -0
  48. package/dist/plugin-containers.js.map +1 -0
  49. package/dist/plugin-inline-directives.d.ts +18 -0
  50. package/dist/plugin-inline-directives.d.ts.map +1 -0
  51. package/dist/plugin-inline-directives.js +121 -0
  52. package/dist/plugin-inline-directives.js.map +1 -0
  53. package/dist/serialize.d.ts +25 -0
  54. package/dist/serialize.d.ts.map +1 -0
  55. package/dist/serialize.js +712 -0
  56. package/dist/serialize.js.map +1 -0
  57. package/dist/types.d.ts +40 -0
  58. package/dist/types.d.ts.map +1 -0
  59. package/dist/types.js +10 -0
  60. package/dist/types.js.map +1 -0
  61. package/dist/yaml.d.ts +22 -0
  62. package/dist/yaml.d.ts.map +1 -0
  63. package/dist/yaml.js +113 -0
  64. package/dist/yaml.js.map +1 -0
  65. 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 `![${alt}](${src}${title})`;
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"}
@@ -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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nav.js","sourceRoot":"","sources":["../src/nav.ts"],"names":[],"mappings":"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"}