@dogsbay/format-astro 0.2.0-beta.146 → 0.2.0-beta.148
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/project.d.ts +40 -1
- package/dist/project.js +56 -6
- package/dist/redirects.d.ts +2 -0
- package/dist/redirects.js +1 -1
- package/package.json +7 -7
package/dist/project.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ExportPage, NavItem } from "@dogsbay/types";
|
|
2
|
-
import { type ProductModel } from "@dogsbay/site-files";
|
|
2
|
+
import { type VersionMenu, type ProductModel } from "@dogsbay/site-files";
|
|
3
3
|
import { type BlogEmitConfig } from "./blog.js";
|
|
4
4
|
import { type RedirectsConfig } from "./redirects.js";
|
|
5
5
|
/**
|
|
@@ -189,8 +189,21 @@ export interface AstroProjectOptions {
|
|
|
189
189
|
canonical?: "self";
|
|
190
190
|
noindex?: boolean;
|
|
191
191
|
sitemap?: boolean;
|
|
192
|
+
badge?: boolean;
|
|
192
193
|
path?: "";
|
|
193
194
|
}>;
|
|
195
|
+
/**
|
|
196
|
+
* Units whose home has no page, forwarded to their first page (site-files `homeForwards`):
|
|
197
|
+
* `[from, to]` site paths without the base path, written as redirects.
|
|
198
|
+
*/
|
|
199
|
+
homeForwards?: Array<[string, string]>;
|
|
200
|
+
/** Leave out a redirect the build cannot honour, with a warning, instead of failing. */
|
|
201
|
+
allowBrokenRedirects?: boolean;
|
|
202
|
+
/**
|
|
203
|
+
* The version menu's links and limit (`content.versionMenu`, @dogsbay/site-files
|
|
204
|
+
* version-menu.ts). With a `products:` block each product's comes from the model.
|
|
205
|
+
*/
|
|
206
|
+
versionMenu?: VersionMenu;
|
|
194
207
|
/**
|
|
195
208
|
* Which version is the default. Marked in switcherMap.json so
|
|
196
209
|
* the switcher can highlight it, and drives the default-version
|
|
@@ -603,6 +616,8 @@ export interface SwitcherMap {
|
|
|
603
616
|
default?: boolean;
|
|
604
617
|
/** Served at the site root (`path: ""`): its pages have no version segment. */
|
|
605
618
|
root?: boolean;
|
|
619
|
+
/** "Version: <label>" above each page's title. */
|
|
620
|
+
badge?: boolean;
|
|
606
621
|
}>;
|
|
607
622
|
/**
|
|
608
623
|
* Declared locales in declaration order. Same role as
|
|
@@ -632,6 +647,30 @@ export interface SwitcherMap {
|
|
|
632
647
|
eol?: boolean;
|
|
633
648
|
prerelease?: boolean;
|
|
634
649
|
default?: boolean;
|
|
650
|
+
badge?: boolean;
|
|
651
|
+
home: string;
|
|
652
|
+
}>>;
|
|
653
|
+
/**
|
|
654
|
+
* The version menu's links (hrefs as served, base path included) and limit,
|
|
655
|
+
* keyed by product name, `_` for the site-wide versions and the unnamed
|
|
656
|
+
* source. Left out when no menu has any.
|
|
657
|
+
*/
|
|
658
|
+
versionMenu?: Record<string, {
|
|
659
|
+
links: Array<{
|
|
660
|
+
label: string;
|
|
661
|
+
href: string;
|
|
662
|
+
}>;
|
|
663
|
+
limit?: number;
|
|
664
|
+
}>;
|
|
665
|
+
/**
|
|
666
|
+
* With a `products:` block and two or more products: the product menu per
|
|
667
|
+
* locale (`<locale|_>`), in the block's order, each entry going to the
|
|
668
|
+
* product's home (its default version, in the locale when it has one) —
|
|
669
|
+
* site-files `productSwitcher`, as dogsbay-ssg's product menu.
|
|
670
|
+
*/
|
|
671
|
+
products?: Record<string, Array<{
|
|
672
|
+
name: string;
|
|
673
|
+
label: string;
|
|
635
674
|
home: string;
|
|
636
675
|
}>>;
|
|
637
676
|
}
|
package/dist/project.js
CHANGED
|
@@ -30,7 +30,7 @@ import { buildSitemapFiles, buildSitemapIndex } from "./sitemap.js";
|
|
|
30
30
|
import { canonicalPlan } from "./canonical.js";
|
|
31
31
|
import { headMetadata } from "./head-metadata.js";
|
|
32
32
|
import { buildHeadersFile, buildRobotsTxt as buildRobotsTxtFile } from "@dogsbay/site-files";
|
|
33
|
-
import { mergeHeaders, mergeRedirects, productRootRedirects, siteFiles, siteRootTarget, switcher as productSwitcherRows, toPagesScript, toScript, unitKeyString, unitManifest, unitOf, } from "@dogsbay/site-files";
|
|
33
|
+
import { mergeHeaders, mergeRedirects, productRootRedirects, siteFiles, siteRootTarget, switcher as productSwitcherRows, productSwitcher, isVersionMenuHref, versionMenuHref, toPagesScript, toScript, unitKeyString, unitManifest, unitOf, } from "@dogsbay/site-files";
|
|
34
34
|
import { assembleBook, estimateTreeBytes } from "./granularity.js";
|
|
35
35
|
/**
|
|
36
36
|
* The per-topic "Read as single page" affordance (granularity book view).
|
|
@@ -1479,6 +1479,8 @@ function emitNotFoundPage(outputDir, options) {
|
|
|
1479
1479
|
const sitemap = withBasePath(combined, "/sitemap-index.xml");
|
|
1480
1480
|
const llms = withBasePath(combined, "/llms.txt");
|
|
1481
1481
|
const showLlms = options.llmsTxt !== false;
|
|
1482
|
+
// The product menu (only its menu: the page is in no product, so it reads "Product")
|
|
1483
|
+
const productMenu = options.products ? productMenuRows(options.products, combined) !== undefined : false;
|
|
1482
1484
|
writeFileSync(path, `---
|
|
1483
1485
|
// AUTO-GENERATED by dogsbay site build — safe to delete; will regenerate next build.
|
|
1484
1486
|
// To customize this page, remove the marker line above (or modify the file
|
|
@@ -1487,7 +1489,8 @@ import "@/styles/global.css";
|
|
|
1487
1489
|
import DocsLayout from "@dogsbay/docs-layout/DocsLayout.astro";
|
|
1488
1490
|
import type { SiteConfig } from "@dogsbay/types";
|
|
1489
1491
|
import navData from "@/data/nav.json";
|
|
1490
|
-
import siteConfigData from "@/data/site.json"
|
|
1492
|
+
import siteConfigData from "@/data/site.json";${productMenu ? `
|
|
1493
|
+
import switcherMapData from "@/data/switcherMap.json";` : ""}
|
|
1491
1494
|
const siteConfig = siteConfigData as SiteConfig;
|
|
1492
1495
|
const favicon = (siteConfigData as { favicon?: string }).favicon;
|
|
1493
1496
|
---
|
|
@@ -1502,7 +1505,8 @@ const favicon = (siteConfigData as { favicon?: string }).favicon;
|
|
|
1502
1505
|
favicon={favicon}
|
|
1503
1506
|
noindex={true}
|
|
1504
1507
|
excludeFromSearch={true}
|
|
1505
|
-
ogType="website"
|
|
1508
|
+
ogType="website"${productMenu ? `
|
|
1509
|
+
switcherMap={{ versions: [], byLogicalKey: {}, products: switcherMapData.products } as never}` : ""}
|
|
1506
1510
|
>
|
|
1507
1511
|
<article class="docs-prose">
|
|
1508
1512
|
<h1>Page not found</h1>
|
|
@@ -2454,6 +2458,8 @@ export function emitConfigDerivedFiles(outputDir, options, pages = []) {
|
|
|
2454
2458
|
*/
|
|
2455
2459
|
function emitRedirects(outputDir, options, pages) {
|
|
2456
2460
|
const prefix = combinedPrefix(options);
|
|
2461
|
+
// A product's root goes straight to where its default version's home forwards (one hop, no chain to report)
|
|
2462
|
+
const forwarded = new Map(options.homeForwards ?? []);
|
|
2457
2463
|
const { rules, errors, warnings } = collectRedirects({
|
|
2458
2464
|
pages: pages.map((p) => ({
|
|
2459
2465
|
slug: p.slug,
|
|
@@ -2462,11 +2468,13 @@ function emitRedirects(outputDir, options, pages) {
|
|
|
2462
2468
|
// Front matter paths are relative to the page's language and version (#096): collectRedirects needs its original slug.
|
|
2463
2469
|
multiSource: p.multiSource,
|
|
2464
2470
|
})),
|
|
2465
|
-
// A product's root forwards to its default version's home
|
|
2466
|
-
config
|
|
2467
|
-
|
|
2471
|
+
// A product's root forwards to its default version's home, a unit with no home page to its
|
|
2472
|
+
// first page (homeForwards); a redirect the config gives for either wins.
|
|
2473
|
+
config: options.products || options.homeForwards?.length
|
|
2474
|
+
? { ...Object.fromEntries((options.products ? productRootRedirects(options.products) : []).map(([from, to]) => [from, forwarded.get(to) ?? to])), ...Object.fromEntries(forwarded), ...options.redirects }
|
|
2468
2475
|
: options.redirects,
|
|
2469
2476
|
prefix,
|
|
2477
|
+
lenient: options.allowBrokenRedirects === true,
|
|
2470
2478
|
});
|
|
2471
2479
|
if (errors.length > 0) {
|
|
2472
2480
|
throw new Error(`redirects: ${errors.length} problem(s) — refusing to build.\n - ${errors.join("\n - ")}`);
|
|
@@ -2621,14 +2629,54 @@ export function emitSwitcherMap(pages, outputDir, options) {
|
|
|
2621
2629
|
byLogicalKey[key].push(variant);
|
|
2622
2630
|
}
|
|
2623
2631
|
const data = { versions, locales, byLogicalKey };
|
|
2632
|
+
const menus = versionMenus(options, combined);
|
|
2633
|
+
if (menus)
|
|
2634
|
+
data.versionMenu = menus;
|
|
2624
2635
|
if (options.products) {
|
|
2625
2636
|
data.productVersions = productVersionRows(options.products, combined);
|
|
2637
|
+
const productMenu = productMenuRows(options.products, combined);
|
|
2638
|
+
if (productMenu)
|
|
2639
|
+
data.products = productMenu;
|
|
2626
2640
|
emitProductDataFiles(pages, outputDir, options, options.products);
|
|
2627
2641
|
}
|
|
2628
2642
|
mkdirSync(dataDir, { recursive: true });
|
|
2629
2643
|
writeFileSync(outPath, JSON.stringify(data, null, 2));
|
|
2630
2644
|
}
|
|
2645
|
+
/** The product menu per locale (`<locale|_>`), or nothing with fewer than two products. */
|
|
2646
|
+
function productMenuRows(model, combined) {
|
|
2647
|
+
const locales = model.localeAxis ? [...new Set(model.units.map((u) => u.locale))] : [undefined];
|
|
2648
|
+
const out = {};
|
|
2649
|
+
for (const l of locales) {
|
|
2650
|
+
const rows = productSwitcher(model, l);
|
|
2651
|
+
if (rows.length < 2)
|
|
2652
|
+
continue;
|
|
2653
|
+
out[l ?? "_"] = rows.map((r) => ({ name: r.name, label: r.label, home: `${combined}${r.home}` }));
|
|
2654
|
+
}
|
|
2655
|
+
return Object.keys(out).length ? out : undefined;
|
|
2656
|
+
}
|
|
2631
2657
|
/** Each product's switcher rows per locale (`<product|_>/<locale|_>`), with full home URLs. */
|
|
2658
|
+
/** Each version menu's links and limit, per product (`_`: site-wide), hrefs as the site serves them. */
|
|
2659
|
+
function versionMenus(options, combined) {
|
|
2660
|
+
const out = {};
|
|
2661
|
+
// A site path may be written with the base path (as redirects take it): dropped, then the served prefix added
|
|
2662
|
+
const base = normalizeBasePath(options.basePath).replace(/\/+$/, "");
|
|
2663
|
+
const unbased = (href) => (base && (href === base || href.startsWith(`${base}/`)) ? href.slice(base.length) || "/" : href);
|
|
2664
|
+
const add = (key, m) => {
|
|
2665
|
+
if (!m)
|
|
2666
|
+
return;
|
|
2667
|
+
// Checked at config load; options built in code are checked again, so a script URL never reaches the page
|
|
2668
|
+
const links = m.links.filter((l) => typeof l.label === "string" && l.label.trim() && isVersionMenuHref(l.href));
|
|
2669
|
+
if (!links.length && m.limit === undefined)
|
|
2670
|
+
return;
|
|
2671
|
+
out[key] = { links: links.map((l) => ({ label: l.label, href: versionMenuHref(unbased(l.href), combined) })), ...(m.limit !== undefined ? { limit: m.limit } : {}) };
|
|
2672
|
+
};
|
|
2673
|
+
if (options.products)
|
|
2674
|
+
for (const p of options.products.products)
|
|
2675
|
+
add(p.name ?? "_", p.versionMenu);
|
|
2676
|
+
else
|
|
2677
|
+
add("_", options.versionMenu);
|
|
2678
|
+
return Object.keys(out).length ? out : undefined;
|
|
2679
|
+
}
|
|
2632
2680
|
function productVersionRows(model, combined) {
|
|
2633
2681
|
const out = {};
|
|
2634
2682
|
const locales = model.localeAxis ? [...new Set(model.units.map((u) => u.locale))] : [undefined];
|
|
@@ -2645,6 +2693,7 @@ function productVersionRows(model, combined) {
|
|
|
2645
2693
|
...(r.eol ? { eol: true } : {}),
|
|
2646
2694
|
...(r.prerelease ? { prerelease: true } : {}),
|
|
2647
2695
|
...(r.isDefault ? { default: true } : {}),
|
|
2696
|
+
...(r.badge ? { badge: true } : {}),
|
|
2648
2697
|
home: `${combined}${r.home}`,
|
|
2649
2698
|
}));
|
|
2650
2699
|
}
|
|
@@ -2715,6 +2764,7 @@ function composeAxisHeader(declared, seen, defaultId, allowEol) {
|
|
|
2715
2764
|
...(allowEol && d.eol === true ? { eol: true } : {}),
|
|
2716
2765
|
...(allowEol && d.prerelease === true ? { prerelease: true } : {}),
|
|
2717
2766
|
...(allowEol && d.hidden === true ? { hidden: true } : {}),
|
|
2767
|
+
...(allowEol && d.badge === true ? { badge: true } : {}),
|
|
2718
2768
|
...(defaultId === d.id ? { default: true } : {}),
|
|
2719
2769
|
});
|
|
2720
2770
|
seenInDeclared.add(d.id);
|
package/dist/redirects.d.ts
CHANGED
|
@@ -54,6 +54,8 @@ export interface CollectRedirectsInput {
|
|
|
54
54
|
config?: RedirectsConfig;
|
|
55
55
|
/** URL prefix the site is served under (urlBase + basePath), "" when at the root. */
|
|
56
56
|
prefix: string;
|
|
57
|
+
/** A rule the build cannot honour is left out with a warning instead of failing (`--allow-broken-redirects`). */
|
|
58
|
+
lenient?: boolean;
|
|
57
59
|
}
|
|
58
60
|
export interface CollectRedirectsResult {
|
|
59
61
|
rules: RedirectRule[];
|
package/dist/redirects.js
CHANGED
|
@@ -172,7 +172,7 @@ export function collectRedirects(input) {
|
|
|
172
172
|
});
|
|
173
173
|
}
|
|
174
174
|
const resolved = resolveRedirects(candidates, {
|
|
175
|
-
strict: true,
|
|
175
|
+
strict: input.lenient !== true,
|
|
176
176
|
// A target's "#anchor" or "?query" is not part of the page it names.
|
|
177
177
|
isPage: (path) => livePaths.has(path.split(/[?#]/)[0]),
|
|
178
178
|
cloudflareLimits: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dogsbay/format-astro",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.148",
|
|
4
4
|
"description": "Astro format plugin for Dogsbay — export TreeNode[] to Astro component markup",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -32,12 +32,12 @@
|
|
|
32
32
|
"license": "MIT",
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"markdown-it": "^14.3.0",
|
|
35
|
-
"@dogsbay/format-dogsbay-md": "0.2.0-beta.
|
|
36
|
-
"@dogsbay/
|
|
37
|
-
"@dogsbay/
|
|
38
|
-
"@dogsbay/site-files": "0.2.0-beta.
|
|
39
|
-
"@dogsbay/types": "0.2.0-beta.
|
|
40
|
-
"@dogsbay/ui": "0.2.0-beta.
|
|
35
|
+
"@dogsbay/format-dogsbay-md": "0.2.0-beta.148",
|
|
36
|
+
"@dogsbay/serialize-core": "0.2.0-beta.148",
|
|
37
|
+
"@dogsbay/render-html": "0.2.0-beta.148",
|
|
38
|
+
"@dogsbay/site-files": "0.2.0-beta.148",
|
|
39
|
+
"@dogsbay/types": "0.2.0-beta.148",
|
|
40
|
+
"@dogsbay/ui": "0.2.0-beta.148"
|
|
41
41
|
},
|
|
42
42
|
"repository": {
|
|
43
43
|
"type": "git",
|