@ascribed/astro 0.2.1-next.14 → 0.2.1-next.16
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/README.md +19 -7
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +62 -0
- package/dist/cli.js.map +1 -0
- package/dist/content.d.ts +26 -0
- package/dist/content.js +70 -1
- package/dist/content.js.map +1 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +87 -11
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +41 -0
- package/dist/init.js +130 -0
- package/dist/init.js.map +1 -0
- package/dist/nav.d.ts +51 -0
- package/dist/nav.js +97 -0
- package/dist/nav.js.map +1 -0
- package/dist/permalinks.d.ts +6 -0
- package/dist/permalinks.js +13 -0
- package/dist/permalinks.js.map +1 -1
- package/dist/project.d.ts +24 -0
- package/dist/project.js +31 -0
- package/dist/project.js.map +1 -1
- package/dist/projects.d.ts +0 -6
- package/dist/projects.js +1 -14
- package/dist/projects.js.map +1 -1
- package/dist/records.d.ts +78 -0
- package/dist/records.js +153 -0
- package/dist/records.js.map +1 -0
- package/dist/root-files.d.ts +63 -0
- package/dist/root-files.js +100 -0
- package/dist/root-files.js.map +1 -0
- package/dist/run.js +3 -2
- package/dist/run.js.map +1 -1
- package/package.json +12 -6
- package/src/Head.astro +40 -0
- package/starter/src/content.config.ts +8 -0
- package/starter/src/layouts/Docs.astro +87 -0
- package/starter/src/nav.ts +13 -0
- package/starter/src/pages/[...slug].astro +20 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { normalizeBase } from "./project.js";
|
|
2
|
+
import { robotsText, routeAs, routeKey, sitemapAddresses, sitemapRoute, sitemapXml, } from "./records.js";
|
|
3
|
+
/** The sitemap and `robots.txt` for a site, and what the build says about them. */
|
|
4
|
+
export function rootFiles(sources) {
|
|
5
|
+
const notes = [];
|
|
6
|
+
const base = normalizeBase(sources.base);
|
|
7
|
+
let sitemap;
|
|
8
|
+
let sitemapAddress;
|
|
9
|
+
if (sources.ownSitemap) {
|
|
10
|
+
// The site's own, at the same address, which robots.txt points to when there's an address.
|
|
11
|
+
notes.push({ level: "info", text: "no sitemap.xml: public/sitemap.xml is the site's own." });
|
|
12
|
+
if (sources.site !== undefined)
|
|
13
|
+
sitemapAddress = new URL(sitemapRoute(base), sources.site).href;
|
|
14
|
+
}
|
|
15
|
+
else if (sources.site === undefined) {
|
|
16
|
+
notes.push({
|
|
17
|
+
level: "warn",
|
|
18
|
+
text: "no sitemap.xml: a sitemap lists pages by their absolute addresses, and the site has no address. Set [consumer] site in ascribe.toml, or Astro's `site`.",
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
sitemap = sitemapXml(sitemapAddresses({ ...sources, site: sources.site }));
|
|
23
|
+
sitemapAddress = new URL(sitemapRoute(base), sources.site).href;
|
|
24
|
+
}
|
|
25
|
+
let robots = robotsText(sources.lines, sitemapAddress);
|
|
26
|
+
if (robots !== undefined && base !== "/") {
|
|
27
|
+
notes.push({
|
|
28
|
+
level: "info",
|
|
29
|
+
text: `no robots.txt: Astro's \`base\` is "${base}", and a crawler reads robots.txt only at the host's root. Put these lines in the host's own:\n${robots}`,
|
|
30
|
+
});
|
|
31
|
+
robots = undefined;
|
|
32
|
+
}
|
|
33
|
+
else if (robots !== undefined && sources.ownRobots) {
|
|
34
|
+
notes.push({
|
|
35
|
+
level: "info",
|
|
36
|
+
text: `no robots.txt: public/robots.txt is the site's own. Ascribe's lines for it:\n${robots}`,
|
|
37
|
+
});
|
|
38
|
+
robots = undefined;
|
|
39
|
+
}
|
|
40
|
+
return { sitemap, robots, notes };
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* A dev-server middleware serving the sitemap at `<base>sitemap.xml` and
|
|
44
|
+
* `robots.txt` at `/`, each as `files` has it when it's asked for, so each
|
|
45
|
+
* rebuild's records are served.
|
|
46
|
+
*/
|
|
47
|
+
export function rootFilesMiddleware(base, files) {
|
|
48
|
+
const sitemapPath = sitemapRoute(base);
|
|
49
|
+
return (req, res, next) => {
|
|
50
|
+
if (req.method !== "GET" && req.method !== "HEAD")
|
|
51
|
+
return next();
|
|
52
|
+
// Vite strips the base from `req.url`; connect keeps the original in `originalUrl`.
|
|
53
|
+
const pathname = (req.originalUrl ?? req.url ?? "").split(/[?#]/, 1)[0] ?? "";
|
|
54
|
+
if (pathname !== sitemapPath && pathname !== "/robots.txt")
|
|
55
|
+
return next();
|
|
56
|
+
const current = files();
|
|
57
|
+
const [text, type] = pathname === sitemapPath
|
|
58
|
+
? [current.sitemap, "application/xml; charset=utf-8"]
|
|
59
|
+
: [current.robots, "text/plain; charset=utf-8"];
|
|
60
|
+
if (text === undefined)
|
|
61
|
+
return next();
|
|
62
|
+
res.setHeader("Content-Type", type);
|
|
63
|
+
res.setHeader("Content-Length", Buffer.byteLength(text));
|
|
64
|
+
res.end(req.method === "HEAD" ? undefined : text);
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The site's own pages `astro dev` can name: each route of the site's own
|
|
69
|
+
* with no parameters, other than the not-found and error pages, by its route
|
|
70
|
+
* from the host's root. A page a route's `getStaticPaths` lists is known only
|
|
71
|
+
* to a build.
|
|
72
|
+
*/
|
|
73
|
+
export function devSitePages(routes, astro) {
|
|
74
|
+
const root = normalizeBase(astro.base);
|
|
75
|
+
return routes
|
|
76
|
+
.filter((route) => route.type === "page" &&
|
|
77
|
+
route.origin === "project" &&
|
|
78
|
+
route.params.length === 0 &&
|
|
79
|
+
route.pattern !== "/404" &&
|
|
80
|
+
route.pattern !== "/500")
|
|
81
|
+
.map((route) => routeAs(`${root}${route.pattern.replace(/^\//, "")}`, astro.trailingSlash));
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Whether a project's page, by its route from the host's root, has a route
|
|
85
|
+
* in the site in `astro dev`: whether one of the site's own routes matches
|
|
86
|
+
* it. A project no route shows, such as one whose collection a page only
|
|
87
|
+
* quotes from, has no pages in the sitemap.
|
|
88
|
+
*/
|
|
89
|
+
export function devRouted(routes, astro) {
|
|
90
|
+
const root = normalizeBase(astro.base);
|
|
91
|
+
const pages = routes.filter((route) => route.type === "page" && route.origin === "project");
|
|
92
|
+
return (route) => {
|
|
93
|
+
if (!`${route}/`.startsWith(root))
|
|
94
|
+
return false;
|
|
95
|
+
const under = `/${route.slice(root.length)}`;
|
|
96
|
+
const forms = new Set([under, routeKey(under), `${routeKey(under)}/`]);
|
|
97
|
+
return pages.some((page) => [...forms].some((form) => page.patternRegex.test(form)));
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=root-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root-files.js","sourceRoot":"","sources":["../src/root-files.ts"],"names":[],"mappings":"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","sourcesContent":["// The site's sitemap and `robots.txt`, from every project's page records\n// together: one of each for the site, at the host's root, which is above the\n// base path of every project that isn't mounted there. `astro build` writes\n// them into its output, and `astro dev` serves them.\n//\n// A site whose Astro `base` isn't `/` has no host root: its sitemap goes\n// under the base, and no `robots.txt` is written, since one under a path does\n// nothing. The build says what the host's own needs instead.\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { normalizeBase } from \"./project.js\";\nimport {\n robotsText,\n routeAs,\n routeKey,\n sitemapAddresses,\n sitemapRoute,\n sitemapXml,\n type SitemapSources,\n} from \"./records.js\";\n\n/** What the files are drawn from. */\nexport interface RootFileSources extends Omit<SitemapSources, \"site\"> {\n /** Each project's `robots.txt` lines, from the host's root, each project once. */\n lines: readonly (readonly string[])[];\n /** Astro's `site`, when it's set. */\n site: string | undefined;\n /** Astro's `base`. */\n base: string;\n /** Whether the site has its own `robots.txt`, in `public/`. */\n ownRobots: boolean;\n /** Whether the site has its own `sitemap.xml`, in `public/`. */\n ownSitemap: boolean;\n}\n\n/** A thing the build says about the files, and how loudly. */\ninterface RootFileNote {\n level: \"info\" | \"warn\";\n text: string;\n}\n\n/** The files, each `undefined` when none is written, and what to say about them. */\nexport interface RootFiles {\n sitemap: string | undefined;\n robots: string | undefined;\n notes: RootFileNote[];\n}\n\n/** The sitemap and `robots.txt` for a site, and what the build says about them. */\nexport function rootFiles(sources: RootFileSources): RootFiles {\n const notes: RootFileNote[] = [];\n const base = normalizeBase(sources.base);\n let sitemap: string | undefined;\n let sitemapAddress: string | undefined;\n if (sources.ownSitemap) {\n // The site's own, at the same address, which robots.txt points to when there's an address.\n notes.push({ level: \"info\", text: \"no sitemap.xml: public/sitemap.xml is the site's own.\" });\n if (sources.site !== undefined) sitemapAddress = new URL(sitemapRoute(base), sources.site).href;\n } else if (sources.site === undefined) {\n notes.push({\n level: \"warn\",\n text: \"no sitemap.xml: a sitemap lists pages by their absolute addresses, and the site has no address. Set [consumer] site in ascribe.toml, or Astro's `site`.\",\n });\n } else {\n sitemap = sitemapXml(sitemapAddresses({ ...sources, site: sources.site }));\n sitemapAddress = new URL(sitemapRoute(base), sources.site).href;\n }\n let robots = robotsText(sources.lines, sitemapAddress);\n if (robots !== undefined && base !== \"/\") {\n notes.push({\n level: \"info\",\n text: `no robots.txt: Astro's \\`base\\` is \"${base}\", and a crawler reads robots.txt only at the host's root. Put these lines in the host's own:\\n${robots}`,\n });\n robots = undefined;\n } else if (robots !== undefined && sources.ownRobots) {\n notes.push({\n level: \"info\",\n text: `no robots.txt: public/robots.txt is the site's own. Ascribe's lines for it:\\n${robots}`,\n });\n robots = undefined;\n }\n return { sitemap, robots, notes };\n}\n\ntype Middleware = (req: IncomingMessage, res: ServerResponse, next: () => void) => void;\n\n/**\n * A dev-server middleware serving the sitemap at `<base>sitemap.xml` and\n * `robots.txt` at `/`, each as `files` has it when it's asked for, so each\n * rebuild's records are served.\n */\nexport function rootFilesMiddleware(base: string, files: () => RootFiles): Middleware {\n const sitemapPath = sitemapRoute(base);\n return (req, res, next) => {\n if (req.method !== \"GET\" && req.method !== \"HEAD\") return next();\n // Vite strips the base from `req.url`; connect keeps the original in `originalUrl`.\n const pathname =\n ((req as { originalUrl?: string }).originalUrl ?? req.url ?? \"\").split(/[?#]/, 1)[0] ?? \"\";\n if (pathname !== sitemapPath && pathname !== \"/robots.txt\") return next();\n const current = files();\n const [text, type] =\n pathname === sitemapPath\n ? [current.sitemap, \"application/xml; charset=utf-8\"]\n : [current.robots, \"text/plain; charset=utf-8\"];\n if (text === undefined) return next();\n res.setHeader(\"Content-Type\", type);\n res.setHeader(\"Content-Length\", Buffer.byteLength(text));\n res.end(req.method === \"HEAD\" ? undefined : text);\n };\n}\n\n/** A route as `astro:routes:resolved` gives it, as much as these read. */\nexport interface AstroRoute {\n pattern: string;\n patternRegex: RegExp;\n params: string[];\n type: string;\n origin: string;\n}\n\n/** Astro's routing, as much as these read. */\ninterface Routing {\n base: string;\n trailingSlash: \"always\" | \"never\" | \"ignore\";\n}\n\n/**\n * The site's own pages `astro dev` can name: each route of the site's own\n * with no parameters, other than the not-found and error pages, by its route\n * from the host's root. A page a route's `getStaticPaths` lists is known only\n * to a build.\n */\nexport function devSitePages(routes: readonly AstroRoute[], astro: Routing): string[] {\n const root = normalizeBase(astro.base);\n return routes\n .filter(\n (route) =>\n route.type === \"page\" &&\n route.origin === \"project\" &&\n route.params.length === 0 &&\n route.pattern !== \"/404\" &&\n route.pattern !== \"/500\",\n )\n .map((route) => routeAs(`${root}${route.pattern.replace(/^\\//, \"\")}`, astro.trailingSlash));\n}\n\n/**\n * Whether a project's page, by its route from the host's root, has a route\n * in the site in `astro dev`: whether one of the site's own routes matches\n * it. A project no route shows, such as one whose collection a page only\n * quotes from, has no pages in the sitemap.\n */\nexport function devRouted(\n routes: readonly AstroRoute[],\n astro: Routing,\n): (route: string) => boolean {\n const root = normalizeBase(astro.base);\n const pages = routes.filter((route) => route.type === \"page\" && route.origin === \"project\");\n return (route) => {\n if (!`${route}/`.startsWith(root)) return false;\n const under = `/${route.slice(root.length)}`;\n const forms = new Set([under, routeKey(under), `${routeKey(under)}/`]);\n return pages.some((page) => [...forms].some((form) => page.patternRegex.test(form)));\n };\n}\n"]}
|
package/dist/run.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
// its JSON output while review is on.
|
|
3
3
|
import { execFile } from "node:child_process";
|
|
4
4
|
/**
|
|
5
|
-
* A check's summary line, the report's last: `checked 14 files
|
|
6
|
-
*
|
|
5
|
+
* A check's summary line, the report's last: `checked 14 files for build \`site\`, reported on 14:
|
|
6
|
+
* 0 errors, 0 warnings, 0 advice`. An older binary wrote `checked 14 files: 0 errors,
|
|
7
|
+
* 0 warnings`, with `, 3 advice` after only when there was advice.
|
|
7
8
|
*/
|
|
8
9
|
const SUMMARY = /^checked [^:\n]*: (\d+) errors?, (\d+) warnings?(?:, \d+ advice)?$/;
|
|
9
10
|
/**
|
package/dist/run.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,sCAAsC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAU9C
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,sCAAsC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAU9C;;;;GAIG;AACH,MAAM,OAAO,GAAG,oEAAoE,CAAC;AAErF;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,WAAmB;IAC7C,IAAI,WAAW,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IACrC,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7E,OAAO,OAAO,KAAK,IAAI,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC;AACtE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CACxB,OAAoC,EACpC,OAAe,EACf,MAAM,GAAG,KAAK;IAEd,OAAO,OAAO,KAAK,IAAI,IAAI,CAAC,OAAO,KAAK,KAAK,IAAI,CAAC,OAAO,KAAK,KAAK,IAAI,MAAM,CAAC,CAAC,CAAC;AAClF,CAAC;AAED,kHAAkH;AAClH,MAAM,UAAU,SAAS,CAAC,MAAoC,EAAE,OAAe;IAC7E,OAAO,OAAO,KAAK,KAAK,IAAI,MAAM,KAAK,KAAK,CAAC;AAC/C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,OASxB;IACC,MAAM,IAAI,GAAG;QACX,OAAO;QACP,QAAQ;QACR,CAAC,OAAO,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QACvC,SAAS;QACT,OAAO,CAAC,KAAK;QACb,UAAU;QACV,OAAO,CAAC,UAAU;QAClB,SAAS;QACT,OAAO;KACR,CAAC;IACF,IAAI,OAAO,CAAC,OAAO,KAAK,IAAI;QAAE,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACrD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,QAAQ,CACN,OAAO,CAAC,MAAM,EACd,IAAI,EACJ,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,EACjD,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YACxB,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBACnB,MAAM,MAAM,GAAG,GAAG,MAAM,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC;gBAC3C,MAAM,CACJ,IAAI,KAAK,CACP,2BAA2B,OAAO,CAAC,KAAK,YAAY,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,MAAM,MAAM,EAAE,EAAE,EAC3G,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CACF,CAAC;gBACF,OAAO;YACT,CAAC;YACD,OAAO,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAClE,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["// Running `ascribe build` for the configured build: its site output, and\n// its JSON output while review is on.\nimport { execFile } from \"node:child_process\";\n\n/** What a successful build printed. */\nexport interface BuildResult {\n /** The checks' report, which is more than a summary line when there is anything to report. */\n diagnostics: string;\n /** The `built <build>/site: …` lines. */\n summary: string;\n}\n\n/**\n * A check's summary line, the report's last: `checked 14 files for build \\`site\\`, reported on 14:\n * 0 errors, 0 warnings, 0 advice`. An older binary wrote `checked 14 files: 0 errors,\n * 0 warnings`, with `, 3 advice` after only when there was advice.\n */\nconst SUMMARY = /^checked [^:\\n]*: (\\d+) errors?, (\\d+) warnings?(?:, \\d+ advice)?$/;\n\n/**\n * Whether a build's report is worth a warning: anything but a report whose\n * summary line counts no errors and no warnings, which is logged at info\n * level, advice and all.\n */\nexport function hasWarnings(diagnostics: string): boolean {\n if (diagnostics === \"\") return false;\n const summary = SUMMARY.exec(diagnostics.trimEnd().split(\"\\n\").at(-1) ?? \"\");\n return summary === null || summary[1] !== \"0\" || summary[2] !== \"0\";\n}\n\n/**\n * Whether an Astro command builds with source anchors, given the integration's\n * `anchors` option: `\"dev\"` means in `astro dev` only, so `astro build` has\n * them only when it is `true`. Review in the site preview (`review`) needs\n * them in `astro dev`.\n */\nexport function anchorsFor(\n anchors: boolean | \"dev\" | undefined,\n command: string,\n review = false,\n): boolean {\n return anchors === true || (command === \"dev\" && (anchors === \"dev\" || review));\n}\n\n/** Whether an Astro command has review in the site preview, given the `review` option (an entry's name is on). */\nexport function reviewFor(review: boolean | string | undefined, command: string): boolean {\n return command === \"dev\" && review !== false;\n}\n\n/**\n * Builds one build's outputs, the site output by default. Rejects, with the compiler's report as the\n * message, if the build has errors: the Astro build fails with them.\n */\nexport function runBuild(options: {\n binary: string;\n configPath: string;\n build: string;\n cwd: string;\n /** Write source anchors (`--anchors`). */\n anchors?: boolean;\n /** The outputs to write. Default: `[\"site\"]`. */\n outputs?: readonly (\"site\" | \"plain\" | \"json\")[];\n}): Promise<BuildResult> {\n const args = [\n \"build\",\n \"--emit\",\n (options.outputs ?? [\"site\"]).join(\",\"),\n \"--build\",\n options.build,\n \"--config\",\n options.configPath,\n \"--color\",\n \"never\",\n ];\n if (options.anchors === true) args.push(\"--anchors\");\n return new Promise((resolve, reject) => {\n execFile(\n options.binary,\n args,\n { cwd: options.cwd, maxBuffer: 64 * 1024 * 1024 },\n (error, stdout, stderr) => {\n if (error !== null) {\n const report = `${stdout}${stderr}`.trim();\n reject(\n new Error(\n `\\`ascribe build --build ${options.build}\\` failed${report === \"\" ? `: ${error.message}` : `:\\n${report}`}`,\n { cause: error },\n ),\n );\n return;\n }\n resolve({ diagnostics: stdout.trim(), summary: stderr.trim() });\n },\n );\n });\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ascribed/astro",
|
|
3
|
-
"version": "0.2.1-next.
|
|
3
|
+
"version": "0.2.1-next.16",
|
|
4
4
|
"description": "The Astro integration for Ascribe.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MPL-2.0",
|
|
@@ -12,12 +12,17 @@
|
|
|
12
12
|
"homepage": "https://github.com/ascribed-dev/ascribe/tree/main/packages/astro#readme",
|
|
13
13
|
"bugs": "https://github.com/ascribed-dev/ascribe/issues",
|
|
14
14
|
"sideEffects": false,
|
|
15
|
+
"bin": {
|
|
16
|
+
"ascribe-astro": "./dist/cli.js"
|
|
17
|
+
},
|
|
15
18
|
"files": [
|
|
16
19
|
"dist",
|
|
20
|
+
"starter",
|
|
17
21
|
"src/Elements.astro",
|
|
18
22
|
"src/Availability.astro",
|
|
19
23
|
"src/Permalink.astro",
|
|
20
|
-
"src/MissingPermalink.astro"
|
|
24
|
+
"src/MissingPermalink.astro",
|
|
25
|
+
"src/Head.astro"
|
|
21
26
|
],
|
|
22
27
|
"exports": {
|
|
23
28
|
".": {
|
|
@@ -35,6 +40,7 @@
|
|
|
35
40
|
"./Elements.astro": "./src/Elements.astro",
|
|
36
41
|
"./Availability.astro": "./src/Availability.astro",
|
|
37
42
|
"./MissingPermalink.astro": "./src/MissingPermalink.astro",
|
|
43
|
+
"./Head.astro": "./src/Head.astro",
|
|
38
44
|
"./content": {
|
|
39
45
|
"types": "./dist/content.d.ts",
|
|
40
46
|
"default": "./dist/content.js"
|
|
@@ -48,9 +54,9 @@
|
|
|
48
54
|
"node": ">=24"
|
|
49
55
|
},
|
|
50
56
|
"dependencies": {
|
|
51
|
-
"@ascribed/cli": "0.2.1-next.
|
|
52
|
-
"@ascribed/elements": "0.2.1-next.
|
|
53
|
-
"@ascribed/review": "0.2.1-next.
|
|
57
|
+
"@ascribed/cli": "0.2.1-next.16",
|
|
58
|
+
"@ascribed/elements": "0.2.1-next.16",
|
|
59
|
+
"@ascribed/review": "0.2.1-next.16",
|
|
54
60
|
"github-slugger": "^2.0.0",
|
|
55
61
|
"property-information": "^7.2.0",
|
|
56
62
|
"smol-toml": "^1.9.0"
|
|
@@ -86,7 +92,7 @@
|
|
|
86
92
|
"publishConfig": {
|
|
87
93
|
"access": "public"
|
|
88
94
|
},
|
|
89
|
-
"gitHead": "
|
|
95
|
+
"gitHead": "0077fa1e1fcd4ef67f9c6d48575c3f06f98adce8",
|
|
90
96
|
"scripts": {
|
|
91
97
|
"build": "tsc -p tsconfig.build.json",
|
|
92
98
|
"typecheck": "tsc --noEmit",
|
package/src/Head.astro
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
// What a page's head says about it to every reader that isn't a person, from
|
|
3
|
+
// its record in the build's `_ascribe/pages.json`: its `<title>`, its
|
|
4
|
+
// description, its canonical link, and `<meta name="robots" content="noindex">`
|
|
5
|
+
// when it isn't listed. Each comes from the record, as the sitemap and
|
|
6
|
+
// `llms.txt` do, so none can disagree with them. A layout includes it in
|
|
7
|
+
// `<head>`, given the page's collection entry:
|
|
8
|
+
//
|
|
9
|
+
// <Head entry={entry} title={(page, record) => `${page} · Loom`} />
|
|
10
|
+
//
|
|
11
|
+
// `title` is a function from the page's title and its record, which has its
|
|
12
|
+
// route, to the whole title. A page with no record, such as an Astro page of
|
|
13
|
+
// the site's own, gets nothing: it writes its own head.
|
|
14
|
+
import { pageRecord, type PageRecord } from "@ascribed/astro/content";
|
|
15
|
+
|
|
16
|
+
interface Props {
|
|
17
|
+
/** The page's collection entry, or `undefined` for a page that has none. */
|
|
18
|
+
entry?: { filePath?: string | undefined; data?: unknown } | undefined;
|
|
19
|
+
/** The whole title, from the page's title and its record. Default: the page's title. */
|
|
20
|
+
title?: (title: string, record: PageRecord) => string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const { entry, title } = Astro.props;
|
|
24
|
+
const record = entry === undefined ? undefined : pageRecord(entry);
|
|
25
|
+
const whole =
|
|
26
|
+
record?.title === undefined
|
|
27
|
+
? undefined
|
|
28
|
+
: title === undefined
|
|
29
|
+
? record.title
|
|
30
|
+
: title(record.title, record);
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
{whole !== undefined && <title>{whole}</title>}
|
|
34
|
+
{
|
|
35
|
+
record?.description !== undefined && (
|
|
36
|
+
<meta name="description" content={record.description} />
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
{record?.canonical !== undefined && <link rel="canonical" href={record.canonical} />}
|
|
40
|
+
{record?.listing === "unlisted" && <meta name="robots" content="noindex" />}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { defineCollection } from "astro:content";
|
|
2
|
+
import { ascribeCollection } from "@ascribed/astro/content";
|
|
3
|
+
// Generated by `ascribe build --emit site`; the integration runs it first.
|
|
4
|
+
import { schema } from "../.ascribe/build/site/site/_ascribe/schema.ts";
|
|
5
|
+
|
|
6
|
+
export const collections = {
|
|
7
|
+
docs: defineCollection(ascribeCollection({ schema })),
|
|
8
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { getCollection, type CollectionEntry } from "astro:content";
|
|
3
|
+
import type { ComponentProps } from "astro/types";
|
|
4
|
+
import Availability from "@ascribed/astro/Availability.astro";
|
|
5
|
+
import Elements from "@ascribed/astro/Elements.astro";
|
|
6
|
+
// The page's title, description, canonical link, and robots rule, from its record.
|
|
7
|
+
import Head from "@ascribed/astro/Head.astro";
|
|
8
|
+
import { sidebar } from "@ascribed/astro/content";
|
|
9
|
+
import { nav } from "../nav.ts";
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
entry: CollectionEntry<"docs">;
|
|
13
|
+
headings: { depth: number; slug: string; text: string }[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* What the layout reads of a page's frontmatter: `title`, which every content
|
|
18
|
+
* type has, and what a type may have. `title` is plain text; `formatted.title`
|
|
19
|
+
* is the title as HTML, with its code spans as <code>, when the content model
|
|
20
|
+
* sets `inline = "code"` on `title`.
|
|
21
|
+
*/
|
|
22
|
+
interface Page {
|
|
23
|
+
title: string;
|
|
24
|
+
available?: ComponentProps<typeof Availability>["available"];
|
|
25
|
+
formatted?: { title?: string };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const { entry, headings } = Astro.props;
|
|
29
|
+
const page = (of: CollectionEntry<"docs">) => of.data as Page;
|
|
30
|
+
/** A page's title as HTML: its formatted form, or its plain text escaped. */
|
|
31
|
+
const titleHtml = (of: CollectionEntry<"docs">) =>
|
|
32
|
+
page(of).formatted?.title ??
|
|
33
|
+
page(of).title.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
34
|
+
const { available } = page(entry);
|
|
35
|
+
// The sidebar: src/nav.ts's groups, each page with its address. The build
|
|
36
|
+
// fails when the list leaves out a page, or names one that doesn't exist.
|
|
37
|
+
const groups = sidebar(nav, await getCollection("docs"));
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
<!doctype html>
|
|
41
|
+
<html lang="en">
|
|
42
|
+
<head>
|
|
43
|
+
<meta charset="utf-8" />
|
|
44
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
45
|
+
<Head entry={entry} />
|
|
46
|
+
<Elements />
|
|
47
|
+
</head>
|
|
48
|
+
<body>
|
|
49
|
+
<nav aria-label="Docs">
|
|
50
|
+
{
|
|
51
|
+
groups.map((group) => (
|
|
52
|
+
<section>
|
|
53
|
+
<h2>{group.label}</h2>
|
|
54
|
+
<ul>
|
|
55
|
+
{group.pages.map((item) => (
|
|
56
|
+
<li>
|
|
57
|
+
<a
|
|
58
|
+
href={item.href}
|
|
59
|
+
aria-current={item.entry.id === entry.id ? "page" : undefined}
|
|
60
|
+
>
|
|
61
|
+
<Fragment set:html={titleHtml(item.entry)} />
|
|
62
|
+
</a>
|
|
63
|
+
</li>
|
|
64
|
+
))}
|
|
65
|
+
</ul>
|
|
66
|
+
</section>
|
|
67
|
+
))
|
|
68
|
+
}
|
|
69
|
+
</nav>
|
|
70
|
+
<main>
|
|
71
|
+
<h1 set:html={titleHtml(entry)} />
|
|
72
|
+
<Availability available={available} />
|
|
73
|
+
<nav aria-label="On this page">
|
|
74
|
+
<ul>
|
|
75
|
+
{
|
|
76
|
+
headings.map((heading) => (
|
|
77
|
+
<li data-depth={heading.depth}>
|
|
78
|
+
<a href={`#${heading.slug}`}>{heading.text}</a>
|
|
79
|
+
</li>
|
|
80
|
+
))
|
|
81
|
+
}
|
|
82
|
+
</ul>
|
|
83
|
+
</nav>
|
|
84
|
+
<article><slot /></article>
|
|
85
|
+
</main>
|
|
86
|
+
</body>
|
|
87
|
+
</html>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// The sidebar's list: its groups, in order, each with its pages' source files,
|
|
2
|
+
// relative to the content root. `npx @ascribed/astro init` wrote it from the
|
|
3
|
+
// pages there were then; it's yours to reorder, relabel, and add to. Ascribe's
|
|
4
|
+
// content model has no page order or grouping yet, so the site keeps the list,
|
|
5
|
+
// and the layout's `sidebar()` keeps it complete: the build fails on a page the
|
|
6
|
+
// list leaves out, and on a file it names that isn't a page.
|
|
7
|
+
import type { NavGroup } from "@ascribed/astro/content";
|
|
8
|
+
|
|
9
|
+
export const nav: NavGroup[] = [
|
|
10
|
+
{ label: "Start", pages: ["index.md"] },
|
|
11
|
+
{ label: "Guides", pages: ["Guides/My Setup.md"] },
|
|
12
|
+
{ label: "Reference", pages: ["Reference/Options.md"] },
|
|
13
|
+
];
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { getCollection, render } from "astro:content";
|
|
3
|
+
import Docs from "../layouts/Docs.astro";
|
|
4
|
+
|
|
5
|
+
// A page's route is the base path plus its entry id, and the root page (id
|
|
6
|
+
// `index`) is at the base path itself: what Ascribe's router computes for
|
|
7
|
+
// every link it writes.
|
|
8
|
+
export async function getStaticPaths() {
|
|
9
|
+
const entries = await getCollection("docs");
|
|
10
|
+
return entries.map((entry) => ({
|
|
11
|
+
params: { slug: entry.id === "index" ? undefined : entry.id },
|
|
12
|
+
props: { entry },
|
|
13
|
+
}));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const { entry } = Astro.props;
|
|
17
|
+
const { Content, headings } = await render(entry);
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
<Docs entry={entry} headings={headings}><Content /></Docs>
|