@dogsbay/format-astro 0.2.0-beta.154 → 0.2.0-beta.156
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/llms-txt.d.ts +6 -0
- package/dist/llms-txt.js +3 -3
- package/dist/project.d.ts +12 -0
- package/dist/project.js +88 -17
- package/dist/serialize.js +12 -2
- package/package.json +8 -8
package/dist/llms-txt.d.ts
CHANGED
|
@@ -24,6 +24,12 @@ export interface BuildLlmsTxtOptions {
|
|
|
24
24
|
* the host root.
|
|
25
25
|
*/
|
|
26
26
|
hrefPrefix?: string;
|
|
27
|
+
/**
|
|
28
|
+
* The home page's Markdown copy, when the host knows it (format-astro's project: `<prefix>.md`
|
|
29
|
+
* beside a `site.basePath` mount, `<prefix>/index.md` under a base from `site.url`'s path, where
|
|
30
|
+
* Astro serves only below the base). Default: `<prefix>.md`, `/index.md` at the host root.
|
|
31
|
+
*/
|
|
32
|
+
indexMirrorHref?: string;
|
|
27
33
|
/**
|
|
28
34
|
* When the site has a siteUrl, emitted entry URLs become absolute
|
|
29
35
|
* (e.g. `https://example.com/docs/x.md`). When omitted, URLs stay
|
package/dist/llms-txt.js
CHANGED
|
@@ -77,7 +77,7 @@ function mapInputs(siteConfig, nav, pages, options) {
|
|
|
77
77
|
const key = item.href ? hrefToSlug(item.href, hrefPrefix) : null;
|
|
78
78
|
return { label: item.label, ...(key !== null ? { key } : {}), children: toNav(item.children ?? []) };
|
|
79
79
|
});
|
|
80
|
-
return { pages: records, byKey, nav: toNav(nav), url: (key) => absoluteUrl(slugToHref(key, hrefPrefix), siteUrl) };
|
|
80
|
+
return { pages: records, byKey, nav: toNav(nav), url: (key) => absoluteUrl(slugToHref(key, hrefPrefix, options.indexMirrorHref), siteUrl) };
|
|
81
81
|
}
|
|
82
82
|
// ── Internals: page lookup + URL math ───────────────────────────
|
|
83
83
|
function indexPagesBySlug(pages) {
|
|
@@ -96,10 +96,10 @@ function hrefToSlug(href, hrefPrefix) {
|
|
|
96
96
|
let slug = href.slice(prefix.length).replace(/^\//, "").replace(/\/$/, "");
|
|
97
97
|
return slug === "" ? "index" : slug;
|
|
98
98
|
}
|
|
99
|
-
function slugToHref(slug, hrefPrefix) {
|
|
99
|
+
function slugToHref(slug, hrefPrefix, indexHref) {
|
|
100
100
|
const prefix = hrefPrefix.endsWith("/") ? hrefPrefix.slice(0, -1) : hrefPrefix;
|
|
101
101
|
if (slug === "" || slug === "index")
|
|
102
|
-
return indexMirrorHref(prefix);
|
|
102
|
+
return indexHref ?? indexMirrorHref(prefix);
|
|
103
103
|
return `${prefix}/${slug}.md`;
|
|
104
104
|
}
|
|
105
105
|
/**
|
package/dist/project.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ExportPage, NavItem } from "@dogsbay/types";
|
|
2
|
+
import { type MarkdownStyle } from "@dogsbay/format-dogsbay-md";
|
|
2
3
|
import { type SiteChrome } from "@dogsbay/site-files";
|
|
3
4
|
import { type VersionMenu, type ProductModel } from "@dogsbay/site-files";
|
|
4
5
|
import { type BlogEmitConfig } from "./blog.js";
|
|
@@ -370,6 +371,17 @@ export interface AstroProjectOptions {
|
|
|
370
371
|
* to static `.md` files in `dist/`. Default `true`.
|
|
371
372
|
*/
|
|
372
373
|
mdMirror?: boolean;
|
|
374
|
+
/**
|
|
375
|
+
* Where links in the Markdown copies (each `.md` and llms-full.txt) go: `html` (default), the
|
|
376
|
+
* page as the site serves it, so a person follows what an agent quotes; `md`, a page's `.md`
|
|
377
|
+
* copy (links to anything without one stay as they are). Absolute with `siteUrl`. Same key and
|
|
378
|
+
* meaning as dogsbay-ssg's `agent.mdMirrorLinks` (plans/md-mirror-links.md).
|
|
379
|
+
*/
|
|
380
|
+
mdMirrorLinks?: "html" | "md";
|
|
381
|
+
/** Write llms-full.txt. Default true; off with `mdMirror: false` too (as dogsbay-ssg). */
|
|
382
|
+
llmsFull?: boolean;
|
|
383
|
+
/** How the Markdown copies are written: the site's `style:` (dogsbay.config.yml). */
|
|
384
|
+
markdownStyle?: MarkdownStyle;
|
|
373
385
|
/**
|
|
374
386
|
* Granularity views — emit aggregate "single-page" views alongside the
|
|
375
387
|
* topic-grained pages. Additive: the topic site is unchanged; aggregates
|
package/dist/project.js
CHANGED
|
@@ -12,7 +12,7 @@ import { fileURLToPath } from "node:url";
|
|
|
12
12
|
import { treeToDogsbayMd } from "@dogsbay/format-dogsbay-md";
|
|
13
13
|
import { treeToAstro } from "./serialize.js";
|
|
14
14
|
import { apiPageLayout } from "@dogsbay/serialize-core";
|
|
15
|
-
import { missingTranslations } from "@dogsbay/site-files";
|
|
15
|
+
import { missingTranslations, rewriteTreeLinks } from "@dogsbay/site-files";
|
|
16
16
|
import { sanitizeTree } from "@dogsbay/render-html/sanitize";
|
|
17
17
|
import { generateGlobalCss, themeCss } from "./theme-css.js";
|
|
18
18
|
/**
|
|
@@ -143,8 +143,8 @@ function rewriteTreeHrefs(nodes, prefix) {
|
|
|
143
143
|
if (node.inline) {
|
|
144
144
|
rewriteInlineHrefs(node.inline, prefix);
|
|
145
145
|
}
|
|
146
|
-
// Rewrite hrefs in pre-rendered HTML
|
|
147
|
-
if (node.html) {
|
|
146
|
+
// Rewrite hrefs in pre-rendered HTML; a code block's `html` is its code, shown as written
|
|
147
|
+
if (node.html && node.type !== "code" && node.type !== "math-block") {
|
|
148
148
|
node.html = rewriteHtmlHrefs(node.html, prefix);
|
|
149
149
|
}
|
|
150
150
|
// Rewrite href in props (e.g. card, link-button)
|
|
@@ -203,7 +203,8 @@ function rewriteTreeImageSrcs(nodes, prefix) {
|
|
|
203
203
|
if (node.inline) {
|
|
204
204
|
rewriteInlineImageSrcs(node.inline, prefix);
|
|
205
205
|
}
|
|
206
|
-
|
|
206
|
+
// a code block's `html` is its code, shown as written
|
|
207
|
+
if (node.html && node.type !== "code" && node.type !== "math-block") {
|
|
207
208
|
node.html = rewriteHtmlImageSrcs(node.html, prefix);
|
|
208
209
|
}
|
|
209
210
|
if (node.children) {
|
|
@@ -1683,6 +1684,7 @@ export async function emitAstroPages(pages, nav, outputDir, options) {
|
|
|
1683
1684
|
// combined === basePath (back-compat). For GH Pages project pages
|
|
1684
1685
|
// and multi-mount Cloudflare, combined adds the urlBase layer.
|
|
1685
1686
|
const hrefPrefix = combined;
|
|
1687
|
+
const mirrorMd = mirrorSerializer(options, pages);
|
|
1686
1688
|
// Route-exclusion gate. Two signals, either is enough:
|
|
1687
1689
|
// - frontmatter `_fragment: true` (loader-stamped — the importer
|
|
1688
1690
|
// knew this .md was include-only, not a navigable page)
|
|
@@ -2295,7 +2297,7 @@ export async function emitAstroPages(pages, nav, outputDir, options) {
|
|
|
2295
2297
|
if (options.mdMirror !== false) {
|
|
2296
2298
|
const mdEndpointPath = join(pagesDir, section ? section : "", `${page.slug}.md.ts`);
|
|
2297
2299
|
mkdirSync(dirname(mdEndpointPath), { recursive: true });
|
|
2298
|
-
const endpointBody = buildMdEndpoint(page, sourceRel);
|
|
2300
|
+
const endpointBody = buildMdEndpoint(page, sourceRel, mirrorMd);
|
|
2299
2301
|
writeFileSync(mdEndpointPath, endpointBody);
|
|
2300
2302
|
generatedPaths.add(relative(outputDir, mdEndpointPath));
|
|
2301
2303
|
// Sibling-level mirror for the index page under a non-empty
|
|
@@ -3128,10 +3130,66 @@ const LLMS_TXT_EXTERNAL_RE = /^(?:https?:\/\/|mailto:|tel:|#)/i;
|
|
|
3128
3130
|
* frontmatter included, body normalized. Used for both the .md
|
|
3129
3131
|
* mirror endpoints and the body summary in llms-full.txt.
|
|
3130
3132
|
*/
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3133
|
+
/**
|
|
3134
|
+
* The URL of a page's Markdown copy. The home page's is `<prefix>.md` beside a `site.basePath`
|
|
3135
|
+
* mount (a sibling endpoint is written there) and `<prefix>/index.md` otherwise: under a base from
|
|
3136
|
+
* `site.url`'s path Astro serves nothing outside the base, and llms.txt named `/repo.md`, a 404.
|
|
3137
|
+
*/
|
|
3138
|
+
function mirrorHref(slug, options) {
|
|
3139
|
+
const prefix = combinedPrefix(options).replace(/\/$/, "");
|
|
3140
|
+
if (slug === "" || slug === "index")
|
|
3141
|
+
return basePathSegments(normalizeBasePath(options.basePath)).length ? `${prefix}.md` : `${prefix}/index.md`;
|
|
3142
|
+
return `${prefix}/${slug}.md`;
|
|
3143
|
+
}
|
|
3144
|
+
/** A page with a route (and a `.md` copy): not a fragment, not excluded from routes, not a redirect. */
|
|
3145
|
+
function hasRoute(p, options) {
|
|
3146
|
+
const fragment = p.fragment === true || p.frontmatter?._fragment === true;
|
|
3147
|
+
return !fragment && !makeSlugExcluder(options.excludeFromRoutes)(p.slug) && !(typeof p.redirect === "string" && p.redirect.trim());
|
|
3148
|
+
}
|
|
3149
|
+
/**
|
|
3150
|
+
* Writes a page's Markdown copy (its `.md`, its part of llms-full.txt) in the site's `style:`, its
|
|
3151
|
+
* links as the site serves them: the tree already carries the base path (emitAstroPages), so
|
|
3152
|
+
* here a link becomes absolute with `siteUrl`, and with `mdMirrorLinks: md` a page link names that
|
|
3153
|
+
* page's copy. In llms-full.txt (`where: "full"`) a same-page `#x` names its page. Shared walker:
|
|
3154
|
+
* `rewriteTreeLinks` (@dogsbay/site-files), as dogsbay-ssg's copies.
|
|
3155
|
+
*/
|
|
3156
|
+
function mirrorSerializer(options, pages) {
|
|
3157
|
+
const prefix = combinedPrefix(options).replace(/\/$/, "");
|
|
3158
|
+
const site = options.siteUrl && /^https?:\/\//i.test(options.siteUrl) ? parseSiteUrl(options.siteUrl).origin : undefined;
|
|
3159
|
+
const htmlPath = (slug) => (slug === "" || slug === "index" ? `${prefix}/` : `${prefix}/${slug}`);
|
|
3160
|
+
const key = (path) => path.replace(/\/index$/, "").replace(/\/+$/, "") || "/";
|
|
3161
|
+
// Only pages with a route of their own have a copy: not a fragment, a slug excludeFromRoutes
|
|
3162
|
+
// names, or a page that is a `redirect:` (review: `.md` links to them were 404s)
|
|
3163
|
+
const bySite = new Map();
|
|
3164
|
+
for (const p of pages)
|
|
3165
|
+
if (hasRoute(p, options))
|
|
3166
|
+
bySite.set(key(htmlPath(p.slug)), p.slug);
|
|
3167
|
+
const abs = (path) => (site && path.startsWith("/") && !path.startsWith("//") ? `${site}${path}` : path);
|
|
3168
|
+
return (page, where) => {
|
|
3169
|
+
const link = (href) => {
|
|
3170
|
+
// A same-page `#x` names its page in llms-full.txt, and in a copy too when links are absolute
|
|
3171
|
+
// (dogsbay-ssg writes it so: a copy pasted elsewhere still points at the page)
|
|
3172
|
+
if (href.startsWith("#"))
|
|
3173
|
+
return where === "full" || site ? abs(htmlPath(page.slug)) + href : href;
|
|
3174
|
+
if (!href.startsWith("/") || href.startsWith("//"))
|
|
3175
|
+
return href;
|
|
3176
|
+
const at = href.search(/[?#]/);
|
|
3177
|
+
const path = at === -1 ? href : href.slice(0, at);
|
|
3178
|
+
const rest = at === -1 ? "" : href.slice(at);
|
|
3179
|
+
const slug = options.mdMirrorLinks === "md" ? bySite.get(key(path)) : undefined;
|
|
3180
|
+
return abs(slug !== undefined ? mirrorHref(slug, options) : path) + rest;
|
|
3181
|
+
};
|
|
3182
|
+
const restore = rewriteTreeLinks(page.tree, { link, asset: (src) => abs(src) });
|
|
3183
|
+
try {
|
|
3184
|
+
return treeToDogsbayMd(page.tree, {
|
|
3185
|
+
frontmatter: page.frontmatter,
|
|
3186
|
+
...(options.markdownStyle ?? {}),
|
|
3187
|
+
});
|
|
3188
|
+
}
|
|
3189
|
+
finally {
|
|
3190
|
+
restore();
|
|
3191
|
+
}
|
|
3192
|
+
};
|
|
3135
3193
|
}
|
|
3136
3194
|
/**
|
|
3137
3195
|
* Build the contents of `public/robots.txt`.
|
|
@@ -3265,8 +3323,8 @@ function buildMiddlewareSource(config) {
|
|
|
3265
3323
|
* markdown body is embedded as a string literal at convert time;
|
|
3266
3324
|
* Astro prerenders the endpoint into a static `.md` file under `dist/`.
|
|
3267
3325
|
*/
|
|
3268
|
-
function buildMdEndpoint(page, sourceRel) {
|
|
3269
|
-
const body =
|
|
3326
|
+
function buildMdEndpoint(page, sourceRel, serialize) {
|
|
3327
|
+
const body = serialize(page, "copy");
|
|
3270
3328
|
return [
|
|
3271
3329
|
"// AUTO-GENERATED by `dogsbay convert` — do not edit.",
|
|
3272
3330
|
`// Source: ${sourceRel}`,
|
|
@@ -3316,14 +3374,27 @@ function emitLlmsTxtFiles(outputDir, siteName, options, nav, pages) {
|
|
|
3316
3374
|
mkdirSync(mountDir, { recursive: true });
|
|
3317
3375
|
writeFileSync(join(mountDir, "llms.txt"), buildLlmsTxt(siteConfig, nav, pages, {
|
|
3318
3376
|
hrefPrefix,
|
|
3377
|
+
indexMirrorHref: mirrorHref("index", options),
|
|
3319
3378
|
aggregates: options.aggregates,
|
|
3320
3379
|
whenToUse: options.llmsWhenToUse,
|
|
3321
3380
|
}));
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3381
|
+
// llms-full.txt is every page's copy in one file: off with `llmsFull: false`, and with
|
|
3382
|
+
// `mdMirror: false` (no copies), as in dogsbay-ssg
|
|
3383
|
+
const full = join(mountDir, "llms-full.txt");
|
|
3384
|
+
if (options.llmsFull !== false && options.mdMirror !== false) {
|
|
3385
|
+
const serialize = mirrorSerializer(options, pages);
|
|
3386
|
+
writeFileSync(full,
|
|
3387
|
+
// only pages with a route: the others' links never got the site's prefix, and they have no
|
|
3388
|
+
// page for an agent to cite (review)
|
|
3389
|
+
buildLlmsFullTxt(siteConfig, nav, pages.filter((p) => hasRoute(p, options)), {
|
|
3390
|
+
summary: "body",
|
|
3391
|
+
serializePage: (page) => serialize(page, "full"),
|
|
3392
|
+
hrefPrefix,
|
|
3393
|
+
indexMirrorHref: mirrorHref("index", options),
|
|
3394
|
+
}));
|
|
3395
|
+
}
|
|
3396
|
+
else if (existsSync(full))
|
|
3397
|
+
rmSync(full);
|
|
3327
3398
|
// Per-section files. `deriveSectionDir` returns a host-absolute
|
|
3328
3399
|
// path derived from nav hrefs, which since the combined-prefix
|
|
3329
3400
|
// refactor (commit 132891e) include urlBase + basePath — NOT just
|
|
@@ -3364,7 +3435,7 @@ function emitLlmsTxtFiles(outputDir, siteName, options, nav, pages) {
|
|
|
3364
3435
|
}
|
|
3365
3436
|
const sectionPath = join(mountDir, relDir, "llms.txt");
|
|
3366
3437
|
mkdirSync(dirname(sectionPath), { recursive: true });
|
|
3367
|
-
writeFileSync(sectionPath, buildSectionLlmsTxt(siteConfig, group, pages, { hrefPrefix }));
|
|
3438
|
+
writeFileSync(sectionPath, buildSectionLlmsTxt(siteConfig, group, pages, { hrefPrefix, indexMirrorHref: mirrorHref("index", options) }));
|
|
3368
3439
|
}
|
|
3369
3440
|
}
|
|
3370
3441
|
/**
|
package/dist/serialize.js
CHANGED
|
@@ -276,8 +276,18 @@ function nodeToAstroBase(node, ctx) {
|
|
|
276
276
|
});
|
|
277
277
|
case "unordered-list":
|
|
278
278
|
return wrapBlock("ul", "", node, childrenToAstro(node, ctx));
|
|
279
|
-
case "list-item":
|
|
280
|
-
|
|
279
|
+
case "list-item": {
|
|
280
|
+
// a task item: a disabled checkbox before its text, as render-html draws it
|
|
281
|
+
const c = node.props?.checked;
|
|
282
|
+
if (typeof c !== "boolean")
|
|
283
|
+
return wrapBlock("li", "", node, leafContent(node, ctx));
|
|
284
|
+
// the checkbox labelled by the item's text (axe: a form control needs a label); nested blocks after
|
|
285
|
+
const text = node.inline && node.inline.length ? leafContent({ ...node, children: undefined }, ctx) : "";
|
|
286
|
+
const rest = node.children?.length ? childrenToAstro({ ...node, inline: undefined }, ctx) : "";
|
|
287
|
+
// no text of its own: the checkbox is named "Task"
|
|
288
|
+
const lead = text ? `<label><input type="checkbox" disabled${c ? " checked" : ""} /> ${text}</label>` : `<input type="checkbox" disabled${c ? " checked" : ""} aria-label="Task" />`;
|
|
289
|
+
return wrapBlock("li", "", node, `${lead}${rest}`);
|
|
290
|
+
}
|
|
281
291
|
case "blockquote":
|
|
282
292
|
return wrapBlock("blockquote", "border-l-4 border-border pl-4 italic text-muted-foreground", node, childrenToAstro(node, ctx));
|
|
283
293
|
// A transparent grouping wrapper: renders its children inside a plain
|
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.156",
|
|
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,13 +32,13 @@
|
|
|
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/icons": "0.2.0-beta.
|
|
37
|
-
"@dogsbay/serialize-core": "0.2.0-beta.
|
|
38
|
-
"@dogsbay/render-html": "0.2.0-beta.
|
|
39
|
-
"@dogsbay/site-files": "0.2.0-beta.
|
|
40
|
-
"@dogsbay/types": "0.2.0-beta.
|
|
41
|
-
"@dogsbay/ui": "0.2.0-beta.
|
|
35
|
+
"@dogsbay/format-dogsbay-md": "0.2.0-beta.156",
|
|
36
|
+
"@dogsbay/icons": "0.2.0-beta.156",
|
|
37
|
+
"@dogsbay/serialize-core": "0.2.0-beta.156",
|
|
38
|
+
"@dogsbay/render-html": "0.2.0-beta.156",
|
|
39
|
+
"@dogsbay/site-files": "0.2.0-beta.156",
|
|
40
|
+
"@dogsbay/types": "0.2.0-beta.156",
|
|
41
|
+
"@dogsbay/ui": "0.2.0-beta.156"
|
|
42
42
|
},
|
|
43
43
|
"repository": {
|
|
44
44
|
"type": "git",
|