@dogsbay/format-asciidoc 0.2.0-beta.101

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 (58) hide show
  1. package/README.md +40 -0
  2. package/dist/cli.d.ts +4 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +354 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/detect.d.ts +47 -0
  7. package/dist/detect.d.ts.map +1 -0
  8. package/dist/detect.js +73 -0
  9. package/dist/detect.js.map +1 -0
  10. package/dist/index.d.ts +10 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +9 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/inline-walker.d.ts +17 -0
  15. package/dist/inline-walker.d.ts.map +1 -0
  16. package/dist/inline-walker.js +167 -0
  17. package/dist/inline-walker.js.map +1 -0
  18. package/dist/loaders/antora.d.ts +3 -0
  19. package/dist/loaders/antora.d.ts.map +1 -0
  20. package/dist/loaders/antora.js +874 -0
  21. package/dist/loaders/antora.js.map +1 -0
  22. package/dist/loaders/asciibinder.d.ts +3 -0
  23. package/dist/loaders/asciibinder.d.ts.map +1 -0
  24. package/dist/loaders/asciibinder.js +229 -0
  25. package/dist/loaders/asciibinder.js.map +1 -0
  26. package/dist/loaders/dominance.d.ts +15 -0
  27. package/dist/loaders/dominance.d.ts.map +1 -0
  28. package/dist/loaders/dominance.js +86 -0
  29. package/dist/loaders/dominance.js.map +1 -0
  30. package/dist/loaders/master-adoc.d.ts +3 -0
  31. package/dist/loaders/master-adoc.d.ts.map +1 -0
  32. package/dist/loaders/master-adoc.js +178 -0
  33. package/dist/loaders/master-adoc.js.map +1 -0
  34. package/dist/loaders/plain.d.ts +3 -0
  35. package/dist/loaders/plain.d.ts.map +1 -0
  36. package/dist/loaders/plain.js +58 -0
  37. package/dist/loaders/plain.js.map +1 -0
  38. package/dist/loaders/types.d.ts +117 -0
  39. package/dist/loaders/types.d.ts.map +1 -0
  40. package/dist/loaders/types.js +2 -0
  41. package/dist/loaders/types.js.map +1 -0
  42. package/dist/loaders/util.d.ts +15 -0
  43. package/dist/loaders/util.d.ts.map +1 -0
  44. package/dist/loaders/util.js +49 -0
  45. package/dist/loaders/util.js.map +1 -0
  46. package/dist/parse.d.ts +178 -0
  47. package/dist/parse.d.ts.map +1 -0
  48. package/dist/parse.js +150 -0
  49. package/dist/parse.js.map +1 -0
  50. package/dist/tree.d.ts +2 -0
  51. package/dist/tree.d.ts.map +1 -0
  52. package/dist/tree.js +2 -0
  53. package/dist/tree.js.map +1 -0
  54. package/dist/walker.d.ts +8 -0
  55. package/dist/walker.d.ts.map +1 -0
  56. package/dist/walker.js +210 -0
  57. package/dist/walker.js.map +1 -0
  58. package/package.json +54 -0
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Plain corpus loader — walks every `.adoc` under the source dir
3
+ * and treats directory structure as nav. The MVP / default loader;
4
+ * also the fallback when no topology marker is present.
5
+ *
6
+ * Lineage: this logic used to live inline in `cli.ts` (Phase 8a).
7
+ * Phase 9a extracted it behind the CorpusLoader contract so other
8
+ * loaders (master-adoc, asciibinder, antora) can sit alongside it.
9
+ */
10
+ import { existsSync, readdirSync, statSync } from "node:fs";
11
+ import { basename, join, relative } from "node:path";
12
+ import { isAsciidocDominant } from "./dominance.js";
13
+ import { buildNavFromDirectoryMirror, prettifyName } from "./util.js";
14
+ function walkAdocFiles(dir, root, out = []) {
15
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
16
+ const full = join(dir, entry.name);
17
+ if (entry.isDirectory()) {
18
+ walkAdocFiles(full, root, out);
19
+ }
20
+ else if (entry.name.endsWith(".adoc")) {
21
+ const rel = relative(root, full).replace(/\\/g, "/");
22
+ const slug = rel.replace(/\.adoc$/, "");
23
+ out.push({ filePath: full, slug });
24
+ }
25
+ }
26
+ return out;
27
+ }
28
+ export const plainLoader = {
29
+ name: "plain",
30
+ /**
31
+ * Claims a directory only when it is asciidoc-DOMINANT (more `.adoc` than
32
+ * `.md`, ignoring dot/dependency dirs) — not merely when some `.adoc` file
33
+ * exists somewhere. Mere presence meant one vendored
34
+ * `node_modules/**\/README.adoc` made an entire markdown corpus import as
35
+ * AsciiDoc, dropping every page. See ./dominance.ts.
36
+ */
37
+ detect(sourceDir) {
38
+ return isAsciidocDominant(sourceDir);
39
+ },
40
+ async load(sourceDir, opts) {
41
+ if (!existsSync(sourceDir)) {
42
+ throw new Error(`Source directory not found: ${sourceDir}`);
43
+ }
44
+ const st = statSync(sourceDir);
45
+ if (!st.isDirectory()) {
46
+ throw new Error(`Source must be a directory: ${sourceDir}`);
47
+ }
48
+ const hrefPrefix = opts.hrefPrefix ?? "";
49
+ const files = walkAdocFiles(sourceDir, sourceDir);
50
+ const pages = files.map((f) => ({
51
+ filePath: f.filePath,
52
+ slug: f.slug,
53
+ }));
54
+ const nav = buildNavFromDirectoryMirror(pages.map((p) => ({ slug: p.slug, title: prettifyName(basename(p.slug)) })), hrefPrefix);
55
+ return { pages, nav };
56
+ },
57
+ };
58
+ //# sourceMappingURL=plain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plain.js","sourceRoot":"","sources":["../../src/loaders/plain.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,2BAA2B,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAEtE,SAAS,aAAa,CACpB,GAAW,EACX,IAAY,EACZ,MAA4C,EAAE;IAE9C,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QAC9D,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;QACjC,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YACxC,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YACrD,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;YACxC,GAAG,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAiB;IACvC,IAAI,EAAE,OAAO;IAEb;;;;;;OAMG;IACH,MAAM,CAAC,SAAiB;QACtB,OAAO,kBAAkB,CAAC,SAAS,CAAC,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,SAAiB,EAAE,IAAgB;QAC5C,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,+BAA+B,SAAS,EAAE,CAAC,CAAC;QAC9D,CAAC;QACD,MAAM,EAAE,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;QAC/B,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,+BAA+B,SAAS,EAAE,CAAC,CAAC;QAC9D,CAAC;QACD,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,aAAa,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAClD,MAAM,KAAK,GAAiB,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC5C,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,IAAI,EAAE,CAAC,CAAC,IAAI;SACb,CAAC,CAAC,CAAC;QACJ,MAAM,GAAG,GAAG,2BAA2B,CACrC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,EAC3E,UAAU,CACX,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;IACxB,CAAC;CACF,CAAC"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * CorpusLoader contract — Phase 9a.
3
+ *
4
+ * AsciiDoc corpora come in distinct topology shapes:
5
+ *
6
+ * - **plain** (Phase 9a) — flat .adoc walk; directory mirrors nav.
7
+ * - **master-adoc** (Phase 9a) — AAP convention: each `master.adoc`
8
+ * is a title; its include:: chain
9
+ * defines the module ordering.
10
+ * - **asciibinder** (Phase 9b) — `_topic_maps/_topic_map.yml` +
11
+ * distros (OpenShift).
12
+ * - **antora** (Phase 9c) — `antora-playbook.yml` + per-component
13
+ * `antora.yml` (version axis maps to
14
+ * Dogsbay's version axis).
15
+ *
16
+ * Each loader is a thin enumerator: discover the corpus, decide which
17
+ * .adoc files become pages, propose a nav. Loaders do NOT parse —
18
+ * `cli.ts` runs `parseAsciidocToTree` on each `PageSource.filePath`.
19
+ * Keeping the split clean means loaders can be tested in isolation
20
+ * against their topology fixtures without depending on the full
21
+ * markdown-it pipeline.
22
+ *
23
+ * See plans/asciidoc-corpus-loaders.md for the four-loader rollout
24
+ * + axis-translation bridge spec.
25
+ */
26
+ import type { NavItem } from "@dogsbay/types";
27
+ export interface PageSource {
28
+ /** Absolute filesystem path to the .adoc file. */
29
+ filePath: string;
30
+ /**
31
+ * URL slug for this page (e.g. `"guides/install"`). The cli's
32
+ * caller is free to prepend its own hrefPrefix when emitting.
33
+ */
34
+ slug: string;
35
+ /**
36
+ * AsciiDoc `:leveloffset:` adjustment if the loader's topology
37
+ * implies one (master.adoc includes carry `leveloffset=+1`, etc.).
38
+ * Engine handles the actual offset; loader just declares it.
39
+ */
40
+ leveloffset?: number;
41
+ /**
42
+ * Per-page attribute context contributed by the loader's topology
43
+ * (AAP `_attributes/attributes.adoc`, Antora component attributes,
44
+ * AsciiBinder distro filters). Merged onto the global attributes
45
+ * passed to the engine + Minja.
46
+ */
47
+ attributes?: Record<string, unknown>;
48
+ }
49
+ export interface LoaderOpts {
50
+ /**
51
+ * URL prefix to apply when building nav hrefs. The CLI fills this
52
+ * in from the consumer site's section mount; loaders just append
53
+ * `/<slug>` after.
54
+ */
55
+ hrefPrefix?: string;
56
+ /**
57
+ * Target distro for AsciiBinder corpora. When set, topic-map
58
+ * entries whose `Distros:` list doesn't include this distro are
59
+ * skipped (both from the page list and the nav). Entries with no
60
+ * `Distros:` field are treated as universal and always kept —
61
+ * matching AsciiBinder's own build-time filter.
62
+ *
63
+ * Without this option, every entry flows through and pages with
64
+ * the same `File:` but different `Distros:` collide in the nav
65
+ * (`Fedora CoreOS` and `Red Hat Enterprise Linux CoreOS` both
66
+ * pointing at `architecture-rhcos.md`). Other loaders ignore this
67
+ * option — they don't have a distro concept.
68
+ */
69
+ distro?: string;
70
+ /**
71
+ * Topic-map path for the AsciiBinder loader, relative to the source
72
+ * root. Defaults to the AsciiBinder convention
73
+ * `_topic_maps/_topic_map.yml`. Override for corpora that name their
74
+ * topic map differently (e.g. a distro-specific `_topic_map_ms.yml`),
75
+ * used by both `detect()` and `load()` so a custom-named corpus can
76
+ * be auto-detected and loaded without editing the source. Other
77
+ * loaders ignore this option.
78
+ */
79
+ topicMap?: string;
80
+ }
81
+ export interface LoaderResult {
82
+ pages: PageSource[];
83
+ nav: NavItem[];
84
+ /**
85
+ * Optional list of distro names declared by the corpus (AsciiBinder
86
+ * `_distro_map.yml` or topic-level `Distros:` aggregation). Surfaced
87
+ * for forward-compatibility with distro-axis emission per the
88
+ * "axis translation" bridge spec in plans/asciidoc-corpus-loaders.md.
89
+ * Today's callers don't consume it; the data is informational only.
90
+ */
91
+ distros?: string[];
92
+ }
93
+ export interface CorpusLoader {
94
+ /** Identifier surfaced via the CLI's `--loader <name>` override. */
95
+ name: "plain" | "asciibinder" | "master-adoc" | "antora";
96
+ /**
97
+ * Heuristic detection: does this directory look like this corpus
98
+ * shape? Detection is best-effort; the CLI's `--loader` override
99
+ * always wins. detect() must NOT throw — return false on any
100
+ * unexpected state.
101
+ *
102
+ * `opts` carries the same loader options passed to `load()` so
103
+ * detection can honour overrides (e.g. a non-default `topicMap`
104
+ * name for AsciiBinder). Loaders that don't need options may accept
105
+ * just `sourceDir`.
106
+ */
107
+ detect(sourceDir: string, opts?: LoaderOpts): boolean;
108
+ /**
109
+ * Enumerate pages + build nav. Loaders may walk the filesystem,
110
+ * read topology files (`master.adoc`, `_topic_map.yml`,
111
+ * `antora.yml`), and resolve includes. They MUST NOT parse the
112
+ * `.adoc` body — that's `cli.ts`'s job, per the separation in
113
+ * the plan.
114
+ */
115
+ load(sourceDir: string, opts: LoaderOpts): Promise<LoaderResult>;
116
+ }
117
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/loaders/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAE9C,MAAM,WAAW,UAAU;IACzB,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,UAAU;IACzB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,GAAG,EAAE,OAAO,EAAE,CAAC;IACf;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,oEAAoE;IACpE,IAAI,EAAE,OAAO,GAAG,aAAa,GAAG,aAAa,GAAG,QAAQ,CAAC;IACzD;;;;;;;;;;OAUG;IACH,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC;IACtD;;;;;;OAMG;IACH,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CAClE"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/loaders/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,15 @@
1
+ import type { NavItem } from "@dogsbay/types";
2
+ export declare function prettifyName(slug: string): string;
3
+ /**
4
+ * Compose a nav tree that mirrors an on-disk directory layout from
5
+ * a flat list of `{ slug, title }` pages. Subdirectories become
6
+ * NavItem groups; leaves are `{ label, href: <hrefPrefix>/<slug> }`.
7
+ *
8
+ * Shared between the plain loader and (with topology overlay) the
9
+ * master-adoc loader's per-title grouping.
10
+ */
11
+ export declare function buildNavFromDirectoryMirror(pages: {
12
+ slug: string;
13
+ title: string;
14
+ }[], hrefPrefix: string): NavItem[];
15
+ //# sourceMappingURL=util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/loaders/util.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAE9C,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMjD;AAED;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,EACxC,UAAU,EAAE,MAAM,GACjB,OAAO,EAAE,CA6BX"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Shared utilities for the corpus loaders.
3
+ */
4
+ import { basename, dirname } from "node:path";
5
+ export function prettifyName(slug) {
6
+ return slug
7
+ .split(/[-_]/)
8
+ .filter(Boolean)
9
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
10
+ .join(" ");
11
+ }
12
+ /**
13
+ * Compose a nav tree that mirrors an on-disk directory layout from
14
+ * a flat list of `{ slug, title }` pages. Subdirectories become
15
+ * NavItem groups; leaves are `{ label, href: <hrefPrefix>/<slug> }`.
16
+ *
17
+ * Shared between the plain loader and (with topology overlay) the
18
+ * master-adoc loader's per-title grouping.
19
+ */
20
+ export function buildNavFromDirectoryMirror(pages, hrefPrefix) {
21
+ const groups = new Map();
22
+ groups.set("", []);
23
+ for (const page of pages) {
24
+ const dir = dirname(page.slug);
25
+ const key = dir === "." ? "" : dir;
26
+ if (!groups.has(key))
27
+ groups.set(key, []);
28
+ groups.get(key).push({
29
+ label: page.title,
30
+ href: `${hrefPrefix}/${page.slug}`,
31
+ });
32
+ }
33
+ // Wrap each non-root directory as a group under its parent —
34
+ // deepest-first so child groups exist when parents wrap them.
35
+ const sortedKeys = [...groups.keys()].sort((a, b) => b.split("/").length - a.split("/").length);
36
+ for (const key of sortedKeys) {
37
+ if (key === "")
38
+ continue;
39
+ const parent = dirname(key) === "." ? "" : dirname(key);
40
+ if (!groups.has(parent))
41
+ groups.set(parent, []);
42
+ groups.get(parent).push({
43
+ label: prettifyName(basename(key)),
44
+ children: groups.get(key),
45
+ });
46
+ }
47
+ return groups.get("");
48
+ }
49
+ //# sourceMappingURL=util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/loaders/util.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAG9C,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,IAAI;SACR,KAAK,CAAC,MAAM,CAAC;SACb,MAAM,CAAC,OAAO,CAAC;SACf,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;SAClD,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,2BAA2B,CACzC,KAAwC,EACxC,UAAkB;IAElB,MAAM,MAAM,GAAG,IAAI,GAAG,EAAqB,CAAC;IAC5C,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAEnB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,MAAM,GAAG,GAAG,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;QACnC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAC1C,MAAM,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,IAAI,CAAC;YACpB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,IAAI,EAAE,GAAG,UAAU,IAAI,IAAI,CAAC,IAAI,EAAE;SACnC,CAAC,CAAC;IACL,CAAC;IAED,6DAA6D;IAC7D,8DAA8D;IAC9D,MAAM,UAAU,GAAG,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CACxC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CACpD,CAAC;IACF,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;QAC7B,IAAI,GAAG,KAAK,EAAE;YAAE,SAAS;QACzB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAChD,MAAM,CAAC,GAAG,CAAC,MAAM,CAAE,CAAC,IAAI,CAAC;YACvB,KAAK,EAAE,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YAClC,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,CAAE;SAC3B,CAAC,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC,GAAG,CAAC,EAAE,CAAE,CAAC;AACzB,CAAC"}
@@ -0,0 +1,178 @@
1
+ /**
2
+ * AsciiDoc → TreeNode pipeline.
3
+ *
4
+ * Three composable stages, each independently testable:
5
+ *
6
+ * 1. @dogsbay/adoc2md-modular — AsciiDoc → Markdown-with-Minja-directives.
7
+ * Pure syntactic translation. The engine emits `{{ name }}` for
8
+ * attribute refs, `{% if name %}` for conditionals, `{% include %}`
9
+ * for include::, `{% set name = "value" %}` for in-doc :name: entries.
10
+ * 2. @dogsbay/minja — resolve directives against the
11
+ * attribute context. undefinedBehavior: "preserve" lets unresolved
12
+ * attrs / conditionals survive into the markdown so a downstream
13
+ * pass can finish the job.
14
+ * 3. markdown-it + walker — Markdown → Token[] → TreeNode[].
15
+ * Each format-X package owns its own walker per the convention
16
+ * (see plans/format-asciidoc-import.md Phase 6).
17
+ *
18
+ * Notably we do NOT register markdown-it-attrs here. The engine emits
19
+ * `{id="…"}` after headings (from AsciiDoc `[id="…"]` block anchors)
20
+ * and md-attrs would consume those — but it would also consume any
21
+ * trailing `{% endif %}` / `{{ var }}` at the end of a paragraph as a
22
+ * malformed attribute block, dropping the directive and breaking the
23
+ * build-time preprocessor pass. md2md sidestepped this by fully
24
+ * resolving Nunjucks at convert time (single-pass), so no directives
25
+ * survived to collide. We support a two-pass model (migrate-asciidoc
26
+ * preserves directives so the build-time preprocessor can substitute
27
+ * later from config), which means the directives ARE present when
28
+ * markdown-it tokenises. So we leave md-attrs out of this stage and
29
+ * let `extractHeadings` (cli.ts) parse `{id="…"}` / `{#anchor}`
30
+ * trailers out of heading text manually — a localised concern that
31
+ * doesn't risk eating directives in paragraph content. md-attrs runs
32
+ * at Astro render time, after the preprocessor has resolved
33
+ * directives, where the collision can't happen.
34
+ *
35
+ * See plans/format-asciidoc-import.md for the full architecture rationale.
36
+ */
37
+ import type MarkdownIt from "markdown-it";
38
+ import type { TreeNode } from "@dogsbay/types";
39
+ export interface ParseAsciidocOptions {
40
+ /**
41
+ * Filesystem path of the source `.adoc` file. Used as the base for
42
+ * `include::` directive resolution (both at AsciiDoc convert time
43
+ * via adoc2md-modular's `basePath` AND at Minja resolution time
44
+ * for the same directive after conversion). Optional; without it
45
+ * includes won't resolve, but unresolved `{% include %}` directives
46
+ * are preserved as text per Minja's `undefinedBehavior: "preserve"`.
47
+ */
48
+ filePath?: string;
49
+ /**
50
+ * Containment root for `{% include %}` resolution at the Minja stage
51
+ * (issue #027). When set, an include target that resolves (after
52
+ * symlinks) OUTSIDE this directory is refused and the parse fails —
53
+ * without it, a crafted include in an untrusted corpus splices any
54
+ * file the process can read into the page. Callers that walk a corpus
55
+ * pass the corpus root; unset = uncontained (backward compatible for
56
+ * direct single-file API use).
57
+ */
58
+ includeRoot?: string;
59
+ /**
60
+ * AsciiDoc attribute values. Surfaces in two places:
61
+ * - `adoc2md-modular`'s `attributes` option (mostly a no-op today —
62
+ * the engine emits `{{ name }}` for everything anyway).
63
+ * - Minja's render context — this is where the actual substitution
64
+ * happens. Any `{{ name }}` whose `name` is in this map gets
65
+ * resolved; the rest are preserved.
66
+ */
67
+ attributes?: Record<string, unknown>;
68
+ /**
69
+ * Caller-supplied env values merged into the markdown-it env object.
70
+ * Available to plugins; format-asciidoc itself doesn't read from env.
71
+ */
72
+ envOverrides?: Record<string, unknown>;
73
+ /**
74
+ * Definition-list output format passed to the engine. Dogsbay defaults
75
+ * to `'pandoc'` (a real `term` / `: desc` list that markdown-it-deflist
76
+ * renders as `<dl>`) rather than the engine's generic `'markdown'`
77
+ * default (a lossy bulleted-bold-term hack that can't nest). `<dl>` is
78
+ * what Dogsbay's renderer natively understands (deflist → `deflist`
79
+ * TreeNode → `<dl>`). See plans/dlist-markdown-indentation.md and
80
+ * adoc2md-modular DD-009.
81
+ */
82
+ dlistFormat?: "markdown" | "html" | "pandoc";
83
+ /**
84
+ * Admonition output format passed to the engine. Dogsbay defaults to
85
+ * `'docusaurus'` (`:::note`) — its native callout directive, which
86
+ * renders as an Alert component — rather than the engine's generic
87
+ * `'html'` default (`<dl><dt>📌 NOTE</dt>…`). adoc2md-modular DD-010.
88
+ * All three admonition shapes respect this option — block-form
89
+ * `[NOTE]\n====`, bare `[NOTE]` + paragraph, and line-form
90
+ * `NOTE: …` (which the engine expands to block form internally).
91
+ */
92
+ admonitionFormat?: "html" | "mkdocs" | "docusaurus" | "github";
93
+ /**
94
+ * Attribute names the caller KNOWS are false — the corpus's other distro
95
+ * flags in a single-distro migrate (AsciiBinder loaders surface the full
96
+ * set on LoaderResult.distros). Drives the engine's conditional STATE
97
+ * tracking only; never drops output text.
98
+ */
99
+ falseAttributes?: string[];
100
+ /** Clamp over-declared rowspans to fitting rows (issue #031). Opt-in. */
101
+ clampRowspans?: boolean;
102
+ /**
103
+ * Docroot-relative slug of the source page (e.g. `"architecture/admission-plug-ins"`).
104
+ * Threaded into adoc2md-modular so cross-doc xref hrefs resolve to
105
+ * root-relative paths instead of file-relative `../…` (which is off by one
106
+ * under Astro's default directory-style URLs). See
107
+ * plans/xref-absolute-hrefs.md.
108
+ */
109
+ sourcePath?: string;
110
+ /**
111
+ * Antora corpus mode — passed through to the engine. Enables
112
+ * module-coordinate xref translation (`xref:module:page[]`),
113
+ * resource-ID include resolution (`include::partial$…[]`), and
114
+ * output-relative `{% include %}` paths. Set by cli.ts when the
115
+ * antora corpus loader was picked. See plans/antora-support-gaps.md.
116
+ */
117
+ antora?: boolean;
118
+ /**
119
+ * Emit `[role]`/`[.role]` as markdown-it-attrs classes and keep
120
+ * role-styled inline marks as `<span class>` — only safe when the
121
+ * downstream parser runs markdown-it-attrs (the migrate → dogsbay-md
122
+ * flow). Fragments always had this on; pages did not, so a page and
123
+ * its own fragment copy disagreed on every role (#076 follow-up).
124
+ */
125
+ blockRoles?: boolean;
126
+ /** Antora playbook mode (#067): loaded components by name → {dir, prefix}. */
127
+ antoraComponents?: Record<string, {
128
+ dir: string;
129
+ prefix: string;
130
+ }>;
131
+ }
132
+ /**
133
+ * Parse an AsciiDoc source string into TreeNode[].
134
+ *
135
+ * Async because Minja's renderer is async (handles I/O for includes
136
+ * and supports timeout enforcement).
137
+ *
138
+ * Mirrors `parseToTree` from `format-mkdocs` in shape: takes a
139
+ * markdown-it instance + source, returns `{ tree, env }`.
140
+ */
141
+ export declare function parseAsciidocToTree(md: MarkdownIt, src: string, opts?: ParseAsciidocOptions): Promise<{
142
+ tree: TreeNode[];
143
+ env: Record<string, unknown>;
144
+ /**
145
+ * Raw engine output — `asciidocToMarkdown(src)` — before any
146
+ * Minja evaluation, before markdown-it tokenisation. The most
147
+ * faithful possible markdown rendition of the AsciiDoc source:
148
+ *
149
+ * - `{% set %}` directives intact (build-time preprocessor
150
+ * evaluates them when it has the attribute context).
151
+ * - `{% include %}` directives intact (not inlined here —
152
+ * build-time preprocessor handles inclusion against the
153
+ * resolved attribute context).
154
+ * - `{%- if %}` directives with whitespace-control intact
155
+ * (`-` stripping fires at *resolution* time, not parse
156
+ * time; preserving it keeps the migrated file rendering
157
+ * identically to the original `.adoc` once a real build
158
+ * resolves the conditionals).
159
+ * - `{{ var }}` references intact, regardless of whether
160
+ * `opts.attributes` supplied a value.
161
+ *
162
+ * Surfaced for `migrate-asciidoc`'s benefit: writing this to disk
163
+ * preserves the line structure of the original `.adoc` (each
164
+ * `ifdef`/`endif` block on its own line, blank lines between
165
+ * major blocks). Running Minja at migrate time would collapse
166
+ * lines (`-` trim fuses adjacent lines), consume `{% set %}`
167
+ * metadata, and inline `{% include %}` content — all undesirable
168
+ * for editable source.
169
+ *
170
+ * The `tree` field is the post-Minja-preserve, fully-parsed
171
+ * TreeNode shape — that's what the runtime path (format-astro
172
+ * emit, direct `from: asciidoc` builds) consumes. The two
173
+ * fields exist for two different downstream consumers; they're
174
+ * NOT derived from each other.
175
+ */
176
+ intermediateMd: string;
177
+ }>;
178
+ //# sourceMappingURL=parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,OAAO,KAAK,UAAU,MAAM,aAAa,CAAC;AAK1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG/C,MAAM,WAAW,oBAAoB;IACnC;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;;;;;;OAQG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAErC;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEvC;;;;;;;;OAQG;IACH,WAAW,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,QAAQ,CAAC;IAE7C;;;;;;;;OAQG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,YAAY,GAAG,QAAQ,CAAC;IAE/D;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,yEAAyE;IACzE,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,8EAA8E;IAC9E,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpE;AAED;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,UAAU,EACd,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,oBAAyB,GAC9B,OAAO,CAAC;IACT,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACH,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC,CAiGD"}
package/dist/parse.js ADDED
@@ -0,0 +1,150 @@
1
+ import { dirname } from "node:path";
2
+ import { asciidocToMarkdown } from "@dogsbay/adoc2md-modular";
3
+ import { render as minjaRender, FileSystemLoader } from "@dogsbay/minja";
4
+ import { maskMinjaInCode, unmaskMinjaInCode, stripInlineMinjaMarkers } from "@dogsbay/format-dogsbay-md";
5
+ import { tokensToTree } from "./walker.js";
6
+ /**
7
+ * Parse an AsciiDoc source string into TreeNode[].
8
+ *
9
+ * Async because Minja's renderer is async (handles I/O for includes
10
+ * and supports timeout enforcement).
11
+ *
12
+ * Mirrors `parseToTree` from `format-mkdocs` in shape: takes a
13
+ * markdown-it instance + source, returns `{ tree, env }`.
14
+ */
15
+ export async function parseAsciidocToTree(md, src, opts = {}) {
16
+ // Stage 1 — AsciiDoc → Markdown-with-Minja-directives.
17
+ // Dogsbay house defaults: 'pandoc' dlists + 'docusaurus' admonitions —
18
+ // the shapes Dogsbay's renderer natively understands (<dl> via
19
+ // markdown-it-deflist; :::note → Alert). The engine keeps its generic
20
+ // 'markdown'/'html' defaults; the consumer selects. See DD-009/DD-010.
21
+ const intermediateMd = asciidocToMarkdown(src, {
22
+ attributes: opts.attributes,
23
+ basePath: opts.filePath,
24
+ dlistFormat: opts.dlistFormat ?? "pandoc",
25
+ admonitionFormat: opts.admonitionFormat ?? "docusaurus",
26
+ sourcePath: opts.sourcePath,
27
+ antora: opts.antora ?? false,
28
+ ...(opts.antoraComponents ? { antoraComponents: opts.antoraComponents } : {}),
29
+ blockRoles: opts.blockRoles ?? false,
30
+ falseAttributes: opts.falseAttributes ?? [],
31
+ clampRowspans: opts.clampRowspans ?? false,
32
+ // Tag-filtered includes splice file content at CONVERT time, below
33
+ // the Minja layer — the engine enforces the same containment there
34
+ // (#027 review finding F1).
35
+ includeRoot: opts.includeRoot,
36
+ });
37
+ // Stage 2 — resolve Minja directives. Preserve mode means unresolved
38
+ // refs survive as literal `{{ name }}` / `{% if … %}` text in the
39
+ // output. That text passes through markdown-it as part of the
40
+ // surrounding paragraph; the consumer can re-render with a richer
41
+ // context later if needed.
42
+ //
43
+ // basePath is the file's DIRECTORY (not the file itself) so
44
+ // Minja's FileSystemLoader can join relative `{% include %}`
45
+ // targets correctly. Earlier passes used `opts.filePath` directly,
46
+ // which produced ENOTDIR errors trying to treat the .adoc file as
47
+ // a directory.
48
+ //
49
+ // We wrap the render call in try/catch so that a Minja parse
50
+ // error (e.g. hyphenated identifiers in `{% if product-version %}`,
51
+ // which Minja's expression grammar doesn't accept) doesn't abort
52
+ // the entire import. Fall back to the engine output unchanged —
53
+ // every surviving directive lives on as literal text in the
54
+ // markdown, matching `preserve` mode's intent for whole-block
55
+ // failures the same way it handles single-variable failures. We
56
+ // log a warning so the user knows why some directives didn't
57
+ // resolve.
58
+ const basePath = opts.filePath ? dirname(opts.filePath) : "";
59
+ let resolvedMd;
60
+ try {
61
+ // Code regions are content, not directives: mask `{{`/`{%` openers
62
+ // inside fences and inline code before the string evaluator runs
63
+ // (the same shared guard site-build uses), restore verbatim after.
64
+ // Without this, a code example like Helm's `{{ .Values.name }}`
65
+ // whose name collides with a defined doc attribute was silently
66
+ // substituted at import.
67
+ const rendered = await minjaRender(maskMinjaInCode(intermediateMd), {
68
+ context: opts.attributes ?? {},
69
+ undefinedBehavior: "preserve",
70
+ hyphenToUnderscore: true,
71
+ basePath,
72
+ loader: new FileSystemLoader(process.cwd(), { root: opts.includeRoot }),
73
+ });
74
+ // The inline `{minja}` markers (engine-planted attr refs, #030) have
75
+ // done their job — strip them, or this parser (which deliberately
76
+ // excludes markdown-it-attrs) leaks them as literal text nodes.
77
+ resolvedMd = stripInlineMinjaMarkers(unmaskMinjaInCode(rendered));
78
+ }
79
+ catch (err) {
80
+ // A containment refusal (#027) is a security event, not a parse
81
+ // hiccup — rethrow so the caller's per-page handling surfaces it
82
+ // (cli.ts logs the page and counts the failure) instead of quietly
83
+ // preserving directives.
84
+ if (err instanceof Error && err.name === "IncludeContainmentError") {
85
+ throw err;
86
+ }
87
+ const msg = err instanceof Error ? err.message : String(err);
88
+ const where = opts.filePath ? ` in ${opts.filePath}` : "";
89
+ console.warn(`asciidoc parse: Minja render failed${where}, preserving raw directives (${msg})`);
90
+ resolvedMd = stripInlineMinjaMarkers(intermediateMd);
91
+ }
92
+ // Stage 3 — markdown-it parse + walker.
93
+ const env = { ...(opts.envOverrides ?? {}) };
94
+ const tokens = md.parse(resolvedMd, env);
95
+ const tree = flattenProseWrappers(tokensToTree(tokens));
96
+ // `intermediateMd` is the RAW engine output — pre-Minja,
97
+ // pre-markdown-it. The faithful AsciiDoc → markdown
98
+ // translation. migrate-asciidoc writes this to disk so all
99
+ // `{% set %}` / `{% include %}` / `{%- if %}` directives
100
+ // survive verbatim for the build-time preprocessor to handle.
101
+ // (Returning `resolvedMd` here would consume `{% set %}` lines,
102
+ // inline `{% include %}` content, and fuse lines via `{%-`
103
+ // whitespace control — all wrong for editable migrated source.)
104
+ // The `tree` is parsed from the Minja-resolved form (`resolvedMd`)
105
+ // for accurate runtime rendering when used directly via
106
+ // `from: asciidoc` — a separate consumer with separate needs.
107
+ return { tree, env, intermediateMd };
108
+ }
109
+ /**
110
+ * Normalise the walker's "wrapped" shape into the "flat" shape that
111
+ * every other TreeNode consumer expects.
112
+ *
113
+ * The format-mkdocs walker (which we inherit) wraps inline content
114
+ * of heading / paragraph / list-item / dt / dd nodes in a single
115
+ * `{type:"prose", inline: [...]}` child instead of attaching
116
+ * `inline` directly to the parent. format-dogsbay-md's serializer
117
+ * and most downstream consumers read `node.inline`; lifting the
118
+ * inline up here means every consumer sees a single canonical shape
119
+ * and we don't have to thread cross-shape tolerance through every
120
+ * call site.
121
+ *
122
+ * Recursive — handles nested blocks (e.g. list_item containing a
123
+ * paragraph).
124
+ */
125
+ function flattenProseWrappers(nodes) {
126
+ const FLATTENABLE = new Set([
127
+ "heading",
128
+ "paragraph",
129
+ "list_item",
130
+ "dt",
131
+ "dd",
132
+ ]);
133
+ return nodes.map((node) => {
134
+ let out = node;
135
+ if (FLATTENABLE.has(node.type) &&
136
+ Array.isArray(node.children) &&
137
+ node.children.length === 1 &&
138
+ node.children[0].type === "prose" &&
139
+ Array.isArray(node.children[0].inline)) {
140
+ const prose = node.children[0];
141
+ out = { ...node, inline: prose.inline };
142
+ delete out.children;
143
+ }
144
+ else if (Array.isArray(node.children)) {
145
+ out = { ...node, children: flattenProseWrappers(node.children) };
146
+ }
147
+ return out;
148
+ });
149
+ }
150
+ //# sourceMappingURL=parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.js","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"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"}
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,8 @@
1
+ import type Token from "markdown-it/lib/token.mjs";
2
+ import type { TreeNode } from "./tree.js";
3
+ /**
4
+ * Convert a markdown-it token stream into a TreeNode[].
5
+ * Handles open/close token pairs by recursively collecting children.
6
+ */
7
+ export declare function tokensToTree(tokens: Token[]): TreeNode[];
8
+ //# sourceMappingURL=walker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"walker.d.ts","sourceRoot":"","sources":["../src/walker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,2BAA2B,CAAC;AACnD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAG1C;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,QAAQ,EAAE,CAkCxD"}