@dogsbay/format-dogsbay-md 0.2.0-beta.5 → 0.2.0-beta.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/attributes.d.ts +16 -1
- package/dist/attributes.d.ts.map +1 -1
- package/dist/attributes.js +14 -2
- package/dist/attributes.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +44 -3
- package/dist/cli.js.map +1 -1
- package/dist/directives.js +23 -1
- package/dist/directives.js.map +1 -1
- package/dist/escape.d.ts +29 -0
- package/dist/escape.d.ts.map +1 -1
- package/dist/escape.js +66 -1
- package/dist/escape.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/inline.d.ts +17 -0
- package/dist/inline.d.ts.map +1 -1
- package/dist/inline.js +123 -6
- package/dist/inline.js.map +1 -1
- package/dist/nav-file.d.ts +24 -3
- package/dist/nav-file.d.ts.map +1 -1
- package/dist/nav-file.js +31 -11
- package/dist/nav-file.js.map +1 -1
- package/dist/nav.d.ts +7 -0
- package/dist/nav.d.ts.map +1 -1
- package/dist/nav.js +14 -2
- package/dist/nav.js.map +1 -1
- package/dist/parse.d.ts.map +1 -1
- package/dist/parse.js +328 -4
- package/dist/parse.js.map +1 -1
- package/dist/plugin-containers.d.ts +1 -1
- package/dist/plugin-containers.d.ts.map +1 -1
- package/dist/plugin-containers.js +13 -0
- package/dist/plugin-containers.js.map +1 -1
- package/dist/renderers/diagrams.d.ts +25 -0
- package/dist/renderers/diagrams.d.ts.map +1 -0
- package/dist/renderers/diagrams.js +71 -0
- package/dist/renderers/diagrams.js.map +1 -0
- package/dist/renderers/mermaid.d.ts +24 -0
- package/dist/renderers/mermaid.d.ts.map +1 -0
- package/dist/renderers/mermaid.js +135 -0
- package/dist/renderers/mermaid.js.map +1 -0
- package/dist/serialize.js +80 -6
- package/dist/serialize.js.map +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +18 -1
- package/dist/types.js.map +1 -1
- package/package.json +5 -2
package/dist/attributes.d.ts
CHANGED
|
@@ -8,12 +8,27 @@
|
|
|
8
8
|
export interface RenderedAttrs {
|
|
9
9
|
id?: string;
|
|
10
10
|
classes?: string[];
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Props captured from `TreeNode.props`. Primitive values (string,
|
|
13
|
+
* number, boolean) round-trip through inline `{key="value"}` form.
|
|
14
|
+
* Arrays / plain objects are preserved here too — `renderAttrs`
|
|
15
|
+
* skips them (inline form can't represent them) but the
|
|
16
|
+
* directive renderer's `shouldUseYaml` branch picks them up and
|
|
17
|
+
* routes the directive to YAML body form, where complex values
|
|
18
|
+
* survive intact.
|
|
19
|
+
*/
|
|
20
|
+
props?: Record<string, unknown>;
|
|
12
21
|
}
|
|
13
22
|
/**
|
|
14
23
|
* Render an attribute block.
|
|
15
24
|
* Returns `{ #id .a .b key="value" }` or "" if no attrs.
|
|
16
25
|
* Leading space included so callers can append directly to content.
|
|
26
|
+
*
|
|
27
|
+
* Inline form can only carry primitive values. Arrays / objects in
|
|
28
|
+
* `props` are silently skipped here — the caller is expected to have
|
|
29
|
+
* already routed those to YAML body form (see `shouldUseYaml` in
|
|
30
|
+
* directives.ts). If a non-primitive does reach this point, dropping
|
|
31
|
+
* it is the safest fallback (there's no inline syntax for it).
|
|
17
32
|
*/
|
|
18
33
|
export declare function renderAttrs(attrs: RenderedAttrs): string;
|
|
19
34
|
/**
|
package/dist/attributes.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attributes.d.ts","sourceRoot":"","sources":["../src/attributes.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,aAAa;IAC5B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"attributes.d.ts","sourceRoot":"","sources":["../src/attributes.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,aAAa;IAC5B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CA6BxD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC1C,WAAW,GAAE,MAAM,EAAO,GACzB,aAAa,CAiCf"}
|
package/dist/attributes.js
CHANGED
|
@@ -9,6 +9,12 @@
|
|
|
9
9
|
* Render an attribute block.
|
|
10
10
|
* Returns `{ #id .a .b key="value" }` or "" if no attrs.
|
|
11
11
|
* Leading space included so callers can append directly to content.
|
|
12
|
+
*
|
|
13
|
+
* Inline form can only carry primitive values. Arrays / objects in
|
|
14
|
+
* `props` are silently skipped here — the caller is expected to have
|
|
15
|
+
* already routed those to YAML body form (see `shouldUseYaml` in
|
|
16
|
+
* directives.ts). If a non-primitive does reach this point, dropping
|
|
17
|
+
* it is the safest fallback (there's no inline syntax for it).
|
|
12
18
|
*/
|
|
13
19
|
export function renderAttrs(attrs) {
|
|
14
20
|
const parts = [];
|
|
@@ -29,6 +35,10 @@ export function renderAttrs(attrs) {
|
|
|
29
35
|
parts.push(key);
|
|
30
36
|
continue;
|
|
31
37
|
}
|
|
38
|
+
// Skip arrays / plain objects — inline form can't represent
|
|
39
|
+
// them. They survive via the YAML body branch upstream.
|
|
40
|
+
if (typeof value === "object")
|
|
41
|
+
continue;
|
|
32
42
|
parts.push(`${key}=${escapeAttrValue(String(value))}`);
|
|
33
43
|
}
|
|
34
44
|
}
|
|
@@ -71,8 +81,10 @@ export function splitProps(props, excludeKeys = []) {
|
|
|
71
81
|
}
|
|
72
82
|
if (value === null || value === undefined)
|
|
73
83
|
continue;
|
|
74
|
-
|
|
75
|
-
|
|
84
|
+
// Pass arrays / objects through. Inline-form rendering will skip
|
|
85
|
+
// them (`renderAttrs`); YAML-body rendering preserves them
|
|
86
|
+
// (`attrsToYaml`). The `shouldUseYaml` heuristic in directives.ts
|
|
87
|
+
// detects their presence and routes the directive accordingly.
|
|
76
88
|
cleanProps[key] = value;
|
|
77
89
|
}
|
|
78
90
|
if (Object.keys(cleanProps).length > 0) {
|
package/dist/attributes.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attributes.js","sourceRoot":"","sources":["../src/attributes.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"attributes.js","sourceRoot":"","sources":["../src/attributes.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,YAAY,EAAgC,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,YAAY,EAAgC,MAAM,gBAAgB,CAAC;AAoKjF,eAAO,MAAM,MAAM,EAAE,YAcpB,CAAC"}
|
package/dist/cli.js
CHANGED
|
@@ -9,6 +9,7 @@ import { join, relative, basename } from "node:path";
|
|
|
9
9
|
import { parseMeta } from "@dogsbay/types";
|
|
10
10
|
import { dogsbayMdToTree } from "./parse.js";
|
|
11
11
|
import { buildNavFromDirectory } from "./nav.js";
|
|
12
|
+
import { renderDiagrams } from "./renderers/diagrams.js";
|
|
12
13
|
/**
|
|
13
14
|
* Detect whether a directory contains a Dogsbay MD source tree.
|
|
14
15
|
*
|
|
@@ -61,22 +62,51 @@ async function importDogsbayMd(source, opts) {
|
|
|
61
62
|
const taxonomyNames = Array.isArray(opts.taxonomyNames)
|
|
62
63
|
? opts.taxonomyNames
|
|
63
64
|
: undefined;
|
|
65
|
+
// Pre-parse pass — opt-out via `diagrams: false`. When enabled
|
|
66
|
+
// (default), `code` fences with `lang: mermaid` get transformed
|
|
67
|
+
// to `diagram` nodes with pre-rendered SVG. Fail-open: if mmdc
|
|
68
|
+
// (mermaid-cli) isn't installed the code block is left as-is and
|
|
69
|
+
// a one-line warning is printed. See plans/mermaid-dogsbay-md.md.
|
|
70
|
+
const diagramsOn = opts.diagrams !== false;
|
|
64
71
|
for (const { filePath, slug } of files) {
|
|
65
72
|
const content = readFileSync(filePath, "utf-8");
|
|
66
73
|
const { tree, frontmatter } = dogsbayMdToTree(content);
|
|
74
|
+
if (diagramsOn) {
|
|
75
|
+
await renderDiagrams(tree);
|
|
76
|
+
}
|
|
67
77
|
const title = String(frontmatter.title
|
|
68
78
|
?? prettifyName(basename(slug)));
|
|
69
|
-
// Extract heading metadata for TOC
|
|
79
|
+
// Extract heading metadata for TOC AND backfill `node.props.slug`
|
|
80
|
+
// so format-astro's headingToAstro emits a matching `id` on the
|
|
81
|
+
// rendered `<h2>` / `<h3>`. Without this, the TOC's
|
|
82
|
+
// `<a href="#quick-start">` had nothing to scroll to — the
|
|
83
|
+
// heading was emitted without an id because the tree didn't
|
|
84
|
+
// carry the slug. Mirrors format-mkdocs/src/loader.ts:312.
|
|
85
|
+
//
|
|
86
|
+
// Duplicate slugs get `-1`, `-2` suffixes so two `## Foo`
|
|
87
|
+
// headings on the same page produce distinct anchors. Same
|
|
88
|
+
// collision-handling shape as MkDocs.
|
|
70
89
|
const headings = [];
|
|
90
|
+
const seen = new Map();
|
|
71
91
|
for (const node of tree) {
|
|
72
92
|
if (node.type === "heading" && node.inline) {
|
|
73
93
|
const text = node.inline
|
|
74
94
|
.map((n) => (n.type === "text" ? n.text : ""))
|
|
75
95
|
.join("")
|
|
76
96
|
.trim();
|
|
97
|
+
let slug = node.props?.slug;
|
|
98
|
+
if (!slug) {
|
|
99
|
+
const baseSlug = slugify(text);
|
|
100
|
+
const count = seen.get(baseSlug) ?? 0;
|
|
101
|
+
slug = count === 0 ? baseSlug : `${baseSlug}-${count}`;
|
|
102
|
+
seen.set(baseSlug, count + 1);
|
|
103
|
+
if (!node.props)
|
|
104
|
+
node.props = { level: 1 };
|
|
105
|
+
node.props.slug = slug;
|
|
106
|
+
}
|
|
77
107
|
headings.push({
|
|
78
108
|
depth: node.props?.level ?? 1,
|
|
79
|
-
slug
|
|
109
|
+
slug,
|
|
80
110
|
text,
|
|
81
111
|
});
|
|
82
112
|
}
|
|
@@ -95,7 +125,18 @@ async function importDogsbayMd(source, opts) {
|
|
|
95
125
|
}
|
|
96
126
|
const navFilePath = opts.nav || undefined;
|
|
97
127
|
const hrefPrefix = typeof opts.hrefPrefix === "string" ? opts.hrefPrefix : undefined;
|
|
98
|
-
|
|
128
|
+
// `--strict` at the CLI layer maps to "fail" here. `loadNavFile`
|
|
129
|
+
// already throws on the first miss in fail mode; the build
|
|
130
|
+
// surfaces the throw as a fatal error and exits non-zero.
|
|
131
|
+
// See plans/site-build-strict.md.
|
|
132
|
+
const missingNavTargets = opts.missingNavTargets === "fail"
|
|
133
|
+
? "fail"
|
|
134
|
+
: undefined;
|
|
135
|
+
const nav = buildNavFromDirectory(source, {
|
|
136
|
+
navFilePath,
|
|
137
|
+
hrefPrefix,
|
|
138
|
+
missingFile: missingNavTargets,
|
|
139
|
+
});
|
|
99
140
|
return { pages, nav };
|
|
100
141
|
}
|
|
101
142
|
function prettifyName(slug) {
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC1E,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/directives.js
CHANGED
|
@@ -38,6 +38,17 @@ export function renderBlockDirective(input, ctx) {
|
|
|
38
38
|
}
|
|
39
39
|
/**
|
|
40
40
|
* Decide if a set of attributes warrants YAML body form.
|
|
41
|
+
*
|
|
42
|
+
* Forced when:
|
|
43
|
+
* - prop count exceeds the threshold (default 3),
|
|
44
|
+
* - any string value contains a newline (inline form normalises
|
|
45
|
+
* newlines to spaces, which loses author intent),
|
|
46
|
+
* - any value is a boolean (the inline shorthand `{flag}` parses
|
|
47
|
+
* as the empty string, not `true`, so booleans don't round-
|
|
48
|
+
* trip — caught during Phase 4 autodoc-directive tests), OR
|
|
49
|
+
* - any value is an array / object / nested structure (inline
|
|
50
|
+
* form has no syntax for those — without YAML they get
|
|
51
|
+
* dropped by `renderAttrs`).
|
|
41
52
|
*/
|
|
42
53
|
function shouldUseYaml(attrs, ctx) {
|
|
43
54
|
const propCount = (attrs.id ? 1 : 0) +
|
|
@@ -45,11 +56,22 @@ function shouldUseYaml(attrs, ctx) {
|
|
|
45
56
|
Object.keys(attrs.props ?? {}).length;
|
|
46
57
|
if (propCount > ctx.yamlThreshold)
|
|
47
58
|
return true;
|
|
48
|
-
// Multi-line or complex string values force YAML
|
|
49
59
|
if (attrs.props) {
|
|
50
60
|
for (const value of Object.values(attrs.props)) {
|
|
61
|
+
// Multi-line strings would lose their structure in inline form.
|
|
51
62
|
if (typeof value === "string" && value.includes("\n"))
|
|
52
63
|
return true;
|
|
64
|
+
// Booleans don't round-trip through the inline-attrs form —
|
|
65
|
+
// `{flag}` parses as "" (empty string). YAML preserves the
|
|
66
|
+
// type. Same logic applies to numbers in principle, but
|
|
67
|
+
// numbers do survive inline (parsed back as strings; consumers
|
|
68
|
+
// tolerate that). Booleans break consumers that check `=== true`.
|
|
69
|
+
if (typeof value === "boolean")
|
|
70
|
+
return true;
|
|
71
|
+
// Arrays / nested objects can't be expressed in inline form at
|
|
72
|
+
// all; only YAML body preserves them.
|
|
73
|
+
if (value !== null && typeof value === "object")
|
|
74
|
+
return true;
|
|
53
75
|
}
|
|
54
76
|
}
|
|
55
77
|
return false;
|
package/dist/directives.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"directives.js","sourceRoot":"","sources":["../src/directives.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,WAAW,EAAE,UAAU,EAAsB,MAAM,iBAAiB,CAAC;AAC9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,UAAU,EAAkB,MAAM,WAAW,CAAC;AAgBvD;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAClC,KAA2B,EAC3B,GAAoB;IAEpB,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,GAAG,EAAE,EAAE,IAAI,GAAG,EAAE,EAAE,GAAG,KAAK,CAAC;IAC5D,MAAM,YAAY,GAAG,KAAK,CAAC,YAAY,IAAI,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAExE,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,YAAY,IAAI,aAAa,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAE1D,MAAM,KAAK,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAElE,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,IAAI,GAAG,OAAO,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,IAAI,EAAE,CAAC;QACzE,OAAO,GAAG,MAAM,KAAK,IAAI,KAAK,KAAK,EAAE,CAAC;IACxC,CAAC;IAED,uEAAuE;IACvE,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAClC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IAE1D,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,GAAG,KAAK,GAAG,IAAI,KAAK,MAAM,KAAK,KAAK,EAAE,CAAC;IAChD,CAAC;IACD,OAAO,GAAG,KAAK,GAAG,IAAI,KAAK,MAAM,KAAK,IAAI,KAAK,KAAK,EAAE,CAAC;AACzD,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"directives.js","sourceRoot":"","sources":["../src/directives.ts"],"names":[],"mappings":"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"}
|
package/dist/escape.d.ts
CHANGED
|
@@ -4,10 +4,39 @@
|
|
|
4
4
|
* Two rules drive everything here:
|
|
5
5
|
* 1. Content must round-trip through a markdown-it parser cleanly.
|
|
6
6
|
* 2. When ambiguous, prefer escaping — a reader never sees broken syntax.
|
|
7
|
+
*
|
|
8
|
+
* Dogsbay-MD is a SUPERSET of CommonMark: it includes Minja directives
|
|
9
|
+
* (`{{ var }}`, `{% if %}`, `{% include %}`, `{# comment #}`). Those
|
|
10
|
+
* directives have their own syntax and must NOT be escape-mangled —
|
|
11
|
+
* a path like `./_attributes/foo.md` inside `{% include "..." %}`
|
|
12
|
+
* has to survive as-is so the build-time preprocessor can resolve it.
|
|
13
|
+
* `escapePreservingMinja` is the seam: it splits text on directive
|
|
14
|
+
* boundaries, escapes only the prose segments, and leaves directives
|
|
15
|
+
* verbatim. Every text-escape entry point in this package is built
|
|
16
|
+
* on top of it.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Apply a per-segment escape function to text, treating Minja
|
|
20
|
+
* directives as opaque atoms that pass through verbatim.
|
|
21
|
+
*
|
|
22
|
+
* The shape:
|
|
23
|
+
* "Hello {{ name }} — see {% include \"path.md\" %} for details."
|
|
24
|
+
* ↓ split on directives
|
|
25
|
+
* ["Hello ", "{{ name }}", " — see ", "{% include \"path.md\" %}", " for details."]
|
|
26
|
+
* ↓ escape only non-directive segments
|
|
27
|
+
* [esc("Hello "), "{{ name }}", esc(" — see "), "{% include \"path.md\" %}", esc(" for details.")]
|
|
28
|
+
* ↓ join
|
|
29
|
+
* "Hello {{ name }} — see {% include \"path.md\" %} for details."
|
|
30
|
+
*
|
|
31
|
+
* Text with no directives short-circuits to a single escape call,
|
|
32
|
+
* so the fast path is unchanged for the common case.
|
|
7
33
|
*/
|
|
34
|
+
export declare function escapePreservingMinja(text: string, escape: (segment: string) => string): string;
|
|
8
35
|
/**
|
|
9
36
|
* Escape markdown-active characters in text content.
|
|
10
37
|
* Only escapes what would change parsing; leaves everything else alone.
|
|
38
|
+
* Minja directives (`{{ … }}`, `{% … %}`, `{# … #}`) pass through
|
|
39
|
+
* verbatim — see escapePreservingMinja.
|
|
11
40
|
*/
|
|
12
41
|
export declare function escapeText(text: string): string;
|
|
13
42
|
/**
|
package/dist/escape.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"escape.d.ts","sourceRoot":"","sources":["../src/escape.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"escape.d.ts","sourceRoot":"","sources":["../src/escape.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAiBH;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAClC,MAAM,CAkBR;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;AAQD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMvD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,aAAa,GAAE,GAAG,GAAG,GAAS,GAC7B,MAAM,CAOR;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAO1D;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAM9D;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAKnE"}
|
package/dist/escape.js
CHANGED
|
@@ -4,13 +4,78 @@
|
|
|
4
4
|
* Two rules drive everything here:
|
|
5
5
|
* 1. Content must round-trip through a markdown-it parser cleanly.
|
|
6
6
|
* 2. When ambiguous, prefer escaping — a reader never sees broken syntax.
|
|
7
|
+
*
|
|
8
|
+
* Dogsbay-MD is a SUPERSET of CommonMark: it includes Minja directives
|
|
9
|
+
* (`{{ var }}`, `{% if %}`, `{% include %}`, `{# comment #}`). Those
|
|
10
|
+
* directives have their own syntax and must NOT be escape-mangled —
|
|
11
|
+
* a path like `./_attributes/foo.md` inside `{% include "..." %}`
|
|
12
|
+
* has to survive as-is so the build-time preprocessor can resolve it.
|
|
13
|
+
* `escapePreservingMinja` is the seam: it splits text on directive
|
|
14
|
+
* boundaries, escapes only the prose segments, and leaves directives
|
|
15
|
+
* verbatim. Every text-escape entry point in this package is built
|
|
16
|
+
* on top of it.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Matches a complete Minja directive:
|
|
20
|
+
* - `{{ … }}` variable / filter pipeline
|
|
21
|
+
* - `{% … %}` block (if / endif / for / set / include / raw / …)
|
|
22
|
+
* - `{# … #}` comment
|
|
23
|
+
*
|
|
24
|
+
* Lazy match (`*?`) terminates at the FIRST closing delimiter, which
|
|
25
|
+
* matches Minja's grammar — directives can't legitimately nest by
|
|
26
|
+
* the same delimiter. `[\s\S]` so multi-line directives are handled
|
|
27
|
+
* (e.g. `{% if x %}\n…\n{% endif %}` is two separate matches; the
|
|
28
|
+
* inner content isn't a single directive).
|
|
29
|
+
*/
|
|
30
|
+
const MINJA_DIRECTIVE_RE = /\{\{[\s\S]*?\}\}|\{%[\s\S]*?%\}|\{#[\s\S]*?#\}/g;
|
|
31
|
+
/**
|
|
32
|
+
* Apply a per-segment escape function to text, treating Minja
|
|
33
|
+
* directives as opaque atoms that pass through verbatim.
|
|
34
|
+
*
|
|
35
|
+
* The shape:
|
|
36
|
+
* "Hello {{ name }} — see {% include \"path.md\" %} for details."
|
|
37
|
+
* ↓ split on directives
|
|
38
|
+
* ["Hello ", "{{ name }}", " — see ", "{% include \"path.md\" %}", " for details."]
|
|
39
|
+
* ↓ escape only non-directive segments
|
|
40
|
+
* [esc("Hello "), "{{ name }}", esc(" — see "), "{% include \"path.md\" %}", esc(" for details.")]
|
|
41
|
+
* ↓ join
|
|
42
|
+
* "Hello {{ name }} — see {% include \"path.md\" %} for details."
|
|
43
|
+
*
|
|
44
|
+
* Text with no directives short-circuits to a single escape call,
|
|
45
|
+
* so the fast path is unchanged for the common case.
|
|
7
46
|
*/
|
|
47
|
+
export function escapePreservingMinja(text, escape) {
|
|
48
|
+
// Fast path: no directive opener anywhere → straight escape.
|
|
49
|
+
if (!text.includes("{{") && !text.includes("{%") && !text.includes("{#")) {
|
|
50
|
+
return escape(text);
|
|
51
|
+
}
|
|
52
|
+
const parts = [];
|
|
53
|
+
let lastEnd = 0;
|
|
54
|
+
// matchAll iterates left-to-right with global flag; safe to reset
|
|
55
|
+
// via fresh exec context per call (regex is stateless when used
|
|
56
|
+
// this way).
|
|
57
|
+
for (const match of text.matchAll(MINJA_DIRECTIVE_RE)) {
|
|
58
|
+
const start = match.index ?? 0;
|
|
59
|
+
if (start > lastEnd)
|
|
60
|
+
parts.push(escape(text.slice(lastEnd, start)));
|
|
61
|
+
parts.push(match[0]);
|
|
62
|
+
lastEnd = start + match[0].length;
|
|
63
|
+
}
|
|
64
|
+
if (lastEnd < text.length)
|
|
65
|
+
parts.push(escape(text.slice(lastEnd)));
|
|
66
|
+
return parts.join("");
|
|
67
|
+
}
|
|
8
68
|
/**
|
|
9
69
|
* Escape markdown-active characters in text content.
|
|
10
70
|
* Only escapes what would change parsing; leaves everything else alone.
|
|
71
|
+
* Minja directives (`{{ … }}`, `{% … %}`, `{# … #}`) pass through
|
|
72
|
+
* verbatim — see escapePreservingMinja.
|
|
11
73
|
*/
|
|
12
74
|
export function escapeText(text) {
|
|
13
|
-
return text
|
|
75
|
+
return escapePreservingMinja(text, escapeTextSegment);
|
|
76
|
+
}
|
|
77
|
+
function escapeTextSegment(segment) {
|
|
78
|
+
return segment
|
|
14
79
|
.replace(/\\/g, "\\\\")
|
|
15
80
|
.replace(/([*_`~[\]()<>#|{}])/g, "\\$1");
|
|
16
81
|
}
|
package/dist/escape.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"escape.js","sourceRoot":"","sources":["../src/escape.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"escape.js","sourceRoot":"","sources":["../src/escape.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,5 +6,8 @@ export type { ParseResult, ParseOptions } from "./parse.js";
|
|
|
6
6
|
export { buildNavFromDirectory } from "./nav.js";
|
|
7
7
|
export type { BuildNavOptions } from "./nav.js";
|
|
8
8
|
export { loadNavFile } from "./nav-file.js";
|
|
9
|
-
export type { NavFile, NavFileItem, LoadNavFileOptions } from "./nav-file.js";
|
|
9
|
+
export type { NavFile, NavFileItem, LoadNavFileOptions, MissingNavTarget, } from "./nav-file.js";
|
|
10
|
+
export { renderDiagrams } from "./renderers/diagrams.js";
|
|
11
|
+
export type { RenderDiagramsOptions } from "./renderers/diagrams.js";
|
|
12
|
+
export { renderMermaidSvg, setMmdcOverride, findMmdc } from "./renderers/mermaid.js";
|
|
10
13
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAClE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC1D,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AACjD,YAAY,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAClE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC1D,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AACjD,YAAY,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EACV,OAAO,EACP,WAAW,EACX,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,YAAY,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -7,4 +7,6 @@ export { inlineToDogsbayMd } from "./inline.js";
|
|
|
7
7
|
export { dogsbayMdToTree, parseInline } from "./parse.js";
|
|
8
8
|
export { buildNavFromDirectory } from "./nav.js";
|
|
9
9
|
export { loadNavFile } from "./nav-file.js";
|
|
10
|
+
export { renderDiagrams } from "./renderers/diagrams.js";
|
|
11
|
+
export { renderMermaidSvg, setMmdcOverride, findMmdc } from "./renderers/mermaid.js";
|
|
10
12
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,EAAE;AACF,kDAAkD;AAClD,mCAAmC;AAEnC,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGlE,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAGjD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,EAAE;AACF,kDAAkD;AAClD,mCAAmC;AAEnC,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGlE,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAGjD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAQ5C,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEzD,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC"}
|
package/dist/inline.d.ts
CHANGED
|
@@ -4,6 +4,23 @@
|
|
|
4
4
|
import type { InlineNode } from "@dogsbay/types";
|
|
5
5
|
/**
|
|
6
6
|
* Serialize a flat array of inline nodes to markdown.
|
|
7
|
+
*
|
|
8
|
+
* Stack-based emit: maintains a stack of currently-open formatting
|
|
9
|
+
* marks and, between successive text nodes, opens/closes only the
|
|
10
|
+
* delta between current-stack and desired-formats. This produces:
|
|
11
|
+
*
|
|
12
|
+
* - `*foo bar*` (not `*foo** **bar*`) for sibling italic text runs,
|
|
13
|
+
* fixing the multi-line-emphasis case where the parser splits
|
|
14
|
+
* `*a\nb*` into three italic text nodes.
|
|
15
|
+
* - `*foo \`code\` bar*` (not `*foo*\`code\`*bar*`) for italic
|
|
16
|
+
* text bracketing inline code — non-text nodes don't disturb the
|
|
17
|
+
* open-format stack, so the wrapper stays open across them.
|
|
18
|
+
* - `**This is *not* news.**` (not `**This is *****not***** news.**`)
|
|
19
|
+
* for bold-around-italic — only italic opens/closes between the
|
|
20
|
+
* three text spans; the outer bold stays open the whole time.
|
|
21
|
+
*
|
|
22
|
+
* Round-trip stable on every case validated against the parser's
|
|
23
|
+
* output (see roundtrip.test.ts).
|
|
7
24
|
*/
|
|
8
25
|
export declare function inlineToDogsbayMd(nodes: InlineNode[] | undefined): string;
|
|
9
26
|
//# sourceMappingURL=inline.d.ts.map
|
package/dist/inline.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inline.d.ts","sourceRoot":"","sources":["../src/inline.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"inline.d.ts","sourceRoot":"","sources":["../src/inline.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAe,UAAU,EAAY,MAAM,gBAAgB,CAAC;AAiBxE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,SAAS,GAAG,MAAM,CA2CzE"}
|
package/dist/inline.js
CHANGED
|
@@ -1,11 +1,88 @@
|
|
|
1
1
|
import { escapeAttrValue } from "./attributes.js";
|
|
2
|
+
import { escapePreservingMinja } from "./escape.js";
|
|
3
|
+
// Canonical nesting order: outermost → innermost. Strike outer, italic
|
|
4
|
+
// middle, bold inner. Matches the historical renderText() ordering and
|
|
5
|
+
// CommonMark community convention (`***strong-em***` reads as em(strong)).
|
|
6
|
+
const FORMAT_ORDER = ["strikethrough", "italic", "bold"];
|
|
7
|
+
const FORMAT_MARK = {
|
|
8
|
+
strikethrough: "~~",
|
|
9
|
+
italic: "*",
|
|
10
|
+
bold: "**",
|
|
11
|
+
};
|
|
2
12
|
/**
|
|
3
13
|
* Serialize a flat array of inline nodes to markdown.
|
|
14
|
+
*
|
|
15
|
+
* Stack-based emit: maintains a stack of currently-open formatting
|
|
16
|
+
* marks and, between successive text nodes, opens/closes only the
|
|
17
|
+
* delta between current-stack and desired-formats. This produces:
|
|
18
|
+
*
|
|
19
|
+
* - `*foo bar*` (not `*foo** **bar*`) for sibling italic text runs,
|
|
20
|
+
* fixing the multi-line-emphasis case where the parser splits
|
|
21
|
+
* `*a\nb*` into three italic text nodes.
|
|
22
|
+
* - `*foo \`code\` bar*` (not `*foo*\`code\`*bar*`) for italic
|
|
23
|
+
* text bracketing inline code — non-text nodes don't disturb the
|
|
24
|
+
* open-format stack, so the wrapper stays open across them.
|
|
25
|
+
* - `**This is *not* news.**` (not `**This is *****not***** news.**`)
|
|
26
|
+
* for bold-around-italic — only italic opens/closes between the
|
|
27
|
+
* three text spans; the outer bold stays open the whole time.
|
|
28
|
+
*
|
|
29
|
+
* Round-trip stable on every case validated against the parser's
|
|
30
|
+
* output (see roundtrip.test.ts).
|
|
4
31
|
*/
|
|
5
32
|
export function inlineToDogsbayMd(nodes) {
|
|
6
33
|
if (!nodes || nodes.length === 0)
|
|
7
34
|
return "";
|
|
8
|
-
|
|
35
|
+
const out = [];
|
|
36
|
+
const stack = [];
|
|
37
|
+
for (const node of nodes) {
|
|
38
|
+
if (node.type === "text") {
|
|
39
|
+
const desired = desiredFormatSet(node);
|
|
40
|
+
// Keep the stack prefix while every entry is still wanted;
|
|
41
|
+
// pop the first unwanted format and everything above it. This
|
|
42
|
+
// avoids unnecessary close/reopen pairs — e.g. when we go from
|
|
43
|
+
// {bold} to {bold, italic}, we just open italic INSIDE the
|
|
44
|
+
// already-open bold rather than closing bold, opening italic,
|
|
45
|
+
// reopening bold (which produces the dreaded `*****`).
|
|
46
|
+
let keep = 0;
|
|
47
|
+
while (keep < stack.length && desired.has(stack[keep]))
|
|
48
|
+
keep++;
|
|
49
|
+
while (stack.length > keep) {
|
|
50
|
+
out.push(FORMAT_MARK[stack.pop()]);
|
|
51
|
+
}
|
|
52
|
+
// Add still-missing formats. Canonical order is used only when
|
|
53
|
+
// the stack is empty; otherwise additions append on top of
|
|
54
|
+
// whatever's already nested. Both nestings round-trip to the
|
|
55
|
+
// same tree.
|
|
56
|
+
for (const fmt of FORMAT_ORDER) {
|
|
57
|
+
if (desired.has(fmt) && !stack.includes(fmt)) {
|
|
58
|
+
out.push(FORMAT_MARK[fmt]);
|
|
59
|
+
stack.push(fmt);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
out.push(escapeInlineText(node.text));
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
// Non-text inline (code, link, image, …) doesn't carry format
|
|
66
|
+
// flags; leave the open-format stack alone so a wrapper that
|
|
67
|
+
// bridges across it (like `*foo \`code\` bar*`) survives.
|
|
68
|
+
out.push(renderInline(node));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Close anything still open at the end.
|
|
72
|
+
while (stack.length > 0) {
|
|
73
|
+
out.push(FORMAT_MARK[stack.pop()]);
|
|
74
|
+
}
|
|
75
|
+
return out.join("");
|
|
76
|
+
}
|
|
77
|
+
function desiredFormatSet(node) {
|
|
78
|
+
const s = new Set();
|
|
79
|
+
if (node.strikethrough)
|
|
80
|
+
s.add("strikethrough");
|
|
81
|
+
if (node.italic)
|
|
82
|
+
s.add("italic");
|
|
83
|
+
if (node.bold)
|
|
84
|
+
s.add("bold");
|
|
85
|
+
return s;
|
|
9
86
|
}
|
|
10
87
|
function renderInline(node) {
|
|
11
88
|
switch (node.type) {
|
|
@@ -37,9 +114,11 @@ function renderInline(node) {
|
|
|
37
114
|
}
|
|
38
115
|
}
|
|
39
116
|
function renderText(node) {
|
|
117
|
+
// Single-node fallback for callers that bypass inlineToDogsbayMd.
|
|
118
|
+
// The grouped path strips format flags before emitting body content;
|
|
119
|
+
// this branch handles the rare standalone case (e.g. nodeToDogsbayMd
|
|
120
|
+
// on a lone text node).
|
|
40
121
|
let text = escapeInlineText(node.text);
|
|
41
|
-
// Apply annotations from outside in: strikethrough, italic, bold
|
|
42
|
-
// Order produces canonical output: ~~*_**text**_*~~
|
|
43
122
|
if (node.bold)
|
|
44
123
|
text = `**${text}**`;
|
|
45
124
|
if (node.italic)
|
|
@@ -52,13 +131,46 @@ function renderLink(node) {
|
|
|
52
131
|
const label = inlineToDogsbayMd(node.children);
|
|
53
132
|
const href = escapeLinkDest(node.href);
|
|
54
133
|
const title = node.title ? ` ${escapeAttrValue(node.title)}` : "";
|
|
55
|
-
|
|
134
|
+
const attrs = renderInlineAttrs(node.attrs);
|
|
135
|
+
return `[${label}](${href}${title})${attrs}`;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Render an inline attribute block (`{.class #id key="val"}`) for
|
|
139
|
+
* InlineLink (and, in future phases, InlineImage / InlineSpan).
|
|
140
|
+
*
|
|
141
|
+
* Mirrors the block-level `renderAttrs` ordering: id, classes, then
|
|
142
|
+
* remaining keys. Returns "" when no attrs are present so the caller
|
|
143
|
+
* can append unconditionally.
|
|
144
|
+
*/
|
|
145
|
+
function renderInlineAttrs(attrs) {
|
|
146
|
+
if (!attrs)
|
|
147
|
+
return "";
|
|
148
|
+
const parts = [];
|
|
149
|
+
if (attrs.id)
|
|
150
|
+
parts.push(`#${attrs.id}`);
|
|
151
|
+
if (attrs.class) {
|
|
152
|
+
for (const cls of attrs.class.split(/\s+/)) {
|
|
153
|
+
if (cls)
|
|
154
|
+
parts.push(`.${cls}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
158
|
+
if (value === undefined)
|
|
159
|
+
continue;
|
|
160
|
+
if (key === "id" || key === "class")
|
|
161
|
+
continue;
|
|
162
|
+
parts.push(`${key}=${escapeAttrValue(String(value))}`);
|
|
163
|
+
}
|
|
164
|
+
if (parts.length === 0)
|
|
165
|
+
return "";
|
|
166
|
+
return `{${parts.join(" ")}}`;
|
|
56
167
|
}
|
|
57
168
|
function renderImage(node) {
|
|
58
169
|
const alt = node.alt ? escapeInlineText(node.alt) : "";
|
|
59
170
|
const src = escapeLinkDest(node.src);
|
|
60
171
|
const title = node.title ? ` ${escapeAttrValue(node.title)}` : "";
|
|
61
|
-
|
|
172
|
+
const attrs = renderInlineAttrs(node.attrs);
|
|
173
|
+
return `${attrs}`;
|
|
62
174
|
}
|
|
63
175
|
function renderInlineCode(text) {
|
|
64
176
|
// Choose a backtick run that doesn't appear in the content
|
|
@@ -98,9 +210,14 @@ function renderIcon(node) {
|
|
|
98
210
|
/**
|
|
99
211
|
* Escape markdown-active characters in inline text.
|
|
100
212
|
* Balance: aggressive enough to round-trip, not so aggressive that readable text
|
|
101
|
-
* becomes a thicket of backslashes.
|
|
213
|
+
* becomes a thicket of backslashes. Minja directives (`{{ … }}`, `{% … %}`,
|
|
214
|
+
* `{# … #}`) pass through verbatim — see escapePreservingMinja in escape.ts
|
|
215
|
+
* for the seam.
|
|
102
216
|
*/
|
|
103
217
|
function escapeInlineText(text) {
|
|
218
|
+
return escapePreservingMinja(text, escapeInlineTextSegment);
|
|
219
|
+
}
|
|
220
|
+
function escapeInlineTextSegment(text) {
|
|
104
221
|
// CommonMark rule: `_` is emphasis only at word boundaries (not intraword).
|
|
105
222
|
// So `snake_case` needs no escaping. Only escape `_` at word boundaries.
|
|
106
223
|
return text
|