@dogsbay/ssg 0.2.0-beta.140 → 0.2.0-beta.142
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/package/THIRD-PARTY-NOTICES.txt +5 -5
- package/dist/package/browser.mjs +382 -220
- package/dist/package/types/core/build.d.ts +17 -0
- package/dist/package/types/core/config.d.ts +8 -0
- package/dist/package/types/core/dev.d.ts +11 -0
- package/dist/package/types/core/openapi-pages.d.ts +31 -0
- package/dist/package/types/package/browser.d.ts +1 -0
- package/package.json +29 -29
|
@@ -1,31 +1,31 @@
|
|
|
1
1
|
Third-party software in @dogsbay/ssg (dist/package/)
|
|
2
2
|
|
|
3
3
|
========================================================================
|
|
4
|
-
@dogsbay/elements@0.2.0-beta.
|
|
4
|
+
@dogsbay/elements@0.2.0-beta.142 (MIT)
|
|
5
5
|
https://github.com/dogsbay/dogsbay.git
|
|
6
6
|
|
|
7
7
|
Licensed under MIT; the package ships no licence file.
|
|
8
8
|
|
|
9
9
|
========================================================================
|
|
10
|
-
@dogsbay/primitives@0.2.0-beta.
|
|
10
|
+
@dogsbay/primitives@0.2.0-beta.142 (MIT)
|
|
11
11
|
https://github.com/dogsbay/dogsbay.git
|
|
12
12
|
|
|
13
13
|
Licensed under MIT; the package ships no licence file.
|
|
14
14
|
|
|
15
15
|
========================================================================
|
|
16
|
-
@dogsbay/render-html@0.2.0-beta.
|
|
16
|
+
@dogsbay/render-html@0.2.0-beta.142 (MIT)
|
|
17
17
|
https://github.com/dogsbay/dogsbay.git
|
|
18
18
|
|
|
19
19
|
Licensed under MIT; the package ships no licence file.
|
|
20
20
|
|
|
21
21
|
========================================================================
|
|
22
|
-
@dogsbay/search@0.2.0-beta.
|
|
22
|
+
@dogsbay/search@0.2.0-beta.142 (MIT)
|
|
23
23
|
https://github.com/dogsbay/dogsbay.git
|
|
24
24
|
|
|
25
25
|
Licensed under MIT; the package ships no licence file.
|
|
26
26
|
|
|
27
27
|
========================================================================
|
|
28
|
-
@dogsbay/ui@0.2.0-beta.
|
|
28
|
+
@dogsbay/ui@0.2.0-beta.142 (MIT)
|
|
29
29
|
https://github.com/dogsbay/dogsbay.git
|
|
30
30
|
|
|
31
31
|
Licensed under MIT; the package ships no licence file.
|
package/dist/package/browser.mjs
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
|
|
19
19
|
// src/core/build.ts
|
|
20
20
|
import { dogsbayMdToTree, splitFrontmatter as splitFrontmatter2, treeToDogsbayMd } from "@dogsbay/format-dogsbay-md/browser";
|
|
21
|
-
import { buildHreflangLinks, missingTranslations, namespaceNavGroup, prefixSlug, rewriteContentRefs } from "@dogsbay/site-files";
|
|
21
|
+
import { buildHreflangLinks, isMountRoot, missingTranslations, mountRefs, mountRootNote, namespaceNavGroup, placeMountedNav, prefixSlug as prefixSlug2, rewriteContentRefs } from "@dogsbay/site-files";
|
|
22
22
|
|
|
23
23
|
// src/core/canonical.ts
|
|
24
24
|
import { decideCanonical } from "@dogsbay/site-files";
|
|
@@ -26,7 +26,7 @@ import { declaresParams, splitFrontmatter } from "@dogsbay/format-dogsbay-md/bro
|
|
|
26
26
|
|
|
27
27
|
// src/core/config.ts
|
|
28
28
|
import { scriptUrl } from "@dogsbay/render-html";
|
|
29
|
-
import { assertLeafSitemap, checkVersionSeo, isNamespaceAxisActive, namespaceSegments } from "@dogsbay/site-files";
|
|
29
|
+
import { assertLeafSitemap, checkVersionSeo, isNamespaceAxisActive, namespaceSegments, parseMount } from "@dogsbay/site-files";
|
|
30
30
|
|
|
31
31
|
// src/core/yaml.ts
|
|
32
32
|
import { load } from "js-yaml";
|
|
@@ -67,6 +67,14 @@ function unitId(kind, v) {
|
|
|
67
67
|
if (!/^[a-z0-9][a-z0-9._-]*$/i.test(id)) throw new ConfigError(`${kind}: id "${id.slice(0, 80)}" must be one URL segment of letters, digits, dots, dashes and underscores`);
|
|
68
68
|
return id;
|
|
69
69
|
}
|
|
70
|
+
function sourceMount(s) {
|
|
71
|
+
const at = `content.sources[${String(s.path)}]`;
|
|
72
|
+
if (s.from !== "openapi") throw new ConfigError(`${at}: mount is only for a "from: openapi" source`);
|
|
73
|
+
if (s.name != null) throw new ConfigError(`${at} has both name and mount: a mounted source goes inside its version, so it takes no name`);
|
|
74
|
+
const parsed = parseMount(s.mount);
|
|
75
|
+
if ("error" in parsed) throw new ConfigError(`${at}: ${parsed.error}`);
|
|
76
|
+
return parsed.segments.join("/");
|
|
77
|
+
}
|
|
70
78
|
function sourceFrom(v, path) {
|
|
71
79
|
if (v === "dogsbay-md" || v === "openapi") return v;
|
|
72
80
|
throw new ConfigError(`content.sources[${path}].from must be "dogsbay-md" or "openapi"; got ${JSON.stringify(v)}`);
|
|
@@ -139,6 +147,7 @@ function parseConfig(text, overrides = {}) {
|
|
|
139
147
|
path: sourcePath(s),
|
|
140
148
|
...s.name != null ? { name: sourceName(s.name) } : {},
|
|
141
149
|
...s.from != null ? { from: sourceFrom(s.from, String(s.path)) } : {},
|
|
150
|
+
...s.mount != null ? { mount: sourceMount(s) } : {},
|
|
142
151
|
version: s.version != null ? unitId("version", s.version) : void 0,
|
|
143
152
|
locale: s.locale != null ? unitId("locale", s.locale) : void 0,
|
|
144
153
|
attributes: s.attributes ?? void 0,
|
|
@@ -414,29 +423,41 @@ function buildUnits(cfg) {
|
|
|
414
423
|
throw new ConfigError(`source "${u.source.path}" has locale "${u.locale}" which is not declared in locales`);
|
|
415
424
|
}
|
|
416
425
|
}
|
|
417
|
-
const namespaces = isNamespaceAxisActive(cfg.sources);
|
|
426
|
+
const namespaces = isNamespaceAxisActive(cfg.sources.filter((s) => !s.mount));
|
|
418
427
|
if (namespaces && (versionAxis || localeAxis)) {
|
|
419
428
|
throw new ConfigError("named sources (content.sources name:) cannot be combined with versions or locales yet (plans/openapi-sources.md)");
|
|
420
429
|
}
|
|
421
430
|
for (const s of cfg.sources) {
|
|
422
|
-
if (s.from === "openapi" && cfg.sources.length > 1 && !s.name) throw new ConfigError(`content.sources[${s.path}]: an OpenAPI source beside other sources needs a name:, so its pages do not land on theirs`);
|
|
431
|
+
if (s.from === "openapi" && cfg.sources.length > 1 && !s.name && !s.mount) throw new ConfigError(`content.sources[${s.path}]: an OpenAPI source beside other sources needs a name: or a mount:, so its pages do not land on theirs`);
|
|
423
432
|
}
|
|
424
433
|
const units = /* @__PURE__ */ new Map();
|
|
425
434
|
for (const u of withDefaults2) {
|
|
426
435
|
const atRoot = cfg.versions.find((v) => v.id === u.version)?.path === "";
|
|
427
436
|
const prefix = [localeAxis ? u.locale : void 0, versionAxis && !atRoot ? u.version : void 0].filter(Boolean).join("/");
|
|
428
|
-
const member = { source: u.source, namespace: namespaceSegments(u.source, cfg.sources).join("/") };
|
|
437
|
+
const member = u.source.mount ? { source: u.source, namespace: "", mount: u.source.mount } : { source: u.source, namespace: namespaceSegments(u.source, cfg.sources).join("/") };
|
|
429
438
|
const unit = units.get(prefix);
|
|
430
439
|
if (!unit) {
|
|
431
440
|
units.set(prefix, { source: u.source, members: [member], version: versionAxis ? u.version : void 0, locale: localeAxis ? u.locale : void 0, prefix });
|
|
432
441
|
continue;
|
|
433
442
|
}
|
|
434
|
-
if (
|
|
435
|
-
|
|
436
|
-
throw new ConfigError(
|
|
443
|
+
if (member.mount) {
|
|
444
|
+
const taken = unit.members.find((m) => m.mount === member.mount);
|
|
445
|
+
if (taken) throw new ConfigError(`two sources are mounted at "${member.mount}" in the same version and locale (${taken.source.path}, ${u.source.path})`);
|
|
446
|
+
} else if (unit.members.some((m) => !m.mount)) {
|
|
447
|
+
if (!namespaces) throw new ConfigError(`two sources publish to the same prefix "${prefix || "/"}"`);
|
|
448
|
+
if (unit.members.some((m) => !m.mount && m.namespace === member.namespace)) {
|
|
449
|
+
throw new ConfigError(member.namespace ? `two sources are named "${member.namespace}"` : `two unnamed sources publish to the same prefix "${prefix || "/"}"`);
|
|
450
|
+
}
|
|
437
451
|
}
|
|
438
452
|
unit.members.push(member);
|
|
439
|
-
|
|
453
|
+
const main = unit.members.find((m) => m.source === unit.source);
|
|
454
|
+
if (!member.namespace && !member.mount && (main.namespace || main.mount)) unit.source = u.source;
|
|
455
|
+
}
|
|
456
|
+
for (const unit of units.values()) {
|
|
457
|
+
for (const m of unit.members) {
|
|
458
|
+
const ns = m.mount && unit.members.find((o) => o.namespace && m.mount.split("/")[0] === o.namespace);
|
|
459
|
+
if (ns) throw new ConfigError(`content.sources[${m.source.path}]: mount "${m.mount}" is inside the folder of the source named "${ns.namespace}"; mount it elsewhere`);
|
|
460
|
+
}
|
|
440
461
|
}
|
|
441
462
|
return [...units.values()];
|
|
442
463
|
}
|
|
@@ -454,8 +475,7 @@ function projectPath(unit, path) {
|
|
|
454
475
|
for (const m of unit.members) {
|
|
455
476
|
if (m.namespace && m.source.from !== "openapi" && (path === m.namespace || path.startsWith(m.namespace + "/"))) return sourceRoot(m.source) + path.slice(m.namespace.length + 1);
|
|
456
477
|
}
|
|
457
|
-
|
|
458
|
-
return sourceRoot(main.source) + path;
|
|
478
|
+
return sourceRoot(unit.source) + path;
|
|
459
479
|
}
|
|
460
480
|
var TOPIC_MAP = /\.topicmap\.ya?ml$/;
|
|
461
481
|
function sourceNav(v, path, from) {
|
|
@@ -559,6 +579,209 @@ function createResolver(table, page) {
|
|
|
559
579
|
};
|
|
560
580
|
}
|
|
561
581
|
|
|
582
|
+
// src/core/openapi-pages.ts
|
|
583
|
+
import { mountSlug, prefixSlug } from "@dogsbay/site-files";
|
|
584
|
+
|
|
585
|
+
// src/core/resolve.ts
|
|
586
|
+
import { inlineText } from "@dogsbay/render-html";
|
|
587
|
+
import { slugify } from "@dogsbay/render-html/html";
|
|
588
|
+
import { CORE_SCHEMA, load as loadYaml2 } from "js-yaml";
|
|
589
|
+
function hasPlaceholder(nodes, types) {
|
|
590
|
+
return (nodes ?? []).some((n) => types.has(n.type) || hasPlaceholder(n.children, types));
|
|
591
|
+
}
|
|
592
|
+
async function resolvePlaceholders(tree, resolvers, ctx) {
|
|
593
|
+
const byType = new Map(resolvers.map((r) => [r.type, r]));
|
|
594
|
+
if (!hasPlaceholder(tree, new Set(byType.keys()))) return void 0;
|
|
595
|
+
const usedIds = headingIds(tree);
|
|
596
|
+
const warnings = [];
|
|
597
|
+
const walk = async (nodes) => {
|
|
598
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
599
|
+
const r = byType.get(nodes[i].type);
|
|
600
|
+
if (!r) {
|
|
601
|
+
if (nodes[i].children) await walk(nodes[i].children);
|
|
602
|
+
continue;
|
|
603
|
+
}
|
|
604
|
+
let got;
|
|
605
|
+
try {
|
|
606
|
+
got = await r.resolve(nodes[i], { ...ctx, usedIds });
|
|
607
|
+
} catch (e) {
|
|
608
|
+
got = { warning: e.message || String(e) };
|
|
609
|
+
}
|
|
610
|
+
if ("warning" in got) {
|
|
611
|
+
warnings.push(got.warning);
|
|
612
|
+
nodes.splice(i, 1, ...nodes[i].children ?? []);
|
|
613
|
+
i--;
|
|
614
|
+
continue;
|
|
615
|
+
}
|
|
616
|
+
headingIds(got.nodes, usedIds);
|
|
617
|
+
nodes.splice(i, 1, ...got.nodes);
|
|
618
|
+
i += got.nodes.length - 1;
|
|
619
|
+
}
|
|
620
|
+
};
|
|
621
|
+
await walk(tree);
|
|
622
|
+
return warnings;
|
|
623
|
+
}
|
|
624
|
+
function headingIds(nodes, ids = /* @__PURE__ */ new Set()) {
|
|
625
|
+
const take = (raw) => {
|
|
626
|
+
const base = RESERVED_ID.test(raw) ? `h-${raw}` : raw;
|
|
627
|
+
let id = base || "section";
|
|
628
|
+
for (let n = 2; ids.has(id); n++) id = `${base}-${n}`;
|
|
629
|
+
ids.add(id);
|
|
630
|
+
};
|
|
631
|
+
const walk = (list) => {
|
|
632
|
+
for (const n of list) {
|
|
633
|
+
if (n.type === "heading") {
|
|
634
|
+
const p = n.props ?? {};
|
|
635
|
+
take(typeof p.id === "string" ? p.id : typeof p.slug === "string" ? p.slug : slugify(inlineText(n.inline)));
|
|
636
|
+
} else if ((n.type === "prose" || n.type === "html") && typeof n.html === "string") {
|
|
637
|
+
for (const m of n.html.matchAll(/<h[1-6]\b[^>]*\sid="([^"]*)"/gi)) take(m[1]);
|
|
638
|
+
}
|
|
639
|
+
if (n.children) walk(n.children);
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
walk(nodes);
|
|
643
|
+
return ids;
|
|
644
|
+
}
|
|
645
|
+
var RESERVED_ID = /^(db-|tab-\d|tabpanel-\d|acc-\d|accpanel-\d|fn-|fnref-)/;
|
|
646
|
+
var autodocUnresolved = {
|
|
647
|
+
type: "autodoc-ref",
|
|
648
|
+
async resolve(node) {
|
|
649
|
+
const ref = typeof node.props?.ref === "string" ? node.props.ref : "";
|
|
650
|
+
return { warning: `:::autodoc${ref ? `{ref="${ref}"}` : ""} is not resolved in this build: resolve it at migration time (dogsbay migrate-mkdocs --inline-autodoc)` };
|
|
651
|
+
}
|
|
652
|
+
};
|
|
653
|
+
var coreLoad;
|
|
654
|
+
var loadCore = () => coreLoad ??= import("@dogsbay/format-openapi/core");
|
|
655
|
+
function openApiResolver(read, maxChars = 8e6) {
|
|
656
|
+
let specs;
|
|
657
|
+
return {
|
|
658
|
+
type: "openapi-ref",
|
|
659
|
+
async resolve(node, ctx) {
|
|
660
|
+
const spec = typeof node.props?.spec === "string" ? node.props.spec : "";
|
|
661
|
+
const core = await loadCore();
|
|
662
|
+
const selected = core.directiveOperation(node.props);
|
|
663
|
+
if ("error" in selected) return { warning: `:::openapi: ${selected.error}` };
|
|
664
|
+
const slug = ctx.source.replace(/\.(md|markdown)$/i, "").replace(/(^|\/)index$/, "");
|
|
665
|
+
const found = core.findSpec(spec, slug, (p) => {
|
|
666
|
+
const at = ctx.scope ? ctx.scope(p) : p;
|
|
667
|
+
return ctx.has(at) ? { path: at } : void 0;
|
|
668
|
+
});
|
|
669
|
+
if ("error" in found) return { warning: `:::openapi: ${found.error}` };
|
|
670
|
+
specs ??= core.createSpecCache(async (p) => {
|
|
671
|
+
const text = await read(p);
|
|
672
|
+
if (text === void 0) throw new Error("not found");
|
|
673
|
+
return text;
|
|
674
|
+
}, (text) => loadYaml2(text, { schema: CORE_SCHEMA }));
|
|
675
|
+
let prepared;
|
|
676
|
+
try {
|
|
677
|
+
prepared = await specs.get(found.path);
|
|
678
|
+
} catch (e) {
|
|
679
|
+
return { warning: `:::openapi: cannot read ${spec}: ${e.message}` };
|
|
680
|
+
}
|
|
681
|
+
let tree;
|
|
682
|
+
try {
|
|
683
|
+
tree = core.embedSpec(prepared, { usedIds: ctx.usedIds, operation: selected.operation }).tree;
|
|
684
|
+
} catch (e) {
|
|
685
|
+
return { warning: `:::openapi: cannot use ${spec}: ${e.message}` };
|
|
686
|
+
}
|
|
687
|
+
const size = boundedSize(tree, maxChars);
|
|
688
|
+
if (size === "cycle") return { warning: `:::openapi: ${spec} has a cyclic example or schema, which cannot be shown` };
|
|
689
|
+
if (size === "over") return { warning: `:::openapi: ${spec} is too large to embed in one page (over ${Math.round(maxChars / 1e6)}M characters)` };
|
|
690
|
+
return { nodes: tree };
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
async function openApiSourcePages(read, specPath, reservedSlugs) {
|
|
695
|
+
const core = await loadCore();
|
|
696
|
+
const specs = core.createSpecCache(async (p) => {
|
|
697
|
+
const text = await read(p);
|
|
698
|
+
if (text === void 0) throw new Error("not found");
|
|
699
|
+
return text;
|
|
700
|
+
}, (text) => loadYaml2(text, { schema: CORE_SCHEMA }));
|
|
701
|
+
try {
|
|
702
|
+
return core.specToPages(await specs.get(specPath), { reservedSlugs });
|
|
703
|
+
} catch (e) {
|
|
704
|
+
return { error: `cannot read ${specPath}: ${e.message}` };
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
async function specFileOf(path, has, read) {
|
|
708
|
+
const p = path.replace(/^\.\//, "").replace(/\/+$/, "");
|
|
709
|
+
if (/\.(ya?ml|json)$/i.test(p)) return has(p) ? p : void 0;
|
|
710
|
+
const dir = p === "." || p === "" ? "" : p + "/";
|
|
711
|
+
const found = ["openapi", "swagger", "spec"].flatMap((b) => ["yaml", "yml", "json"].map((e) => `${dir}${b}.${e}`)).filter(has);
|
|
712
|
+
for (const f of found) {
|
|
713
|
+
const head = (await read(f))?.slice(0, 2048) ?? "";
|
|
714
|
+
if (/(?:^|[{,])\s*"?(openapi|swagger)"?\s*[:=]/m.test(head)) return f;
|
|
715
|
+
}
|
|
716
|
+
return found[0];
|
|
717
|
+
}
|
|
718
|
+
function boundedSize(value, max) {
|
|
719
|
+
let total = 0;
|
|
720
|
+
const path = /* @__PURE__ */ new Set();
|
|
721
|
+
const walk = (v) => {
|
|
722
|
+
if (typeof v === "string") total += v.length;
|
|
723
|
+
else if (v && typeof v === "object") {
|
|
724
|
+
if (path.has(v)) return "cycle";
|
|
725
|
+
path.add(v);
|
|
726
|
+
total += 2;
|
|
727
|
+
for (const x of Array.isArray(v) ? v : Object.values(v)) {
|
|
728
|
+
const r = walk(x);
|
|
729
|
+
if (r) return r;
|
|
730
|
+
if (total > max) return "over";
|
|
731
|
+
}
|
|
732
|
+
path.delete(v);
|
|
733
|
+
} else total += 4;
|
|
734
|
+
return total > max ? "over" : void 0;
|
|
735
|
+
};
|
|
736
|
+
return walk(value) ?? total;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
// src/core/openapi-pages.ts
|
|
740
|
+
function urlPathOf(url) {
|
|
741
|
+
if (!url) return [];
|
|
742
|
+
try {
|
|
743
|
+
return new URL(url).pathname.split("/").filter(Boolean);
|
|
744
|
+
} catch {
|
|
745
|
+
return [];
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
async function placeApiPages(cfg, unit, m, files, readText2) {
|
|
749
|
+
const read = async (p) => {
|
|
750
|
+
const c = files.get(p);
|
|
751
|
+
return c == null ? void 0 : readText2(c);
|
|
752
|
+
};
|
|
753
|
+
const spec = await specFileOf(m.source.path, (p) => files.has(p), read);
|
|
754
|
+
if (!spec) return { missing: true };
|
|
755
|
+
const ns = m.namespace;
|
|
756
|
+
const mount = m.mount?.split("/");
|
|
757
|
+
const segs = mount ?? (ns ? [ns] : []);
|
|
758
|
+
const first = ns || unit.prefix.split("/")[0];
|
|
759
|
+
const combined = [...urlPathOf(cfg.url), ...cfg.basePath.split("/").filter(Boolean)];
|
|
760
|
+
const taxonomies = new Set(cfg.taxonomies.map((t) => t.path.split("/")[0]).filter(Boolean));
|
|
761
|
+
const got = await openApiSourcePages(read, spec, [...first ? [first] : [], ...combined.slice(0, 1), ...taxonomies]);
|
|
762
|
+
if ("error" in got) return { spec, error: got.error };
|
|
763
|
+
const pages = got.pages.map((page) => {
|
|
764
|
+
const slug = mount ? mountSlug(mount, page.slug) : prefixSlug(segs, page.slug === "index" ? "" : page.slug);
|
|
765
|
+
const isIndex = page.slug === "index" || got.pages.some((q) => q.slug.startsWith(page.slug + "/"));
|
|
766
|
+
return { slug, out: outFileFor(slug, isIndex && slug !== ""), isIndex, page };
|
|
767
|
+
});
|
|
768
|
+
return { spec, pages, nav: got.nav, segs };
|
|
769
|
+
}
|
|
770
|
+
var outsCache = /* @__PURE__ */ new WeakMap();
|
|
771
|
+
async function apiPageOuts(cfg, unit, m, files, readText2) {
|
|
772
|
+
let cache = outsCache.get(files);
|
|
773
|
+
if (!cache) outsCache.set(files, cache = /* @__PURE__ */ new Map());
|
|
774
|
+
const id = `${unit.prefix}\0${m.source.path}`;
|
|
775
|
+
const key = JSON.stringify([m.namespace, m.mount, cfg.url, cfg.basePath, cfg.taxonomies.map((t) => t.path)]);
|
|
776
|
+
const hit = cache.get(id);
|
|
777
|
+
const specFile = /\.(ya?ml|json)$/i.test(m.source.path) ? files.get(m.source.path.replace(/^\.\//, "")) : void 0;
|
|
778
|
+
if (hit && hit.key === key && specFile !== void 0 && hit.spec === specFile) return hit.outs;
|
|
779
|
+
const placed = await placeApiPages(cfg, unit, m, files, readText2);
|
|
780
|
+
const outs = "pages" in placed ? placed.pages.map((p) => [p.slug, p.out]) : [];
|
|
781
|
+
cache.set(id, { spec: specFile, key, outs });
|
|
782
|
+
return outs;
|
|
783
|
+
}
|
|
784
|
+
|
|
562
785
|
// src/core/nav.ts
|
|
563
786
|
import { scriptUrl as scriptUrl2 } from "@dogsbay/render-html";
|
|
564
787
|
function slugFromPath(p) {
|
|
@@ -851,7 +1074,7 @@ async function defaultTables(cfg, units, built, files, read, warn = () => {
|
|
|
851
1074
|
const tables = /* @__PURE__ */ new Map();
|
|
852
1075
|
if (!cfg.defaultVersion || !built.some((u) => u.version && u.version !== cfg.defaultVersion)) return tables;
|
|
853
1076
|
for (const unit of units.filter((u) => u.version === cfg.defaultVersion)) {
|
|
854
|
-
const t = await scan(cfg, unit, units, files, fm ?? frontMatterCache(read));
|
|
1077
|
+
const t = await scan(cfg, unit, units, files, fm ?? frontMatterCache(read), read);
|
|
855
1078
|
if (!t.pages.size && built.some((u) => u.version !== cfg.defaultVersion && u.locale === unit.locale)) {
|
|
856
1079
|
warn(`[canonical] the default version "${cfg.defaultVersion}" has no pages under ${unit.source.path}, so older versions' pages keep themselves as canonical`);
|
|
857
1080
|
}
|
|
@@ -864,17 +1087,17 @@ async function languageTables(cfg, units, built, files, read, fm) {
|
|
|
864
1087
|
if (new Set(units.map((u) => u.locale).filter(Boolean)).size < 2) return tables;
|
|
865
1088
|
const versions = new Set(built.map((u) => u.version));
|
|
866
1089
|
const fmOf = fm ?? frontMatterCache(read);
|
|
867
|
-
for (const unit of units) if (versions.has(unit.version)) tables.set(unit.prefix, (await scan(cfg, unit, units, files, fmOf)).pages);
|
|
1090
|
+
for (const unit of units) if (versions.has(unit.version)) tables.set(unit.prefix, (await scan(cfg, unit, units, files, fmOf, read)).pages);
|
|
868
1091
|
return tables;
|
|
869
1092
|
}
|
|
870
1093
|
async function translationTables(cfg, units, files, read, includeDrafts, fm) {
|
|
871
1094
|
const tables = /* @__PURE__ */ new Map();
|
|
872
1095
|
if (new Set(units.map((u) => u.locale).filter(Boolean)).size < 2) return tables;
|
|
873
1096
|
const fmOf = fm ?? frontMatterCache(read);
|
|
874
|
-
for (const unit of units) tables.set(unit.prefix, await scan(cfg, unit, units, files, fmOf, { kind: "built", includeDrafts }));
|
|
1097
|
+
for (const unit of units) tables.set(unit.prefix, await scan(cfg, unit, units, files, fmOf, read, { kind: "built", includeDrafts }));
|
|
875
1098
|
return tables;
|
|
876
1099
|
}
|
|
877
|
-
async function scan(cfg, unit, units, files, frontMatterOf, mode = { kind: "targets" }) {
|
|
1100
|
+
async function scan(cfg, unit, units, files, frontMatterOf, read, mode = { kind: "targets" }) {
|
|
878
1101
|
const rootOf = (u) => u.source.path === "." ? "" : u.source.path.replace(/^\.\//, "").replace(/\/+$/, "") + "/";
|
|
879
1102
|
const root = rootOf(unit);
|
|
880
1103
|
const otherRoots = units.filter((u) => u !== unit).map(rootOf).filter((r) => r && r.startsWith(root));
|
|
@@ -912,6 +1135,10 @@ async function scan(cfg, unit, units, files, frontMatterOf, mode = { kind: "targ
|
|
|
912
1135
|
for (const a of fm.aliases) if (typeof a === "string" && inUnit(a) !== void 0) setFirst(inUnit(a), detach(key(slug)));
|
|
913
1136
|
}
|
|
914
1137
|
}
|
|
1138
|
+
for (const m of unit.members) {
|
|
1139
|
+
if (m.source.from !== "openapi") continue;
|
|
1140
|
+
for (const [slug, out] of await apiPageOuts(cfg, unit, m, files, read)) if (!pages.has(key(slug))) pages.set(detach(key(slug)), out);
|
|
1141
|
+
}
|
|
915
1142
|
for (const r of cfg.redirects) {
|
|
916
1143
|
const from = sitePath(r.from, cfg.basePath);
|
|
917
1144
|
if (!mine(from)) continue;
|
|
@@ -1085,31 +1312,31 @@ var CLIENT_CANDIDATES = ['""', '"&"', '"\\\\$&")).join("|")})', '"\\\\$&")}"', '
|
|
|
1085
1312
|
var SITE_NOTICES = `Third-party software in this site's _dogsbay/ files (dogsbay.js, site.css) and pages
|
|
1086
1313
|
|
|
1087
1314
|
========================================================================
|
|
1088
|
-
@dogsbay/elements@0.2.0-beta.
|
|
1315
|
+
@dogsbay/elements@0.2.0-beta.142 (MIT)
|
|
1089
1316
|
https://github.com/dogsbay/dogsbay.git
|
|
1090
1317
|
|
|
1091
1318
|
Licensed under MIT; the package ships no licence file.
|
|
1092
1319
|
|
|
1093
1320
|
========================================================================
|
|
1094
|
-
@dogsbay/primitives@0.2.0-beta.
|
|
1321
|
+
@dogsbay/primitives@0.2.0-beta.142 (MIT)
|
|
1095
1322
|
https://github.com/dogsbay/dogsbay.git
|
|
1096
1323
|
|
|
1097
1324
|
Licensed under MIT; the package ships no licence file.
|
|
1098
1325
|
|
|
1099
1326
|
========================================================================
|
|
1100
|
-
@dogsbay/render-html@0.2.0-beta.
|
|
1327
|
+
@dogsbay/render-html@0.2.0-beta.142 (MIT)
|
|
1101
1328
|
https://github.com/dogsbay/dogsbay.git
|
|
1102
1329
|
|
|
1103
1330
|
Licensed under MIT; the package ships no licence file.
|
|
1104
1331
|
|
|
1105
1332
|
========================================================================
|
|
1106
|
-
@dogsbay/search@0.2.0-beta.
|
|
1333
|
+
@dogsbay/search@0.2.0-beta.142 (MIT)
|
|
1107
1334
|
https://github.com/dogsbay/dogsbay.git
|
|
1108
1335
|
|
|
1109
1336
|
Licensed under MIT; the package ships no licence file.
|
|
1110
1337
|
|
|
1111
1338
|
========================================================================
|
|
1112
|
-
@dogsbay/ui@0.2.0-beta.
|
|
1339
|
+
@dogsbay/ui@0.2.0-beta.142 (MIT)
|
|
1113
1340
|
https://github.com/dogsbay/dogsbay.git
|
|
1114
1341
|
|
|
1115
1342
|
Licensed under MIT; the package ships no licence file.
|
|
@@ -1560,160 +1787,6 @@ import { FAVICON_SVG } from "@dogsbay/render-html/favicon";
|
|
|
1560
1787
|
import { codeRequest, createRenderContext, highlightInputs, inlineText as inlineText2, renderBlocks, scriptUrl as scriptUrl3 } from "@dogsbay/render-html";
|
|
1561
1788
|
import { apiPageLayout } from "@dogsbay/serialize-core";
|
|
1562
1789
|
|
|
1563
|
-
// src/core/resolve.ts
|
|
1564
|
-
import { inlineText } from "@dogsbay/render-html";
|
|
1565
|
-
import { slugify } from "@dogsbay/render-html/html";
|
|
1566
|
-
import { CORE_SCHEMA, load as loadYaml2 } from "js-yaml";
|
|
1567
|
-
function hasPlaceholder(nodes, types) {
|
|
1568
|
-
return (nodes ?? []).some((n) => types.has(n.type) || hasPlaceholder(n.children, types));
|
|
1569
|
-
}
|
|
1570
|
-
async function resolvePlaceholders(tree, resolvers, ctx) {
|
|
1571
|
-
const byType = new Map(resolvers.map((r) => [r.type, r]));
|
|
1572
|
-
if (!hasPlaceholder(tree, new Set(byType.keys()))) return void 0;
|
|
1573
|
-
const usedIds = headingIds(tree);
|
|
1574
|
-
const warnings = [];
|
|
1575
|
-
const walk = async (nodes) => {
|
|
1576
|
-
for (let i = 0; i < nodes.length; i++) {
|
|
1577
|
-
const r = byType.get(nodes[i].type);
|
|
1578
|
-
if (!r) {
|
|
1579
|
-
if (nodes[i].children) await walk(nodes[i].children);
|
|
1580
|
-
continue;
|
|
1581
|
-
}
|
|
1582
|
-
let got;
|
|
1583
|
-
try {
|
|
1584
|
-
got = await r.resolve(nodes[i], { ...ctx, usedIds });
|
|
1585
|
-
} catch (e) {
|
|
1586
|
-
got = { warning: e.message || String(e) };
|
|
1587
|
-
}
|
|
1588
|
-
if ("warning" in got) {
|
|
1589
|
-
warnings.push(got.warning);
|
|
1590
|
-
nodes.splice(i, 1, ...nodes[i].children ?? []);
|
|
1591
|
-
i--;
|
|
1592
|
-
continue;
|
|
1593
|
-
}
|
|
1594
|
-
headingIds(got.nodes, usedIds);
|
|
1595
|
-
nodes.splice(i, 1, ...got.nodes);
|
|
1596
|
-
i += got.nodes.length - 1;
|
|
1597
|
-
}
|
|
1598
|
-
};
|
|
1599
|
-
await walk(tree);
|
|
1600
|
-
return warnings;
|
|
1601
|
-
}
|
|
1602
|
-
function headingIds(nodes, ids = /* @__PURE__ */ new Set()) {
|
|
1603
|
-
const take = (raw) => {
|
|
1604
|
-
const base = RESERVED_ID.test(raw) ? `h-${raw}` : raw;
|
|
1605
|
-
let id = base || "section";
|
|
1606
|
-
for (let n = 2; ids.has(id); n++) id = `${base}-${n}`;
|
|
1607
|
-
ids.add(id);
|
|
1608
|
-
};
|
|
1609
|
-
const walk = (list) => {
|
|
1610
|
-
for (const n of list) {
|
|
1611
|
-
if (n.type === "heading") {
|
|
1612
|
-
const p = n.props ?? {};
|
|
1613
|
-
take(typeof p.id === "string" ? p.id : typeof p.slug === "string" ? p.slug : slugify(inlineText(n.inline)));
|
|
1614
|
-
} else if ((n.type === "prose" || n.type === "html") && typeof n.html === "string") {
|
|
1615
|
-
for (const m of n.html.matchAll(/<h[1-6]\b[^>]*\sid="([^"]*)"/gi)) take(m[1]);
|
|
1616
|
-
}
|
|
1617
|
-
if (n.children) walk(n.children);
|
|
1618
|
-
}
|
|
1619
|
-
};
|
|
1620
|
-
walk(nodes);
|
|
1621
|
-
return ids;
|
|
1622
|
-
}
|
|
1623
|
-
var RESERVED_ID = /^(db-|tab-\d|tabpanel-\d|acc-\d|accpanel-\d|fn-|fnref-)/;
|
|
1624
|
-
var autodocUnresolved = {
|
|
1625
|
-
type: "autodoc-ref",
|
|
1626
|
-
async resolve(node) {
|
|
1627
|
-
const ref = typeof node.props?.ref === "string" ? node.props.ref : "";
|
|
1628
|
-
return { warning: `:::autodoc${ref ? `{ref="${ref}"}` : ""} is not resolved in this build: resolve it at migration time (dogsbay migrate-mkdocs --inline-autodoc)` };
|
|
1629
|
-
}
|
|
1630
|
-
};
|
|
1631
|
-
var coreLoad;
|
|
1632
|
-
var loadCore = () => coreLoad ??= import("@dogsbay/format-openapi/core");
|
|
1633
|
-
function openApiResolver(read, maxChars = 8e6) {
|
|
1634
|
-
let specs;
|
|
1635
|
-
return {
|
|
1636
|
-
type: "openapi-ref",
|
|
1637
|
-
async resolve(node, ctx) {
|
|
1638
|
-
const spec = typeof node.props?.spec === "string" ? node.props.spec : "";
|
|
1639
|
-
const core = await loadCore();
|
|
1640
|
-
const selected = core.directiveOperation(node.props);
|
|
1641
|
-
if ("error" in selected) return { warning: `:::openapi: ${selected.error}` };
|
|
1642
|
-
const slug = ctx.source.replace(/\.(md|markdown)$/i, "").replace(/(^|\/)index$/, "");
|
|
1643
|
-
const found = core.findSpec(spec, slug, (p) => {
|
|
1644
|
-
const at = ctx.scope ? ctx.scope(p) : p;
|
|
1645
|
-
return ctx.has(at) ? { path: at } : void 0;
|
|
1646
|
-
});
|
|
1647
|
-
if ("error" in found) return { warning: `:::openapi: ${found.error}` };
|
|
1648
|
-
specs ??= core.createSpecCache(async (p) => {
|
|
1649
|
-
const text = await read(p);
|
|
1650
|
-
if (text === void 0) throw new Error("not found");
|
|
1651
|
-
return text;
|
|
1652
|
-
}, (text) => loadYaml2(text, { schema: CORE_SCHEMA }));
|
|
1653
|
-
let prepared;
|
|
1654
|
-
try {
|
|
1655
|
-
prepared = await specs.get(found.path);
|
|
1656
|
-
} catch (e) {
|
|
1657
|
-
return { warning: `:::openapi: cannot read ${spec}: ${e.message}` };
|
|
1658
|
-
}
|
|
1659
|
-
let tree;
|
|
1660
|
-
try {
|
|
1661
|
-
tree = core.embedSpec(prepared, { usedIds: ctx.usedIds, operation: selected.operation }).tree;
|
|
1662
|
-
} catch (e) {
|
|
1663
|
-
return { warning: `:::openapi: cannot use ${spec}: ${e.message}` };
|
|
1664
|
-
}
|
|
1665
|
-
const size = boundedSize(tree, maxChars);
|
|
1666
|
-
if (size === "cycle") return { warning: `:::openapi: ${spec} has a cyclic example or schema, which cannot be shown` };
|
|
1667
|
-
if (size === "over") return { warning: `:::openapi: ${spec} is too large to embed in one page (over ${Math.round(maxChars / 1e6)}M characters)` };
|
|
1668
|
-
return { nodes: tree };
|
|
1669
|
-
}
|
|
1670
|
-
};
|
|
1671
|
-
}
|
|
1672
|
-
async function openApiSourcePages(read, specPath, reservedSlugs) {
|
|
1673
|
-
const core = await loadCore();
|
|
1674
|
-
const specs = core.createSpecCache(async (p) => {
|
|
1675
|
-
const text = await read(p);
|
|
1676
|
-
if (text === void 0) throw new Error("not found");
|
|
1677
|
-
return text;
|
|
1678
|
-
}, (text) => loadYaml2(text, { schema: CORE_SCHEMA }));
|
|
1679
|
-
try {
|
|
1680
|
-
return core.specToPages(await specs.get(specPath), { reservedSlugs });
|
|
1681
|
-
} catch (e) {
|
|
1682
|
-
return { error: `cannot read ${specPath}: ${e.message}` };
|
|
1683
|
-
}
|
|
1684
|
-
}
|
|
1685
|
-
async function specFileOf(path, has, read) {
|
|
1686
|
-
const p = path.replace(/^\.\//, "").replace(/\/+$/, "");
|
|
1687
|
-
if (/\.(ya?ml|json)$/i.test(p)) return has(p) ? p : void 0;
|
|
1688
|
-
const dir = p === "." || p === "" ? "" : p + "/";
|
|
1689
|
-
const found = ["openapi", "swagger", "spec"].flatMap((b) => ["yaml", "yml", "json"].map((e) => `${dir}${b}.${e}`)).filter(has);
|
|
1690
|
-
for (const f of found) {
|
|
1691
|
-
const head = (await read(f))?.slice(0, 2048) ?? "";
|
|
1692
|
-
if (/(?:^|[{,])\s*"?(openapi|swagger)"?\s*[:=]/m.test(head)) return f;
|
|
1693
|
-
}
|
|
1694
|
-
return found[0];
|
|
1695
|
-
}
|
|
1696
|
-
function boundedSize(value, max) {
|
|
1697
|
-
let total = 0;
|
|
1698
|
-
const path = /* @__PURE__ */ new Set();
|
|
1699
|
-
const walk = (v) => {
|
|
1700
|
-
if (typeof v === "string") total += v.length;
|
|
1701
|
-
else if (v && typeof v === "object") {
|
|
1702
|
-
if (path.has(v)) return "cycle";
|
|
1703
|
-
path.add(v);
|
|
1704
|
-
total += 2;
|
|
1705
|
-
for (const x of Array.isArray(v) ? v : Object.values(v)) {
|
|
1706
|
-
const r = walk(x);
|
|
1707
|
-
if (r) return r;
|
|
1708
|
-
if (total > max) return "over";
|
|
1709
|
-
}
|
|
1710
|
-
path.delete(v);
|
|
1711
|
-
} else total += 4;
|
|
1712
|
-
return total > max ? "over" : void 0;
|
|
1713
|
-
};
|
|
1714
|
-
return walk(value) ?? total;
|
|
1715
|
-
}
|
|
1716
|
-
|
|
1717
1790
|
// src/theme/regions.ts
|
|
1718
1791
|
import { attrs, esc } from "@dogsbay/render-html/html";
|
|
1719
1792
|
var svg = (body, cls, size = 16, extra = "") => `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="${cls}" aria-hidden="true" focusable="false"${extra}>${body}</svg>`;
|
|
@@ -3134,7 +3207,7 @@ async function build(input, hooks = {}) {
|
|
|
3134
3207
|
phase.init = now() - t0;
|
|
3135
3208
|
let pages = 0;
|
|
3136
3209
|
let assets = 0;
|
|
3137
|
-
const state = { redirects: [], navCache: navCache(), written: /* @__PURE__ */ new Set(), stubs: [], pageOut: /* @__PURE__ */ new Map(), sitemaps: /* @__PURE__ */ new Map(), supplied: /* @__PURE__ */ new Set(), usedMath: false, diagrams: diagramStore(input, cfg), scan: hooks.keep || hooks.lazy ? new ScanCache() : void 0, parseCache: input.parseCacheBytes === 0 ? false : new ParseCache(input.parseCacheBytes ?? DEFAULT_PARSE_CACHE_BYTES), templateBudget: templateBudget(input.untrusted === true, input.templateMs), dataCache: new DataCache(input.dataCacheBytes ?? DEFAULT_DATA_CACHE_BYTES) };
|
|
3210
|
+
const state = { redirects: [], notes: /* @__PURE__ */ new Map(), navCache: navCache(), written: /* @__PURE__ */ new Set(), stubs: [], pageOut: /* @__PURE__ */ new Map(), sitemaps: /* @__PURE__ */ new Map(), supplied: /* @__PURE__ */ new Set(), usedMath: false, diagrams: diagramStore(input, cfg), scan: hooks.keep || hooks.lazy ? new ScanCache() : void 0, parseCache: input.parseCacheBytes === 0 ? false : new ParseCache(input.parseCacheBytes ?? DEFAULT_PARSE_CACHE_BYTES), templateBudget: templateBudget(input.untrusted === true, input.templateMs), dataCache: new DataCache(input.dataCacheBytes ?? DEFAULT_DATA_CACHE_BYTES) };
|
|
3138
3211
|
for (const r of cfg.redirects) {
|
|
3139
3212
|
const from = sitePath(r.from, cfg.basePath);
|
|
3140
3213
|
if (!from) {
|
|
@@ -3211,6 +3284,7 @@ async function build(input, hooks = {}) {
|
|
|
3211
3284
|
files: out,
|
|
3212
3285
|
warnings,
|
|
3213
3286
|
problems,
|
|
3287
|
+
notes: [...state.notes.values()].flat(),
|
|
3214
3288
|
config: cfg,
|
|
3215
3289
|
stats: { pages, assets, units: units.length, ms: Math.round(now() - t0), msByPhase: roundAll(phase), highlightCache: highlighter.cacheStats(), ...state.parseCache ? { parseCache: state.parseCache.stats() } : {} }
|
|
3216
3290
|
};
|
|
@@ -3486,6 +3560,7 @@ ${marker}${title}${marker}`, path, attributesOf(path), includesFor(path, read2),
|
|
|
3486
3560
|
const diagrams = state.diagrams;
|
|
3487
3561
|
const taxonomies = cfg.taxonomies.length ? new TaxonomyCollector(cfg.taxonomies) : void 0;
|
|
3488
3562
|
const blog = cfg.blog ? new BlogCollector(cfg.blog) : void 0;
|
|
3563
|
+
state.scanned?.(unit, "start");
|
|
3489
3564
|
const infos = /* @__PURE__ */ new Map();
|
|
3490
3565
|
const stubTargets = /* @__PURE__ */ new Map();
|
|
3491
3566
|
const frontMatter = /* @__PURE__ */ new Map();
|
|
@@ -3522,7 +3597,7 @@ ${marker}${title}${marker}`, path, attributesOf(path), includesFor(path, read2),
|
|
|
3522
3597
|
if (templatesOf(p).isPartial(fm, p)) continue;
|
|
3523
3598
|
const inUnit = (s) => unit.prefix && (s === unit.prefix || s.startsWith(unit.prefix + "/")) ? s : [unit.prefix, s].filter(Boolean).join("/");
|
|
3524
3599
|
const pageNs = named.length ? namespaceOf(p) : "";
|
|
3525
|
-
const inScope = (s) => inUnit(pageNs && !(s === pageNs || s.startsWith(pageNs + "/")) ?
|
|
3600
|
+
const inScope = (s) => inUnit(pageNs && !(s === pageNs || s.startsWith(pageNs + "/")) ? prefixSlug2([pageNs], s) : s);
|
|
3526
3601
|
const target = (raw) => isExternal(raw) ? raw : inScope(sitePath(raw, cfg.basePath));
|
|
3527
3602
|
const isIndexFile = /(^|\/)index\.(md|markdown)$/i.test(p) && slug !== "";
|
|
3528
3603
|
if (typeof fm.redirect === "string" && fm.redirect.trim()) {
|
|
@@ -3594,36 +3669,48 @@ ${marker}${title}${marker}`, path, attributesOf(path), includesFor(path, read2),
|
|
|
3594
3669
|
if (activeFiles.length) warn("html", `content.html is sanitize, so ${activeFiles.length} HTML, XML or script file(s) are not copied: ${some(activeFiles)}`);
|
|
3595
3670
|
if (untrustedFiles.length) report("untrusted", `[untrusted] ${untrustedFiles.length} file(s) not copied: only images, fonts, media, stylesheets, documents and data are, and no SVG that runs script or host configuration (_headers, _redirects): ${some(untrustedFiles)}`);
|
|
3596
3671
|
const specNavs = /* @__PURE__ */ new Map();
|
|
3597
|
-
const
|
|
3672
|
+
const mountedNavs = /* @__PURE__ */ new Map();
|
|
3673
|
+
const mountedSlugs = /* @__PURE__ */ new Set();
|
|
3674
|
+
const notes = [];
|
|
3675
|
+
state.notes.set(unit.prefix, notes);
|
|
3598
3676
|
for (const m of unit.members) {
|
|
3599
3677
|
if (m.source.from !== "openapi") continue;
|
|
3600
|
-
const
|
|
3601
|
-
|
|
3602
|
-
return c == null ? void 0 : readText(c);
|
|
3603
|
-
};
|
|
3604
|
-
const spec = await specFileOf(m.source.path, (p) => input.files.has(p), readProject);
|
|
3605
|
-
if (!spec) {
|
|
3678
|
+
const placed = await placeApiPages(cfg, unit, m, input.files, readText);
|
|
3679
|
+
if ("missing" in placed) {
|
|
3606
3680
|
warn("openapi", `${m.source.path}: no OpenAPI spec there (a .yaml, .yml or .json file, or a folder holding openapi.*, swagger.* or spec.*)`);
|
|
3607
3681
|
continue;
|
|
3608
3682
|
}
|
|
3609
|
-
const
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
const got = await openApiSourcePages(readProject, spec, reserved);
|
|
3613
|
-
if ("error" in got) {
|
|
3614
|
-
warnFile("openapi", m.source.path, spec, got.error);
|
|
3683
|
+
const spec = placed.spec;
|
|
3684
|
+
if ("error" in placed) {
|
|
3685
|
+
warnFile("openapi", m.source.path, spec, placed.error);
|
|
3615
3686
|
continue;
|
|
3616
3687
|
}
|
|
3688
|
+
const ns = m.namespace;
|
|
3689
|
+
const mount = m.mount?.split("/");
|
|
3690
|
+
const segs = placed.segs;
|
|
3617
3691
|
const limit = input.openApiChars ?? 8e6;
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3692
|
+
const own = new Set(placed.pages.map((p) => p.page.slug));
|
|
3693
|
+
let label2 = placed.pages.find((p) => isMountRoot(p.page.slug))?.page.title ?? m.mount ?? "";
|
|
3694
|
+
for (const { slug, out, isIndex, page } of placed.pages) {
|
|
3695
|
+
if (mount) {
|
|
3696
|
+
const there = infos.get(slug);
|
|
3697
|
+
if (there && isMountRoot(page.slug) && !there.virtual) {
|
|
3698
|
+
notes.push(mountRootNote(m.mount, prefixSlug2(unit.prefix ? unit.prefix.split("/") : [], slug)));
|
|
3699
|
+
label2 = there.title;
|
|
3700
|
+
continue;
|
|
3701
|
+
}
|
|
3702
|
+
if (there) throw mountClash(slug, m.mount, srcOf(there), there.virtual ? `a page of ${srcOf(there)}` : `another page (${srcOf(there)})`);
|
|
3703
|
+
mountRefs(page.tree, mount, own);
|
|
3704
|
+
mountedSlugs.add(slug);
|
|
3705
|
+
} else if (mountedSlugs.has(slug)) {
|
|
3706
|
+
const by = unit.members.find((o) => o.mount && (slug === o.mount || slug.startsWith(o.mount + "/")))?.mount ?? "";
|
|
3707
|
+
throw mountClash(slug, by, spec, `a page of ${spec}`);
|
|
3708
|
+
} else if (infos.has(slug)) {
|
|
3621
3709
|
warn("duplicate-page", `two sources map to the page "${slug || "/"}"; keeping ${srcOf(infos.get(slug))}, ignoring ${spec}'s`, { file: spec });
|
|
3622
3710
|
continue;
|
|
3623
3711
|
}
|
|
3624
|
-
const isIndex = page.slug === "index" || got.pages.some((q) => q.slug.startsWith(page.slug + "/"));
|
|
3625
3712
|
let tree = page.tree;
|
|
3626
|
-
if (ns) rewriteContentRefs(tree, segs, globalPrefixes);
|
|
3713
|
+
if (ns && !mount) rewriteContentRefs(tree, segs, globalPrefixes);
|
|
3627
3714
|
const size = boundedSize(tree, limit);
|
|
3628
3715
|
if (typeof size !== "number") {
|
|
3629
3716
|
warnFile("openapi", spec, spec, `the page ${slug || "/"} ${size === "cycle" ? "has a cyclic example or schema, which cannot be shown" : `is too large to build (over ${Math.round(limit / 1e6 * 10) / 10}M characters)`}`);
|
|
@@ -3632,13 +3719,17 @@ ${marker}${title}${marker}`, path, attributesOf(path), includesFor(path, read2),
|
|
|
3632
3719
|
infos.set(slug, {
|
|
3633
3720
|
slug,
|
|
3634
3721
|
source: isIndex ? slug ? `${slug}/index.md` : "index.md" : `${slug}.md`,
|
|
3635
|
-
out
|
|
3722
|
+
out,
|
|
3636
3723
|
title: detach(page.title),
|
|
3637
3724
|
draft: false,
|
|
3638
3725
|
virtual: { tree, project: spec }
|
|
3639
3726
|
});
|
|
3640
3727
|
}
|
|
3641
|
-
|
|
3728
|
+
if (mount) {
|
|
3729
|
+
mountedNavs.set(m, { nav: specNav(placed.nav, segs), label: label2 });
|
|
3730
|
+
continue;
|
|
3731
|
+
}
|
|
3732
|
+
specNavs.set(ns, specNav(placed.nav, segs));
|
|
3642
3733
|
const strays = [...infos.values()].filter((i) => !i.virtual && ns && (i.slug === ns || i.slug.startsWith(ns + "/")));
|
|
3643
3734
|
if (strays.length) warn("openapi", `${strays.length} page(s) are in the folder of the OpenAPI source "${ns}", and are left out of the nav: ${strays.slice(0, 5).map(srcOf).join(", ")}${strays.length > 5 ? ", \u2026" : ""}`);
|
|
3644
3735
|
}
|
|
@@ -3646,16 +3737,16 @@ ${marker}${title}${marker}`, path, attributesOf(path), includesFor(path, read2),
|
|
|
3646
3737
|
const navLoad = state.scan ? (text) => state.scan.yaml(text, loadYaml) : void 0;
|
|
3647
3738
|
const memberNav = async (ns) => {
|
|
3648
3739
|
const dir2 = ns ? ns + "/" : "";
|
|
3649
|
-
const member = unit.members.find((m) => m.namespace === ns);
|
|
3740
|
+
const member = unit.members.find((m) => m.namespace === ns && !m.mount);
|
|
3650
3741
|
if (member?.source.nav) return ownNav(member.source, ns);
|
|
3651
3742
|
const file = NAV_FILES.map((f) => dir2 + f).find((f) => files.has(f));
|
|
3652
3743
|
const others = unit.members.filter((m) => m.namespace);
|
|
3653
|
-
const mine = [...infos.values()].filter((i) => ns ? i.slug === ns || i.slug.startsWith(dir2) : !others.some((m) => i.slug === m.namespace || i.slug.startsWith(m.namespace + "/")));
|
|
3744
|
+
const mine = [...infos.values()].filter((i) => !mountedSlugs.has(i.slug) && (ns ? i.slug === ns || i.slug.startsWith(dir2) : !others.some((m) => i.slug === m.namespace || i.slug.startsWith(m.namespace + "/"))));
|
|
3654
3745
|
const fromPages = () => ns ? prefixNav(navFromPages(mine.map((i) => ({ ...i, slug: i.slug === ns ? "" : i.slug.slice(dir2.length) }))), ns) : navFromPages(mine);
|
|
3655
3746
|
if (!file) return { nav: fromPages() };
|
|
3656
3747
|
try {
|
|
3657
3748
|
const text = await read(file);
|
|
3658
|
-
return { nav: file.endsWith(".json") ? prefixNav(parseNavJson(text, (slug) => labelFor(ns ?
|
|
3749
|
+
return { nav: file.endsWith(".json") ? prefixNav(parseNavJson(text, (slug) => labelFor(ns ? prefixSlug2([ns], slug) : slug)), ns) : parseNavYaml(text, labelFor, ns, navLoad), file, project: abs(file) };
|
|
3659
3750
|
} catch (e) {
|
|
3660
3751
|
const mark = e.mark;
|
|
3661
3752
|
warn("nav", `could not read ${file}: ${e.message}; using the directory layout`, {
|
|
@@ -3667,7 +3758,7 @@ ${marker}${title}${marker}`, path, attributesOf(path), includesFor(path, read2),
|
|
|
3667
3758
|
};
|
|
3668
3759
|
const ownNav = async (src, ns) => {
|
|
3669
3760
|
const path = src.nav;
|
|
3670
|
-
const nsLabel = (slug) => labelFor(ns ?
|
|
3761
|
+
const nsLabel = (slug) => labelFor(ns ? prefixSlug2([ns], slug) : slug);
|
|
3671
3762
|
const map = topicMaps.get(path);
|
|
3672
3763
|
if (map) return { nav: prefixNav(parseNavJson(JSON.stringify(map.entries), nsLabel), ns), file: path, project: path, files: map.maps };
|
|
3673
3764
|
const c = input.files.get(path);
|
|
@@ -3678,7 +3769,13 @@ ${marker}${title}${marker}`, path, attributesOf(path), includesFor(path, read2),
|
|
|
3678
3769
|
let nav = [];
|
|
3679
3770
|
const navFiles = [];
|
|
3680
3771
|
const navProjectFiles = [];
|
|
3772
|
+
const host = unit.members.find((m) => !m.namespace && !m.mount && m.source.from !== "openapi");
|
|
3681
3773
|
for (const m of unit.members) {
|
|
3774
|
+
const placed = mountedNavs.get(m);
|
|
3775
|
+
if (placed) {
|
|
3776
|
+
if (!host) nav.push(...placed.nav);
|
|
3777
|
+
continue;
|
|
3778
|
+
}
|
|
3682
3779
|
if (m.source.from === "openapi") {
|
|
3683
3780
|
const items = specNavs.get(m.namespace) ?? [];
|
|
3684
3781
|
const group2 = namespaceNavGroup(m.namespace || void 0, items);
|
|
@@ -3687,6 +3784,7 @@ ${marker}${title}${marker}`, path, attributesOf(path), includesFor(path, read2),
|
|
|
3687
3784
|
continue;
|
|
3688
3785
|
}
|
|
3689
3786
|
const got = await memberNav(m.namespace);
|
|
3787
|
+
if (m === host) for (const [mm, { nav: items, label: label2 }] of mountedNavs) got.nav = placeMounted(got.nav, items, mm.mount, label2);
|
|
3690
3788
|
if (got.file) navFiles.push(got.file);
|
|
3691
3789
|
if (got.project) navProjectFiles.push(got.project);
|
|
3692
3790
|
const group = namespaceNavGroup(m.namespace || void 0, got.nav);
|
|
@@ -3782,6 +3880,7 @@ ${marker}${title}${marker}`, path, attributesOf(path), includesFor(path, read2),
|
|
|
3782
3880
|
let search = cfg.search && cfg.searchEngine !== "pagefind" ? new SearchIndexBuilder({ lang: unit.locale ?? cfg.defaultLocale ?? "en" }) : void 0;
|
|
3783
3881
|
const siteDepthPrefix = unit.prefix ? "../".repeat(unit.prefix.split("/").length) : "";
|
|
3784
3882
|
phase.scan = (phase.scan ?? 0) + now() - t;
|
|
3883
|
+
state.scanned?.(unit, "end");
|
|
3785
3884
|
input.onUnitAssets?.(unit.prefix, assetPaths);
|
|
3786
3885
|
if (diagrams) {
|
|
3787
3886
|
t = now();
|
|
@@ -4594,22 +4693,28 @@ function postHeader(p, lang, t, asset) {
|
|
|
4594
4693
|
function specNav(items, segs) {
|
|
4595
4694
|
return items.map((it) => ({
|
|
4596
4695
|
label: it.label,
|
|
4597
|
-
...it.href != null ? { slug:
|
|
4696
|
+
...it.href != null ? { slug: prefixSlug2(segs, it.href.replace(/^\/+|\/+$/g, "")) } : {},
|
|
4598
4697
|
children: specNav(it.children ?? [], segs)
|
|
4599
4698
|
}));
|
|
4600
4699
|
}
|
|
4700
|
+
var PageClashError = class extends Error {
|
|
4701
|
+
constructor(message, file) {
|
|
4702
|
+
super(message);
|
|
4703
|
+
this.file = file;
|
|
4704
|
+
}
|
|
4705
|
+
};
|
|
4706
|
+
function mountClash(slug, mount, file, other) {
|
|
4707
|
+
return new PageClashError(`"${slug}" is both a page of the source mounted at "${mount}" and ${other}; move the page, or mount the source somewhere else`, file);
|
|
4708
|
+
}
|
|
4709
|
+
function placeMounted(host, mounted, mount, label) {
|
|
4710
|
+
const to = (nodes) => nodes.map((n) => ({ label: n.label, href: n.slug != null ? `/${n.slug}` : n.href, children: to(n.children), node: n }));
|
|
4711
|
+
const back = (items) => items.map((i) => i.node ? { ...i.node, children: back(i.children ?? []) } : { label: i.label, slug: i.href.replace(/^\/+|\/+$/g, ""), children: back(i.children ?? []) });
|
|
4712
|
+
return back(placeMountedNav(to(host), to(mounted), `/${mount}`, label, mount).nav);
|
|
4713
|
+
}
|
|
4601
4714
|
function prefixNav(nodes, ns) {
|
|
4602
4715
|
if (!ns) return nodes;
|
|
4603
4716
|
return nodes.map((n) => ({ ...n, ...n.slug != null ? { slug: n.slug ? `${ns}/${n.slug}` : ns } : {}, children: prefixNav(n.children, ns) }));
|
|
4604
4717
|
}
|
|
4605
|
-
function urlPathOf(url) {
|
|
4606
|
-
if (!url) return [];
|
|
4607
|
-
try {
|
|
4608
|
-
return new URL(url).pathname.split("/").filter(Boolean);
|
|
4609
|
-
} catch {
|
|
4610
|
-
return [];
|
|
4611
|
-
}
|
|
4612
|
-
}
|
|
4613
4718
|
function dropMount(tree, mounts) {
|
|
4614
4719
|
if (!mounts.length) return tree;
|
|
4615
4720
|
const strip = (u) => {
|
|
@@ -4879,6 +4984,14 @@ async function openDevSession(input, o = {}) {
|
|
|
4879
4984
|
let buildError;
|
|
4880
4985
|
let buildProblem;
|
|
4881
4986
|
let diagnosed = /* @__PURE__ */ new Map();
|
|
4987
|
+
let scanProblems = /* @__PURE__ */ new Map();
|
|
4988
|
+
const watchScan = (c, into) => {
|
|
4989
|
+
let from = 0;
|
|
4990
|
+
c.state.scanned = (unit, at) => {
|
|
4991
|
+
if (at === "start") from = c.problems.length;
|
|
4992
|
+
else into.set(unit.prefix, c.problems.slice(from));
|
|
4993
|
+
};
|
|
4994
|
+
};
|
|
4882
4995
|
const failed = (e) => {
|
|
4883
4996
|
buildError = e.message || String(e);
|
|
4884
4997
|
buildProblem = problemOf(e, buildError);
|
|
@@ -4887,6 +5000,7 @@ async function openDevSession(input, o = {}) {
|
|
|
4887
5000
|
const next = /* @__PURE__ */ new Map();
|
|
4888
5001
|
const nextUnits = /* @__PURE__ */ new Map();
|
|
4889
5002
|
const nextDiagnosed = /* @__PURE__ */ new Map();
|
|
5003
|
+
const nextScan = /* @__PURE__ */ new Map();
|
|
4890
5004
|
let nextCtx;
|
|
4891
5005
|
const sink = async (path, content) => {
|
|
4892
5006
|
next.set(path, isStream(content) ? await joinChunks(content.chunks()) : content);
|
|
@@ -4908,7 +5022,10 @@ async function openDevSession(input, o = {}) {
|
|
|
4908
5022
|
{
|
|
4909
5023
|
keep: true,
|
|
4910
5024
|
lazy: o.lazy,
|
|
4911
|
-
context: (c) =>
|
|
5025
|
+
context: (c) => {
|
|
5026
|
+
nextCtx = c;
|
|
5027
|
+
watchScan(c, nextScan);
|
|
5028
|
+
},
|
|
4912
5029
|
diagnostics: (f, list) => nextDiagnosed.set(f, list),
|
|
4913
5030
|
// A copy: the build keeps adding the classes of pages it renders later to its own set.
|
|
4914
5031
|
unitBuilt: (unit, handle, classes) => nextUnits.set(unit.prefix, { unit, handle, classes: new Set(classes) })
|
|
@@ -4922,6 +5039,7 @@ async function openDevSession(input, o = {}) {
|
|
|
4922
5039
|
units = nextUnits;
|
|
4923
5040
|
ctx = nextCtx;
|
|
4924
5041
|
diagnosed = nextDiagnosed;
|
|
5042
|
+
scanProblems = nextScan;
|
|
4925
5043
|
config = nextCtx?.cfg ?? nextConfig;
|
|
4926
5044
|
taken = 0;
|
|
4927
5045
|
takenProblems = 0;
|
|
@@ -5028,6 +5146,9 @@ async function openDevSession(input, o = {}) {
|
|
|
5028
5146
|
sourceFor(path) {
|
|
5029
5147
|
return unitForSitePath(path)?.handle.sourceForPage(path);
|
|
5030
5148
|
},
|
|
5149
|
+
notes() {
|
|
5150
|
+
return [...ctx?.state.notes.values() ?? []].flat();
|
|
5151
|
+
},
|
|
5031
5152
|
takeWarnings() {
|
|
5032
5153
|
const all = ctx?.warnings ?? [];
|
|
5033
5154
|
const fresh = all.slice(taken);
|
|
@@ -5052,7 +5173,11 @@ async function openDevSession(input, o = {}) {
|
|
|
5052
5173
|
get,
|
|
5053
5174
|
async change(paths) {
|
|
5054
5175
|
const t0 = Date.now();
|
|
5055
|
-
const
|
|
5176
|
+
const before = allFiles();
|
|
5177
|
+
const done = (r, marked) => {
|
|
5178
|
+
const stale2 = marked ?? /* @__PURE__ */ new Set([...before, ...allFiles()]);
|
|
5179
|
+
return { ...r, stale: [...stale2].sort(), ms: Date.now() - t0 };
|
|
5180
|
+
};
|
|
5056
5181
|
try {
|
|
5057
5182
|
if (buildError !== void 0) {
|
|
5058
5183
|
await full();
|
|
@@ -5060,14 +5185,33 @@ async function openDevSession(input, o = {}) {
|
|
|
5060
5185
|
buildProblem = void 0;
|
|
5061
5186
|
return done({ css: true, handled: "full" });
|
|
5062
5187
|
}
|
|
5063
|
-
|
|
5188
|
+
const marked = /* @__PURE__ */ new Set();
|
|
5189
|
+
const r = await budgeted(() => changeParts(paths, marked));
|
|
5190
|
+
return done(r, r.handled === "full" ? void 0 : marked);
|
|
5064
5191
|
} catch (e) {
|
|
5065
5192
|
failed(e);
|
|
5066
|
-
return done({ css: false, handled: "full" });
|
|
5193
|
+
return done({ css: false, handled: "full" }, /* @__PURE__ */ new Set());
|
|
5067
5194
|
}
|
|
5068
5195
|
}
|
|
5069
5196
|
};
|
|
5070
|
-
|
|
5197
|
+
function allFiles() {
|
|
5198
|
+
const all = /* @__PURE__ */ new Set();
|
|
5199
|
+
for (const u of units.values()) for (const o2 of u.handle.pageOuts) {
|
|
5200
|
+
const src = u.handle.sourceForPage(o2);
|
|
5201
|
+
if (src !== void 0) all.add(src);
|
|
5202
|
+
}
|
|
5203
|
+
for (const list of scanProblems.values()) for (const p of list) if (p.file !== void 0) all.add(p.file);
|
|
5204
|
+
return all;
|
|
5205
|
+
}
|
|
5206
|
+
async function changeParts(paths, marked) {
|
|
5207
|
+
const again = /* @__PURE__ */ new Map();
|
|
5208
|
+
const markStale = (page, u = unitForSitePath(page), rebuilt = false) => {
|
|
5209
|
+
stale.add(page);
|
|
5210
|
+
const src = u?.handle.sourceForPage(page);
|
|
5211
|
+
if (src === void 0) return;
|
|
5212
|
+
marked.add(src);
|
|
5213
|
+
if (!rebuilt) again.set(src, u.unit.prefix);
|
|
5214
|
+
};
|
|
5071
5215
|
const themeDir = config.themeDir ? config.themeDir.replace(/\/+$/, "") + "/" : void 0;
|
|
5072
5216
|
const extraCss = new Set(config.css.map((c) => c.replace(/^\.\//, "")));
|
|
5073
5217
|
const lazy = /* @__PURE__ */ new Set();
|
|
@@ -5113,7 +5257,7 @@ async function openDevSession(input, o = {}) {
|
|
|
5113
5257
|
}
|
|
5114
5258
|
x.handle.invalidateData();
|
|
5115
5259
|
for (const page of x.handle.dataReaders(p)) {
|
|
5116
|
-
|
|
5260
|
+
markStale(page, x);
|
|
5117
5261
|
mark("page");
|
|
5118
5262
|
}
|
|
5119
5263
|
}
|
|
@@ -5126,7 +5270,7 @@ async function openDevSession(input, o = {}) {
|
|
|
5126
5270
|
const page = u.handle.pageForSource(p);
|
|
5127
5271
|
const text = content === void 0 ? void 0 : await textOf(content);
|
|
5128
5272
|
if (page && text !== void 0 && u.handle.sameFrontMatter(p, text) && !u.handle.included(p)) {
|
|
5129
|
-
|
|
5273
|
+
markStale(page, u);
|
|
5130
5274
|
mark("page");
|
|
5131
5275
|
} else {
|
|
5132
5276
|
u.handle.forget(p);
|
|
@@ -5157,21 +5301,30 @@ async function openDevSession(input, o = {}) {
|
|
|
5157
5301
|
if (unitForSitePath(key) === u) out.delete(key);
|
|
5158
5302
|
}
|
|
5159
5303
|
for (const s of [...stale]) if (unitForSitePath(s) === u) stale.delete(s);
|
|
5304
|
+
for (const o2 of u.handle.pageOuts) {
|
|
5305
|
+
const src = u.handle.sourceForPage(o2);
|
|
5306
|
+
if (src !== void 0) marked.add(src);
|
|
5307
|
+
}
|
|
5308
|
+
const named = (list) => {
|
|
5309
|
+
for (const p of list ?? []) if (p.file !== void 0) marked.add(p.file);
|
|
5310
|
+
};
|
|
5311
|
+
named(scanProblems.get(prefix));
|
|
5160
5312
|
const handle = await rebuildUnitLazy({ ...ctx, emit: emitInto }, u.unit);
|
|
5313
|
+
named(scanProblems.get(prefix));
|
|
5161
5314
|
const next = { ...u, handle };
|
|
5162
5315
|
units.set(prefix, next);
|
|
5163
|
-
for (const o2 of handle.pageOuts)
|
|
5316
|
+
for (const o2 of handle.pageOuts) markStale(o2, next, true);
|
|
5164
5317
|
}
|
|
5165
5318
|
if (config.defaultVersion && [...lazy].some((prefix) => units.get(prefix)?.unit.version === config.defaultVersion)) {
|
|
5166
5319
|
const all = [...units.values()].map((u) => u.unit);
|
|
5167
5320
|
ctx.state.canonical = await defaultTables(config, all, all, input.files, readText);
|
|
5168
|
-
for (const u of units.values()) if (u.unit.version !== config.defaultVersion) for (const o2 of u.handle.pageOuts)
|
|
5321
|
+
for (const u of units.values()) if (u.unit.version !== config.defaultVersion) for (const o2 of u.handle.pageOuts) markStale(o2, u);
|
|
5169
5322
|
}
|
|
5170
5323
|
if (lazy.size && ctx.state.languages) {
|
|
5171
5324
|
const all = [...units.values()].map((u) => u.unit);
|
|
5172
5325
|
const versions = new Set([...lazy].map((prefix) => units.get(prefix)?.unit.version));
|
|
5173
5326
|
ctx.state.languages = await languageTables(config, all, all, input.files, readText);
|
|
5174
|
-
for (const u of units.values()) if (versions.has(u.unit.version) && !lazy.has(u.unit.prefix)) for (const o2 of u.handle.pageOuts)
|
|
5327
|
+
for (const u of units.values()) if (versions.has(u.unit.version) && !lazy.has(u.unit.prefix)) for (const o2 of u.handle.pageOuts) markStale(o2, u);
|
|
5175
5328
|
}
|
|
5176
5329
|
if (lazy.size) {
|
|
5177
5330
|
const all = [...units.values()].map((u) => u.unit);
|
|
@@ -5187,6 +5340,14 @@ async function openDevSession(input, o = {}) {
|
|
|
5187
5340
|
(p) => stale.has(p)
|
|
5188
5341
|
);
|
|
5189
5342
|
}
|
|
5343
|
+
for (const [src, prefix] of again) {
|
|
5344
|
+
if (lazy.has(prefix)) continue;
|
|
5345
|
+
for (const p of scanProblems.get(prefix) ?? []) {
|
|
5346
|
+
if (p.file !== src) continue;
|
|
5347
|
+
ctx.problems.push(p);
|
|
5348
|
+
ctx.warnings.push(p.text);
|
|
5349
|
+
}
|
|
5350
|
+
}
|
|
5190
5351
|
return { css: false, handled };
|
|
5191
5352
|
}
|
|
5192
5353
|
async function attributesOnly() {
|
|
@@ -5232,7 +5393,7 @@ function inConfig(e, file) {
|
|
|
5232
5393
|
function problemOf(e, text) {
|
|
5233
5394
|
const err = e ?? {};
|
|
5234
5395
|
const config = err.inConfig === true || e instanceof ConfigError;
|
|
5235
|
-
const file = err.configFile;
|
|
5396
|
+
const file = err.configFile ?? (e instanceof PageClashError ? e.file : void 0);
|
|
5236
5397
|
const at = typeof err.mark?.line === "number" ? { line: err.mark.line + 1, column: (err.mark.column ?? 0) + 1 } : {};
|
|
5237
5398
|
return { code: config ? "config" : "build", ...file ? { file } : {}, ...at, message: err.reason || text.split("\n")[0], text };
|
|
5238
5399
|
}
|
|
@@ -5311,6 +5472,7 @@ function openDevSession2(input, o) {
|
|
|
5311
5472
|
}
|
|
5312
5473
|
export {
|
|
5313
5474
|
ConfigError,
|
|
5475
|
+
PageClashError,
|
|
5314
5476
|
build2 as build,
|
|
5315
5477
|
composePage,
|
|
5316
5478
|
indexNav,
|
|
@@ -204,6 +204,8 @@ export interface BuiltPage {
|
|
|
204
204
|
/** What the site-level files need from every unit. Small: a few strings per page. */
|
|
205
205
|
export interface SiteState {
|
|
206
206
|
redirects: RedirectInput[];
|
|
207
|
+
/** What the build says about the content, not a problem, by unit prefix (replaced when a unit is built again). */
|
|
208
|
+
notes: Map<string, string[]>;
|
|
207
209
|
/** The sidebar items kept between pages (src/theme/regions.ts). */
|
|
208
210
|
navCache: NavCache;
|
|
209
211
|
/** Site path ("en/latest/guide/x") → output file relative to the site root. */
|
|
@@ -226,6 +228,8 @@ export interface SiteState {
|
|
|
226
228
|
stubs: string[];
|
|
227
229
|
/** A dev session's: what pass 1 read from each page, kept between its rebuilds (src/core/scan-cache.ts). */
|
|
228
230
|
scan?: ScanCache;
|
|
231
|
+
/** A dev session's: called as each unit's pass 1 starts and ends, so it knows which problems pass 1 reported (src/core/dev.ts). */
|
|
232
|
+
scanned?: (unit: BuildUnit, at: "start" | "end") => void;
|
|
229
233
|
/** Included templates' parses (false: none kept), kept as long as the state: a dev session's unit rebuilds keep it. */
|
|
230
234
|
parseCache: ParseCache | false;
|
|
231
235
|
/** Template time left for the build, or for a dev session's current request. */
|
|
@@ -260,6 +264,11 @@ export interface BuildResult {
|
|
|
260
264
|
warnings: string[];
|
|
261
265
|
/** The warnings as data, one per warning, in the same order. */
|
|
262
266
|
problems: BuildProblem[];
|
|
267
|
+
/**
|
|
268
|
+
* What the build says that is not a problem: a content page standing as a mounted API's root
|
|
269
|
+
* (@dogsbay/site-files' `mountRootNote`), once per mount. The CLI prints them as the Astro pipeline does.
|
|
270
|
+
*/
|
|
271
|
+
notes: string[];
|
|
263
272
|
stats: BuildStats;
|
|
264
273
|
config: SiteConfig;
|
|
265
274
|
}
|
|
@@ -400,6 +409,14 @@ export declare function selectUnits(allUnits: BuildUnit[], names: string[] | und
|
|
|
400
409
|
* yes only loads KaTeX early; code is skipped, its `$` is not math.
|
|
401
410
|
*/
|
|
402
411
|
export declare function mayHaveMath(v: unknown, inProps?: boolean): boolean;
|
|
412
|
+
/** A page an API mounted beside it would land on: the build stops, on that page's file. */
|
|
413
|
+
export declare class PageClashError extends Error {
|
|
414
|
+
/** The project path of the other page (or spec). */
|
|
415
|
+
readonly file: string;
|
|
416
|
+
constructor(message: string,
|
|
417
|
+
/** The project path of the other page (or spec). */
|
|
418
|
+
file: string);
|
|
419
|
+
}
|
|
403
420
|
export interface LoadedTheme {
|
|
404
421
|
templates: ThemeTemplates;
|
|
405
422
|
themeCss?: string;
|
|
@@ -32,6 +32,12 @@ export interface SourceDef {
|
|
|
32
32
|
name?: string;
|
|
33
33
|
/** What the source holds: Dogsbay-MD pages (the default) or an OpenAPI spec, a page per operation. */
|
|
34
34
|
from?: "dogsbay-md" | "openapi";
|
|
35
|
+
/**
|
|
36
|
+
* Where an OpenAPI source's pages go inside its version and locale (`reference/rest-api`), beside
|
|
37
|
+
* the Markdown it belongs to: its root page is the mount, and a content page there stands in for it
|
|
38
|
+
* (@dogsbay/site-files mounts.ts, plans/openapi-mount.md). Not with `name:`, which puts a source outside its version.
|
|
39
|
+
*/
|
|
40
|
+
mount?: string;
|
|
35
41
|
version?: string;
|
|
36
42
|
locale?: string;
|
|
37
43
|
attributes?: Record<string, unknown>;
|
|
@@ -258,6 +264,8 @@ export declare function unitOfPath<U extends {
|
|
|
258
264
|
export interface UnitMember {
|
|
259
265
|
source: SourceDef;
|
|
260
266
|
namespace: string;
|
|
267
|
+
/** A mounted OpenAPI source's mount (`source.mount`): its pages go there, inside the unit. */
|
|
268
|
+
mount?: string;
|
|
261
269
|
}
|
|
262
270
|
/** One independent build: one or more content roots published under a locale/version prefix. */
|
|
263
271
|
export interface BuildUnit {
|
|
@@ -43,6 +43,8 @@ export interface DevSession {
|
|
|
43
43
|
change(paths: string[]): Promise<ChangeResult>;
|
|
44
44
|
/** Warnings not handed out yet (the start's, then those of each change and page render). */
|
|
45
45
|
takeWarnings(): string[];
|
|
46
|
+
/** What the site's build says that is not a problem (BuildResult.notes: a content page standing as a mounted API's root), as it stands now. */
|
|
47
|
+
notes(): string[];
|
|
46
48
|
/**
|
|
47
49
|
* The same warnings as data, not handed out yet (its own count: taking one kind leaves the other):
|
|
48
50
|
* the file each is about as a project path, with its line when known, and a code.
|
|
@@ -76,6 +78,15 @@ export interface ChangeResult {
|
|
|
76
78
|
* their pages), `page` (pages marked stale), `asset` (files replaced), `none` (nothing it builds).
|
|
77
79
|
*/
|
|
78
80
|
handled: "full" | "unit" | "page" | "asset" | "none";
|
|
81
|
+
/**
|
|
82
|
+
* The source files (project paths) of the pages whose earlier warnings no longer stand: drop
|
|
83
|
+
* them, then read `takeProblems()`. A listed page that still exists renders again when asked for
|
|
84
|
+
* (`get`, `diagnostics`), and its warnings are in `takeProblems()` once that resolves, so one that
|
|
85
|
+
* brings none is clean. Removed pages are listed too. After a `full` change, every page before
|
|
86
|
+
* and after, and `takeProblems()` starts again from the first. Pages with no source file (tag
|
|
87
|
+
* pages, translation stubs) are not listed; pages not listed keep their warnings.
|
|
88
|
+
*/
|
|
89
|
+
stale: string[];
|
|
79
90
|
ms: number;
|
|
80
91
|
}
|
|
81
92
|
/**
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { BuildUnit, SiteConfig, UnitMember } from "./config.js";
|
|
2
|
+
import type { FileContent } from "./build.js";
|
|
3
|
+
import { openApiSourcePages } from "./resolve.js";
|
|
4
|
+
type Got = Exclude<Awaited<ReturnType<typeof openApiSourcePages>>, {
|
|
5
|
+
error: string;
|
|
6
|
+
}>;
|
|
7
|
+
export interface PlacedApiPage {
|
|
8
|
+
/** In the unit: under the namespace or the mount (the root page is the mount itself). */
|
|
9
|
+
slug: string;
|
|
10
|
+
/** Output file relative to the unit: the root and the tag pages are folders of the pages under them (`api/pets/`). */
|
|
11
|
+
out: string;
|
|
12
|
+
isIndex: boolean;
|
|
13
|
+
page: Got["pages"][number];
|
|
14
|
+
}
|
|
15
|
+
export type PlacedApi = {
|
|
16
|
+
missing: true;
|
|
17
|
+
} | {
|
|
18
|
+
spec: string;
|
|
19
|
+
error: string;
|
|
20
|
+
} | {
|
|
21
|
+
spec: string;
|
|
22
|
+
pages: PlacedApiPage[];
|
|
23
|
+
nav: Got["nav"];
|
|
24
|
+
segs: string[];
|
|
25
|
+
};
|
|
26
|
+
/** The path segments of `site.url` (`https://x.github.io/docs/` → ["docs"]). */
|
|
27
|
+
export declare function urlPathOf(url: string | undefined): string[];
|
|
28
|
+
/** An OpenAPI member's pages, placed in its unit. */
|
|
29
|
+
export declare function placeApiPages(cfg: SiteConfig, unit: BuildUnit, m: UnitMember, files: Map<string, FileContent>, readText: (c: FileContent) => Promise<string>): Promise<PlacedApi>;
|
|
30
|
+
export declare function apiPageOuts(cfg: SiteConfig, unit: BuildUnit, m: UnitMember, files: Map<string, FileContent>, readText: (c: FileContent) => Promise<string>): Promise<[string, string][]>;
|
|
31
|
+
export {};
|
|
@@ -20,6 +20,7 @@ export declare function openDevSession(input: BuildInput & {
|
|
|
20
20
|
}, o?: DevOptions): Promise<DevSession>;
|
|
21
21
|
export { prebuiltSiteCss };
|
|
22
22
|
export type { BuildInput, BuildResult, FileContent, OutputContent, SinkContent, SiteCssBuilder, BuiltPage, HighlightPool, BuildProblem, PageDiagnostic } from "../core/build.js";
|
|
23
|
+
export { PageClashError } from "../core/build.js";
|
|
23
24
|
export type { ChangeResult, DevOptions, DevSession } from "../core/dev.js";
|
|
24
25
|
export { parseConfig, projectRoots, ConfigError, type SiteConfig, type DiagramEngine } from "../core/config.js";
|
|
25
26
|
export type { DiagramRenderer, DiagramRenderers, DiagramRequest, DiagramResult } from "../core/diagrams.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dogsbay/ssg",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.142",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Documentation static site generator for Dogsbay-MD that builds inside Cloudflare Workers and publishes sites that work offline",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -49,38 +49,38 @@
|
|
|
49
49
|
"tailwindcss": "^4.3.2"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
|
-
"@dogsbay/elements": "0.2.0-beta.
|
|
53
|
-
"@dogsbay/format-dogsbay-md": "0.2.0-beta.
|
|
54
|
-
"@dogsbay/format-openapi": "0.2.0-beta.
|
|
55
|
-
"@dogsbay/format-topicmap": "0.2.0-beta.
|
|
56
|
-
"@dogsbay/highlight": "0.2.0-beta.
|
|
57
|
-
"@dogsbay/minja": "0.2.0-beta.
|
|
58
|
-
"@dogsbay/primitives": "0.2.0-beta.
|
|
59
|
-
"@dogsbay/render-html": "0.2.0-beta.
|
|
60
|
-
"@dogsbay/search": "0.2.0-beta.
|
|
61
|
-
"@dogsbay/serialize-core": "0.2.0-beta.
|
|
62
|
-
"@dogsbay/site-check": "0.2.0-beta.
|
|
63
|
-
"@dogsbay/site-files": "0.2.0-beta.
|
|
64
|
-
"@dogsbay/types": "0.2.0-beta.
|
|
52
|
+
"@dogsbay/elements": "0.2.0-beta.142",
|
|
53
|
+
"@dogsbay/format-dogsbay-md": "0.2.0-beta.142",
|
|
54
|
+
"@dogsbay/format-openapi": "0.2.0-beta.142",
|
|
55
|
+
"@dogsbay/format-topicmap": "0.2.0-beta.142",
|
|
56
|
+
"@dogsbay/highlight": "0.2.0-beta.142",
|
|
57
|
+
"@dogsbay/minja": "0.2.0-beta.142",
|
|
58
|
+
"@dogsbay/primitives": "0.2.0-beta.142",
|
|
59
|
+
"@dogsbay/render-html": "0.2.0-beta.142",
|
|
60
|
+
"@dogsbay/search": "0.2.0-beta.142",
|
|
61
|
+
"@dogsbay/serialize-core": "0.2.0-beta.142",
|
|
62
|
+
"@dogsbay/site-check": "0.2.0-beta.142",
|
|
63
|
+
"@dogsbay/site-files": "0.2.0-beta.142",
|
|
64
|
+
"@dogsbay/types": "0.2.0-beta.142"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
67
|
"@axe-core/playwright": "^4.12.1",
|
|
68
68
|
"@cloudflare/workers-types": "^5.20260921.1",
|
|
69
|
-
"@dogsbay/elements": "0.2.0-beta.
|
|
70
|
-
"@dogsbay/format-dogsbay-md": "0.2.0-beta.
|
|
71
|
-
"@dogsbay/format-openapi": "0.2.0-beta.
|
|
72
|
-
"@dogsbay/format-topicmap": "0.2.0-beta.
|
|
73
|
-
"@dogsbay/highlight": "0.2.0-beta.
|
|
74
|
-
"@dogsbay/icons": "0.2.0-beta.
|
|
75
|
-
"@dogsbay/minja": "0.2.0-beta.
|
|
76
|
-
"@dogsbay/primitives": "0.2.0-beta.
|
|
77
|
-
"@dogsbay/render-html": "0.2.0-beta.
|
|
78
|
-
"@dogsbay/search": "0.2.0-beta.
|
|
79
|
-
"@dogsbay/serialize-core": "0.2.0-beta.
|
|
80
|
-
"@dogsbay/site-check": "0.2.0-beta.
|
|
81
|
-
"@dogsbay/site-files": "0.2.0-beta.
|
|
82
|
-
"@dogsbay/types": "0.2.0-beta.
|
|
83
|
-
"@dogsbay/ui": "0.2.0-beta.
|
|
69
|
+
"@dogsbay/elements": "0.2.0-beta.142",
|
|
70
|
+
"@dogsbay/format-dogsbay-md": "0.2.0-beta.142",
|
|
71
|
+
"@dogsbay/format-openapi": "0.2.0-beta.142",
|
|
72
|
+
"@dogsbay/format-topicmap": "0.2.0-beta.142",
|
|
73
|
+
"@dogsbay/highlight": "0.2.0-beta.142",
|
|
74
|
+
"@dogsbay/icons": "0.2.0-beta.142",
|
|
75
|
+
"@dogsbay/minja": "0.2.0-beta.142",
|
|
76
|
+
"@dogsbay/primitives": "0.2.0-beta.142",
|
|
77
|
+
"@dogsbay/render-html": "0.2.0-beta.142",
|
|
78
|
+
"@dogsbay/search": "0.2.0-beta.142",
|
|
79
|
+
"@dogsbay/serialize-core": "0.2.0-beta.142",
|
|
80
|
+
"@dogsbay/site-check": "0.2.0-beta.142",
|
|
81
|
+
"@dogsbay/site-files": "0.2.0-beta.142",
|
|
82
|
+
"@dogsbay/types": "0.2.0-beta.142",
|
|
83
|
+
"@dogsbay/ui": "0.2.0-beta.142",
|
|
84
84
|
"@mermanjs/web": "0.7.0",
|
|
85
85
|
"@playwright/test": "1.61.1",
|
|
86
86
|
"@types/node": "^22.10.0",
|