@dogsbay/format-fluidtopics-markdown 0.2.0-beta.92

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 (50) hide show
  1. package/README.md +60 -0
  2. package/dist/cli.d.ts +9 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +20 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/frontmatter.d.ts +17 -0
  7. package/dist/frontmatter.d.ts.map +1 -0
  8. package/dist/frontmatter.js +85 -0
  9. package/dist/frontmatter.js.map +1 -0
  10. package/dist/headings.d.ts +13 -0
  11. package/dist/headings.d.ts.map +1 -0
  12. package/dist/headings.js +76 -0
  13. package/dist/headings.js.map +1 -0
  14. package/dist/html-blocks.d.ts +25 -0
  15. package/dist/html-blocks.d.ts.map +1 -0
  16. package/dist/html-blocks.js +151 -0
  17. package/dist/html-blocks.js.map +1 -0
  18. package/dist/importer.d.ts +14 -0
  19. package/dist/importer.d.ts.map +1 -0
  20. package/dist/importer.js +106 -0
  21. package/dist/importer.js.map +1 -0
  22. package/dist/index.d.ts +23 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +8 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/inline-walker.d.ts +14 -0
  27. package/dist/inline-walker.d.ts.map +1 -0
  28. package/dist/inline-walker.js +109 -0
  29. package/dist/inline-walker.js.map +1 -0
  30. package/dist/links.d.ts +28 -0
  31. package/dist/links.d.ts.map +1 -0
  32. package/dist/links.js +101 -0
  33. package/dist/links.js.map +1 -0
  34. package/dist/nav.d.ts +23 -0
  35. package/dist/nav.d.ts.map +1 -0
  36. package/dist/nav.js +101 -0
  37. package/dist/nav.js.map +1 -0
  38. package/dist/parse.d.ts +27 -0
  39. package/dist/parse.d.ts.map +1 -0
  40. package/dist/parse.js +22 -0
  41. package/dist/parse.js.map +1 -0
  42. package/dist/tree.d.ts +2 -0
  43. package/dist/tree.d.ts.map +1 -0
  44. package/dist/tree.js +2 -0
  45. package/dist/tree.js.map +1 -0
  46. package/dist/walker.d.ts +15 -0
  47. package/dist/walker.d.ts.map +1 -0
  48. package/dist/walker.js +106 -0
  49. package/dist/walker.js.map +1 -0
  50. package/package.json +52 -0
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @dogsbay/format-fluidtopics-markdown
3
+ *
4
+ * Importer for the Fluid Topics Markdown export shape (e.g. the
5
+ * ServiceNow/ServiceNowDocs GitHub repo): a `markdown/{publication}/**.md`
6
+ * tree with per-publication `index.md` TOCs, DITA `topic_type` frontmatter,
7
+ * embedded HTML tables / nav-card grids, dual link schemes, and omitted media.
8
+ *
9
+ * Produces the universal Dogsbay contract — ExportPage[] + NavItem[] — via the
10
+ * shared TreeNode model, so all existing exporters/renderers consume it
11
+ * unchanged.
12
+ */
13
+ export type { TreeNode, ExportPage, NavItem, Heading } from "@dogsbay/types";
14
+ export { parseFluidTopicsMarkdown } from "./parse.js";
15
+ export type { ParseResult, ParseOptions } from "./parse.js";
16
+ export { importFluidTopicsProject, detectSource, } from "./importer.js";
17
+ export type { FluidTopicsImportResult } from "./importer.js";
18
+ export { extractFrontmatter, mapFrontmatter } from "./frontmatter.js";
19
+ export { resolveFtLink, applyLinkResolution, toSlug } from "./links.js";
20
+ export { protectTables, restoreTables } from "./html-blocks.js";
21
+ export { buildNav, parseTocList, parseReadmePublications } from "./nav.js";
22
+ export { extractHeadings, slugify } from "./headings.js";
23
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAE7E,OAAO,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AACtD,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE5D,OAAO,EACL,wBAAwB,EACxB,YAAY,GACb,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAE7D,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export { parseFluidTopicsMarkdown } from "./parse.js";
2
+ export { importFluidTopicsProject, detectSource, } from "./importer.js";
3
+ export { extractFrontmatter, mapFrontmatter } from "./frontmatter.js";
4
+ export { resolveFtLink, applyLinkResolution, toSlug } from "./links.js";
5
+ export { protectTables, restoreTables } from "./html-blocks.js";
6
+ export { buildNav, parseTocList, parseReadmePublications } from "./nav.js";
7
+ export { extractHeadings, slugify } from "./headings.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAGtD,OAAO,EACL,wBAAwB,EACxB,YAAY,GACb,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Inline token walker — converts markdown-it inline tokens into InlineNode[].
3
+ *
4
+ * Flat-annotation (Notion-style) model: bold/italic/strikethrough are
5
+ * annotations on text spans, not nested elements. Links wrap children.
6
+ *
7
+ * Adapted from `@dogsbay/format-mkdocs`'s inline-walker — the inline model is
8
+ * format-agnostic, so this is intentionally a near-identical copy.
9
+ */
10
+ import type Token from "markdown-it/lib/token.mjs";
11
+ import type { InlineNode } from "@dogsbay/types";
12
+ /** Convert markdown-it inline token children into structured InlineNode[]. */
13
+ export declare function walkInlineTokens(tokens: Token[] | null): InlineNode[];
14
+ //# sourceMappingURL=inline-walker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inline-walker.d.ts","sourceRoot":"","sources":["../src/inline-walker.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,KAAK,MAAM,2BAA2B,CAAC;AACnD,OAAO,KAAK,EACV,UAAU,EAOX,MAAM,gBAAgB,CAAC;AAQxB,8EAA8E;AAC9E,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,GAAG,UAAU,EAAE,CA8GrE"}
@@ -0,0 +1,109 @@
1
+ /** Convert markdown-it inline token children into structured InlineNode[]. */
2
+ export function walkInlineTokens(tokens) {
3
+ if (!tokens || tokens.length === 0)
4
+ return [];
5
+ const result = [];
6
+ const annotations = { bold: false, italic: false, strikethrough: false };
7
+ const containerStack = [];
8
+ function target() {
9
+ if (containerStack.length > 0) {
10
+ return containerStack[containerStack.length - 1].node.children;
11
+ }
12
+ return result;
13
+ }
14
+ function pushNode(node) {
15
+ target().push(node);
16
+ }
17
+ function makeText(text) {
18
+ const span = { type: "text", text };
19
+ if (annotations.bold)
20
+ span.bold = true;
21
+ if (annotations.italic)
22
+ span.italic = true;
23
+ if (annotations.strikethrough)
24
+ span.strikethrough = true;
25
+ return span;
26
+ }
27
+ for (const token of tokens) {
28
+ switch (token.type) {
29
+ case "strong_open":
30
+ annotations.bold = true;
31
+ break;
32
+ case "strong_close":
33
+ annotations.bold = false;
34
+ break;
35
+ case "em_open":
36
+ annotations.italic = true;
37
+ break;
38
+ case "em_close":
39
+ annotations.italic = false;
40
+ break;
41
+ case "s_open":
42
+ annotations.strikethrough = true;
43
+ break;
44
+ case "s_close":
45
+ annotations.strikethrough = false;
46
+ break;
47
+ case "link_open": {
48
+ const href = token.attrGet("href") || "";
49
+ const title = token.attrGet("title") || undefined;
50
+ const linkNode = { type: "link", href, children: [] };
51
+ if (title)
52
+ linkNode.title = title;
53
+ containerStack.push({ type: "link", node: linkNode });
54
+ break;
55
+ }
56
+ case "link_close": {
57
+ const container = containerStack.pop();
58
+ if (container)
59
+ target().push(container.node);
60
+ break;
61
+ }
62
+ case "text": {
63
+ if (token.content)
64
+ pushNode(makeText(token.content));
65
+ break;
66
+ }
67
+ case "code_inline": {
68
+ const codeNode = { type: "code", text: token.content };
69
+ pushNode(codeNode);
70
+ break;
71
+ }
72
+ case "image": {
73
+ const src = token.attrGet("src") || "";
74
+ const alt = token.content || token.attrGet("alt") || undefined;
75
+ const title = token.attrGet("title") || undefined;
76
+ const imgNode = { type: "image", src };
77
+ if (alt)
78
+ imgNode.alt = alt;
79
+ if (title)
80
+ imgNode.title = title;
81
+ pushNode(imgNode);
82
+ break;
83
+ }
84
+ case "softbreak": {
85
+ pushNode(makeText(" "));
86
+ break;
87
+ }
88
+ case "hardbreak": {
89
+ const brNode = { type: "break" };
90
+ pushNode(brNode);
91
+ break;
92
+ }
93
+ case "html_inline": {
94
+ const htmlNode = { type: "html-inline", html: token.content };
95
+ pushNode(htmlNode);
96
+ break;
97
+ }
98
+ default: {
99
+ if (token.content) {
100
+ const htmlNode = { type: "html-inline", html: token.content };
101
+ pushNode(htmlNode);
102
+ }
103
+ break;
104
+ }
105
+ }
106
+ }
107
+ return result;
108
+ }
109
+ //# sourceMappingURL=inline-walker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inline-walker.js","sourceRoot":"","sources":["../src/inline-walker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { TreeNode } from "@dogsbay/types";
2
+ export interface LinkResolveOptions {
3
+ /** Route prefix pages are served under (e.g. "/docs"). */
4
+ hrefPrefix?: string;
5
+ }
6
+ /** Strip `.md`, trailing `/index`, and a bare `index` from a relative path. */
7
+ export declare function toSlug(relPath: string): string;
8
+ /**
9
+ * Resolve a single link href.
10
+ *
11
+ * @param href the raw href as written
12
+ * @param fromRelPath the current file's path relative to the `markdown/` root,
13
+ * including `.md` (e.g. "application-development/build.md")
14
+ * @returns the rewritten href and whether it was recognised/resolved. When
15
+ * `resolved` is false the original href is returned unchanged (callers
16
+ * may log a warning for broken cross-references).
17
+ */
18
+ export declare function resolveFtLink(href: string, fromRelPath: string, opts?: LinkResolveOptions): {
19
+ href: string;
20
+ resolved: boolean;
21
+ };
22
+ /**
23
+ * Walk a tree and rewrite every link href via {@link resolveFtLink}. Rewrites
24
+ * structured inline links and `cards` node item hrefs. Returns the list of
25
+ * unresolved hrefs (for warning/reporting).
26
+ */
27
+ export declare function applyLinkResolution(tree: TreeNode[], fromRelPath: string, opts?: LinkResolveOptions): string[];
28
+ //# sourceMappingURL=links.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"links.d.ts","sourceRoot":"","sources":["../src/links.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,WAAW,kBAAkB;IACjC,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAKD,+EAA+E;AAC/E,wBAAgB,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAK9C;AAYD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,IAAI,GAAE,kBAAuB,GAC5B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CA6BrC;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,QAAQ,EAAE,EAChB,WAAW,EAAE,MAAM,EACnB,IAAI,GAAE,kBAAuB,GAC5B,MAAM,EAAE,CA0BV"}
package/dist/links.js ADDED
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Link resolution for the Fluid Topics Markdown export.
3
+ *
4
+ * The export uses TWO link schemes:
5
+ * - Body cross-references: relative `.md` paths (`../concept/plan.md`),
6
+ * resolved against the current file's directory.
7
+ * - TOC (`index.md`) entries: absolute `raw.githubusercontent.com/.../markdown/
8
+ * {pub}/{file}.md` URLs.
9
+ * Both must resolve to Dogsbay routes. Anchors, mailto:, and other external
10
+ * http(s) links pass through unchanged. Non-`.md` relative paths (images,
11
+ * icons) are left to media handling.
12
+ */
13
+ import { posix } from "node:path";
14
+ const RAW_GH_RE = /^https?:\/\/raw\.githubusercontent\.com\/[^/]+\/[^/]+\/[^/]+\/markdown\/(.+)$/i;
15
+ /** Strip `.md`, trailing `/index`, and a bare `index` from a relative path. */
16
+ export function toSlug(relPath) {
17
+ return relPath
18
+ .replace(/\.md$/i, "")
19
+ .replace(/\/index$/i, "")
20
+ .replace(/^index$/i, "");
21
+ }
22
+ function toHref(slug, hrefPrefix) {
23
+ if (!slug)
24
+ return hrefPrefix || "/";
25
+ return `${hrefPrefix}/${slug}`;
26
+ }
27
+ function splitAnchor(s) {
28
+ const i = s.indexOf("#");
29
+ return i >= 0 ? [s.slice(0, i), s.slice(i + 1)] : [s, ""];
30
+ }
31
+ /**
32
+ * Resolve a single link href.
33
+ *
34
+ * @param href the raw href as written
35
+ * @param fromRelPath the current file's path relative to the `markdown/` root,
36
+ * including `.md` (e.g. "application-development/build.md")
37
+ * @returns the rewritten href and whether it was recognised/resolved. When
38
+ * `resolved` is false the original href is returned unchanged (callers
39
+ * may log a warning for broken cross-references).
40
+ */
41
+ export function resolveFtLink(href, fromRelPath, opts = {}) {
42
+ const hrefPrefix = opts.hrefPrefix ?? "/docs";
43
+ if (!href)
44
+ return { href, resolved: false };
45
+ // Pure anchor.
46
+ if (href.startsWith("#"))
47
+ return { href, resolved: true };
48
+ // Absolute raw.githubusercontent URL (TOC scheme).
49
+ const raw = RAW_GH_RE.exec(href);
50
+ if (raw) {
51
+ const [path, anchor] = splitAnchor(raw[1]);
52
+ const slug = toSlug(path);
53
+ return { href: toHref(slug, hrefPrefix) + (anchor ? `#${anchor}` : ""), resolved: true };
54
+ }
55
+ // Other external / scheme links (http(s), mailto:, tel:) pass through.
56
+ if (/^[a-z][a-z0-9+.-]*:/i.test(href))
57
+ return { href, resolved: true };
58
+ // Relative link. Only rewrite `.md` targets (and anchored ones); leave
59
+ // non-markdown relative paths (images/icons) for media handling.
60
+ const [path, anchor] = splitAnchor(href);
61
+ if (/\.md$/i.test(path) || path === "") {
62
+ const dir = posix.dirname(fromRelPath);
63
+ const resolvedRel = posix.normalize(posix.join(dir, path || "."));
64
+ const slug = toSlug(resolvedRel);
65
+ return { href: toHref(slug, hrefPrefix) + (anchor ? `#${anchor}` : ""), resolved: true };
66
+ }
67
+ return { href, resolved: false };
68
+ }
69
+ /**
70
+ * Walk a tree and rewrite every link href via {@link resolveFtLink}. Rewrites
71
+ * structured inline links and `cards` node item hrefs. Returns the list of
72
+ * unresolved hrefs (for warning/reporting).
73
+ */
74
+ export function applyLinkResolution(tree, fromRelPath, opts = {}) {
75
+ const unresolved = [];
76
+ function rewrite(href) {
77
+ const r = resolveFtLink(href, fromRelPath, opts);
78
+ if (!r.resolved)
79
+ unresolved.push(href);
80
+ return r.href;
81
+ }
82
+ function walk(nodes) {
83
+ for (const node of nodes) {
84
+ if (node.inline) {
85
+ for (const span of node.inline) {
86
+ if (span.type === "link")
87
+ span.href = rewrite(span.href);
88
+ }
89
+ }
90
+ // `card` nodes (from nav-card conversion) carry their href in props.
91
+ if (node.type === "card" && typeof node.props?.href === "string") {
92
+ node.props.href = rewrite(node.props.href);
93
+ }
94
+ if (node.children)
95
+ walk(node.children);
96
+ }
97
+ }
98
+ walk(tree);
99
+ return unresolved;
100
+ }
101
+ //# sourceMappingURL=links.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"links.js","sourceRoot":"","sources":["../src/links.ts"],"names":[],"mappings":"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"}
package/dist/nav.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ import type { NavItem } from "@dogsbay/types";
2
+ export interface NavOptions {
3
+ hrefPrefix?: string;
4
+ /** Optional publication allow-list (top-level markdown/{pub} folders). */
5
+ publications?: string[];
6
+ }
7
+ /**
8
+ * Parse an indented Markdown link list (an `index.md` TOC body) into nested
9
+ * NavItem[]. Depth comes from leading indentation; hrefs are resolved relative
10
+ * to `fromRelPath` (so both the relative and raw-github schemes work).
11
+ */
12
+ export declare function parseTocList(markdown: string, fromRelPath: string, opts?: NavOptions): NavItem[];
13
+ /** Parse the README "List of publications" into ordered {label, pub} pairs. */
14
+ export declare function parseReadmePublications(readme: string): {
15
+ label: string;
16
+ pub: string;
17
+ }[];
18
+ /**
19
+ * Build the full site nav from a Fluid Topics Markdown repo root: README order
20
+ * for the top level, each publication's `index.md` TOC for its children.
21
+ */
22
+ export declare function buildNav(sourceDir: string, opts?: NavOptions): NavItem[];
23
+ //# sourceMappingURL=nav.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nav.d.ts","sourceRoot":"","sources":["../src/nav.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAM9C,MAAM,WAAW,UAAU;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,IAAI,GAAE,UAAe,GACpB,OAAO,EAAE,CAyBX;AAED,+EAA+E;AAC/E,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EAAE,CAWxF;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,GAAE,UAAe,GAAG,OAAO,EAAE,CA2C5E"}
package/dist/nav.js ADDED
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Navigation generation for the Fluid Topics Markdown export.
3
+ *
4
+ * Two-level nav, fully encoded in the repo (no API needed):
5
+ * - Top level : the README "List of publications" (curated order).
6
+ * - Per-publication: that publication's `index.md` (doc_type: toc) — an
7
+ * indented Markdown link list = the authored sidebar.
8
+ */
9
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
10
+ import { join } from "node:path";
11
+ import { resolveFtLink } from "./links.js";
12
+ const LIST_ITEM_RE = /^(\s*)[-*]\s+\[([^\]]*)\]\(([^)]+)\)/;
13
+ const README_PUB_RE = /\[([^\]]*)\]\((?:\.\/)?markdown\/([^/]+)\/index\.md\)/;
14
+ /**
15
+ * Parse an indented Markdown link list (an `index.md` TOC body) into nested
16
+ * NavItem[]. Depth comes from leading indentation; hrefs are resolved relative
17
+ * to `fromRelPath` (so both the relative and raw-github schemes work).
18
+ */
19
+ export function parseTocList(markdown, fromRelPath, opts = {}) {
20
+ const roots = [];
21
+ // Stack of { indent, item } for building the hierarchy.
22
+ const stack = [];
23
+ for (const line of markdown.split("\n")) {
24
+ const m = LIST_ITEM_RE.exec(line);
25
+ if (!m)
26
+ continue;
27
+ const indent = m[1].replace(/\t/g, " ").length;
28
+ const label = m[2].trim();
29
+ const href = resolveFtLink(m[3].trim(), fromRelPath, { hrefPrefix: opts.hrefPrefix }).href;
30
+ const item = { label, href };
31
+ while (stack.length > 0 && stack[stack.length - 1].indent >= indent)
32
+ stack.pop();
33
+ if (stack.length === 0) {
34
+ roots.push(item);
35
+ }
36
+ else {
37
+ const parent = stack[stack.length - 1].item;
38
+ (parent.children ??= []).push(item);
39
+ }
40
+ stack.push({ indent, item });
41
+ }
42
+ return roots;
43
+ }
44
+ /** Parse the README "List of publications" into ordered {label, pub} pairs. */
45
+ export function parseReadmePublications(readme) {
46
+ const pubs = [];
47
+ const seen = new Set();
48
+ for (const line of readme.split("\n")) {
49
+ const m = README_PUB_RE.exec(line);
50
+ if (m && !seen.has(m[2])) {
51
+ seen.add(m[2]);
52
+ pubs.push({ label: m[1].trim(), pub: m[2] });
53
+ }
54
+ }
55
+ return pubs;
56
+ }
57
+ /**
58
+ * Build the full site nav from a Fluid Topics Markdown repo root: README order
59
+ * for the top level, each publication's `index.md` TOC for its children.
60
+ */
61
+ export function buildNav(sourceDir, opts = {}) {
62
+ const readmePath = join(sourceDir, "README.md");
63
+ const markdownDir = join(sourceDir, "markdown");
64
+ if (!existsSync(markdownDir))
65
+ return [];
66
+ // README gives curated order + labels — but it can omit publications that
67
+ // exist as dirs (it's hand-curated). So treat the actual markdown/ subdirs
68
+ // as the source of truth, and use the README only for order + labels.
69
+ const readmePubs = existsSync(readmePath)
70
+ ? parseReadmePublications(readFileSync(readmePath, "utf-8"))
71
+ : [];
72
+ const labelByPub = new Map(readmePubs.map((p) => [p.pub, p.label]));
73
+ const readmeOrder = new Map(readmePubs.map((p, i) => [p.pub, i]));
74
+ // Actual publication dirs (those containing an index.md).
75
+ let dirs = readdirSync(markdownDir).filter((entry) => {
76
+ const sub = join(markdownDir, entry);
77
+ return statSync(sub).isDirectory() && existsSync(join(sub, "index.md"));
78
+ });
79
+ if (opts.publications && opts.publications.length > 0) {
80
+ const allow = new Set(opts.publications);
81
+ dirs = dirs.filter((pub) => allow.has(pub));
82
+ }
83
+ // Order: README order first, then any extras alphabetically.
84
+ dirs.sort((a, b) => {
85
+ const ai = readmeOrder.has(a) ? readmeOrder.get(a) : Infinity;
86
+ const bi = readmeOrder.has(b) ? readmeOrder.get(b) : Infinity;
87
+ return ai !== bi ? ai - bi : a.localeCompare(b);
88
+ });
89
+ const nav = [];
90
+ for (const pub of dirs) {
91
+ const label = labelByPub.get(pub) ?? deriveLabel(pub);
92
+ const children = parseTocList(readFileSync(join(markdownDir, pub, "index.md"), "utf-8"), `${pub}/index.md`, opts);
93
+ nav.push(children.length > 0 ? { label, children } : { label });
94
+ }
95
+ return nav;
96
+ }
97
+ /** Title-case a publication dir name when the README has no label for it. */
98
+ function deriveLabel(pub) {
99
+ return pub.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
100
+ }
101
+ //# sourceMappingURL=nav.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nav.js","sourceRoot":"","sources":["../src/nav.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Parse one Fluid-Topics-export markdown file into a TreeNode[] + headings.
3
+ *
4
+ * Pipeline: extract frontmatter -> protect embedded <table> spans -> markdown-it
5
+ * parse (html enabled) -> token walker -> restore tables as structured nodes ->
6
+ * resolve links (dual scheme) -> extract + populate headings.
7
+ */
8
+ import type MarkdownIt from "markdown-it";
9
+ import type { TreeNode, Heading } from "@dogsbay/types";
10
+ export interface ParseResult {
11
+ tree: TreeNode[];
12
+ headings: Heading[];
13
+ frontmatter: Record<string, unknown> | undefined;
14
+ /** Hrefs that could not be resolved (for warning/reporting). */
15
+ unresolvedLinks: string[];
16
+ }
17
+ export interface ParseOptions {
18
+ /**
19
+ * The file's path relative to the `markdown/` root, including `.md`
20
+ * (e.g. "application-development/build.md"). Used to resolve relative links.
21
+ */
22
+ fromRelPath?: string;
23
+ /** Route prefix for resolved links (default "/docs"). */
24
+ hrefPrefix?: string;
25
+ }
26
+ export declare function parseFluidTopicsMarkdown(source: string, md: MarkdownIt, opts?: ParseOptions): ParseResult;
27
+ //# sourceMappingURL=parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,UAAU,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAOxD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IACjD,gEAAgE;IAChE,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,UAAU,EACd,IAAI,GAAE,YAAiB,GACtB,WAAW,CAqBb"}
package/dist/parse.js ADDED
@@ -0,0 +1,22 @@
1
+ import { tokensToTree } from "./walker.js";
2
+ import { extractFrontmatter } from "./frontmatter.js";
3
+ import { protectTables, restoreTables } from "./html-blocks.js";
4
+ import { applyLinkResolution } from "./links.js";
5
+ import { extractHeadings } from "./headings.js";
6
+ export function parseFluidTopicsMarkdown(source, md, opts = {}) {
7
+ const { frontmatter, body } = extractFrontmatter(source);
8
+ // Protect embedded tables before markdown-it sees them.
9
+ const { source: protectedBody, tables } = protectTables(body);
10
+ const tokens = md.parse(protectedBody, {});
11
+ const tree = tokensToTree(tokens);
12
+ // Swap table placeholders for structured nodes.
13
+ restoreTables(tree, tables, md);
14
+ // Resolve links (relative .md + absolute raw-github URLs).
15
+ const unresolvedLinks = applyLinkResolution(tree, opts.fromRelPath ?? "index.md", {
16
+ hrefPrefix: opts.hrefPrefix,
17
+ });
18
+ // Extract + populate headings (writes slug/text onto heading nodes).
19
+ const headings = extractHeadings(tree);
20
+ return { tree, headings, frontmatter, unresolvedLinks };
21
+ }
22
+ //# sourceMappingURL=parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.js","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAoBhD,MAAM,UAAU,wBAAwB,CACtC,MAAc,EACd,EAAc,EACd,OAAqB,EAAE;IAEvB,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAEzD,wDAAwD;IACxD,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAE9D,MAAM,MAAM,GAAG,EAAE,CAAC,KAAK,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;IAC3C,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAElC,gDAAgD;IAChD,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;IAEhC,2DAA2D;IAC3D,MAAM,eAAe,GAAG,mBAAmB,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,IAAI,UAAU,EAAE;QAChF,UAAU,EAAE,IAAI,CAAC,UAAU;KAC5B,CAAC,CAAC;IAEH,qEAAqE;IACrE,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IAEvC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,eAAe,EAAE,CAAC;AAC1D,CAAC"}
package/dist/tree.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export type { TreeNode } from "@dogsbay/types";
2
+ //# sourceMappingURL=tree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../src/tree.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC"}
package/dist/tree.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree.js","sourceRoot":"","sources":["../src/tree.ts"],"names":[],"mappings":""}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Convert a markdown-it token stream into TreeNode[].
3
+ *
4
+ * Handles open/close token pairs by recursively collecting children. The
5
+ * generic `*_open` → `type.replace(/_open$/, "")` fallback covers the GFM
6
+ * table family (table/thead/tbody/tr/th/td) and definition lists, so they
7
+ * need no special cases here.
8
+ *
9
+ * Adapted from `@dogsbay/format-mkdocs`'s walker, trimmed to plain
10
+ * CommonMark + GFM (no MkDocs-specific admonition/tab/autodoc tokens).
11
+ */
12
+ import type Token from "markdown-it/lib/token.mjs";
13
+ import type { TreeNode } from "./tree.js";
14
+ export declare function tokensToTree(tokens: Token[]): TreeNode[];
15
+ //# sourceMappingURL=walker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"walker.d.ts","sourceRoot":"","sources":["../src/walker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,KAAK,MAAM,2BAA2B,CAAC;AACnD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAG1C,wBAAgB,YAAY,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,QAAQ,EAAE,CA+BxD"}