dogsbay 0.2.0-beta.119 → 0.2.0-beta.120
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/import-content.d.ts +3 -29
- package/dist/import-content.js +24 -161
- package/package.json +30 -29
package/dist/import-content.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { FormatPlugin, ExportPage, NavItem } from "@dogsbay/types";
|
|
2
|
+
import { isNamespaceAxisActive, rewriteContentRefs } from "@dogsbay/site-files";
|
|
2
3
|
import type { DogsbayConfig, SourceConfig } from "./config/index.js";
|
|
3
4
|
/**
|
|
4
5
|
* Effective version label for a source. Resolution chain:
|
|
@@ -16,17 +17,7 @@ import type { DogsbayConfig, SourceConfig } from "./config/index.js";
|
|
|
16
17
|
* URLs in another.
|
|
17
18
|
*/
|
|
18
19
|
export declare function effectiveVersion(s: SourceConfig, defaultVersion?: string): string | undefined;
|
|
19
|
-
|
|
20
|
-
* Whether the namespace axis is "active" — i.e. ≥2 distinct
|
|
21
|
-
* effective namespaces appear across the configured sources.
|
|
22
|
-
* `undefined` (an unnamed source) counts as a distinct bucket,
|
|
23
|
-
* so an unnamed + named pair activates the axis: the unnamed
|
|
24
|
-
* source emits flat URLs, the named one gets `/<name>/<slug>`.
|
|
25
|
-
*
|
|
26
|
-
* See plans/multi-source-content.md → "Detecting which axes are
|
|
27
|
-
* active".
|
|
28
|
-
*/
|
|
29
|
-
export declare function isNamespaceAxisActive(sources: SourceConfig[]): boolean;
|
|
20
|
+
export { isNamespaceAxisActive };
|
|
30
21
|
/**
|
|
31
22
|
* Whether the version axis is "active" — i.e. ≥2 distinct
|
|
32
23
|
* effective versions appear across the configured sources.
|
|
@@ -214,24 +205,7 @@ export declare function importContent(resolvedSources: string[], config: Dogsbay
|
|
|
214
205
|
* naming the page and the version.
|
|
215
206
|
*/
|
|
216
207
|
export declare function checkRootVersionCollisions(pages: ExportPage[], rootVersion: string, versionIds: (string | undefined)[]): void;
|
|
217
|
-
|
|
218
|
-
* Prefix INTERNAL-ABSOLUTE refs in a page tree with the source's URL
|
|
219
|
-
* segments, matching the slug rewrite — so a versioned/localised page's
|
|
220
|
-
* links and images resolve under the same `/<segs>/...` prefix as the
|
|
221
|
-
* pages themselves. Covers page links (`href`) and asset refs (`src`),
|
|
222
|
-
* in both markdown nodes (`src`/`href` fields) and raw-HTML nodes
|
|
223
|
-
* (`href="..."`/`src="..."` inside an `html` string).
|
|
224
|
-
*
|
|
225
|
-
* Refs at this pipeline stage are ROOT-RELATIVE and carry NO basePath
|
|
226
|
-
* (importers resolve to `/foo`; basePath is composed downstream), so the
|
|
227
|
-
* match anchor is `/`, not `<basePath>/`. A ref is prefixed iff it is an
|
|
228
|
-
* internal absolute path (starts with `/`), and NOT: external
|
|
229
|
-
* (`scheme:`, `//host`), an anchor (`#`), the site home (`/`), under a
|
|
230
|
-
* GLOBAL prefix (taxonomy index paths — one `/tags/` for the whole site,
|
|
231
|
-
* never version-bucketed), or ALREADY carrying this source's full
|
|
232
|
-
* `/<segs>/` prefix (idempotent). Relative refs are left untouched.
|
|
233
|
-
*/
|
|
234
|
-
export declare function rewriteContentRefs(tree: unknown, prefixSegs: string[], globalPrefixes: Set<string>): void;
|
|
208
|
+
export { rewriteContentRefs };
|
|
235
209
|
/**
|
|
236
210
|
* Build the options bag passed to the format plugin's import method.
|
|
237
211
|
* The shape is intentionally loose (`Record<string, unknown>`) because
|
package/dist/import-content.js
CHANGED
|
@@ -11,20 +11,13 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { isAbsolute, join, resolve } from "node:path";
|
|
13
13
|
import { normalizeBasePath, resolvePrefixes } from "@dogsbay/format-astro";
|
|
14
|
+
import { isNamespaceAxisActive, namespaceNavGroup, prefixNavHrefs, prefixSlug, rewriteContentRefs, sourceNamespace, } from "@dogsbay/site-files";
|
|
14
15
|
// THE registry — shared with `dogsbay convert` (commands/convert.ts). This
|
|
15
16
|
// module used to keep its own copy in a different order, so `site build` and
|
|
16
17
|
// `convert` disagreed about the same directory. See formats.ts.
|
|
17
18
|
import { FORMATS, DOCUSAURUS_ADAPTERS, loadAdapter, MDX_ADAPTERS, detectFormat, importerNames, } from "./formats.js";
|
|
18
|
-
/**
|
|
19
|
-
|
|
20
|
-
* the source has no explicit `name:` — the source's URLs stay
|
|
21
|
-
* flat (no namespace segment) even when paired with named
|
|
22
|
-
* siblings. Avoids surprising "I added one namespace and now
|
|
23
|
-
* everything else got a /docs/ prefix" rewrites.
|
|
24
|
-
*/
|
|
25
|
-
function effectiveNamespace(s) {
|
|
26
|
-
return s.name;
|
|
27
|
-
}
|
|
19
|
+
/** A source's namespace (its `name:`); the rule lives in @dogsbay/site-files. */
|
|
20
|
+
const effectiveNamespace = sourceNamespace;
|
|
28
21
|
/**
|
|
29
22
|
* Effective version label for a source. Resolution chain:
|
|
30
23
|
* 1. `source.version`
|
|
@@ -43,24 +36,9 @@ function effectiveNamespace(s) {
|
|
|
43
36
|
export function effectiveVersion(s, defaultVersion) {
|
|
44
37
|
return s.version ?? defaultVersion;
|
|
45
38
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
* `undefined` (an unnamed source) counts as a distinct bucket,
|
|
50
|
-
* so an unnamed + named pair activates the axis: the unnamed
|
|
51
|
-
* source emits flat URLs, the named one gets `/<name>/<slug>`.
|
|
52
|
-
*
|
|
53
|
-
* See plans/multi-source-content.md → "Detecting which axes are
|
|
54
|
-
* active".
|
|
55
|
-
*/
|
|
56
|
-
export function isNamespaceAxisActive(sources) {
|
|
57
|
-
if (sources.length < 2)
|
|
58
|
-
return false;
|
|
59
|
-
const seen = new Set();
|
|
60
|
-
for (const s of sources)
|
|
61
|
-
seen.add(effectiveNamespace(s));
|
|
62
|
-
return seen.size >= 2;
|
|
63
|
-
}
|
|
39
|
+
// Whether the namespace axis is active: shared with dogsbay-ssg, so both
|
|
40
|
+
// pipelines put a named source under the same /<name>/ URLs.
|
|
41
|
+
export { isNamespaceAxisActive };
|
|
64
42
|
/**
|
|
65
43
|
* Whether the version axis is "active" — i.e. ≥2 distinct
|
|
66
44
|
* effective versions appear across the configured sources.
|
|
@@ -242,6 +220,17 @@ export async function importContent(resolvedSources, config, options = {}) {
|
|
|
242
220
|
if (!firstImporter)
|
|
243
221
|
firstImporter = importer;
|
|
244
222
|
const opts = await buildImportOptions(config, sourceCfg, options);
|
|
223
|
+
// First URL segments a site-root link must not start with by accident:
|
|
224
|
+
// `/api/list-things` on a source published under `/api/` reads as
|
|
225
|
+
// already prefixed (rewriteContentRefs), and under base path `/api` as
|
|
226
|
+
// already based (format-astro). Importers that invent slugs (OpenAPI
|
|
227
|
+
// tags) avoid them.
|
|
228
|
+
const combinedPrefix = resolvePrefixes(config.site.url, config.site.basePath).combined;
|
|
229
|
+
opts.reservedSlugs = [
|
|
230
|
+
...getPrefixSegments(sourceCfg, axes, defaults).slice(0, 1),
|
|
231
|
+
...combinedPrefix.split("/").filter(Boolean).slice(0, 1),
|
|
232
|
+
...globalPrefixes,
|
|
233
|
+
];
|
|
245
234
|
const result = await importer.import(resolved, opts);
|
|
246
235
|
const { pages, nav } = result;
|
|
247
236
|
const ownRoots = [...new Set([result.contentRoot, resolved].filter((r) => !!r))];
|
|
@@ -310,7 +299,7 @@ export async function importContent(resolvedSources, config, options = {}) {
|
|
|
310
299
|
// emitter (page output paths, sitemap, llms.txt, agent-
|
|
311
300
|
// readiness mirrors) reads slug.
|
|
312
301
|
for (const page of pages) {
|
|
313
|
-
page.slug =
|
|
302
|
+
page.slug = prefixSlug(prefixSegs, page.slug);
|
|
314
303
|
}
|
|
315
304
|
// Prefix in-content refs with the source's URL segments, matching
|
|
316
305
|
// the slug rewrite: internal page links AND asset refs (`/foo` →
|
|
@@ -332,11 +321,9 @@ export async function importContent(resolvedSources, config, options = {}) {
|
|
|
332
321
|
// unnamed in a namespace-active site → "default
|
|
333
322
|
// section")
|
|
334
323
|
const ns = axes.namespace ? effectiveNamespace(sourceCfg) : undefined;
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
children: prefixedNav,
|
|
339
|
-
});
|
|
324
|
+
const group = namespaceNavGroup(ns, prefixedNav);
|
|
325
|
+
if (group) {
|
|
326
|
+
allNav.push(group);
|
|
340
327
|
}
|
|
341
328
|
else {
|
|
342
329
|
allNav.push(...prefixedNav);
|
|
@@ -391,133 +378,9 @@ export function checkRootVersionCollisions(pages, rootVersion, versionIds) {
|
|
|
391
378
|
}
|
|
392
379
|
}
|
|
393
380
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
* links and images resolve under the same `/<segs>/...` prefix as the
|
|
398
|
-
* pages themselves. Covers page links (`href`) and asset refs (`src`),
|
|
399
|
-
* in both markdown nodes (`src`/`href` fields) and raw-HTML nodes
|
|
400
|
-
* (`href="..."`/`src="..."` inside an `html` string).
|
|
401
|
-
*
|
|
402
|
-
* Refs at this pipeline stage are ROOT-RELATIVE and carry NO basePath
|
|
403
|
-
* (importers resolve to `/foo`; basePath is composed downstream), so the
|
|
404
|
-
* match anchor is `/`, not `<basePath>/`. A ref is prefixed iff it is an
|
|
405
|
-
* internal absolute path (starts with `/`), and NOT: external
|
|
406
|
-
* (`scheme:`, `//host`), an anchor (`#`), the site home (`/`), under a
|
|
407
|
-
* GLOBAL prefix (taxonomy index paths — one `/tags/` for the whole site,
|
|
408
|
-
* never version-bucketed), or ALREADY carrying this source's full
|
|
409
|
-
* `/<segs>/` prefix (idempotent). Relative refs are left untouched.
|
|
410
|
-
*/
|
|
411
|
-
export function rewriteContentRefs(tree, prefixSegs, globalPrefixes) {
|
|
412
|
-
if (prefixSegs.length === 0)
|
|
413
|
-
return;
|
|
414
|
-
const seg = prefixSegs.join("/");
|
|
415
|
-
const fullPrefix = `/${seg}/`;
|
|
416
|
-
const prefix = (u) => {
|
|
417
|
-
if (!u)
|
|
418
|
-
return u;
|
|
419
|
-
if (/^[a-z][a-z0-9+.-]*:/i.test(u))
|
|
420
|
-
return u; // scheme (http:, mailto:)
|
|
421
|
-
if (u.startsWith("//") || u.startsWith("#"))
|
|
422
|
-
return u; // protocol-rel / anchor
|
|
423
|
-
if (!u.startsWith("/"))
|
|
424
|
-
return u; // relative
|
|
425
|
-
if (u === "/" || u === "")
|
|
426
|
-
return u; // site home — global
|
|
427
|
-
if (u.startsWith(fullPrefix) || u === `/${seg}`)
|
|
428
|
-
return u; // already prefixed
|
|
429
|
-
// Global (taxonomy) prefixes live in one un-bucketed namespace.
|
|
430
|
-
const firstSeg = u.slice(1).split(/[/#?]/)[0];
|
|
431
|
-
if (globalPrefixes.has(firstSeg))
|
|
432
|
-
return u;
|
|
433
|
-
return `/${seg}${u}`;
|
|
434
|
-
};
|
|
435
|
-
// Raw HTML: rewrite the VALUE of each href="..."/src="..." attribute.
|
|
436
|
-
const rewriteHtml = (html) => html.replace(/\b(href|src)=("|')(.*?)\2/gi, (_m, attr, q, val) => `${attr}=${q}${prefix(val)}${q}`);
|
|
437
|
-
const walk = (node) => {
|
|
438
|
-
if (Array.isArray(node)) {
|
|
439
|
-
for (const item of node)
|
|
440
|
-
walk(item);
|
|
441
|
-
return;
|
|
442
|
-
}
|
|
443
|
-
if (!node || typeof node !== "object")
|
|
444
|
-
return;
|
|
445
|
-
const obj = node;
|
|
446
|
-
if (typeof obj.src === "string")
|
|
447
|
-
obj.src = prefix(obj.src);
|
|
448
|
-
if (typeof obj.href === "string")
|
|
449
|
-
obj.href = prefix(obj.href);
|
|
450
|
-
if (typeof obj.html === "string")
|
|
451
|
-
obj.html = rewriteHtml(obj.html);
|
|
452
|
-
// `::child-links{folder=…}` is a site path too: on a versioned or
|
|
453
|
-
// localized site it names a folder in THIS source's namespace.
|
|
454
|
-
if (obj.type === "child-links" && obj.props && typeof obj.props.folder === "string") {
|
|
455
|
-
const props = obj.props;
|
|
456
|
-
props.folder = prefix(`/${String(props.folder).replace(/^\/+/, "")}`);
|
|
457
|
-
}
|
|
458
|
-
// `scope=section` has no folder to prefix: record the source namespace,
|
|
459
|
-
// or the section resolves to the version segment (`/v2/`).
|
|
460
|
-
if (obj.type === "child-links" && obj.props && obj.props.scope === "section") {
|
|
461
|
-
obj.props.scopeBase = `/${seg}`;
|
|
462
|
-
}
|
|
463
|
-
for (const key of Object.keys(obj)) {
|
|
464
|
-
const val = obj[key];
|
|
465
|
-
if (val && typeof val === "object")
|
|
466
|
-
walk(val);
|
|
467
|
-
}
|
|
468
|
-
};
|
|
469
|
-
walk(tree);
|
|
470
|
-
}
|
|
471
|
-
function prefixSlugWithSegments(prefix, slug) {
|
|
472
|
-
if (prefix.length === 0)
|
|
473
|
-
return slug;
|
|
474
|
-
if (!slug)
|
|
475
|
-
return prefix.join("/");
|
|
476
|
-
return `${prefix.join("/")}/${slug}`;
|
|
477
|
-
}
|
|
478
|
-
/**
|
|
479
|
-
* Inject prefix segments into nav hrefs that match the
|
|
480
|
-
* configured basePath prefix. Skips external URLs and hrefs that
|
|
481
|
-
* don't start with basePath (the latter is unusual but worth
|
|
482
|
-
* tolerating — never silently rewrite something we don't
|
|
483
|
-
* recognise).
|
|
484
|
-
*/
|
|
485
|
-
function prefixNavHrefs(items, prefix, basePath) {
|
|
486
|
-
// basePath is normalised: "" or "/<seg>". For matching purposes
|
|
487
|
-
// we always work with a string ending in "/".
|
|
488
|
-
const matchPrefix = basePath ? `${basePath}/` : "/";
|
|
489
|
-
return items.map((item) => {
|
|
490
|
-
const next = { ...item };
|
|
491
|
-
if (item.href !== undefined) {
|
|
492
|
-
next.href = injectPrefixIntoHref(item.href, prefix, matchPrefix);
|
|
493
|
-
}
|
|
494
|
-
if (item.children && item.children.length > 0) {
|
|
495
|
-
next.children = prefixNavHrefs(item.children, prefix, basePath);
|
|
496
|
-
}
|
|
497
|
-
return next;
|
|
498
|
-
});
|
|
499
|
-
}
|
|
500
|
-
function injectPrefixIntoHref(href, prefix, matchPrefix) {
|
|
501
|
-
// External URLs pass through untouched.
|
|
502
|
-
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(href) || href.startsWith("mailto:")) {
|
|
503
|
-
return href;
|
|
504
|
-
}
|
|
505
|
-
if (!href.startsWith(matchPrefix)) {
|
|
506
|
-
// Defensive: nav items not matching basePath stay as-is. In
|
|
507
|
-
// practice all importer-produced hrefs include basePath since
|
|
508
|
-
// we thread `hrefPrefix` through buildImportOptions.
|
|
509
|
-
return href;
|
|
510
|
-
}
|
|
511
|
-
const rest = href.slice(matchPrefix.length);
|
|
512
|
-
return `${matchPrefix}${prefix.join("/")}/${rest}`;
|
|
513
|
-
}
|
|
514
|
-
/** Title-case a namespace key for sidebar display. */
|
|
515
|
-
function prettyNamespaceLabel(ns) {
|
|
516
|
-
return ns
|
|
517
|
-
.split("-")
|
|
518
|
-
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
519
|
-
.join(" ");
|
|
520
|
-
}
|
|
381
|
+
// rewriteContentRefs (a prefixed source's site-root links get its segments)
|
|
382
|
+
// lives in @dogsbay/site-files, shared with dogsbay-ssg.
|
|
383
|
+
export { rewriteContentRefs };
|
|
521
384
|
function resolveImporter(source, fromName) {
|
|
522
385
|
if (fromName && fromName !== "auto") {
|
|
523
386
|
const explicit = FORMATS.find((f) => f.name === fromName && f.canImport);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dogsbay",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.120",
|
|
4
4
|
"description": "CLI for Dogsbay — scaffold, build, and serve documentation sites with markdown / MkDocs / Obsidian / OpenAPI sources",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -43,34 +43,35 @@
|
|
|
43
43
|
"typescript": "^5.9.3",
|
|
44
44
|
"valibot": "1.1.0",
|
|
45
45
|
"yaml": "^2.9.0",
|
|
46
|
-
"@dogsbay/adoc2md-modular": "0.2.0-beta.
|
|
47
|
-
"@dogsbay/
|
|
48
|
-
"@dogsbay/
|
|
49
|
-
"@dogsbay/autodoc-python": "0.2.0-beta.
|
|
50
|
-
"@dogsbay/format-astro": "0.2.0-beta.
|
|
51
|
-
"@dogsbay/format-dita": "0.2.0-beta.
|
|
52
|
-
"@dogsbay/format-docusaurus": "0.2.0-beta.
|
|
53
|
-
"@dogsbay/format-dogsbay-md": "0.2.0-beta.
|
|
54
|
-
"@dogsbay/format-fluidtopics-markdown": "0.2.0-beta.
|
|
55
|
-
"@dogsbay/format-fluidtopics-rest": "0.2.0-beta.
|
|
56
|
-
"@dogsbay/format-gatsby": "0.2.0-beta.
|
|
57
|
-
"@dogsbay/format-gitbook": "0.2.0-beta.
|
|
58
|
-
"@dogsbay/format-hugo": "0.2.0-beta.
|
|
59
|
-
"@dogsbay/format-mdx": "0.2.0-beta.
|
|
60
|
-
"@dogsbay/format-mkdocs": "0.2.0-beta.
|
|
61
|
-
"@dogsbay/format-myst": "0.2.0-beta.
|
|
62
|
-
"@dogsbay/format-
|
|
63
|
-
"@dogsbay/format-
|
|
64
|
-
"@dogsbay/format-openknowledge": "0.2.0-beta.
|
|
65
|
-
"@dogsbay/format-rst": "0.2.0-beta.
|
|
66
|
-
"@dogsbay/format-
|
|
67
|
-
"@dogsbay/format-
|
|
68
|
-
"@dogsbay/format-vitepress": "0.2.0-beta.
|
|
69
|
-
"@dogsbay/minja": "0.2.0-beta.
|
|
70
|
-
"@dogsbay/render": "0.2.0-beta.
|
|
71
|
-
"@dogsbay/serialize-core": "0.2.0-beta.
|
|
72
|
-
"@dogsbay/site-check": "0.2.0-beta.
|
|
73
|
-
"@dogsbay/
|
|
46
|
+
"@dogsbay/adoc2md-modular": "0.2.0-beta.120",
|
|
47
|
+
"@dogsbay/format-asciidoc": "0.2.0-beta.120",
|
|
48
|
+
"@dogsbay/commands": "0.2.0-beta.120",
|
|
49
|
+
"@dogsbay/autodoc-python": "0.2.0-beta.120",
|
|
50
|
+
"@dogsbay/format-astro": "0.2.0-beta.120",
|
|
51
|
+
"@dogsbay/format-dita": "0.2.0-beta.120",
|
|
52
|
+
"@dogsbay/format-docusaurus": "0.2.0-beta.120",
|
|
53
|
+
"@dogsbay/format-dogsbay-md": "0.2.0-beta.120",
|
|
54
|
+
"@dogsbay/format-fluidtopics-markdown": "0.2.0-beta.120",
|
|
55
|
+
"@dogsbay/format-fluidtopics-rest": "0.2.0-beta.120",
|
|
56
|
+
"@dogsbay/format-gatsby": "0.2.0-beta.120",
|
|
57
|
+
"@dogsbay/format-gitbook": "0.2.0-beta.120",
|
|
58
|
+
"@dogsbay/format-hugo": "0.2.0-beta.120",
|
|
59
|
+
"@dogsbay/format-mdx": "0.2.0-beta.120",
|
|
60
|
+
"@dogsbay/format-mkdocs": "0.2.0-beta.120",
|
|
61
|
+
"@dogsbay/format-myst": "0.2.0-beta.120",
|
|
62
|
+
"@dogsbay/format-openapi": "0.2.0-beta.120",
|
|
63
|
+
"@dogsbay/format-obsidian": "0.2.0-beta.120",
|
|
64
|
+
"@dogsbay/format-openknowledge": "0.2.0-beta.120",
|
|
65
|
+
"@dogsbay/format-rst": "0.2.0-beta.120",
|
|
66
|
+
"@dogsbay/format-topicmap": "0.2.0-beta.120",
|
|
67
|
+
"@dogsbay/format-starlight": "0.2.0-beta.120",
|
|
68
|
+
"@dogsbay/format-vitepress": "0.2.0-beta.120",
|
|
69
|
+
"@dogsbay/minja": "0.2.0-beta.120",
|
|
70
|
+
"@dogsbay/render": "0.2.0-beta.120",
|
|
71
|
+
"@dogsbay/serialize-core": "0.2.0-beta.120",
|
|
72
|
+
"@dogsbay/site-check": "0.2.0-beta.120",
|
|
73
|
+
"@dogsbay/site-files": "0.2.0-beta.120",
|
|
74
|
+
"@dogsbay/types": "0.2.0-beta.120"
|
|
74
75
|
},
|
|
75
76
|
"devDependencies": {
|
|
76
77
|
"@mdx-js/mdx": "^3.1.1",
|