@dogsbay/format-topicmap 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.
- package/README.md +32 -0
- package/dist/edit-source.d.ts +29 -0
- package/dist/edit-source.d.ts.map +1 -0
- package/dist/edit-source.js +196 -0
- package/dist/edit-source.js.map +1 -0
- package/dist/edit.d.ts +95 -0
- package/dist/edit.d.ts.map +1 -0
- package/dist/edit.js +295 -0
- package/dist/edit.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/nav-file.d.ts +23 -0
- package/dist/nav-file.d.ts.map +1 -0
- package/dist/nav-file.js +48 -0
- package/dist/nav-file.js.map +1 -0
- package/dist/nav.d.ts +8 -0
- package/dist/nav.d.ts.map +1 -0
- package/dist/nav.js +37 -0
- package/dist/nav.js.map +1 -0
- package/dist/parse.d.ts +12 -0
- package/dist/parse.d.ts.map +1 -0
- package/dist/parse.js +143 -0
- package/dist/parse.js.map +1 -0
- package/dist/paths.d.ts +12 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +55 -0
- package/dist/paths.js.map +1 -0
- package/dist/resolve.d.ts +13 -0
- package/dist/resolve.d.ts.map +1 -0
- package/dist/resolve.js +140 -0
- package/dist/resolve.js.map +1 -0
- package/dist/serialize.d.ts +3 -0
- package/dist/serialize.d.ts.map +1 -0
- package/dist/serialize.js +28 -0
- package/dist/serialize.js.map +1 -0
- package/dist/types.d.ts +54 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +11 -0
- package/dist/types.js.map +1 -0
- package/package.json +45 -0
package/dist/parse.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { load as loadYaml } from "js-yaml";
|
|
2
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3
|
+
const parseEntry = (raw, mapPath, pointer, errors) => {
|
|
4
|
+
if (!isRecord(raw)) {
|
|
5
|
+
errors.push({
|
|
6
|
+
mapPath,
|
|
7
|
+
code: "invalid-entry",
|
|
8
|
+
message: `${pointer}: entry must be a mapping, got ${Array.isArray(raw) ? "array" : typeof raw}`,
|
|
9
|
+
});
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
const entry = {};
|
|
13
|
+
if (raw.navtitle !== undefined) {
|
|
14
|
+
if (typeof raw.navtitle !== "string" || raw.navtitle.trim().length === 0) {
|
|
15
|
+
// Empty labels would pass check but crash the downstream nav loader
|
|
16
|
+
errors.push({ mapPath, code: "invalid-entry", message: `${pointer}: navtitle must be a non-empty string` });
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
entry.navtitle = raw.navtitle;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
if (raw.href !== undefined) {
|
|
23
|
+
if (typeof raw.href !== "string" || raw.href.length === 0) {
|
|
24
|
+
errors.push({ mapPath, code: "invalid-entry", message: `${pointer}: href must be a non-empty string` });
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
entry.href = raw.href;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (raw.format !== undefined) {
|
|
31
|
+
if (raw.format === "topicmap" || raw.format === "md") {
|
|
32
|
+
entry.format = raw.format;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
errors.push({
|
|
36
|
+
mapPath,
|
|
37
|
+
code: "invalid-entry",
|
|
38
|
+
message: `${pointer}: format must be "topicmap" or "md", got ${JSON.stringify(raw.format)}`,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (raw.toc !== undefined) {
|
|
43
|
+
if (typeof raw.toc !== "boolean") {
|
|
44
|
+
errors.push({ mapPath, code: "invalid-entry", message: `${pointer}: toc must be a boolean` });
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
entry.toc = raw.toc;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (raw.singlePage !== undefined) {
|
|
51
|
+
if (raw.singlePage === "yes" || raw.singlePage === "no" || raw.singlePage === "children") {
|
|
52
|
+
entry.singlePage = raw.singlePage;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
errors.push({
|
|
56
|
+
mapPath,
|
|
57
|
+
code: "invalid-entry",
|
|
58
|
+
message: `${pointer}: singlePage must be "yes", "no" or "children"`,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (raw.meta !== undefined) {
|
|
63
|
+
if (isRecord(raw.meta)) {
|
|
64
|
+
entry.meta = raw.meta;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
errors.push({ mapPath, code: "invalid-entry", message: `${pointer}: meta must be a mapping` });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// `topics:` with nothing under it (a placeholder authors leave for later)
|
|
71
|
+
// is an empty list, not an error — it is a common hand-written shape
|
|
72
|
+
if (raw.topics !== undefined && raw.topics !== null) {
|
|
73
|
+
if (!Array.isArray(raw.topics)) {
|
|
74
|
+
errors.push({ mapPath, code: "invalid-entry", message: `${pointer}: topics must be an array` });
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
entry.topics = raw.topics
|
|
78
|
+
.map((child, i) => parseEntry(child, mapPath, `${pointer}.topics[${i}]`, errors))
|
|
79
|
+
.filter((child) => child !== null);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (entry.navtitle === undefined && entry.href === undefined) {
|
|
83
|
+
errors.push({
|
|
84
|
+
mapPath,
|
|
85
|
+
code: "invalid-entry",
|
|
86
|
+
message: `${pointer}: entry needs a navtitle or an href`,
|
|
87
|
+
});
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
if (entry.format === "topicmap" && entry.href === undefined) {
|
|
91
|
+
errors.push({
|
|
92
|
+
mapPath,
|
|
93
|
+
code: "invalid-entry",
|
|
94
|
+
message: `${pointer}: format "topicmap" requires an href`,
|
|
95
|
+
});
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
return entry;
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Parse a topic map YAML source. Never throws — problems are collected as
|
|
102
|
+
* structured errors so builds can report all of them at once.
|
|
103
|
+
*/
|
|
104
|
+
export const parseTopicMap = (source, mapPath = "") => {
|
|
105
|
+
const errors = [];
|
|
106
|
+
let raw;
|
|
107
|
+
try {
|
|
108
|
+
raw = loadYaml(source);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
errors.push({
|
|
112
|
+
mapPath,
|
|
113
|
+
code: "invalid-yaml",
|
|
114
|
+
message: error instanceof Error ? error.message : String(error),
|
|
115
|
+
});
|
|
116
|
+
return { map: null, errors };
|
|
117
|
+
}
|
|
118
|
+
if (!isRecord(raw)) {
|
|
119
|
+
errors.push({ mapPath, code: "invalid-shape", message: "topic map must be a YAML mapping" });
|
|
120
|
+
return { map: null, errors };
|
|
121
|
+
}
|
|
122
|
+
// `topics:` with nothing under it is an empty map (a placeholder authors leave), not an error
|
|
123
|
+
const rawTopics = raw.topics === null ? [] : raw.topics;
|
|
124
|
+
if (!Array.isArray(rawTopics)) {
|
|
125
|
+
errors.push({ mapPath, code: "invalid-shape", message: 'topic map requires a "topics" array' });
|
|
126
|
+
return { map: null, errors };
|
|
127
|
+
}
|
|
128
|
+
const map = {
|
|
129
|
+
topics: rawTopics
|
|
130
|
+
.map((entry, i) => parseEntry(entry, mapPath, `topics[${i}]`, errors))
|
|
131
|
+
.filter((entry) => entry !== null),
|
|
132
|
+
};
|
|
133
|
+
if (raw.title !== undefined) {
|
|
134
|
+
if (typeof raw.title === "string") {
|
|
135
|
+
map.title = raw.title;
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
errors.push({ mapPath, code: "invalid-shape", message: "title must be a string" });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return { map, errors };
|
|
142
|
+
};
|
|
143
|
+
//# 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/paths.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal POSIX-style path helpers. Deliberately not node:path so the
|
|
3
|
+
* package runs unmodified in browsers and extension contexts.
|
|
4
|
+
*/
|
|
5
|
+
export declare const normalizePath: (path: string) => string;
|
|
6
|
+
export declare const dirname: (path: string) => string;
|
|
7
|
+
export declare const joinPath: (...segments: string[]) => string;
|
|
8
|
+
/** True for scheme-prefixed URLs (https://, mailto:, …) that must never be path-resolved. */
|
|
9
|
+
export declare const isExternalHref: (href: string) => boolean;
|
|
10
|
+
/** Resolve `href` (map-relative) against the directory of the containing map. */
|
|
11
|
+
export declare const resolveHref: (href: string, containingMapPath: string) => string;
|
|
12
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,aAAa,GAAI,MAAM,MAAM,KAAG,MAmB5C,CAAC;AAEF,eAAO,MAAM,OAAO,GAAI,MAAM,MAAM,KAAG,MAMtC,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,GAAG,UAAU,MAAM,EAAE,KAAG,MACE,CAAC;AAEpD,6FAA6F;AAC7F,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,KAAG,OAAiD,CAAC;AAEhG,iFAAiF;AACjF,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,EAAE,mBAAmB,MAAM,KAAG,MAerE,CAAC"}
|
package/dist/paths.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal POSIX-style path helpers. Deliberately not node:path so the
|
|
3
|
+
* package runs unmodified in browsers and extension contexts.
|
|
4
|
+
*/
|
|
5
|
+
export const normalizePath = (path) => {
|
|
6
|
+
const isAbsolute = path.startsWith("/");
|
|
7
|
+
const parts = path.split("/");
|
|
8
|
+
const out = [];
|
|
9
|
+
for (const part of parts) {
|
|
10
|
+
if (part === "" || part === ".")
|
|
11
|
+
continue;
|
|
12
|
+
if (part === "..") {
|
|
13
|
+
if (out.length > 0 && out[out.length - 1] !== "..") {
|
|
14
|
+
out.pop();
|
|
15
|
+
}
|
|
16
|
+
else if (!isAbsolute) {
|
|
17
|
+
out.push("..");
|
|
18
|
+
}
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
out.push(part);
|
|
22
|
+
}
|
|
23
|
+
return (isAbsolute ? "/" : "") + out.join("/");
|
|
24
|
+
};
|
|
25
|
+
export const dirname = (path) => {
|
|
26
|
+
const normalized = normalizePath(path);
|
|
27
|
+
const idx = normalized.lastIndexOf("/");
|
|
28
|
+
if (idx === -1)
|
|
29
|
+
return "";
|
|
30
|
+
if (idx === 0)
|
|
31
|
+
return "/";
|
|
32
|
+
return normalized.slice(0, idx);
|
|
33
|
+
};
|
|
34
|
+
export const joinPath = (...segments) => normalizePath(segments.filter(Boolean).join("/"));
|
|
35
|
+
/** True for scheme-prefixed URLs (https://, mailto:, …) that must never be path-resolved. */
|
|
36
|
+
export const isExternalHref = (href) => /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(href);
|
|
37
|
+
/** Resolve `href` (map-relative) against the directory of the containing map. */
|
|
38
|
+
export const resolveHref = (href, containingMapPath) => {
|
|
39
|
+
if (isExternalHref(href))
|
|
40
|
+
return href;
|
|
41
|
+
if (href.startsWith("/"))
|
|
42
|
+
return normalizePath(href);
|
|
43
|
+
// Relative child of a remote map: join scheme-safely so the `//` after
|
|
44
|
+
// the scheme is never collapsed by path normalization
|
|
45
|
+
if (isExternalHref(containingMapPath)) {
|
|
46
|
+
const match = containingMapPath.match(/^([a-zA-Z][a-zA-Z0-9+.-]*:\/\/[^/]+)(\/.*)?$/);
|
|
47
|
+
if (!match)
|
|
48
|
+
return href;
|
|
49
|
+
const origin = match[1];
|
|
50
|
+
const dir = dirname(match[2] ?? "/") || "/";
|
|
51
|
+
return origin + normalizePath(`${dir}/${href}`);
|
|
52
|
+
}
|
|
53
|
+
return joinPath(dirname(containingMapPath), href);
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,IAAY,EAAU,EAAE;IACpD,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9B,MAAM,GAAG,GAAa,EAAE,CAAC;IAEzB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,KAAK,EAAE,IAAI,IAAI,KAAK,GAAG;YAAE,SAAS;QAC1C,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAClB,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;gBACnD,GAAG,CAAC,GAAG,EAAE,CAAC;YACZ,CAAC;iBAAM,IAAI,CAAC,UAAU,EAAE,CAAC;gBACvB,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjB,CAAC;YACD,SAAS;QACX,CAAC;QACD,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,CAAC;IAED,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACjD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,IAAY,EAAU,EAAE;IAC9C,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,GAAG,GAAG,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACxC,IAAI,GAAG,KAAK,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAC1B,IAAI,GAAG,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IAC1B,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAClC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,GAAG,QAAkB,EAAU,EAAE,CACxD,aAAa,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAEpD,6FAA6F;AAC7F,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,IAAY,EAAW,EAAE,CAAC,2BAA2B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEhG,iFAAiF;AACjF,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,IAAY,EAAE,iBAAyB,EAAU,EAAE;IAC7E,IAAI,cAAc,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC;IAErD,uEAAuE;IACvE,sDAAsD;IACtD,IAAI,cAAc,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,iBAAiB,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;QACtF,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QACxB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACxB,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,GAAG,CAAC;QAC5C,OAAO,MAAM,GAAG,aAAa,CAAC,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,CAAC;IAClD,CAAC;IAED,OAAO,QAAQ,CAAC,OAAO,CAAC,iBAAiB,CAAC,EAAE,IAAI,CAAC,CAAC;AACpD,CAAC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ResolveOptions, ResolvedTopicMap } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Recursively resolve a topic map: load and splice `format: topicmap`
|
|
4
|
+
* includes (cycle-safe, depth-limited), resolve minja variables in
|
|
5
|
+
* navtitle/href, and rewrite hrefs relative to the root map's directory so
|
|
6
|
+
* consumers see one consistent path space.
|
|
7
|
+
*
|
|
8
|
+
* Never throws for content problems — all issues land in `errors` and the
|
|
9
|
+
* affected entries are dropped, so a broken sub-map cannot take down the
|
|
10
|
+
* whole build.
|
|
11
|
+
*/
|
|
12
|
+
export declare const resolveTopicMap: (rootPath: string, options: ResolveOptions) => Promise<ResolvedTopicMap>;
|
|
13
|
+
//# sourceMappingURL=resolve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,cAAc,EACd,gBAAgB,EAIjB,MAAM,YAAY,CAAC;AAsCpB;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,GAC1B,UAAU,MAAM,EAChB,SAAS,cAAc,KACtB,OAAO,CAAC,gBAAgB,CA+G1B,CAAC"}
|
package/dist/resolve.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { render, MemoryLoader } from "@dogsbay/minja";
|
|
2
|
+
import { parseTopicMap } from "./parse.js";
|
|
3
|
+
import { normalizePath, resolveHref, isExternalHref } from "./paths.js";
|
|
4
|
+
const DEFAULT_MAX_DEPTH = 10;
|
|
5
|
+
// Shared empty loader: entry-level templating only uses variables, never includes
|
|
6
|
+
const NO_INCLUDES = new MemoryLoader({});
|
|
7
|
+
const hasTemplateSyntax = (value) => value.includes("{{") || value.includes("{%");
|
|
8
|
+
/**
|
|
9
|
+
* Resolve minja variables in a single string value. Undefined variables are
|
|
10
|
+
* preserved (multi-stage pipelines may resolve them later).
|
|
11
|
+
*/
|
|
12
|
+
const renderValue = async (value, variables, mapPath, errors) => {
|
|
13
|
+
if (!hasTemplateSyntax(value))
|
|
14
|
+
return value;
|
|
15
|
+
try {
|
|
16
|
+
return await render(value, {
|
|
17
|
+
context: variables,
|
|
18
|
+
loader: NO_INCLUDES,
|
|
19
|
+
undefinedBehavior: "preserve",
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
errors.push({
|
|
24
|
+
mapPath,
|
|
25
|
+
code: "variable-error",
|
|
26
|
+
message: `failed to resolve variables in ${JSON.stringify(value)}: ${error instanceof Error ? error.message : String(error)}`,
|
|
27
|
+
});
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Recursively resolve a topic map: load and splice `format: topicmap`
|
|
33
|
+
* includes (cycle-safe, depth-limited), resolve minja variables in
|
|
34
|
+
* navtitle/href, and rewrite hrefs relative to the root map's directory so
|
|
35
|
+
* consumers see one consistent path space.
|
|
36
|
+
*
|
|
37
|
+
* Never throws for content problems — all issues land in `errors` and the
|
|
38
|
+
* affected entries are dropped, so a broken sub-map cannot take down the
|
|
39
|
+
* whole build.
|
|
40
|
+
*/
|
|
41
|
+
export const resolveTopicMap = async (rootPath, options) => {
|
|
42
|
+
const { loader, variables = {}, maxDepth = DEFAULT_MAX_DEPTH } = options;
|
|
43
|
+
const errors = [];
|
|
44
|
+
const loadMap = async (path, stack) => {
|
|
45
|
+
// Remote map URLs must not be path-normalized (https:// would collapse)
|
|
46
|
+
const normalized = isExternalHref(path) ? path : normalizePath(path);
|
|
47
|
+
if (stack.includes(normalized)) {
|
|
48
|
+
errors.push({
|
|
49
|
+
mapPath: normalized,
|
|
50
|
+
code: "cycle",
|
|
51
|
+
message: `circular topic map include: ${[...stack, normalized].join(" → ")}`,
|
|
52
|
+
});
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
if (stack.length >= maxDepth) {
|
|
56
|
+
errors.push({
|
|
57
|
+
mapPath: normalized,
|
|
58
|
+
code: "max-depth",
|
|
59
|
+
message: `maximum topic map depth (${maxDepth}) exceeded`,
|
|
60
|
+
});
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
let source;
|
|
64
|
+
try {
|
|
65
|
+
source = await loader(normalized);
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
errors.push({
|
|
69
|
+
mapPath: normalized,
|
|
70
|
+
code: "load-failed",
|
|
71
|
+
message: error instanceof Error ? error.message : String(error),
|
|
72
|
+
});
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
const parsed = parseTopicMap(source, normalized);
|
|
76
|
+
errors.push(...parsed.errors);
|
|
77
|
+
if (!parsed.map)
|
|
78
|
+
return null;
|
|
79
|
+
const resolveEntries = async (entries) => {
|
|
80
|
+
const out = [];
|
|
81
|
+
for (const entry of entries) {
|
|
82
|
+
const resolved = { ...entry };
|
|
83
|
+
if (resolved.navtitle !== undefined) {
|
|
84
|
+
resolved.navtitle = await renderValue(resolved.navtitle, variables, normalized, errors);
|
|
85
|
+
}
|
|
86
|
+
if (resolved.href !== undefined) {
|
|
87
|
+
const rendered = await renderValue(resolved.href, variables, normalized, errors);
|
|
88
|
+
resolved.href = resolveHref(rendered, normalized);
|
|
89
|
+
}
|
|
90
|
+
if (resolved.format === "topicmap" && resolved.href) {
|
|
91
|
+
const child = await loadMap(resolved.href, [...stack, normalized]);
|
|
92
|
+
if (child === null) {
|
|
93
|
+
// Error already recorded; drop the broken include
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
// Entries authored under the include are preserved, appended
|
|
97
|
+
// after the child map's topics (DITA topicref-with-children order)
|
|
98
|
+
const authored = resolved.topics ? await resolveEntries(resolved.topics) : [];
|
|
99
|
+
const mergedTopics = [...child.topics, ...authored];
|
|
100
|
+
const label = resolved.navtitle ?? child.title;
|
|
101
|
+
if (label !== undefined) {
|
|
102
|
+
out.push({
|
|
103
|
+
navtitle: label,
|
|
104
|
+
...(resolved.toc !== undefined && { toc: resolved.toc }),
|
|
105
|
+
...(resolved.singlePage !== undefined && { singlePage: resolved.singlePage }),
|
|
106
|
+
...(resolved.meta !== undefined && { meta: resolved.meta }),
|
|
107
|
+
topics: mergedTopics,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
else if (resolved.toc === false) {
|
|
111
|
+
// No wrapper node to carry the exclusion — propagate it to
|
|
112
|
+
// every spliced entry so hidden content stays hidden
|
|
113
|
+
out.push(...mergedTopics.map((topic) => ({ ...topic, toc: false })));
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
// No label anywhere: splice the child's entries inline
|
|
117
|
+
out.push(...mergedTopics);
|
|
118
|
+
}
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (resolved.topics) {
|
|
122
|
+
resolved.topics = await resolveEntries(resolved.topics);
|
|
123
|
+
}
|
|
124
|
+
out.push(resolved);
|
|
125
|
+
}
|
|
126
|
+
return out;
|
|
127
|
+
};
|
|
128
|
+
let title = parsed.map.title;
|
|
129
|
+
if (title !== undefined) {
|
|
130
|
+
title = await renderValue(title, variables, normalized, errors);
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
...(title !== undefined && { title }),
|
|
134
|
+
topics: await resolveEntries(parsed.map.topics),
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
const map = (await loadMap(rootPath, [])) ?? { topics: [] };
|
|
138
|
+
return { map, errors };
|
|
139
|
+
};
|
|
140
|
+
//# sourceMappingURL=resolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AASxE,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAE7B,kFAAkF;AAClF,MAAM,WAAW,GAAG,IAAI,YAAY,CAAC,EAAE,CAAC,CAAC;AAEzC,MAAM,iBAAiB,GAAG,CAAC,KAAa,EAAW,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAEnG;;;GAGG;AACH,MAAM,WAAW,GAAG,KAAK,EACvB,KAAa,EACb,SAAkC,EAClC,OAAe,EACf,MAAuB,EACN,EAAE;IACnB,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5C,IAAI,CAAC;QACH,OAAO,MAAM,MAAM,CAAC,KAAK,EAAE;YACzB,OAAO,EAAE,SAAS;YAClB,MAAM,EAAE,WAAW;YACnB,iBAAiB,EAAE,UAAU;SAC9B,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC;YACV,OAAO;YACP,IAAI,EAAE,gBAAgB;YACtB,OAAO,EAAE,kCAAkC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,KAC9D,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CACvD,EAAE;SACH,CAAC,CAAC;QACH,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EAClC,QAAgB,EAChB,OAAuB,EACI,EAAE;IAC7B,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,EAAE,EAAE,QAAQ,GAAG,iBAAiB,EAAE,GAAG,OAAO,CAAC;IACzE,MAAM,MAAM,GAAoB,EAAE,CAAC;IAEnC,MAAM,OAAO,GAAG,KAAK,EAAE,IAAY,EAAE,KAAe,EAA4B,EAAE;QAChF,wEAAwE;QACxE,MAAM,UAAU,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QAErE,IAAI,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YAC/B,MAAM,CAAC,IAAI,CAAC;gBACV,OAAO,EAAE,UAAU;gBACnB,IAAI,EAAE,OAAO;gBACb,OAAO,EAAE,+BAA+B,CAAC,GAAG,KAAK,EAAE,UAAU,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;aAC7E,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,KAAK,CAAC,MAAM,IAAI,QAAQ,EAAE,CAAC;YAC7B,MAAM,CAAC,IAAI,CAAC;gBACV,OAAO,EAAE,UAAU;gBACnB,IAAI,EAAE,WAAW;gBACjB,OAAO,EAAE,4BAA4B,QAAQ,YAAY;aAC1D,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,MAAc,CAAC;QACnB,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;QACpC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,CAAC;gBACV,OAAO,EAAE,UAAU;gBACnB,IAAI,EAAE,aAAa;gBACnB,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aAChE,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QACjD,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;QAC9B,IAAI,CAAC,MAAM,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QAE7B,MAAM,cAAc,GAAG,KAAK,EAAE,OAAwB,EAA4B,EAAE;YAClF,MAAM,GAAG,GAAoB,EAAE,CAAC;YAEhC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC5B,MAAM,QAAQ,GAAkB,EAAE,GAAG,KAAK,EAAE,CAAC;gBAE7C,IAAI,QAAQ,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;oBACpC,QAAQ,CAAC,QAAQ,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;gBAC1F,CAAC;gBACD,IAAI,QAAQ,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;oBAChC,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;oBACjF,QAAQ,CAAC,IAAI,GAAG,WAAW,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;gBACpD,CAAC;gBAED,IAAI,QAAQ,CAAC,MAAM,KAAK,UAAU,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;oBACpD,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC;oBACnE,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;wBACnB,kDAAkD;wBAClD,SAAS;oBACX,CAAC;oBAED,6DAA6D;oBAC7D,mEAAmE;oBACnE,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC9E,MAAM,YAAY,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,GAAG,QAAQ,CAAC,CAAC;oBAEpD,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,IAAI,KAAK,CAAC,KAAK,CAAC;oBAC/C,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;wBACxB,GAAG,CAAC,IAAI,CAAC;4BACP,QAAQ,EAAE,KAAK;4BACf,GAAG,CAAC,QAAQ,CAAC,GAAG,KAAK,SAAS,IAAI,EAAE,GAAG,EAAE,QAAQ,CAAC,GAAG,EAAE,CAAC;4BACxD,GAAG,CAAC,QAAQ,CAAC,UAAU,KAAK,SAAS,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC,UAAU,EAAE,CAAC;4BAC7E,GAAG,CAAC,QAAQ,CAAC,IAAI,KAAK,SAAS,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC;4BAC3D,MAAM,EAAE,YAAY;yBACrB,CAAC,CAAC;oBACL,CAAC;yBAAM,IAAI,QAAQ,CAAC,GAAG,KAAK,KAAK,EAAE,CAAC;wBAClC,2DAA2D;wBAC3D,qDAAqD;wBACrD,GAAG,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;oBACvE,CAAC;yBAAM,CAAC;wBACN,uDAAuD;wBACvD,GAAG,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,CAAC;oBAC5B,CAAC;oBACD,SAAS;gBACX,CAAC;gBAED,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;oBACpB,QAAQ,CAAC,MAAM,GAAG,MAAM,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;gBAC1D,CAAC;gBAED,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACrB,CAAC;YAED,OAAO,GAAG,CAAC;QACb,CAAC,CAAC;QAEF,IAAI,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC;QAC7B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,KAAK,GAAG,MAAM,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;QAClE,CAAC;QAED,OAAO;YACL,GAAG,CAAC,KAAK,KAAK,SAAS,IAAI,EAAE,KAAK,EAAE,CAAC;YACrC,MAAM,EAAE,MAAM,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;SAChD,CAAC;IACJ,CAAC,CAAC;IAEF,MAAM,GAAG,GAAG,CAAC,MAAM,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAC5D,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;AACzB,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAiB,MAAM,YAAY,CAAC;AAe1D,eAAO,MAAM,iBAAiB,GAAI,KAAK,QAAQ,KAAG,MAMjD,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { dump as dumpYaml } from "js-yaml";
|
|
2
|
+
// Stable key order so serialized maps diff cleanly in git
|
|
3
|
+
const orderEntry = (entry) => {
|
|
4
|
+
const out = {};
|
|
5
|
+
if (entry.navtitle !== undefined)
|
|
6
|
+
out.navtitle = entry.navtitle;
|
|
7
|
+
if (entry.href !== undefined)
|
|
8
|
+
out.href = entry.href;
|
|
9
|
+
if (entry.format !== undefined)
|
|
10
|
+
out.format = entry.format;
|
|
11
|
+
if (entry.toc !== undefined)
|
|
12
|
+
out.toc = entry.toc;
|
|
13
|
+
if (entry.singlePage !== undefined)
|
|
14
|
+
out.singlePage = entry.singlePage;
|
|
15
|
+
if (entry.meta !== undefined)
|
|
16
|
+
out.meta = entry.meta;
|
|
17
|
+
if (entry.topics !== undefined)
|
|
18
|
+
out.topics = entry.topics.map(orderEntry);
|
|
19
|
+
return out;
|
|
20
|
+
};
|
|
21
|
+
export const serializeTopicMap = (map) => {
|
|
22
|
+
const out = {};
|
|
23
|
+
if (map.title !== undefined)
|
|
24
|
+
out.title = map.title;
|
|
25
|
+
out.topics = map.topics.map(orderEntry);
|
|
26
|
+
return dumpYaml(out, { lineWidth: 100, noRefs: true });
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=serialize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialize.js","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,MAAM,SAAS,CAAC;AAG3C,0DAA0D;AAC1D,MAAM,UAAU,GAAG,CAAC,KAAoB,EAA2B,EAAE;IACnE,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS;QAAE,GAAG,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;IAChE,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;QAAE,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;IACpD,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAC1D,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS;QAAE,GAAG,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC;IACjD,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS;QAAE,GAAG,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;IACtE,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;QAAE,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;IACpD,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC1E,OAAO,GAAG,CAAC;AACb,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,GAAa,EAAU,EAAE;IACzD,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS;QAAE,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC;IACnD,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAExC,OAAO,QAAQ,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;AACzD,CAAC,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Topic map model — the authored source of truth for site structure.
|
|
3
|
+
*
|
|
4
|
+
* A topic map is a recursive YAML file (`*.topicmap.yml`) equivalent to a
|
|
5
|
+
* DITA map: entries reference content files or other topic maps
|
|
6
|
+
* (`format: topicmap`), nested to any depth. Builds resolve maps to
|
|
7
|
+
* NavItem[] (nav.json is a build output, never the source).
|
|
8
|
+
*/
|
|
9
|
+
export interface TopicMapEntry {
|
|
10
|
+
/** Display name in navigation. Optional when `href` provides one. */
|
|
11
|
+
navtitle?: string;
|
|
12
|
+
/** Path to a content file or (with `format: topicmap`) another map, relative to the containing map. */
|
|
13
|
+
href?: string;
|
|
14
|
+
/** `topicmap` marks `href` as a nested topic map to splice in. */
|
|
15
|
+
format?: "topicmap" | "md";
|
|
16
|
+
/** `false` excludes the entry from navigation (still built/addressable). */
|
|
17
|
+
toc?: boolean;
|
|
18
|
+
/** Granularity roll-up, passed through to NavItem (plans/granularity-views.md). */
|
|
19
|
+
singlePage?: "yes" | "no" | "children";
|
|
20
|
+
/** Child entries. */
|
|
21
|
+
topics?: TopicMapEntry[];
|
|
22
|
+
/** Provenance and processing hints (e.g. `meta.dita` from import-ditamap). */
|
|
23
|
+
meta?: Record<string, unknown>;
|
|
24
|
+
}
|
|
25
|
+
export interface TopicMap {
|
|
26
|
+
/** Map title; used as the group label when the map is included without a navtitle. */
|
|
27
|
+
title?: string;
|
|
28
|
+
topics: TopicMapEntry[];
|
|
29
|
+
}
|
|
30
|
+
export interface TopicMapError {
|
|
31
|
+
/** Path of the map file the error was found in ("" for inline sources). */
|
|
32
|
+
mapPath: string;
|
|
33
|
+
/** Machine-readable error kind. */
|
|
34
|
+
code: "invalid-yaml" | "invalid-shape" | "invalid-entry" | "cycle" | "max-depth" | "load-failed" | "variable-error";
|
|
35
|
+
message: string;
|
|
36
|
+
}
|
|
37
|
+
/** Loads a topic map source by normalized path. Injectable: fs, fetch, memory. */
|
|
38
|
+
export type TopicMapLoader = (path: string) => Promise<string>;
|
|
39
|
+
export interface ResolveOptions {
|
|
40
|
+
loader: TopicMapLoader;
|
|
41
|
+
/** minja context for `{{ var }}` resolution in navtitle/href values. */
|
|
42
|
+
variables?: Record<string, unknown>;
|
|
43
|
+
/** Maximum include depth (defence against runaway nesting). Default 10. */
|
|
44
|
+
maxDepth?: number;
|
|
45
|
+
}
|
|
46
|
+
export interface ResolvedTopicMap {
|
|
47
|
+
/** Fully resolved map: no `format: topicmap` entries remain. */
|
|
48
|
+
map: TopicMap;
|
|
49
|
+
/** Non-fatal errors collected during resolution (MarkMind pattern). */
|
|
50
|
+
errors: TopicMapError[];
|
|
51
|
+
}
|
|
52
|
+
export declare const TOPICMAP_EXTENSIONS: readonly [".topicmap.yml", ".topicmap.yaml"];
|
|
53
|
+
export declare const isTopicMapPath: (path: string) => boolean;
|
|
54
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,aAAa;IAC5B,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uGAAuG;IACvG,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,MAAM,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC3B,4EAA4E;IAC5E,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,mFAAmF;IACnF,UAAU,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,UAAU,CAAC;IACvC,qBAAqB;IACrB,MAAM,CAAC,EAAE,aAAa,EAAE,CAAC;IACzB,8EAA8E;IAC9E,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,QAAQ;IACvB,sFAAsF;IACtF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,2EAA2E;IAC3E,OAAO,EAAE,MAAM,CAAC;IAChB,mCAAmC;IACnC,IAAI,EACA,cAAc,GACd,eAAe,GACf,eAAe,GACf,OAAO,GACP,WAAW,GACX,aAAa,GACb,gBAAgB,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,kFAAkF;AAClF,MAAM,MAAM,cAAc,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAE/D,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,cAAc,CAAC;IACvB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,gEAAgE;IAChE,GAAG,EAAE,QAAQ,CAAC;IACd,uEAAuE;IACvE,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB;AAED,eAAO,MAAM,mBAAmB,8CAA+C,CAAC;AAEhF,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,KAAG,OACS,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Topic map model — the authored source of truth for site structure.
|
|
3
|
+
*
|
|
4
|
+
* A topic map is a recursive YAML file (`*.topicmap.yml`) equivalent to a
|
|
5
|
+
* DITA map: entries reference content files or other topic maps
|
|
6
|
+
* (`format: topicmap`), nested to any depth. Builds resolve maps to
|
|
7
|
+
* NavItem[] (nav.json is a build output, never the source).
|
|
8
|
+
*/
|
|
9
|
+
export const TOPICMAP_EXTENSIONS = [".topicmap.yml", ".topicmap.yaml"];
|
|
10
|
+
export const isTopicMapPath = (path) => TOPICMAP_EXTENSIONS.some((ext) => path.endsWith(ext));
|
|
11
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA0DH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,eAAe,EAAE,gBAAgB,CAAU,CAAC;AAEhF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,IAAY,EAAW,EAAE,CACtD,mBAAmB,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dogsbay/format-topicmap",
|
|
3
|
+
"version": "0.2.0-beta.101",
|
|
4
|
+
"description": "Recursive YAML topic maps — DITA-map-equivalent authored structure, resolved to NavItem trees",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/js-yaml": "^4.0.9",
|
|
20
|
+
"@types/node": "^25.9.5",
|
|
21
|
+
"typescript": "^5.9.3",
|
|
22
|
+
"vitest": "^4.1.10"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"js-yaml": "^5.2.1",
|
|
26
|
+
"yaml": "^2.9.0",
|
|
27
|
+
"@dogsbay/minja": "0.2.0-beta.101",
|
|
28
|
+
"@dogsbay/types": "0.2.0-beta.101"
|
|
29
|
+
},
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/dogsbay/dogsbay.git",
|
|
34
|
+
"directory": "packages/format-topicmap"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/dogsbay/dogsbay/tree/main/packages/format-topicmap",
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/dogsbay/dogsbay/issues"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsc",
|
|
42
|
+
"test": "vitest run",
|
|
43
|
+
"test:watch": "vitest"
|
|
44
|
+
}
|
|
45
|
+
}
|