@dogsbay/format-astro 0.2.0-beta.144 → 0.2.0-beta.145
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/canonical.d.ts +3 -1
- package/dist/canonical.js +10 -6
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/project.d.ts +23 -0
- package/dist/project.js +82 -9
- package/package.json +7 -7
package/dist/canonical.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* plans/canonical-versions.md, step 2.
|
|
8
8
|
*/
|
|
9
9
|
import type { ExportPage } from "@dogsbay/types";
|
|
10
|
-
import { type VersionSeo } from "@dogsbay/site-files";
|
|
10
|
+
import { type ProductModel, type VersionSeo } from "@dogsbay/site-files";
|
|
11
11
|
export interface CanonicalPlanOptions {
|
|
12
12
|
/** Pages the build does not emit (fragments, `excludeFromRoutes`): never a canonical target. */
|
|
13
13
|
isEmitted?: (page: ExportPage) => boolean;
|
|
@@ -18,6 +18,8 @@ export interface CanonicalPlanOptions {
|
|
|
18
18
|
id: string;
|
|
19
19
|
} & VersionSeo>;
|
|
20
20
|
defaultVersion?: string;
|
|
21
|
+
/** With a `products:` block: each product's own versions and default replace `versions`/`defaultVersion`. */
|
|
22
|
+
products?: ProductModel;
|
|
21
23
|
redirects?: Record<string, string | {
|
|
22
24
|
to: string;
|
|
23
25
|
status?: number;
|
package/dist/canonical.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { decideCanonical } from "@dogsbay/site-files";
|
|
1
|
+
import { decideCanonical, productDefault } from "@dogsbay/site-files";
|
|
2
2
|
import { normalizePrefix, originOf, pageHref } from "./sitemap.js";
|
|
3
3
|
import { inPageUnit, pageUnitPrefix } from "./redirects.js";
|
|
4
4
|
const URL_RE = /^[a-z][a-z0-9+.-]*:/i;
|
|
@@ -60,27 +60,31 @@ export function canonicalPlan(pages, o) {
|
|
|
60
60
|
return s;
|
|
61
61
|
return s.endsWith("/" + inV) ? s.slice(0, -inV.length - 1) : s === inV ? "" : s;
|
|
62
62
|
};
|
|
63
|
+
// The page's product's versions and default (the site-wide ones without a products: block).
|
|
64
|
+
const axisOf = (p) => o.products ? productDefault(o.products, p.multiSource?.namespace) ?? {} : { versions: o.versions, defaultVersion: o.defaultVersion };
|
|
63
65
|
const defaults = new Map();
|
|
64
|
-
|
|
66
|
+
{
|
|
65
67
|
for (const p of pages) {
|
|
66
|
-
|
|
68
|
+
const dv = axisOf(p).defaultVersion;
|
|
69
|
+
if (!dv || p.multiSource?.version !== dv || !live(p))
|
|
67
70
|
continue;
|
|
68
71
|
const g = defaults.get(group(p)) ?? { prefix: prefixOf(p), pages: new Map() };
|
|
69
72
|
g.pages.set(inVersion(p), slugOf(p));
|
|
70
73
|
defaults.set(group(p), g);
|
|
71
74
|
}
|
|
72
75
|
}
|
|
73
|
-
const hasOlder = pages.some((p) => p.multiSource?.version && p.multiSource.version !==
|
|
76
|
+
const hasOlder = pages.some((p) => p.multiSource?.version && p.multiSource.version !== axisOf(p).defaultVersion);
|
|
74
77
|
const url = (slug) => (origin ? `${origin}${pageHref(slug, prefix)}` : undefined);
|
|
75
78
|
for (const p of pages) {
|
|
76
79
|
if (!live(p))
|
|
77
80
|
continue;
|
|
78
|
-
const
|
|
81
|
+
const axis = axisOf(p);
|
|
82
|
+
const version = axis.versions?.find((v) => v.id === p.multiSource?.version);
|
|
79
83
|
const table = defaults.get(group(p));
|
|
80
84
|
const fm = typeof p.frontmatter?.canonicalUrl === "string" ? p.frontmatter.canonicalUrl : undefined;
|
|
81
85
|
const d = decideCanonical({
|
|
82
86
|
selfUrl: url(slugOf(p)),
|
|
83
|
-
isDefault: !hasOlder || !p.multiSource?.version || p.multiSource.version ===
|
|
87
|
+
isDefault: !hasOlder || !p.multiSource?.version || p.multiSource.version === axis.defaultVersion || !table,
|
|
84
88
|
path: inVersion(p),
|
|
85
89
|
version,
|
|
86
90
|
frontmatter: fm,
|
package/dist/index.d.ts
CHANGED
|
@@ -31,5 +31,6 @@ export type { DiffMarks } from "./diff-decoration.js";
|
|
|
31
31
|
*/
|
|
32
32
|
export { BOOK_SEGMENT, assembleBook, bookHeading, bookSlugFrom, estimateTreeBytes, openAsPageNode, rewriteIntraBookLinks, shiftHeadings, slugFromHref, } from "./granularity.js";
|
|
33
33
|
export type { AssembleBookOptions, BookView } from "./granularity.js";
|
|
34
|
-
export { pageUnitPrefix, inPageUnit, normalizeTarget } from "./redirects.js";
|
|
34
|
+
export { pageUnitPrefix, inPageUnit, normalizeTarget, collectRedirects } from "./redirects.js";
|
|
35
|
+
export { canonicalPlan } from "./canonical.js";
|
|
35
36
|
export { TONES, CARD_TONE_CLASSES, TAB_VARIANTS, THEME_SCOPES } from "./tone-classes.js";
|
package/dist/index.js
CHANGED
|
@@ -59,6 +59,7 @@ export { DIFF_CSS, decorateDiff, diffMarksOf, hasDiffMarks, injectAttrs, inlineS
|
|
|
59
59
|
* See plans/granularity-views.md and docs/single-page-view.md.
|
|
60
60
|
*/
|
|
61
61
|
export { BOOK_SEGMENT, assembleBook, bookHeading, bookSlugFrom, estimateTreeBytes, openAsPageNode, rewriteIntraBookLinks, shiftHeadings, slugFromHref, } from "./granularity.js";
|
|
62
|
-
export { pageUnitPrefix, inPageUnit, normalizeTarget } from "./redirects.js";
|
|
62
|
+
export { pageUnitPrefix, inPageUnit, normalizeTarget, collectRedirects } from "./redirects.js";
|
|
63
|
+
export { canonicalPlan } from "./canonical.js";
|
|
63
64
|
// Variants, tones and scoped themes: the class tables both renderers draw from
|
|
64
65
|
export { TONES, CARD_TONE_CLASSES, TAB_VARIANTS, THEME_SCOPES } from "./tone-classes.js";
|
package/dist/project.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ExportPage, NavItem } from "@dogsbay/types";
|
|
2
|
+
import { type ProductModel } from "@dogsbay/site-files";
|
|
2
3
|
import { type BlogEmitConfig } from "./blog.js";
|
|
3
4
|
import { type RedirectsConfig } from "./redirects.js";
|
|
4
5
|
/**
|
|
@@ -197,6 +198,14 @@ export interface AstroProjectOptions {
|
|
|
197
198
|
* default (enforced at config load).
|
|
198
199
|
*/
|
|
199
200
|
defaultVersion?: string;
|
|
201
|
+
/**
|
|
202
|
+
* With a `products:` block: the model from @dogsbay/site-files
|
|
203
|
+
* (`productModel`). Each product's own versions and default then drive
|
|
204
|
+
* canonicals, the version switcher (`productVersions`), the forward from
|
|
205
|
+
* a product's root to its default version, and the data files
|
|
206
|
+
* `_dogsbay/versions.js` and `<unit home>_dogsbay/pages.js`.
|
|
207
|
+
*/
|
|
208
|
+
products?: ProductModel;
|
|
200
209
|
/**
|
|
201
210
|
* Declared locales for the i18n axis. Same role as
|
|
202
211
|
* `versions` for the locale axis.
|
|
@@ -601,6 +610,20 @@ export interface SwitcherMap {
|
|
|
601
610
|
* the full URL plus the (locale, version) combo it represents.
|
|
602
611
|
*/
|
|
603
612
|
byLogicalKey: Record<string, SwitcherVariant[]>;
|
|
613
|
+
/**
|
|
614
|
+
* With a `products:` block: each product's version rows per locale, keyed
|
|
615
|
+
* `<product|_>/<locale|_>`, in config order, hidden versions left out, each
|
|
616
|
+
* with its home URL (where the row goes when the page is not in it). The
|
|
617
|
+
* version switcher uses these instead of `versions`.
|
|
618
|
+
*/
|
|
619
|
+
productVersions?: Record<string, Array<{
|
|
620
|
+
id: string;
|
|
621
|
+
label?: string;
|
|
622
|
+
eol?: boolean;
|
|
623
|
+
prerelease?: boolean;
|
|
624
|
+
default?: boolean;
|
|
625
|
+
home: string;
|
|
626
|
+
}>>;
|
|
604
627
|
}
|
|
605
628
|
export interface SwitcherVariant {
|
|
606
629
|
/** Present when the locale axis is active for this site. */
|
package/dist/project.js
CHANGED
|
@@ -30,6 +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 { productRootRedirects, siteFiles, siteRootTarget, switcher as productSwitcherRows, toPagesScript, toScript, unitKeyString, unitManifest, unitOf, } from "@dogsbay/site-files";
|
|
33
34
|
import { assembleBook, estimateTreeBytes } from "./granularity.js";
|
|
34
35
|
/**
|
|
35
36
|
* The per-topic "Read as single page" affordance (granularity book view).
|
|
@@ -1776,6 +1777,7 @@ export async function emitAstroPages(pages, nav, outputDir, options) {
|
|
|
1776
1777
|
basePath: combined,
|
|
1777
1778
|
versions: options.versions,
|
|
1778
1779
|
defaultVersion: options.defaultVersion,
|
|
1780
|
+
products: options.products,
|
|
1779
1781
|
redirects: options.redirects,
|
|
1780
1782
|
});
|
|
1781
1783
|
// hreflang alternates, WebSite on home pages, BreadcrumbList from the nav (src/head-metadata.ts).
|
|
@@ -2348,13 +2350,16 @@ export async function emitAstroPages(pages, nav, outputDir, options) {
|
|
|
2348
2350
|
// With the default version at the root (`path: ""`) and no locale or
|
|
2349
2351
|
// namespace, it has no subtree: its entries are the ones outside every
|
|
2350
2352
|
// other version's subtree.
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2353
|
+
// With products and no unnamed source, `/` goes to the first product's default home (site-files siteRootTarget).
|
|
2354
|
+
const productRoot = options.products ? siteRootTarget(options.products) : undefined;
|
|
2355
|
+
const firstHref = productRoot !== undefined ? `${combined}${productRoot}` :
|
|
2356
|
+
(defaultVersionPrefixes.length > 0
|
|
2357
|
+
? findFirstNavHrefUnder(outputNav, defaultVersionPrefixes, [...subtreeRoots]
|
|
2358
|
+
.filter(([q]) => defaultVersionPrefixes.some((d) => `${combined}/${q}`.startsWith(`${d}/`)))
|
|
2359
|
+
.map(([q]) => `${combined}/${q}`))
|
|
2360
|
+
: rootVersion && subtreeRoots.size > 0
|
|
2361
|
+
? findFirstNavHrefUnder(outputNav, [combined], nestedIn(""))
|
|
2362
|
+
: undefined) ?? findFirstNavHref(outputNav, basePath);
|
|
2358
2363
|
const needsRootRedirect = basePath !== "" ||
|
|
2359
2364
|
(!section && !rootIndexEmitted && firstHref !== "");
|
|
2360
2365
|
if (needsRootRedirect) {
|
|
@@ -2457,7 +2462,10 @@ function emitRedirects(outputDir, options, pages) {
|
|
|
2457
2462
|
// Front matter paths are relative to the page's language and version (#096): collectRedirects needs its original slug.
|
|
2458
2463
|
multiSource: p.multiSource,
|
|
2459
2464
|
})),
|
|
2460
|
-
config
|
|
2465
|
+
// A product's root forwards to its default version's home; a redirect the config gives for it wins.
|
|
2466
|
+
config: options.products
|
|
2467
|
+
? { ...Object.fromEntries(productRootRedirects(options.products)), ...options.redirects }
|
|
2468
|
+
: options.redirects,
|
|
2461
2469
|
prefix,
|
|
2462
2470
|
});
|
|
2463
2471
|
if (errors.length > 0) {
|
|
@@ -2572,7 +2580,7 @@ export function emitSwitcherMap(pages, outputDir, options) {
|
|
|
2572
2580
|
}
|
|
2573
2581
|
const versionAxisActive = versionsSeen.size >= 2;
|
|
2574
2582
|
const localeAxisActive = localesSeen.size >= 2;
|
|
2575
|
-
if (!versionAxisActive && !localeAxisActive) {
|
|
2583
|
+
if (!versionAxisActive && !localeAxisActive && !options.products) {
|
|
2576
2584
|
mkdirSync(dataDir, { recursive: true });
|
|
2577
2585
|
writeFileSync(outPath, JSON.stringify({ versions: [], locales: [], byLogicalKey: {} }, null, 2));
|
|
2578
2586
|
return;
|
|
@@ -2601,9 +2609,73 @@ export function emitSwitcherMap(pages, outputDir, options) {
|
|
|
2601
2609
|
byLogicalKey[key].push(variant);
|
|
2602
2610
|
}
|
|
2603
2611
|
const data = { versions, locales, byLogicalKey };
|
|
2612
|
+
if (options.products) {
|
|
2613
|
+
data.productVersions = productVersionRows(options.products, combined);
|
|
2614
|
+
emitProductDataFiles(pages, outputDir, options, options.products);
|
|
2615
|
+
}
|
|
2604
2616
|
mkdirSync(dataDir, { recursive: true });
|
|
2605
2617
|
writeFileSync(outPath, JSON.stringify(data, null, 2));
|
|
2606
2618
|
}
|
|
2619
|
+
/** Each product's switcher rows per locale (`<product|_>/<locale|_>`), with full home URLs. */
|
|
2620
|
+
function productVersionRows(model, combined) {
|
|
2621
|
+
const out = {};
|
|
2622
|
+
const locales = model.localeAxis ? [...new Set(model.units.map((u) => u.locale))] : [undefined];
|
|
2623
|
+
for (const p of model.products) {
|
|
2624
|
+
if (!p.versions.length)
|
|
2625
|
+
continue;
|
|
2626
|
+
for (const l of locales) {
|
|
2627
|
+
const rows = productSwitcherRows(model, p.name, l);
|
|
2628
|
+
if (!rows.length)
|
|
2629
|
+
continue;
|
|
2630
|
+
out[`${p.name ?? "_"}/${l ?? "_"}`] = rows.map((r) => ({
|
|
2631
|
+
id: r.id,
|
|
2632
|
+
label: r.label,
|
|
2633
|
+
...(r.eol ? { eol: true } : {}),
|
|
2634
|
+
...(r.prerelease ? { prerelease: true } : {}),
|
|
2635
|
+
...(r.isDefault ? { default: true } : {}),
|
|
2636
|
+
home: `${combined}${r.home}`,
|
|
2637
|
+
}));
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2640
|
+
return out;
|
|
2641
|
+
}
|
|
2642
|
+
/**
|
|
2643
|
+
* The data files dogsbay-ssg writes too (@dogsbay/site-files products.ts):
|
|
2644
|
+
* `_dogsbay/versions.js` (+ `.json`), the switchers and product switcher every
|
|
2645
|
+
* page can load, and each unit's page list in its own folder,
|
|
2646
|
+
* `<home>_dogsbay/pages.js`. Scripts, so they also work from `file://`.
|
|
2647
|
+
*/
|
|
2648
|
+
function emitProductDataFiles(pages, outputDir, options, model) {
|
|
2649
|
+
const publicDir = join(outputDir, "public", ...basePathSegments(normalizeBasePath(options.basePath)));
|
|
2650
|
+
const byUnit = new Map();
|
|
2651
|
+
for (const page of pages) {
|
|
2652
|
+
const ms = page.multiSource;
|
|
2653
|
+
if (!ms || page.redirect || page.fragment === true)
|
|
2654
|
+
continue;
|
|
2655
|
+
const key = unitKeyString({ ...(ms.namespace ? { product: ms.namespace } : {}), ...(ms.locale ? { locale: ms.locale } : {}), ...(ms.version ? { version: ms.version } : {}) });
|
|
2656
|
+
const list = byUnit.get(key) ?? [];
|
|
2657
|
+
list.push(ms.originalSlug.replace(/^\/+|\/+$/g, "").replace(/(^|\/)index$/, ""));
|
|
2658
|
+
byUnit.set(key, list);
|
|
2659
|
+
}
|
|
2660
|
+
const built = new Set(model.units.map(unitKeyString));
|
|
2661
|
+
const manifests = [...byUnit.entries()]
|
|
2662
|
+
.filter(([key]) => built.has(key))
|
|
2663
|
+
.map(([key, list]) => {
|
|
2664
|
+
const [product, locale, version] = key.split("/").map((x) => (x === "_" ? undefined : x));
|
|
2665
|
+
return unitManifest({ ...(product ? { product } : {}), ...(locale ? { locale } : {}), ...(version ? { version } : {}) }, { pages: [...new Set(list)].sort(), sitemap: [], redirects: [] });
|
|
2666
|
+
});
|
|
2667
|
+
const files = siteFiles(model, manifests);
|
|
2668
|
+
const dataDir = join(publicDir, "_dogsbay");
|
|
2669
|
+
mkdirSync(dataDir, { recursive: true });
|
|
2670
|
+
writeFileSync(join(dataDir, "versions.js"), toScript("__DOGSBAY_VERSIONS__", files.versions));
|
|
2671
|
+
writeFileSync(join(dataDir, "versions.json"), JSON.stringify(files.versions));
|
|
2672
|
+
for (const m of manifests) {
|
|
2673
|
+
const unit = unitOf(model, { ...(m.product ? { product: m.product } : {}), ...(m.locale ? { locale: m.locale } : {}), ...(m.version ? { version: m.version } : {}) });
|
|
2674
|
+
const dest = join(publicDir, ...unit.segments, "_dogsbay", "pages.js");
|
|
2675
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
2676
|
+
writeFileSync(dest, toPagesScript(unit.home, m.pages));
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2607
2679
|
/**
|
|
2608
2680
|
* Compose a switcherMap axis header from declared entries +
|
|
2609
2681
|
* the set of ids actually seen in pages. Honours declared
|
|
@@ -3163,6 +3235,7 @@ function emitSitemapFiles(outputDir, options, pages) {
|
|
|
3163
3235
|
basePath: combined,
|
|
3164
3236
|
versions: options.versions,
|
|
3165
3237
|
defaultVersion: options.defaultVersion,
|
|
3238
|
+
products: options.products,
|
|
3166
3239
|
redirects: options.redirects,
|
|
3167
3240
|
});
|
|
3168
3241
|
// More than one file past 50,000 URLs, the protocol's limit per file.
|
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.145",
|
|
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/
|
|
40
|
-
"@dogsbay/
|
|
35
|
+
"@dogsbay/format-dogsbay-md": "0.2.0-beta.145",
|
|
36
|
+
"@dogsbay/serialize-core": "0.2.0-beta.145",
|
|
37
|
+
"@dogsbay/render-html": "0.2.0-beta.145",
|
|
38
|
+
"@dogsbay/site-files": "0.2.0-beta.145",
|
|
39
|
+
"@dogsbay/ui": "0.2.0-beta.145",
|
|
40
|
+
"@dogsbay/types": "0.2.0-beta.145"
|
|
41
41
|
},
|
|
42
42
|
"repository": {
|
|
43
43
|
"type": "git",
|