@dogsbay/format-hugo 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/dist/builtins.d.ts +12 -0
- package/dist/builtins.d.ts.map +1 -0
- package/dist/builtins.js +207 -0
- package/dist/builtins.js.map +1 -0
- package/dist/cli.d.ts +33 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +190 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +72 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +191 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/kubernetes.d.ts +14 -0
- package/dist/kubernetes.d.ts.map +1 -0
- package/dist/kubernetes.js +186 -0
- package/dist/kubernetes.js.map +1 -0
- package/dist/nav.d.ts +88 -0
- package/dist/nav.d.ts.map +1 -0
- package/dist/nav.js +381 -0
- package/dist/nav.js.map +1 -0
- package/dist/registry.d.ts +79 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +41 -0
- package/dist/registry.js.map +1 -0
- package/dist/resolve.d.ts +26 -0
- package/dist/resolve.d.ts.map +1 -0
- package/dist/resolve.js +81 -0
- package/dist/resolve.js.map +1 -0
- package/dist/shortcodes.d.ts +66 -0
- package/dist/shortcodes.d.ts.map +1 -0
- package/dist/shortcodes.js +216 -0
- package/dist/shortcodes.js.map +1 -0
- package/dist/translate.d.ts +19 -0
- package/dist/translate.d.ts.map +1 -0
- package/dist/translate.js +99 -0
- package/dist/translate.js.map +1 -0
- package/package.json +39 -0
package/dist/nav.js
ADDED
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hugo's implicit navigation, and page identity.
|
|
3
|
+
*
|
|
4
|
+
* Every importer so far reads nav from a FILE — `SUMMARY.md`, `mkdocs.yml`,
|
|
5
|
+
* `sidebars.js`, `docs.json`. Hugo has none. The tree is the directory
|
|
6
|
+
* structure, and the rules are:
|
|
7
|
+
*
|
|
8
|
+
* content/en/docs/concepts/_index.md → a SECTION at /docs/concepts/
|
|
9
|
+
* content/en/docs/concepts/pods.md → a page at /docs/concepts/pods/
|
|
10
|
+
* content/en/docs/setup/index.md → a LEAF BUNDLE (its directory is
|
|
11
|
+
* the page; images sit beside it)
|
|
12
|
+
* weight: 40 → sort position, ascending
|
|
13
|
+
* linkTitle: / title: → nav label
|
|
14
|
+
* slug: / url: → override the URL
|
|
15
|
+
*
|
|
16
|
+
* `_index.md` and `index.md` differ by one character and mean opposite things:
|
|
17
|
+
* the first makes a directory a section with children, the second makes the
|
|
18
|
+
* directory a single page. Getting that wrong either flattens the tree or
|
|
19
|
+
* loses every co-located image.
|
|
20
|
+
*/
|
|
21
|
+
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
22
|
+
import { join, relative } from "node:path";
|
|
23
|
+
import { parse as parseYaml } from "yaml";
|
|
24
|
+
import { parse as parseToml } from "smol-toml";
|
|
25
|
+
/** Split Hugo frontmatter: `---` YAML, `+++` TOML, or `{ }` JSON. */
|
|
26
|
+
export function splitFrontmatter(source) {
|
|
27
|
+
const yamlMatch = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(source);
|
|
28
|
+
if (yamlMatch) {
|
|
29
|
+
try {
|
|
30
|
+
return {
|
|
31
|
+
frontmatter: (parseYaml(yamlMatch[1]) ?? {}),
|
|
32
|
+
body: source.slice(yamlMatch[0].length),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return { frontmatter: {}, body: source.slice(yamlMatch[0].length) };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const tomlMatch = /^\+\+\+\r?\n([\s\S]*?)\r?\n\+\+\+\r?\n?/.exec(source);
|
|
40
|
+
if (tomlMatch) {
|
|
41
|
+
try {
|
|
42
|
+
return {
|
|
43
|
+
frontmatter: parseToml(tomlMatch[1]),
|
|
44
|
+
body: source.slice(tomlMatch[0].length),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return { frontmatter: {}, body: source.slice(tomlMatch[0].length) };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return { frontmatter: {}, body: source };
|
|
52
|
+
}
|
|
53
|
+
function titleFromSlug(slug) {
|
|
54
|
+
const last = slug.split("/").filter(Boolean).pop() ?? "";
|
|
55
|
+
return last
|
|
56
|
+
.split(/[-_]/)
|
|
57
|
+
.filter(Boolean)
|
|
58
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
59
|
+
.join(" ");
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Directories whose markdown is include-only by convention. Hugo has no formal
|
|
63
|
+
* marker, but `{{< include "x.md" >}}` resolves here, so these files are
|
|
64
|
+
* fragments rather than pages.
|
|
65
|
+
*/
|
|
66
|
+
const FRAGMENT_DIRS = ["includes", "included", "_includes", "shortcodes"];
|
|
67
|
+
/**
|
|
68
|
+
* A page's `menu.main` entry, if it declares one.
|
|
69
|
+
*
|
|
70
|
+
* Hugo accepts several shapes: `menu: main`, `menu: [main]`, and
|
|
71
|
+
* `menu: {main: {weight, title, parent}}`. Only the last carries ordering, but
|
|
72
|
+
* all three mean "this page is in the main menu".
|
|
73
|
+
*/
|
|
74
|
+
function readMenuMain(fm) {
|
|
75
|
+
const menu = fm.menu;
|
|
76
|
+
if (!menu)
|
|
77
|
+
return {};
|
|
78
|
+
if (typeof menu === "string" || Array.isArray(menu))
|
|
79
|
+
return {};
|
|
80
|
+
const main = menu.main;
|
|
81
|
+
if (!main || typeof main !== "object")
|
|
82
|
+
return {};
|
|
83
|
+
const entry = main;
|
|
84
|
+
return {
|
|
85
|
+
weight: typeof entry.weight === "number" ? entry.weight : undefined,
|
|
86
|
+
title: typeof entry.title === "string" ? entry.title : undefined,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/** Whether Hugo would exclude this page from the built site / its lists. */
|
|
90
|
+
function isHidden(fm) {
|
|
91
|
+
if (fm.draft === true)
|
|
92
|
+
return true;
|
|
93
|
+
if (fm.headless === true)
|
|
94
|
+
return true;
|
|
95
|
+
const build = fm._build;
|
|
96
|
+
if (build && (build.render === "never" || build.list === "never"))
|
|
97
|
+
return true;
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Walk a Hugo content directory into a flat page list.
|
|
102
|
+
*
|
|
103
|
+
* Ordinary markdown only — `.html` content pages exist in Hugo but carry no
|
|
104
|
+
* markdown to import, and pulling them in would emit pages of raw template.
|
|
105
|
+
*/
|
|
106
|
+
/**
|
|
107
|
+
* Apply a Hugo `[permalinks]` pattern to a page.
|
|
108
|
+
*
|
|
109
|
+
* Tokens Hugo defines that matter for docs corpora: `:section`, `:sections`,
|
|
110
|
+
* `:year`, `:month`, `:day`, `:slug`, `:title`, `:filename`, `:contentbasename`.
|
|
111
|
+
* An unknown token is left in place rather than silently emitting an empty
|
|
112
|
+
* segment — a wrong URL you can see beats one you cannot.
|
|
113
|
+
*/
|
|
114
|
+
export function applyPermalink(pattern, ctx) {
|
|
115
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
116
|
+
const d = ctx.date;
|
|
117
|
+
const values = {
|
|
118
|
+
section: ctx.section,
|
|
119
|
+
sections: ctx.sections.join("/"),
|
|
120
|
+
slug: ctx.slug,
|
|
121
|
+
title: ctx.title,
|
|
122
|
+
filename: ctx.filename,
|
|
123
|
+
contentbasename: ctx.filename,
|
|
124
|
+
year: d ? String(d.getUTCFullYear()) : "",
|
|
125
|
+
month: d ? pad(d.getUTCMonth() + 1) : "",
|
|
126
|
+
day: d ? pad(d.getUTCDate()) : "",
|
|
127
|
+
monthname: d
|
|
128
|
+
? d.toLocaleString("en-US", { month: "long", timeZone: "UTC" })
|
|
129
|
+
: "",
|
|
130
|
+
};
|
|
131
|
+
const out = pattern.replace(/:([a-zA-Z]+)/g, (whole, token) => token in values ? values[token] : whole);
|
|
132
|
+
return out
|
|
133
|
+
.split("/")
|
|
134
|
+
.filter((seg) => seg !== "")
|
|
135
|
+
.join("/");
|
|
136
|
+
}
|
|
137
|
+
export function collectHugoPages(contentRoot, permalinks = {}, sectionPermalinks = {}) {
|
|
138
|
+
const pages = [];
|
|
139
|
+
const walk = (dir) => {
|
|
140
|
+
let entries;
|
|
141
|
+
try {
|
|
142
|
+
entries = readdirSync(dir).sort();
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
for (const name of entries) {
|
|
148
|
+
const path = join(dir, name);
|
|
149
|
+
let stats;
|
|
150
|
+
try {
|
|
151
|
+
stats = statSync(path);
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (stats.isDirectory()) {
|
|
157
|
+
walk(path);
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const isHtml = name.endsWith(".html") || name.endsWith(".htm");
|
|
161
|
+
if (!name.endsWith(".md") && !name.endsWith(".markdown") && !isHtml)
|
|
162
|
+
continue;
|
|
163
|
+
const rel = relative(contentRoot, path).split("\\").join("/");
|
|
164
|
+
const source = readFileSync(path, "utf-8");
|
|
165
|
+
const { frontmatter } = splitFrontmatter(source);
|
|
166
|
+
const isSection = /^_index\.(md|markdown|html?)$/.test(name);
|
|
167
|
+
const isLeafBundle = /^index\.(md|markdown|html?)$/.test(name);
|
|
168
|
+
// The slug is the directory for a bundle/section, the filename otherwise.
|
|
169
|
+
let slug = rel.replace(/\.(md|markdown|html?)$/, "");
|
|
170
|
+
if (isSection || isLeafBundle)
|
|
171
|
+
slug = slug.replace(/\/?(_?index)$/, "");
|
|
172
|
+
const sections = slug.split("/").slice(0, -1).filter(Boolean);
|
|
173
|
+
const filename = name.replace(/\.(md|markdown|html?)$/, "");
|
|
174
|
+
const baseName = isSection || isLeafBundle
|
|
175
|
+
? (slug.split("/").pop() ?? filename)
|
|
176
|
+
: filename;
|
|
177
|
+
// Hugo lets frontmatter override it. `url:` is absolute; `slug:` renames
|
|
178
|
+
// only the last segment.
|
|
179
|
+
const url = typeof frontmatter.url === "string" ? frontmatter.url : undefined;
|
|
180
|
+
const slugOverride = typeof frontmatter.slug === "string" ? frontmatter.slug : undefined;
|
|
181
|
+
const topSection = slug.split("/")[0] ?? "";
|
|
182
|
+
// A SECTION page takes a pattern only from `[permalinks.section]`.
|
|
183
|
+
// Hugo keeps the two apart and so must we: kubernetes declares only
|
|
184
|
+
// `[permalinks.page]`, and applying its blog pattern to
|
|
185
|
+
// `blog/_index.md` rewrote the section's own slug to `blog/blog`,
|
|
186
|
+
// which dropped "Blog" out of its menu position in the nav.
|
|
187
|
+
// A page's own `url:` may itself be a PATTERN — 192 kubernetes blog
|
|
188
|
+
// posts write `url: /:section/:year/:month/:slug/`. Treating it as a
|
|
189
|
+
// literal path emitted files at `docs/:section/:year/:month/:slug/`,
|
|
190
|
+
// which is not a URL and which Docusaurus then tried to compile.
|
|
191
|
+
const patternSource = isSection ? sectionPermalinks : permalinks;
|
|
192
|
+
const pattern = url && /:[a-zA-Z]/.test(url) ? url : patternSource[topSection];
|
|
193
|
+
if (url && !/:[a-zA-Z]/.test(url)) {
|
|
194
|
+
slug = url.replace(/^\/+|\/+$/g, "");
|
|
195
|
+
}
|
|
196
|
+
else if (pattern) {
|
|
197
|
+
// A permalink pattern REPLACES the path-derived slug. This is what
|
|
198
|
+
// removes `_posts/2018` from a kubernetes blog URL.
|
|
199
|
+
const rawDate = frontmatter.date;
|
|
200
|
+
const date = rawDate instanceof Date
|
|
201
|
+
? rawDate
|
|
202
|
+
: typeof rawDate === "string" || typeof rawDate === "number"
|
|
203
|
+
? new Date(rawDate)
|
|
204
|
+
: undefined;
|
|
205
|
+
slug = applyPermalink(pattern, {
|
|
206
|
+
section: topSection,
|
|
207
|
+
sections,
|
|
208
|
+
slug: slugOverride ?? baseName,
|
|
209
|
+
filename: baseName,
|
|
210
|
+
title: typeof frontmatter.title === "string" ? frontmatter.title : baseName,
|
|
211
|
+
date: date && !Number.isNaN(date.getTime()) ? date : undefined,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
else if (slugOverride) {
|
|
215
|
+
const parts = slug.split("/");
|
|
216
|
+
parts[parts.length - 1] = slugOverride;
|
|
217
|
+
slug = parts.join("/");
|
|
218
|
+
}
|
|
219
|
+
slug = slug.replace(/^\/+|\/+$/g, "");
|
|
220
|
+
const title = (typeof frontmatter.title === "string" && frontmatter.title) ||
|
|
221
|
+
titleFromSlug(slug || "home");
|
|
222
|
+
// Hugo's MENU system is a second, independent source of both the nav
|
|
223
|
+
// label and the ordering: kubernetes gives `docs` no top-level `weight`
|
|
224
|
+
// at all and writes `menu: {main: {weight: 10}}` instead. Reading only
|
|
225
|
+
// `weight` put every top-level section in the unweighted bucket, so they
|
|
226
|
+
// sorted alphabetically and "Blog" landed above "Documentation".
|
|
227
|
+
const menuMain = readMenuMain(frontmatter);
|
|
228
|
+
const linkTitle = (typeof frontmatter.linkTitle === "string" && frontmatter.linkTitle) ||
|
|
229
|
+
(typeof frontmatter.linktitle === "string" && frontmatter.linktitle) ||
|
|
230
|
+
menuMain.title ||
|
|
231
|
+
title;
|
|
232
|
+
pages.push({
|
|
233
|
+
file: path,
|
|
234
|
+
rel,
|
|
235
|
+
slug,
|
|
236
|
+
title,
|
|
237
|
+
linkTitle,
|
|
238
|
+
weight: typeof frontmatter.weight === "number"
|
|
239
|
+
? frontmatter.weight
|
|
240
|
+
: menuMain.weight,
|
|
241
|
+
frontmatter,
|
|
242
|
+
isSection,
|
|
243
|
+
isLeafBundle,
|
|
244
|
+
unrenderable: isHtml,
|
|
245
|
+
hidden: isHidden(frontmatter),
|
|
246
|
+
fragment: FRAGMENT_DIRS.some((d) => rel === `${d}` || rel.startsWith(`${d}/`)),
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
walk(contentRoot);
|
|
251
|
+
return pages;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Build the nav tree from the page set.
|
|
255
|
+
*
|
|
256
|
+
* Ordering is Hugo's: `weight` ascending, then title. Pages with no weight
|
|
257
|
+
* sort AFTER weighted ones — Hugo treats a missing weight as 0 and then falls
|
|
258
|
+
* back to date/title, but for docs the practical rule authors rely on is
|
|
259
|
+
* "weighted items lead, the rest follow alphabetically".
|
|
260
|
+
*/
|
|
261
|
+
export function buildHugoNav(pages, hrefPrefix = "") {
|
|
262
|
+
const visible = pages.filter((p) => !p.hidden && !p.fragment);
|
|
263
|
+
const bySlug = new Map();
|
|
264
|
+
for (const p of visible)
|
|
265
|
+
bySlug.set(p.slug, p);
|
|
266
|
+
const href = (slug) => `${hrefPrefix}/${slug}`.replace(/\/{2,}/g, "/");
|
|
267
|
+
// EVERY directory is a section in Hugo — `_index.md` only lets you give one
|
|
268
|
+
// front matter. Recursing solely into pages that have an `_index.md` lost
|
|
269
|
+
// 1,479 of 2,445 pages, because a directory like `blog/_posts/2022/` has no
|
|
270
|
+
// index file and its children were therefore never reached. So the tree is
|
|
271
|
+
// built from the SLUG PATHS, and a directory with no page of its own becomes
|
|
272
|
+
// a label-only node.
|
|
273
|
+
const childSlugs = new Map();
|
|
274
|
+
const register = (parent, child) => {
|
|
275
|
+
const set = childSlugs.get(parent) ?? new Set();
|
|
276
|
+
set.add(child);
|
|
277
|
+
childSlugs.set(parent, set);
|
|
278
|
+
};
|
|
279
|
+
for (const page of visible) {
|
|
280
|
+
if (page.slug === "")
|
|
281
|
+
continue;
|
|
282
|
+
const parts = page.slug.split("/");
|
|
283
|
+
for (let i = 0; i < parts.length; i++) {
|
|
284
|
+
const child = parts.slice(0, i + 1).join("/");
|
|
285
|
+
const parent = i === 0 ? "" : parts.slice(0, i).join("/");
|
|
286
|
+
register(parent, child);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
const sortKey = (slug) => {
|
|
290
|
+
const page = bySlug.get(slug);
|
|
291
|
+
const label = page?.linkTitle ?? titleFromSlug(slug);
|
|
292
|
+
// Weighted items lead, unweighted follow alphabetically — the rule docs
|
|
293
|
+
// authors actually rely on.
|
|
294
|
+
return [page?.weight === undefined ? 1 : 0, page?.weight ?? 0, label];
|
|
295
|
+
};
|
|
296
|
+
const build = (parent, depth) => {
|
|
297
|
+
if (depth > 12)
|
|
298
|
+
return [];
|
|
299
|
+
const slugs = [...(childSlugs.get(parent) ?? [])].sort((a, b) => {
|
|
300
|
+
const [ag, aw, al] = sortKey(a);
|
|
301
|
+
const [bg, bw, bl] = sortKey(b);
|
|
302
|
+
return ag - bg || aw - bw || al.localeCompare(bl);
|
|
303
|
+
});
|
|
304
|
+
return slugs.map((slug) => {
|
|
305
|
+
const page = bySlug.get(slug);
|
|
306
|
+
const kids = build(slug, depth + 1);
|
|
307
|
+
const item = { label: page?.linkTitle ?? titleFromSlug(slug) };
|
|
308
|
+
// A directory with no page of its own is a label only — giving it an
|
|
309
|
+
// href would produce a nav entry that 404s.
|
|
310
|
+
if (page)
|
|
311
|
+
item.href = href(slug);
|
|
312
|
+
if (kids.length > 0)
|
|
313
|
+
item.children = kids;
|
|
314
|
+
return item;
|
|
315
|
+
});
|
|
316
|
+
};
|
|
317
|
+
return build("", 0);
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Resolve a `{{< ref >}}` / `{{< relref >}}` target against the page set.
|
|
321
|
+
*
|
|
322
|
+
* 2,570 calls in the kubernetes corpus — the most common shortcode by a wide
|
|
323
|
+
* margin, and every unresolved one is a broken link. Hugo accepts a lot of
|
|
324
|
+
* forms for the same page:
|
|
325
|
+
*
|
|
326
|
+
* "/docs/concepts/_index.md" root-relative with the file name
|
|
327
|
+
* "docs/concepts" root-relative, no extension
|
|
328
|
+
* "pods.md" relative to the calling page
|
|
329
|
+
* "#anchor" / "page.md#anchor" with a fragment
|
|
330
|
+
*/
|
|
331
|
+
export function makeRefResolver(pages, hrefPrefix = "") {
|
|
332
|
+
const bySlug = new Map();
|
|
333
|
+
const byRel = new Map();
|
|
334
|
+
const byBasename = new Map();
|
|
335
|
+
for (const page of pages) {
|
|
336
|
+
bySlug.set(page.slug, page);
|
|
337
|
+
byRel.set(page.rel, page);
|
|
338
|
+
byRel.set(page.rel.replace(/\.(md|markdown)$/, ""), page);
|
|
339
|
+
const base = page.rel.split("/").pop().replace(/\.(md|markdown)$/, "");
|
|
340
|
+
const list = byBasename.get(base) ?? [];
|
|
341
|
+
list.push(page);
|
|
342
|
+
byBasename.set(base, list);
|
|
343
|
+
}
|
|
344
|
+
const href = (page, fragment) => `${hrefPrefix}/${page.slug}${fragment}`.replace(/\/{2,}/g, "/");
|
|
345
|
+
return (target, from) => {
|
|
346
|
+
if (!target)
|
|
347
|
+
return undefined;
|
|
348
|
+
// A bare fragment stays on the page.
|
|
349
|
+
if (target.startsWith("#"))
|
|
350
|
+
return target;
|
|
351
|
+
const hashAt = target.indexOf("#");
|
|
352
|
+
const fragment = hashAt === -1 ? "" : target.slice(hashAt);
|
|
353
|
+
let path = (hashAt === -1 ? target : target.slice(0, hashAt)).trim();
|
|
354
|
+
path = path.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
355
|
+
const noExt = path.replace(/\.(md|markdown)$/, "").replace(/\/?_?index$/, "");
|
|
356
|
+
// Root-relative, by slug or by source path.
|
|
357
|
+
for (const key of [noExt, path]) {
|
|
358
|
+
const hit = bySlug.get(key) ?? byRel.get(key);
|
|
359
|
+
if (hit)
|
|
360
|
+
return href(hit, fragment);
|
|
361
|
+
}
|
|
362
|
+
// Relative to the calling page's directory.
|
|
363
|
+
if (from) {
|
|
364
|
+
const dir = from.split("/").slice(0, -1).join("/");
|
|
365
|
+
const joined = [dir, path].filter(Boolean).join("/");
|
|
366
|
+
const joinedNoExt = joined.replace(/\.(md|markdown)$/, "").replace(/\/?_?index$/, "");
|
|
367
|
+
const hit = byRel.get(joined) ?? byRel.get(joinedNoExt) ?? bySlug.get(joinedNoExt);
|
|
368
|
+
if (hit)
|
|
369
|
+
return href(hit, fragment);
|
|
370
|
+
}
|
|
371
|
+
// Last resort: a unique basename match. Hugo itself does this, and it is
|
|
372
|
+
// how `{{< ref "pods.md" >}}` works from anywhere in the tree. Ambiguous
|
|
373
|
+
// names are NOT guessed — the caller records the failure instead.
|
|
374
|
+
const base = noExt.split("/").pop() ?? "";
|
|
375
|
+
const candidates = byBasename.get(base);
|
|
376
|
+
if (candidates && candidates.length === 1)
|
|
377
|
+
return href(candidates[0], fragment);
|
|
378
|
+
return undefined;
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
//# sourceMappingURL=nav.js.map
|
package/dist/nav.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nav.js","sourceRoot":"","sources":["../src/nav.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,WAAW,CAAC;AAwC/C,qEAAqE;AACrE,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAI7C,MAAM,SAAS,GAAG,mCAAmC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACnE,IAAI,SAAS,EAAE,CAAC;QACd,IAAI,CAAC;YACH,OAAO;gBACL,WAAW,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAA4B;gBACvE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;aACxC,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QACtE,CAAC;IACH,CAAC;IACD,MAAM,SAAS,GAAG,yCAAyC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACzE,IAAI,SAAS,EAAE,CAAC;QACd,IAAI,CAAC;YACH,OAAO;gBACL,WAAW,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CAA4B;gBAC/D,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;aACxC,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QACtE,CAAC;IACH,CAAC;IACD,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAC3C,CAAC;AAED,SAAS,aAAa,CAAC,IAAY;IACjC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;IACzD,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;;;;GAIG;AACH,MAAM,aAAa,GAAG,CAAC,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;AAE1E;;;;;;GAMG;AACH,SAAS,YAAY,CAAC,EAA2B;IAC/C,MAAM,IAAI,GAAG,EAAE,CAAC,IAAI,CAAC;IACrB,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAC/D,MAAM,IAAI,GAAI,IAAgC,CAAC,IAAI,CAAC;IACpD,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IACjD,MAAM,KAAK,GAAG,IAA+B,CAAC;IAC9C,OAAO;QACL,MAAM,EAAE,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;QACnE,KAAK,EAAE,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;KACjE,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,SAAS,QAAQ,CAAC,EAA2B;IAC3C,IAAI,EAAE,CAAC,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACnC,IAAI,EAAE,CAAC,QAAQ,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACtC,MAAM,KAAK,GAAG,EAAE,CAAC,MAA6C,CAAC;IAC/D,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,OAAO,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/E,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAC5B,OAAe,EACf,GAOC;IAED,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACtD,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC;IACnB,MAAM,MAAM,GAA2B;QACrC,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,QAAQ,EAAE,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC;QAChC,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,eAAe,EAAE,GAAG,CAAC,QAAQ;QAC7B,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE;QACzC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;QACxC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE;QACjC,SAAS,EAAE,CAAC;YACV,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;YAC/D,CAAC,CAAC,EAAE;KACP,CAAC;IACF,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC,KAAK,EAAE,KAAa,EAAE,EAAE,CACpE,KAAK,IAAI,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CACxC,CAAC;IACF,OAAO,GAAG;SACP,KAAK,CAAC,GAAG,CAAC;SACV,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC;SAC3B,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,WAAmB,EACnB,aAAqC,EAAE,EACvC,oBAA4C,EAAE;IAE9C,MAAM,KAAK,GAAe,EAAE,CAAC;IAE7B,MAAM,IAAI,GAAG,CAAC,GAAW,EAAE,EAAE;QAC3B,IAAI,OAAiB,CAAC;QACtB,IAAI,CAAC;YACH,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;YAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YAC7B,IAAI,KAAK,CAAC;YACV,IAAI,CAAC;gBACH,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;YACzB,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;YACD,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACxB,IAAI,CAAC,IAAI,CAAC,CAAC;gBACX,SAAS;YACX,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAC/D,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM;gBAAE,SAAS;YAE9E,MAAM,GAAG,GAAG,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC9D,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAC3C,MAAM,EAAE,WAAW,EAAE,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;YAEjD,MAAM,SAAS,GAAG,+BAA+B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC7D,MAAM,YAAY,GAAG,8BAA8B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAE/D,0EAA0E;YAC1E,IAAI,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,wBAAwB,EAAE,EAAE,CAAC,CAAC;YACrD,IAAI,SAAS,IAAI,YAAY;gBAAE,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;YAExE,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAC9D,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,wBAAwB,EAAE,EAAE,CAAC,CAAC;YAC5D,MAAM,QAAQ,GACZ,SAAS,IAAI,YAAY;gBACvB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,QAAQ,CAAC;gBACrC,CAAC,CAAC,QAAQ,CAAC;YAEf,yEAAyE;YACzE,yBAAyB;YACzB,MAAM,GAAG,GAAG,OAAO,WAAW,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;YAC9E,MAAM,YAAY,GAChB,OAAO,WAAW,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;YACtE,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC5C,mEAAmE;YACnE,oEAAoE;YACpE,wDAAwD;YACxD,kEAAkE;YAClE,4DAA4D;YAC5D,oEAAoE;YACpE,qEAAqE;YACrE,qEAAqE;YACrE,iEAAiE;YACjE,MAAM,aAAa,GAAG,SAAS,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,UAAU,CAAC;YACjE,MAAM,OAAO,GAAG,GAAG,IAAI,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;YAC/E,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBAClC,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;YACvC,CAAC;iBAAM,IAAI,OAAO,EAAE,CAAC;gBACnB,mEAAmE;gBACnE,oDAAoD;gBACpD,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC;gBACjC,MAAM,IAAI,GACR,OAAO,YAAY,IAAI;oBACrB,CAAC,CAAC,OAAO;oBACT,CAAC,CAAC,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,OAAO,KAAK,QAAQ;wBAC1D,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC;wBACnB,CAAC,CAAC,SAAS,CAAC;gBAClB,IAAI,GAAG,cAAc,CAAC,OAAO,EAAE;oBAC7B,OAAO,EAAE,UAAU;oBACnB,QAAQ;oBACR,IAAI,EAAE,YAAY,IAAI,QAAQ;oBAC9B,QAAQ,EAAE,QAAQ;oBAClB,KAAK,EAAE,OAAO,WAAW,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ;oBAC3E,IAAI,EAAE,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;iBAC/D,CAAC,CAAC;YACL,CAAC;iBAAM,IAAI,YAAY,EAAE,CAAC;gBACxB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAC9B,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,YAAY,CAAC;gBACvC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACzB,CAAC;YACD,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;YAEtC,MAAM,KAAK,GACT,CAAC,OAAO,WAAW,CAAC,KAAK,KAAK,QAAQ,IAAI,WAAW,CAAC,KAAK,CAAC;gBAC5D,aAAa,CAAC,IAAI,IAAI,MAAM,CAAC,CAAC;YAChC,qEAAqE;YACrE,wEAAwE;YACxE,uEAAuE;YACvE,yEAAyE;YACzE,iEAAiE;YACjE,MAAM,QAAQ,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;YAC3C,MAAM,SAAS,GACb,CAAC,OAAO,WAAW,CAAC,SAAS,KAAK,QAAQ,IAAI,WAAW,CAAC,SAAS,CAAC;gBACpE,CAAC,OAAO,WAAW,CAAC,SAAS,KAAK,QAAQ,IAAI,WAAW,CAAC,SAAS,CAAC;gBACpE,QAAQ,CAAC,KAAK;gBACd,KAAK,CAAC;YAER,KAAK,CAAC,IAAI,CAAC;gBACT,IAAI,EAAE,IAAI;gBACV,GAAG;gBACH,IAAI;gBACJ,KAAK;gBACL,SAAS;gBACT,MAAM,EACJ,OAAO,WAAW,CAAC,MAAM,KAAK,QAAQ;oBACpC,CAAC,CAAC,WAAW,CAAC,MAAM;oBACpB,CAAC,CAAC,QAAQ,CAAC,MAAM;gBACrB,WAAW;gBACX,SAAS;gBACT,YAAY;gBACZ,YAAY,EAAE,MAAM;gBACpB,MAAM,EAAE,QAAQ,CAAC,WAAW,CAAC;gBAC7B,QAAQ,EAAE,aAAa,CAAC,IAAI,CAC1B,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CACjD;aACF,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC;IAEF,IAAI,CAAC,WAAW,CAAC,CAAC;IAClB,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,KAAiB,EAAE,UAAU,GAAG,EAAE;IAC7D,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC9D,MAAM,MAAM,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC3C,KAAK,MAAM,CAAC,IAAI,OAAO;QAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAE/C,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,GAAG,UAAU,IAAI,IAAI,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IAE/E,4EAA4E;IAC5E,0EAA0E;IAC1E,4EAA4E;IAC5E,2EAA2E;IAC3E,6EAA6E;IAC7E,qBAAqB;IACrB,MAAM,UAAU,GAAG,IAAI,GAAG,EAAuB,CAAC;IAClD,MAAM,QAAQ,GAAG,CAAC,MAAc,EAAE,KAAa,EAAE,EAAE;QACjD,MAAM,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,GAAG,EAAU,CAAC;QACxD,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACf,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC9B,CAAC,CAAC;IACF,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,IAAI,IAAI,CAAC,IAAI,KAAK,EAAE;YAAE,SAAS;QAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACnC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC9C,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC1D,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAG,CAAC,IAAY,EAA4B,EAAE;QACzD,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC9B,MAAM,KAAK,GAAG,IAAI,EAAE,SAAS,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC;QACrD,wEAAwE;QACxE,4BAA4B;QAC5B,OAAO,CAAC,IAAI,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC;IACxE,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG,CAAC,MAAc,EAAE,KAAa,EAAa,EAAE;QACzD,IAAI,KAAK,GAAG,EAAE;YAAE,OAAO,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YAC9D,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YAChC,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YAChC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;QACH,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YACxB,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC9B,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;YACpC,MAAM,IAAI,GAAY,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,IAAI,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;YACxE,qEAAqE;YACrE,4CAA4C;YAC5C,IAAI,IAAI;gBAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;YACjC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;gBAAE,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;YAC1C,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,OAAO,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AACtB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,eAAe,CAC7B,KAAiB,EACjB,UAAU,GAAG,EAAE;IAEf,MAAM,MAAM,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC3C,MAAM,KAAK,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC1C,MAAM,UAAU,GAAG,IAAI,GAAG,EAAsB,CAAC;IAEjD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC5B,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC1B,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;QAC1D,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAG,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;QACxE,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACxC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChB,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC7B,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,IAAc,EAAE,QAAgB,EAAE,EAAE,CAChD,GAAG,UAAU,IAAI,IAAI,CAAC,IAAI,GAAG,QAAQ,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IAElE,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE;QACtB,IAAI,CAAC,MAAM;YAAE,OAAO,SAAS,CAAC;QAC9B,qCAAqC;QACrC,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,MAAM,CAAC;QAE1C,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACnC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC3D,IAAI,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACrE,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACpD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;QAE9E,4CAA4C;QAC5C,KAAK,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC;YAChC,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC9C,IAAI,GAAG;gBAAE,OAAO,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QACtC,CAAC;QAED,4CAA4C;QAC5C,IAAI,IAAI,EAAE,CAAC;YACT,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACnD,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACrD,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;YACtF,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YACnF,IAAI,GAAG;gBAAE,OAAO,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QACtC,CAAC;QAED,yEAAyE;QACzE,yEAAyE;QACzE,kEAAkE;QAClE,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;QAC1C,MAAM,UAAU,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,UAAU,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;QAEhF,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shortcode mapping registry.
|
|
3
|
+
*
|
|
4
|
+
* Hugo has no fixed shortcode vocabulary: a site defines its own in
|
|
5
|
+
* `layouts/shortcodes/`, and a theme ships more. Kubernetes uses 52 distinct
|
|
6
|
+
* names, of which only a handful are Hugo builtins. So the registry is OPEN,
|
|
7
|
+
* in three layers — builtins < theme (docsy) < site (kubernetes) < user —
|
|
8
|
+
* exactly like the MDX adapters, because a site-specific shortcode must never
|
|
9
|
+
* become a core concern.
|
|
10
|
+
*
|
|
11
|
+
* A shortcode with no mapping is NEVER dropped: it is preserved verbatim and
|
|
12
|
+
* recorded in the fidelity ledger, so an unmapped construct is a known loss
|
|
13
|
+
* rather than a silent one.
|
|
14
|
+
*/
|
|
15
|
+
import type { ShortcodeArgs, ShortcodeNode } from "./shortcodes.js";
|
|
16
|
+
import type { HugoConfig } from "./config.js";
|
|
17
|
+
export interface ShortcodeContext {
|
|
18
|
+
/** Translate a Hugo source fragment (a shortcode body) to Dogsbay-MD. */
|
|
19
|
+
translate: (source: string) => string;
|
|
20
|
+
/** Site config — params, baseURL, languages. */
|
|
21
|
+
config: HugoConfig;
|
|
22
|
+
/** Record a fidelity note. */
|
|
23
|
+
report: (entry: Omit<HugoLedgerEntry, "file">) => void;
|
|
24
|
+
/** Resolve a `{{< ref >}}`-style page reference to an href, if possible. */
|
|
25
|
+
resolveRef?: (target: string, from?: string) => string | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Read a file a shortcode references. The SECOND argument is the calling
|
|
28
|
+
* page: `{{< include "included/x.md" >}}` resolves beside the page, while
|
|
29
|
+
* `{{< include "task-tutorial-prereqs.md" >}}` resolves in the site's
|
|
30
|
+
* conventional `includes/` directory — so the resolver needs both.
|
|
31
|
+
*/
|
|
32
|
+
readContentFile?: (path: string, fromFile?: string) => string | undefined;
|
|
33
|
+
/** Source file currently being translated, relative to the content root. */
|
|
34
|
+
file?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface HugoLedgerEntry {
|
|
37
|
+
/** Shortcode name. */
|
|
38
|
+
shortcode: string;
|
|
39
|
+
/**
|
|
40
|
+
* What happened:
|
|
41
|
+
* - "preserved" — kept verbatim (unmapped);
|
|
42
|
+
* - "stripped" — deliberately dropped by a mapping;
|
|
43
|
+
* - "degraded" — mapped, but something about it could not be carried.
|
|
44
|
+
*/
|
|
45
|
+
action: "preserved" | "stripped" | "degraded";
|
|
46
|
+
/** Why, in one line. */
|
|
47
|
+
note?: string;
|
|
48
|
+
/** 1-based line in the source. */
|
|
49
|
+
line: number;
|
|
50
|
+
file?: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Turn a shortcode into Dogsbay-MD source.
|
|
54
|
+
*
|
|
55
|
+
* Return `null` to drop it (recorded as "stripped"), or `undefined` to fall
|
|
56
|
+
* through to preservation (recorded as "preserved"). Returning a string means
|
|
57
|
+
* the mapping owns the output.
|
|
58
|
+
*/
|
|
59
|
+
export type ShortcodeMapping = (node: ShortcodeNode, ctx: ShortcodeContext) => string | null | undefined;
|
|
60
|
+
export type ShortcodeRegistry = Record<string, ShortcodeMapping>;
|
|
61
|
+
/** First positional argument, or a named one, whichever the call used. */
|
|
62
|
+
export declare function arg(args: ShortcodeArgs, ...names: string[]): string | undefined;
|
|
63
|
+
/** `{key="value"}` attribute block for a Dogsbay-MD directive, or "". */
|
|
64
|
+
export declare function attrBlock(attrs: Record<string, string | undefined>): string;
|
|
65
|
+
/**
|
|
66
|
+
* Emit a `:::` block directive whose fence is longer than any fence inside it.
|
|
67
|
+
*
|
|
68
|
+
* Hugo shortcodes nest arbitrarily (`tabs > tab > note`), and Dogsbay-MD
|
|
69
|
+
* closes at the first bare `:::`, so a fixed three-colon fence would let an
|
|
70
|
+
* inner directive terminate its own parent and spill the rest of the tab into
|
|
71
|
+
* the page.
|
|
72
|
+
*/
|
|
73
|
+
export declare function directive(name: string, attrs: string, body: string): string;
|
|
74
|
+
/**
|
|
75
|
+
* Merge registry layers. Later layers win, which is what lets a site override
|
|
76
|
+
* a theme's shortcode of the same name.
|
|
77
|
+
*/
|
|
78
|
+
export declare function composeRegistry(...layers: ShortcodeRegistry[]): ShortcodeRegistry;
|
|
79
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,WAAW,gBAAgB;IAC/B,yEAAyE;IACzE,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IACtC,gDAAgD;IAChD,MAAM,EAAE,UAAU,CAAC;IACnB,8BAA8B;IAC9B,MAAM,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC,KAAK,IAAI,CAAC;IACvD,4EAA4E;IAC5E,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACnE;;;;;OAKG;IACH,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAC1E,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,sBAAsB;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,MAAM,EAAE,WAAW,GAAG,UAAU,GAAG,UAAU,CAAC;IAC9C,wBAAwB;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAC7B,IAAI,EAAE,aAAa,EACnB,GAAG,EAAE,gBAAgB,KAClB,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;AAE/B,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;AAEjE,0EAA0E;AAC1E,wBAAgB,GAAG,CAAC,IAAI,EAAE,aAAa,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAK/E;AAED,yEAAyE;AACzE,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,MAAM,CAQ3E;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GACX,MAAM,CAUR;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,MAAM,EAAE,iBAAiB,EAAE,GAAG,iBAAiB,CAEjF"}
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** First positional argument, or a named one, whichever the call used. */
|
|
2
|
+
export function arg(args, ...names) {
|
|
3
|
+
for (const name of names) {
|
|
4
|
+
if (args.named[name] !== undefined)
|
|
5
|
+
return args.named[name];
|
|
6
|
+
}
|
|
7
|
+
return args.positional[0];
|
|
8
|
+
}
|
|
9
|
+
/** `{key="value"}` attribute block for a Dogsbay-MD directive, or "". */
|
|
10
|
+
export function attrBlock(attrs) {
|
|
11
|
+
const entries = Object.entries(attrs).filter((e) => e[1] !== undefined && e[1] !== "");
|
|
12
|
+
if (entries.length === 0)
|
|
13
|
+
return "";
|
|
14
|
+
return `{${entries
|
|
15
|
+
.map(([k, v]) => `${k}="${v.replace(/"/g, """)}"`)
|
|
16
|
+
.join(" ")}}`;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Emit a `:::` block directive whose fence is longer than any fence inside it.
|
|
20
|
+
*
|
|
21
|
+
* Hugo shortcodes nest arbitrarily (`tabs > tab > note`), and Dogsbay-MD
|
|
22
|
+
* closes at the first bare `:::`, so a fixed three-colon fence would let an
|
|
23
|
+
* inner directive terminate its own parent and spill the rest of the tab into
|
|
24
|
+
* the page.
|
|
25
|
+
*/
|
|
26
|
+
export function directive(name, attrs, body) {
|
|
27
|
+
const inner = Math.max(0, ...[...body.matchAll(/^(:{3,})/gm)].map((m) => m[1].length));
|
|
28
|
+
const fence = ":".repeat(Math.max(3, inner + 1));
|
|
29
|
+
const trimmed = body.replace(/^\n+|\n+$/g, "");
|
|
30
|
+
return trimmed
|
|
31
|
+
? `${fence}${name}${attrs}\n${trimmed}\n${fence}`
|
|
32
|
+
: `${fence}${name}${attrs}\n${fence}`;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Merge registry layers. Later layers win, which is what lets a site override
|
|
36
|
+
* a theme's shortcode of the same name.
|
|
37
|
+
*/
|
|
38
|
+
export function composeRegistry(...layers) {
|
|
39
|
+
return Object.assign({}, ...layers);
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAoEA,0EAA0E;AAC1E,MAAM,UAAU,GAAG,CAAC,IAAmB,EAAE,GAAG,KAAe;IACzD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;AAC5B,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,SAAS,CAAC,KAAyC;IACjE,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,CAC1C,CAAC,CAAC,EAAyB,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,SAAS,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAChE,CAAC;IACF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACpC,OAAO,IAAI,OAAO;SACf,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,GAAG,CAAC;SACtD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;AAClB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CACvB,IAAY,EACZ,KAAa,EACb,IAAY;IAEZ,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CACpB,CAAC,EACD,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAC5D,CAAC;IACF,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IAC/C,OAAO,OAAO;QACZ,CAAC,CAAC,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,KAAK,OAAO,KAAK,KAAK,EAAE;QACjD,CAAC,CAAC,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,KAAK,KAAK,EAAE,CAAC;AAC1C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,GAAG,MAA2B;IAC5D,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,MAAM,CAAsB,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface ContentResolverOptions {
|
|
2
|
+
/** Absolute path of the language content root, e.g. `<site>/content/en`. */
|
|
3
|
+
contentRoot: string;
|
|
4
|
+
/**
|
|
5
|
+
* Directories to try, relative to the content root, in order. The default
|
|
6
|
+
* covers Hugo's conventional `includes/` plus the `examples/` tree
|
|
7
|
+
* `code_sample` reads from.
|
|
8
|
+
*/
|
|
9
|
+
searchDirs?: string[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Build a reader that takes a shortcode's file argument and the page it was
|
|
13
|
+
* called from, and returns the file's text — or undefined, so the caller
|
|
14
|
+
* records an unresolved reference rather than emitting nothing silently.
|
|
15
|
+
*/
|
|
16
|
+
export declare function makeContentResolver(opts: ContentResolverOptions): (path: string, fromFile?: string) => string | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Whether a shortcode's `include=` argument names a FILE.
|
|
19
|
+
*
|
|
20
|
+
* It does not always: `{{< feature-gate-table include="alpha,beta" >}}` uses
|
|
21
|
+
* the same attribute name as a filter. Treating that as a path produced a
|
|
22
|
+
* guaranteed-failing read and a misleading "could not read included file"
|
|
23
|
+
* ledger entry on every one of them.
|
|
24
|
+
*/
|
|
25
|
+
export declare function looksLikeFile(value: string | undefined): boolean;
|
|
26
|
+
//# sourceMappingURL=resolve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,sBAAsB;IACrC,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,sBAAsB,GAC3B,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CA6CzD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAEhE"}
|
package/dist/resolve.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File resolution for `{{< include >}}`, `{{< code_sample >}}` and
|
|
3
|
+
* `{{< tab include= >}}`.
|
|
4
|
+
*
|
|
5
|
+
* Hugo shortcodes reference files by a site convention, not by a path the
|
|
6
|
+
* caller can use directly. Measured on kubernetes/website:
|
|
7
|
+
*
|
|
8
|
+
* {{< include "task-tutorial-prereqs.md" >}} → content/en/includes/… (131×)
|
|
9
|
+
* {{< include "included/verify-kubectl.md" >}} → beside the page (9×)
|
|
10
|
+
* {{< code_sample file="pods/simple.yaml" >}} → content/en/examples/…
|
|
11
|
+
* {{< tab include="generated/x.md" />}} → beside the page
|
|
12
|
+
*
|
|
13
|
+
* Resolving only one of those left 171 of 171 includes and 106 tabs empty,
|
|
14
|
+
* each with a ledger entry — honest, but the content was right there.
|
|
15
|
+
*/
|
|
16
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
17
|
+
import { dirname, isAbsolute, resolve, sep } from "node:path";
|
|
18
|
+
/**
|
|
19
|
+
* Build a reader that takes a shortcode's file argument and the page it was
|
|
20
|
+
* called from, and returns the file's text — or undefined, so the caller
|
|
21
|
+
* records an unresolved reference rather than emitting nothing silently.
|
|
22
|
+
*/
|
|
23
|
+
export function makeContentResolver(opts) {
|
|
24
|
+
const { contentRoot } = opts;
|
|
25
|
+
const searchDirs = opts.searchDirs ?? ["includes", "examples", "static", "."];
|
|
26
|
+
return (path, fromFile) => {
|
|
27
|
+
if (!path)
|
|
28
|
+
return undefined;
|
|
29
|
+
// Reject `..` outright. Hugo shortcode file arguments are never legitimately
|
|
30
|
+
// parent-relative, and allowing them let a path escape the search
|
|
31
|
+
// directories entirely: `../secret.txt` resolved against `includes/`
|
|
32
|
+
// reaches the content root, and the startsWith guard below does not catch
|
|
33
|
+
// that because the result is still inside the root.
|
|
34
|
+
if (path.split(/[\\/]/).some((seg) => seg === ".."))
|
|
35
|
+
return undefined;
|
|
36
|
+
const clean = path.replace(/^\.\//, "");
|
|
37
|
+
const candidates = [];
|
|
38
|
+
// Page-relative first: `included/verify-kubectl.md` sits beside the page.
|
|
39
|
+
if (fromFile) {
|
|
40
|
+
const pageDir = isAbsolute(fromFile)
|
|
41
|
+
? dirname(fromFile)
|
|
42
|
+
: resolve(contentRoot, dirname(fromFile));
|
|
43
|
+
candidates.push(resolve(pageDir, clean));
|
|
44
|
+
}
|
|
45
|
+
// Then the site conventions.
|
|
46
|
+
for (const dir of searchDirs) {
|
|
47
|
+
candidates.push(resolve(contentRoot, dir, clean.replace(/^\//, "")));
|
|
48
|
+
}
|
|
49
|
+
// Finally, root-relative.
|
|
50
|
+
candidates.push(resolve(contentRoot, clean.replace(/^\//, "")));
|
|
51
|
+
for (const candidate of candidates) {
|
|
52
|
+
// Never escape the content root: a shortcode argument is site data, and
|
|
53
|
+
// `../../etc/passwd` must not be readable through it.
|
|
54
|
+
// `startsWith` alone would admit a sibling directory whose name shares
|
|
55
|
+
// the prefix (`/content/en-old` for a root of `/content/en`).
|
|
56
|
+
if (candidate !== contentRoot && !candidate.startsWith(contentRoot + sep))
|
|
57
|
+
continue;
|
|
58
|
+
try {
|
|
59
|
+
if (existsSync(candidate) && statSync(candidate).isFile()) {
|
|
60
|
+
return readFileSync(candidate, "utf-8");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// Unreadable — keep trying the remaining candidates.
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return undefined;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Whether a shortcode's `include=` argument names a FILE.
|
|
72
|
+
*
|
|
73
|
+
* It does not always: `{{< feature-gate-table include="alpha,beta" >}}` uses
|
|
74
|
+
* the same attribute name as a filter. Treating that as a path produced a
|
|
75
|
+
* guaranteed-failing read and a misleading "could not read included file"
|
|
76
|
+
* ledger entry on every one of them.
|
|
77
|
+
*/
|
|
78
|
+
export function looksLikeFile(value) {
|
|
79
|
+
return typeof value === "string" && /\.[A-Za-z0-9]{1,6}$/.test(value.trim());
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=resolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC7D,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAa9D;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CACjC,IAA4B;IAE5B,MAAM,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC;IAC7B,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;IAE9E,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE;QACxB,IAAI,CAAC,IAAI;YAAE,OAAO,SAAS,CAAC;QAC5B,6EAA6E;QAC7E,kEAAkE;QAClE,qEAAqE;QACrE,0EAA0E;QAC1E,oDAAoD;QACpD,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,IAAI,CAAC;YAAE,OAAO,SAAS,CAAC;QACtE,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACxC,MAAM,UAAU,GAAa,EAAE,CAAC;QAEhC,0EAA0E;QAC1E,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,UAAU,CAAC,QAAQ,CAAC;gBAClC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;gBACnB,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;YAC5C,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;QAC3C,CAAC;QACD,6BAA6B;QAC7B,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;YAC7B,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;QACvE,CAAC;QACD,0BAA0B;QAC1B,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;QAEhE,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACnC,wEAAwE;YACxE,sDAAsD;YACtD,uEAAuE;YACvE,8DAA8D;YAC9D,IAAI,SAAS,KAAK,WAAW,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,WAAW,GAAG,GAAG,CAAC;gBAAE,SAAS;YACpF,IAAI,CAAC;gBACH,IAAI,UAAU,CAAC,SAAS,CAAC,IAAI,QAAQ,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;oBAC1D,OAAO,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;gBAC1C,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,qDAAqD;YACvD,CAAC;QACH,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,KAAyB;IACrD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;AAC/E,CAAC"}
|