@ascribed/astro 0.2.1-next.12 → 0.2.1-next.13
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 +3 -1
- package/dist/dev.js +9 -0
- package/dist/dev.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +20 -5
- package/dist/index.js.map +1 -1
- package/dist/permalinks.d.ts +54 -0
- package/dist/permalinks.js +72 -0
- package/dist/permalinks.js.map +1 -0
- package/dist/project.d.ts +6 -0
- package/dist/project.js +9 -0
- package/dist/project.js.map +1 -1
- package/package.json +13 -6
- package/src/MissingPermalink.astro +56 -0
- package/src/Permalink.astro +77 -0
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The [Astro](https://astro.build) integration for [Ascribe](https://github.com/ascribed-dev/ascribe), for Astro 7.3.5 and later 7.x releases.
|
|
4
4
|
|
|
5
|
-
It runs `ascribe build --emit site` before Astro loads content, gives you a content collection over the output with a schema generated from `ascribe.toml`, applies heading ids and image attributes in Astro's own Markdown pipeline (so Astro keeps its heading, table-of-contents, and image handling), loads the element library,
|
|
5
|
+
It runs `ascribe build --emit site` before Astro loads content, gives you a content collection over the output with a schema generated from `ascribe.toml`, applies heading ids and image attributes in Astro's own Markdown pipeline (so Astro keeps its heading, table-of-contents, and image handling), loads the element library, serves the files pages link to, and gives each permalink an address that forwards to its heading. In `astro dev`, it rebuilds as you edit.
|
|
6
6
|
|
|
7
7
|
```sh
|
|
8
8
|
npm install @ascribed/astro
|
|
@@ -70,6 +70,8 @@ The [Astro guide](https://ascribed-dev.com/guides/astro/) walks through a site:
|
|
|
70
70
|
| `src/Elements.astro` | Loads `@ascribed/elements` (stylesheet and script) |
|
|
71
71
|
| `src/Availability.astro` | A page's availability as the element library's badge |
|
|
72
72
|
| `src/code-titles.ts` | The Shiki transformer that shows code block titles |
|
|
73
|
+
| `src/Permalink.astro`, `src/permalinks.ts` | The route at each permalink's address, and its pages, from the site output's `_ascribe/permalinks.json` |
|
|
74
|
+
| `src/MissingPermalink.astro` | The explanation on a site's 404 page for an address under the permalinks' path that matches nothing |
|
|
73
75
|
| `src/schema.ts` | Copies the generated schema inside the Astro root |
|
|
74
76
|
| `src/attributes.ts` | The attribute-marker and source-anchor rules, on a hast tree |
|
|
75
77
|
| `src/rehype.ts`, `src/satteri.ts` | The rules applied in each of Astro's Markdown processors |
|
package/dist/dev.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { permalinksText } from "./permalinks.js";
|
|
3
4
|
import { consumerMismatches, readProject } from "./project.js";
|
|
4
5
|
/**
|
|
5
6
|
* The assets a build copied from outside the content root, as absolute paths,
|
|
@@ -97,6 +98,7 @@ export function watchDev(options) {
|
|
|
97
98
|
running = true;
|
|
98
99
|
const previousProject = project;
|
|
99
100
|
const previousSiteRoot = previousProject.siteRoot(options.build);
|
|
101
|
+
const previousPermalinks = permalinksText(previousSiteRoot);
|
|
100
102
|
while (dirty) {
|
|
101
103
|
dirty = false;
|
|
102
104
|
try {
|
|
@@ -124,6 +126,13 @@ export function watchDev(options) {
|
|
|
124
126
|
server.watcher.add(project.configPath);
|
|
125
127
|
watchAssets();
|
|
126
128
|
await refreshContent();
|
|
129
|
+
// Astro keeps each route's `getStaticPaths` until content changes, and adding or removing a
|
|
130
|
+
// permalink may change no page: clear it, as Astro does, so the addresses follow the list.
|
|
131
|
+
if (permalinksText(previousSiteRoot) !== previousPermalinks) {
|
|
132
|
+
for (const name of ["ssr", "prerender"]) {
|
|
133
|
+
server.environments[name]?.hot.send("astro:content-changed", {});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
127
136
|
server.ws.send({ type: "full-reload" });
|
|
128
137
|
}
|
|
129
138
|
catch (error) {
|
package/dist/dev.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev.js","sourceRoot":"","sources":["../src/dev.ts"],"names":[],"mappings":"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","sourcesContent":["import { readdirSync, readFileSync, statSync } from \"node:fs\";\nimport path from \"node:path\";\nimport type { AstroIntegration } from \"astro\";\nimport { consumerMismatches, readProject, type ProjectInfo } from \"./project.js\";\n\ntype ServerOptions = Parameters<NonNullable<AstroIntegration[\"hooks\"][\"astro:server:setup\"]>>[0];\n\n/**\n * The assets a build copied from outside the content root, as absolute paths,\n * read from the site output's manifest. Watching the content root doesn't\n * cover them. A manifest that's missing or unreadable gives none: the next\n * successful build writes one.\n */\nexport function outsideAssets(project: ProjectInfo, build: string): string[] {\n const manifest = path.join(path.dirname(project.siteRoot(build)), \"site.manifest.json\");\n let files: unknown;\n try {\n files = (JSON.parse(readFileSync(manifest, \"utf8\")) as { files?: unknown }).files;\n } catch {\n return [];\n }\n if (!Array.isArray(files)) return [];\n const assets = new Set<string>();\n for (const entry of files as { kind?: unknown; source?: unknown }[]) {\n if (entry.kind !== \"asset\" || typeof entry.source !== \"string\") continue;\n const absolute = path.resolve(project.contentRoot, entry.source);\n if (!absolute.startsWith(project.contentRoot + path.sep)) assets.add(absolute);\n }\n return [...assets].sort();\n}\n\n/**\n * Each watched file's size and modification time, as last seen. A watcher\n * can report an edit late, after a build already read it: macOS delivers\n * edits made just before the dev server started some seconds after it\n * started. An event that changes neither is no edit, and rebuilding for it\n * would reload the page under the reader.\n */\nexport class SeenFiles {\n private readonly stamps = new Map<string, string>();\n\n /** Records the files under `paths` (files or folders) as they are now. */\n prime(paths: readonly string[]): void {\n for (const at of paths) {\n let entries: string[];\n try {\n entries = statSync(at).isDirectory()\n ? readdirSync(at, { recursive: true, encoding: \"utf8\" }).map((f) => path.join(at, f))\n : [at];\n } catch {\n continue;\n }\n for (const file of entries) {\n const stamp = stampOf(file);\n if (stamp !== undefined && !this.stamps.has(file)) this.stamps.set(file, stamp);\n }\n }\n }\n\n /** Whether `file` differs from when it was last seen (or wasn't seen), and records it. */\n changed(file: string): boolean {\n const stamp = stampOf(file);\n const before = this.stamps.get(file);\n if (stamp === undefined) this.stamps.delete(file);\n else this.stamps.set(file, stamp);\n return stamp === undefined || stamp !== before;\n }\n}\n\nfunction stampOf(file: string): string | undefined {\n try {\n const stat = statSync(file);\n return stat.isFile() ? `${stat.size}:${stat.mtimeMs}` : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** What `watchDev` gives back. */\nexport interface DevBuilds {\n /** Runs `task` in turn with the compiler runs, never during one. */\n inTurn(task: () => Promise<void>): Promise<void>;\n}\n\n/** Watch source files, serialize compiler runs, and refresh Astro after successful output. */\nexport function watchDev(options: {\n server: ServerOptions[\"server\"];\n refreshContent: () => Promise<void>;\n logger: ServerOptions[\"logger\"];\n project: ProjectInfo;\n astro: { base: string; trailingSlash: string; site: string | undefined };\n build: string;\n rebuild: () => Promise<void>;\n /** Called after each successful rebuild, before Astro reloads the page. */\n onBuilt?: () => void;\n /** The sources as the first build read them. */\n seen?: SeenFiles;\n}): DevBuilds {\n const { server, refreshContent, logger, rebuild } = options;\n let project = options.project;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let pending: Promise<void> = Promise.resolve();\n let dirty = false;\n let running = false;\n let failed = false;\n let assets = new Set<string>();\n const seen = options.seen ?? new SeenFiles();\n const watchAssets = () => {\n assets = new Set(outsideAssets(project, options.build));\n if (assets.size > 0) server.watcher.add([...assets]);\n seen.prime([...assets]);\n };\n\n const drain = async () => {\n running = true;\n const previousProject = project;\n const previousSiteRoot = previousProject.siteRoot(options.build);\n while (dirty) {\n dirty = false;\n try {\n await rebuild();\n failed = false;\n } catch (error) {\n failed = true;\n logger.error(error instanceof Error ? error.message : String(error));\n }\n }\n if (!failed) {\n options.onBuilt?.();\n try {\n const reloaded = readProject(project.dir);\n if (reloaded.siteRoot(options.build) !== previousSiteRoot) {\n throw new Error(\n \"ascribe.toml changed output-dir; restart astro dev to reload the collection.\",\n );\n }\n const mismatches = consumerMismatches(reloaded, options.astro);\n if (mismatches.length > 0) {\n throw new Error(\n `@ascribed/astro: ascribe.toml and astro.config disagree:\\n- ${mismatches.join(\"\\n- \")}`,\n );\n }\n project = reloaded;\n server.watcher.add(project.contentRoot);\n server.watcher.add(project.configPath);\n watchAssets();\n await refreshContent();\n server.ws.send({ type: \"full-reload\" });\n } catch (error) {\n failed = true;\n logger.error(\n `could not refresh Ascribe content: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n running = false;\n };\n const schedule = () => {\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => {\n timer = undefined;\n pending = pending.then(drain);\n }, 50);\n };\n const onChange = (event: string, file: string) => {\n if (![\"add\", \"change\", \"unlink\"].includes(event)) return;\n const absolute = path.resolve(file);\n const source =\n absolute === project.configPath ||\n assets.has(absolute) ||\n (absolute.startsWith(project.contentRoot + path.sep) &&\n !absolute.startsWith(project.outputRoot + path.sep));\n if (!source) return;\n // A late report of an edit the last build already had.\n if (event !== \"unlink\" && !seen.changed(absolute)) return;\n dirty = true;\n schedule();\n };\n server.watcher.add(project.contentRoot);\n server.watcher.add(project.configPath);\n seen.prime([project.contentRoot, project.configPath]);\n watchAssets();\n server.watcher.on(\"all\", onChange);\n server.httpServer?.once(\"close\", () => {\n if (timer) clearTimeout(timer);\n server.watcher.off(\"all\", onChange);\n });\n server.middlewares.use((_req, res, next) => {\n if (!dirty && !running && !failed) return next();\n if (timer) {\n clearTimeout(timer);\n timer = undefined;\n pending = pending.then(drain);\n }\n void pending.then(() => {\n if (failed) {\n res.statusCode = 503;\n res.end(\"Ascribe build failed; fix the source and save to retry.\");\n } else next();\n });\n });\n return {\n inTurn: (task) => {\n const run = pending.then(task);\n pending = run.catch(() => undefined);\n return run;\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"dev.js","sourceRoot":"","sources":["../src/dev.ts"],"names":[],"mappings":"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","sourcesContent":["import { readdirSync, readFileSync, statSync } from \"node:fs\";\nimport path from \"node:path\";\nimport type { AstroIntegration } from \"astro\";\nimport { permalinksText } from \"./permalinks.js\";\nimport { consumerMismatches, readProject, type ProjectInfo } from \"./project.js\";\n\ntype ServerOptions = Parameters<NonNullable<AstroIntegration[\"hooks\"][\"astro:server:setup\"]>>[0];\n\n/**\n * The assets a build copied from outside the content root, as absolute paths,\n * read from the site output's manifest. Watching the content root doesn't\n * cover them. A manifest that's missing or unreadable gives none: the next\n * successful build writes one.\n */\nexport function outsideAssets(project: ProjectInfo, build: string): string[] {\n const manifest = path.join(path.dirname(project.siteRoot(build)), \"site.manifest.json\");\n let files: unknown;\n try {\n files = (JSON.parse(readFileSync(manifest, \"utf8\")) as { files?: unknown }).files;\n } catch {\n return [];\n }\n if (!Array.isArray(files)) return [];\n const assets = new Set<string>();\n for (const entry of files as { kind?: unknown; source?: unknown }[]) {\n if (entry.kind !== \"asset\" || typeof entry.source !== \"string\") continue;\n const absolute = path.resolve(project.contentRoot, entry.source);\n if (!absolute.startsWith(project.contentRoot + path.sep)) assets.add(absolute);\n }\n return [...assets].sort();\n}\n\n/**\n * Each watched file's size and modification time, as last seen. A watcher\n * can report an edit late, after a build already read it: macOS delivers\n * edits made just before the dev server started some seconds after it\n * started. An event that changes neither is no edit, and rebuilding for it\n * would reload the page under the reader.\n */\nexport class SeenFiles {\n private readonly stamps = new Map<string, string>();\n\n /** Records the files under `paths` (files or folders) as they are now. */\n prime(paths: readonly string[]): void {\n for (const at of paths) {\n let entries: string[];\n try {\n entries = statSync(at).isDirectory()\n ? readdirSync(at, { recursive: true, encoding: \"utf8\" }).map((f) => path.join(at, f))\n : [at];\n } catch {\n continue;\n }\n for (const file of entries) {\n const stamp = stampOf(file);\n if (stamp !== undefined && !this.stamps.has(file)) this.stamps.set(file, stamp);\n }\n }\n }\n\n /** Whether `file` differs from when it was last seen (or wasn't seen), and records it. */\n changed(file: string): boolean {\n const stamp = stampOf(file);\n const before = this.stamps.get(file);\n if (stamp === undefined) this.stamps.delete(file);\n else this.stamps.set(file, stamp);\n return stamp === undefined || stamp !== before;\n }\n}\n\nfunction stampOf(file: string): string | undefined {\n try {\n const stat = statSync(file);\n return stat.isFile() ? `${stat.size}:${stat.mtimeMs}` : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** What `watchDev` gives back. */\nexport interface DevBuilds {\n /** Runs `task` in turn with the compiler runs, never during one. */\n inTurn(task: () => Promise<void>): Promise<void>;\n}\n\n/** Watch source files, serialize compiler runs, and refresh Astro after successful output. */\nexport function watchDev(options: {\n server: ServerOptions[\"server\"];\n refreshContent: () => Promise<void>;\n logger: ServerOptions[\"logger\"];\n project: ProjectInfo;\n astro: { base: string; trailingSlash: string; site: string | undefined };\n build: string;\n rebuild: () => Promise<void>;\n /** Called after each successful rebuild, before Astro reloads the page. */\n onBuilt?: () => void;\n /** The sources as the first build read them. */\n seen?: SeenFiles;\n}): DevBuilds {\n const { server, refreshContent, logger, rebuild } = options;\n let project = options.project;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let pending: Promise<void> = Promise.resolve();\n let dirty = false;\n let running = false;\n let failed = false;\n let assets = new Set<string>();\n const seen = options.seen ?? new SeenFiles();\n const watchAssets = () => {\n assets = new Set(outsideAssets(project, options.build));\n if (assets.size > 0) server.watcher.add([...assets]);\n seen.prime([...assets]);\n };\n\n const drain = async () => {\n running = true;\n const previousProject = project;\n const previousSiteRoot = previousProject.siteRoot(options.build);\n const previousPermalinks = permalinksText(previousSiteRoot);\n while (dirty) {\n dirty = false;\n try {\n await rebuild();\n failed = false;\n } catch (error) {\n failed = true;\n logger.error(error instanceof Error ? error.message : String(error));\n }\n }\n if (!failed) {\n options.onBuilt?.();\n try {\n const reloaded = readProject(project.dir);\n if (reloaded.siteRoot(options.build) !== previousSiteRoot) {\n throw new Error(\n \"ascribe.toml changed output-dir; restart astro dev to reload the collection.\",\n );\n }\n const mismatches = consumerMismatches(reloaded, options.astro);\n if (mismatches.length > 0) {\n throw new Error(\n `@ascribed/astro: ascribe.toml and astro.config disagree:\\n- ${mismatches.join(\"\\n- \")}`,\n );\n }\n project = reloaded;\n server.watcher.add(project.contentRoot);\n server.watcher.add(project.configPath);\n watchAssets();\n await refreshContent();\n // Astro keeps each route's `getStaticPaths` until content changes, and adding or removing a\n // permalink may change no page: clear it, as Astro does, so the addresses follow the list.\n if (permalinksText(previousSiteRoot) !== previousPermalinks) {\n for (const name of [\"ssr\", \"prerender\"]) {\n server.environments[name]?.hot.send(\"astro:content-changed\", {});\n }\n }\n server.ws.send({ type: \"full-reload\" });\n } catch (error) {\n failed = true;\n logger.error(\n `could not refresh Ascribe content: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n running = false;\n };\n const schedule = () => {\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => {\n timer = undefined;\n pending = pending.then(drain);\n }, 50);\n };\n const onChange = (event: string, file: string) => {\n if (![\"add\", \"change\", \"unlink\"].includes(event)) return;\n const absolute = path.resolve(file);\n const source =\n absolute === project.configPath ||\n assets.has(absolute) ||\n (absolute.startsWith(project.contentRoot + path.sep) &&\n !absolute.startsWith(project.outputRoot + path.sep));\n if (!source) return;\n // A late report of an edit the last build already had.\n if (event !== \"unlink\" && !seen.changed(absolute)) return;\n dirty = true;\n schedule();\n };\n server.watcher.add(project.contentRoot);\n server.watcher.add(project.configPath);\n seen.prime([project.contentRoot, project.configPath]);\n watchAssets();\n server.watcher.on(\"all\", onChange);\n server.httpServer?.once(\"close\", () => {\n if (timer) clearTimeout(timer);\n server.watcher.off(\"all\", onChange);\n });\n server.middlewares.use((_req, res, next) => {\n if (!dirty && !running && !failed) return next();\n if (timer) {\n clearTimeout(timer);\n timer = undefined;\n pending = pending.then(drain);\n }\n void pending.then(() => {\n if (failed) {\n res.statusCode = 503;\n res.end(\"Ascribe build failed; fix the source and save to retry.\");\n } else next();\n });\n });\n return {\n inTurn: (task) => {\n const run = pending.then(task);\n pending = run.catch(() => undefined);\n return run;\n },\n };\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -33,7 +33,8 @@ export interface AscribeOptions {
|
|
|
33
33
|
codeTitles?: boolean;
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
|
-
* Runs `ascribe build`, checks the site's routing, adds the markdown plugin,
|
|
37
|
-
* files, and, with `[consumer] agents = true`, `llms.txt`
|
|
36
|
+
* Runs `ascribe build`, checks the site's routing, adds the markdown plugin, adds a page for each
|
|
37
|
+
* permalink's address, and serves published files, and, with `[consumer] agents = true`, `llms.txt`
|
|
38
|
+
* and each page's Markdown.
|
|
38
39
|
*/
|
|
39
40
|
export default function ascribe(options: AscribeOptions): AstroIntegration;
|
package/dist/index.js
CHANGED
|
@@ -19,13 +19,17 @@
|
|
|
19
19
|
// // a layout
|
|
20
20
|
// import Elements from "@ascribed/astro/Elements.astro"; // <Elements /> in <head>
|
|
21
21
|
// import Availability from "@ascribed/astro/Availability.astro"; // the page's availability
|
|
22
|
+
//
|
|
23
|
+
// // src/pages/404.astro
|
|
24
|
+
// import MissingPermalink from "@ascribed/astro/MissingPermalink.astro"; // a permalink that isn't here
|
|
22
25
|
import path from "node:path";
|
|
23
26
|
import { fileURLToPath } from "node:url";
|
|
24
27
|
import { findBinary } from "./binary.js";
|
|
25
28
|
import { codeTitles } from "./code-titles.js";
|
|
26
29
|
import { SeenFiles, watchDev } from "./dev.js";
|
|
27
30
|
import { agentsMiddleware, copyAgentFiles, copyPublishedFiles, filesMiddleware } from "./files.js";
|
|
28
|
-
import {
|
|
31
|
+
import { permalinkPattern, readPermalinks } from "./permalinks.js";
|
|
32
|
+
import { consumerMismatches, homeRoute, readProject } from "./project.js";
|
|
29
33
|
import rehypeAscribeAttributes from "./rehype.js";
|
|
30
34
|
import { removeDevFile, siteUrl, writeDevFile } from "./review/devfile.js";
|
|
31
35
|
import { runDiff, runPrompt } from "./review/diff.js";
|
|
@@ -42,8 +46,9 @@ const SITE_MODULE = "virtual:ascribe/site";
|
|
|
42
46
|
/** The toolbar app's icon: a speech bubble over a page. */
|
|
43
47
|
const ICON = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M6 3h8l4 4v6"/><path d="M14 3v4h4"/><path d="M6 3v15"/><path d="M11 14h10v6h-6l-3 2v-2h-1z"/></svg>';
|
|
44
48
|
/**
|
|
45
|
-
* Runs `ascribe build`, checks the site's routing, adds the markdown plugin,
|
|
46
|
-
* files, and, with `[consumer] agents = true`, `llms.txt`
|
|
49
|
+
* Runs `ascribe build`, checks the site's routing, adds the markdown plugin, adds a page for each
|
|
50
|
+
* permalink's address, and serves published files, and, with `[consumer] agents = true`, `llms.txt`
|
|
51
|
+
* and each page's Markdown.
|
|
47
52
|
*/
|
|
48
53
|
export default function ascribe(options) {
|
|
49
54
|
let siteRoot = "";
|
|
@@ -61,7 +66,7 @@ export default function ascribe(options) {
|
|
|
61
66
|
return {
|
|
62
67
|
name: "@ascribed/astro",
|
|
63
68
|
hooks: {
|
|
64
|
-
"astro:config:setup": async ({ config, command, logger, updateConfig, addDevToolbarApp, createCodegenDir, }) => {
|
|
69
|
+
"astro:config:setup": async ({ config, command, logger, updateConfig, addDevToolbarApp, createCodegenDir, injectRoute, }) => {
|
|
65
70
|
const root = fileURLToPath(config.root);
|
|
66
71
|
const project = readProject(path.resolve(root, options.project ?? "."));
|
|
67
72
|
// An unknown build is `ascribe build`'s to report: it knows the implicit `site` build.
|
|
@@ -139,6 +144,15 @@ export default function ascribe(options) {
|
|
|
139
144
|
await rebuild();
|
|
140
145
|
if (command === "dev")
|
|
141
146
|
devProject = project;
|
|
147
|
+
// Each permalink's address, a page that forwards to its heading.
|
|
148
|
+
const permalinks = readPermalinks(siteRoot);
|
|
149
|
+
if (permalinks !== undefined) {
|
|
150
|
+
injectRoute({
|
|
151
|
+
pattern: permalinkPattern(permalinks.path, config.base),
|
|
152
|
+
entrypoint: new URL("../src/Permalink.astro", import.meta.url),
|
|
153
|
+
prerender: true,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
142
156
|
}
|
|
143
157
|
// Dev serving of `_ascribe/files/`, and of the plain output for agents; the build copies them in `astro:build:done`.
|
|
144
158
|
updateConfig({
|
|
@@ -153,7 +167,8 @@ export default function ascribe(options) {
|
|
|
153
167
|
name: "@ascribed/astro:site",
|
|
154
168
|
resolveId: (id) => (id === SITE_MODULE ? `\0${SITE_MODULE}` : undefined),
|
|
155
169
|
load: (id) => id === `\0${SITE_MODULE}`
|
|
156
|
-
? `export const siteRoot = ${JSON.stringify(siteRoot)}
|
|
170
|
+
? `export const siteRoot = ${JSON.stringify(siteRoot)};\n` +
|
|
171
|
+
`export const home = ${JSON.stringify(homeRoute(config.base, config.trailingSlash))};`
|
|
157
172
|
: undefined,
|
|
158
173
|
},
|
|
159
174
|
{
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["// @ascribed/astro: the Astro integration for Ascribe. Written against Astro\n// 7.3.5.\n//\n// // astro.config.mjs\n// import ascribe from \"@ascribed/astro\";\n// export default defineConfig({\n// base: \"/docs\", trailingSlash: \"never\", // as in ascribe.toml's [consumer]\n// integrations: [ascribe({ build: \"site\" })],\n// });\n//\n// // src/content.config.ts\n// import { defineCollection } from \"astro:content\";\n// import { ascribeCollection } from \"@ascribed/astro/content\";\n// // The generated schema. With the project outside the Astro root, import the\n// // copy inside it instead: \"../.astro/integrations/_ascribed_astro/schema.ts\".\n// import { schema } from \"../.ascribe/build/site/site/_ascribe/schema.ts\";\n// export const collections = { docs: defineCollection(ascribeCollection({ schema })) };\n//\n// // a layout\n// import Elements from \"@ascribed/astro/Elements.astro\"; // <Elements /> in <head>\n// import Availability from \"@ascribed/astro/Availability.astro\"; // the page's availability\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { AstroIntegration } from \"astro\";\nimport { findBinary } from \"./binary.js\";\nimport { codeTitles } from \"./code-titles.js\";\nimport { SeenFiles, watchDev } from \"./dev.js\";\nimport { agentsMiddleware, copyAgentFiles, copyPublishedFiles, filesMiddleware } from \"./files.js\";\nimport { consumerMismatches, readProject } from \"./project.js\";\nimport rehypeAscribeAttributes from \"./rehype.js\";\nimport { removeDevFile, siteUrl, writeDevFile } from \"./review/devfile.js\";\nimport { runDiff, runPrompt } from \"./review/diff.js\";\nimport { APP_ID } from \"./review/protocol.js\";\nimport { readRoutes } from \"./review/routes.js\";\nimport { channelProblem, ReviewServer, type ToolbarChannel } from \"./review/server.js\";\nimport { anchorsFor, hasWarnings, reviewFor, runBuild } from \"./run.js\";\nimport { satteriAscribeAttributes } from \"./satteri.js\";\nimport { copySchema } from \"./schema.js\";\n\nexport { default as rehypeAscribeAttributes } from \"./rehype.js\";\nexport { satteriAscribeAttributes } from \"./satteri.js\";\n\n/** The virtual module `content.ts` reads the resolved site output root from. */\nconst SITE_MODULE = \"virtual:ascribe/site\";\n\n/** Options of the integration. */\nexport interface AscribeOptions {\n /** The build whose site output is the collection (a name in `ascribe.toml`). */\n build: string;\n /** The directory holding `ascribe.toml`, relative to the Astro root. Default: the root. */\n project?: string;\n /**\n * The `ascribe` binary, relative to the Astro root. Default: `ASCRIBE_BIN`, then\n * the platform binary installed with `@ascribed/cli`.\n */\n binary?: string;\n /**\n * Mark each block of the site output with the source lines it came from\n * (source anchors), for review: `\"dev\"` in `astro dev` only, `true` always.\n * Default: `false`, though `review` has them in `astro dev`.\n */\n anchors?: boolean | \"dev\";\n /**\n * Review in the site preview: an **Ascribe review** app in `astro dev`'s\n * toolbar, which marks the page's changes and shows the pull request's\n * comments, with source anchors in `astro dev`. Default `true`; `false`\n * removes the app. `astro build` never has it.\n */\n review?: boolean;\n /**\n * Show a code block's title (`title=\"…\"` in its info string, as `@snippet`\n * writes it) above it, with a Shiki transformer that puts the block in a\n * `<figure class=\"code-title\">` with a `<figcaption>`. Default `true`.\n */\n codeTitles?: boolean;\n}\n\n/** The toolbar app's icon: a speech bubble over a page. */\nconst ICON =\n '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M6 3h8l4 4v6\"/><path d=\"M14 3v4h4\"/><path d=\"M6 3v15\"/><path d=\"M11 14h10v6h-6l-3 2v-2h-1z\"/></svg>';\n\n/**\n * Runs `ascribe build`, checks the site's routing, adds the markdown plugin, and serves published\n * files, and, with `[consumer] agents = true`, `llms.txt` and each page's Markdown.\n */\nexport default function ascribe(options: AscribeOptions): AstroIntegration {\n let siteRoot = \"\";\n /** The plain output's root, when the project publishes it for agents. */\n let agentsRoot: string | undefined;\n let devProject: ReturnType<typeof readProject> | undefined;\n let devConfig:\n | {\n base: string;\n trailingSlash: string;\n site: string | undefined;\n }\n | undefined;\n let rebuild: (() => Promise<void>) | undefined;\n /** The dev server's sources as its first build read them. */\n const seen = new SeenFiles();\n let binary: string | undefined;\n let reviewing = false;\n let devHttps = false;\n let review: ReviewServer | undefined;\n return {\n name: \"@ascribed/astro\",\n hooks: {\n \"astro:config:setup\": async ({\n config,\n command,\n logger,\n updateConfig,\n addDevToolbarApp,\n createCodegenDir,\n }) => {\n const root = fileURLToPath(config.root);\n const project = readProject(path.resolve(root, options.project ?? \".\"));\n // An unknown build is `ascribe build`'s to report: it knows the implicit `site` build.\n siteRoot = project.siteRoot(options.build);\n if (project.consumer.agents) agentsRoot = project.plainRoot(options.build);\n\n // `ascribe.toml`'s routing must be Astro's, or every link Ascribe writes is wrong.\n const problems = consumerMismatches(project, {\n base: config.base,\n trailingSlash: config.trailingSlash,\n site: config.site,\n });\n if (problems.length > 0) {\n throw new Error(\n `@ascribed/astro: ascribe.toml and astro.config disagree:\\n- ${problems.join(\"\\n- \")}`,\n );\n }\n\n // Where a markdown plugin goes depends on the processor Astro 7.3 runs.\n const processor = config.markdown.processor;\n const pluginLists = processor.options as {\n rehypePlugins?: unknown[];\n hastPlugins?: unknown[];\n };\n if (processor.name === \"satteri\" && pluginLists.hastPlugins !== undefined) {\n pluginLists.hastPlugins.push(satteriAscribeAttributes());\n } else if (processor.name === \"unified\" && pluginLists.rehypePlugins !== undefined) {\n pluginLists.rehypePlugins.push(rehypeAscribeAttributes);\n } else {\n throw new Error(\n `@ascribed/astro: the markdown processor \"${processor.name}\" is not one it can add its plugin to. ` +\n \"Use Astro's default (satteri) or unified(), or add `rehypeAscribeAttributes` to your own.\",\n );\n }\n\n devConfig = {\n base: config.base,\n trailingSlash: config.trailingSlash,\n site: config.site,\n };\n\n reviewing = reviewFor(options.review, command);\n if (reviewing) {\n addDevToolbarApp({\n id: APP_ID,\n name: \"Ascribe review\",\n icon: ICON,\n entrypoint: new URL(\"./toolbar/app.js\", import.meta.url),\n });\n }\n\n if (command !== \"preview\") {\n const found = findBinary({ binary: options.binary, root });\n binary = found;\n // The schema's copy inside the Astro root, for content.config.ts to import.\n const codegen = fileURLToPath(createCodegenDir());\n rebuild = async () => {\n logger.info(`running ${path.basename(found)} build for \"${options.build}\"`);\n const result = await runBuild({\n binary: found,\n configPath: project.configPath,\n build: options.build,\n cwd: project.dir,\n anchors: anchorsFor(options.anchors, command, reviewing),\n outputs: [\n \"site\",\n // `llms.txt` and each page's Markdown, which agents read.\n ...(agentsRoot === undefined ? [] : ([\"plain\"] as const)),\n // While review is on, the JSON output says which page is at each route.\n ...(review?.active ? ([\"json\"] as const) : []),\n ],\n });\n if (hasWarnings(result.diagnostics)) logger.warn(result.diagnostics);\n else if (result.diagnostics !== \"\") logger.info(result.diagnostics);\n if (result.summary !== \"\") logger.info(result.summary);\n copySchema(siteRoot, codegen);\n };\n // The sources as the first build reads them: the watcher may report their edits late.\n if (command === \"dev\") seen.prime([project.contentRoot, project.configPath]);\n await rebuild();\n if (command === \"dev\") devProject = project;\n }\n\n // Dev serving of `_ascribe/files/`, and of the plain output for agents; the build copies them in `astro:build:done`.\n updateConfig({\n ...(options.codeTitles === false\n ? {}\n : { markdown: { shikiConfig: { transformers: [codeTitles] } } }),\n vite: {\n // `Elements.astro` is Astro source, so Vite must compile it, not load it as a Node module.\n ssr: { noExternal: [\"@ascribed/astro\"] },\n plugins: [\n {\n name: \"@ascribed/astro:site\",\n resolveId: (id) => (id === SITE_MODULE ? `\\0${SITE_MODULE}` : undefined),\n load: (id) =>\n id === `\\0${SITE_MODULE}`\n ? `export const siteRoot = ${JSON.stringify(siteRoot)};`\n : undefined,\n },\n {\n name: \"@ascribed/astro:files\",\n configureServer(server) {\n const base = config.base.endsWith(\"/\") ? config.base : `${config.base}/`;\n server.middlewares.use(filesMiddleware(siteRoot, base));\n if (agentsRoot !== undefined)\n server.middlewares.use(agentsMiddleware(agentsRoot, base));\n },\n },\n ],\n },\n });\n },\n \"astro:server:setup\": ({ server, refreshContent, logger, toolbar }) => {\n if (devProject && rebuild && devConfig) {\n if (!refreshContent)\n throw new Error(\"@ascribed/astro: this Astro version does not support refreshContent.\");\n const project = devProject;\n devHttps = Boolean(server.config.server.https);\n const builds = watchDev({\n server,\n seen,\n refreshContent: () => refreshContent({}),\n logger,\n project,\n astro: devConfig,\n build: options.build,\n rebuild,\n onBuilt: () => review?.rebuilt(),\n });\n if (reviewing && binary !== undefined) {\n const found = binary;\n const run = { binary: found, configPath: project.configPath, cwd: project.dir };\n const jsonRoot = path.join(path.dirname(siteRoot), \"json\");\n review = new ReviewServer({\n channel: toolbar as ToolbarChannel,\n logger,\n build: options.build,\n contentRoot: project.contentRoot,\n channelProblem: channelProblem(server.config),\n diff: (base) => runDiff({ ...run, build: options.build, base }),\n prompt: (page, base) => runPrompt({ ...run, build: options.build, base, path: page }),\n projectDir: project.dir,\n connect: async () => (await import(\"./review/github.js\")).connect(project.dir),\n writeRoutes: () =>\n builds.inTurn(async () => {\n await runBuild({ ...run, build: options.build, outputs: [\"json\"] });\n }),\n readRoutes: () => readRoutes(jsonRoot),\n });\n }\n }\n },\n \"astro:server:start\": ({ address, logger }) => {\n review?.listening(address.address);\n if (!devProject || !devConfig) return;\n const dir = devProject.dir;\n try {\n writeDevFile(dir, {\n url: siteUrl(address, { https: devHttps, base: devConfig.base }),\n build: options.build,\n pid: process.pid,\n });\n // Vite exits on a signal before `astro:server:done` runs.\n process.once(\"exit\", () => removeDevFile(dir, process.pid));\n } catch (error) {\n logger.warn(\n `couldn't write .ascribe/dev.json, which the editor's Open Site Preview reads: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n },\n \"astro:server:done\": () => {\n review?.dispose();\n review = undefined;\n if (devProject) removeDevFile(devProject.dir, process.pid);\n },\n \"astro:build:done\": async ({ dir, logger }) => {\n if (await copyPublishedFiles(siteRoot, dir)) logger.info(\"copied _ascribe/files/\");\n if (agentsRoot !== undefined && (await copyAgentFiles(agentsRoot, dir)))\n logger.info(\"copied llms.txt and each page's Markdown\");\n },\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["// @ascribed/astro: the Astro integration for Ascribe. Written against Astro\n// 7.3.5.\n//\n// // astro.config.mjs\n// import ascribe from \"@ascribed/astro\";\n// export default defineConfig({\n// base: \"/docs\", trailingSlash: \"never\", // as in ascribe.toml's [consumer]\n// integrations: [ascribe({ build: \"site\" })],\n// });\n//\n// // src/content.config.ts\n// import { defineCollection } from \"astro:content\";\n// import { ascribeCollection } from \"@ascribed/astro/content\";\n// // The generated schema. With the project outside the Astro root, import the\n// // copy inside it instead: \"../.astro/integrations/_ascribed_astro/schema.ts\".\n// import { schema } from \"../.ascribe/build/site/site/_ascribe/schema.ts\";\n// export const collections = { docs: defineCollection(ascribeCollection({ schema })) };\n//\n// // a layout\n// import Elements from \"@ascribed/astro/Elements.astro\"; // <Elements /> in <head>\n// import Availability from \"@ascribed/astro/Availability.astro\"; // the page's availability\n//\n// // src/pages/404.astro\n// import MissingPermalink from \"@ascribed/astro/MissingPermalink.astro\"; // a permalink that isn't here\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { AstroIntegration } from \"astro\";\nimport { findBinary } from \"./binary.js\";\nimport { codeTitles } from \"./code-titles.js\";\nimport { SeenFiles, watchDev } from \"./dev.js\";\nimport { agentsMiddleware, copyAgentFiles, copyPublishedFiles, filesMiddleware } from \"./files.js\";\nimport { permalinkPattern, readPermalinks } from \"./permalinks.js\";\nimport { consumerMismatches, homeRoute, readProject } from \"./project.js\";\nimport rehypeAscribeAttributes from \"./rehype.js\";\nimport { removeDevFile, siteUrl, writeDevFile } from \"./review/devfile.js\";\nimport { runDiff, runPrompt } from \"./review/diff.js\";\nimport { APP_ID } from \"./review/protocol.js\";\nimport { readRoutes } from \"./review/routes.js\";\nimport { channelProblem, ReviewServer, type ToolbarChannel } from \"./review/server.js\";\nimport { anchorsFor, hasWarnings, reviewFor, runBuild } from \"./run.js\";\nimport { satteriAscribeAttributes } from \"./satteri.js\";\nimport { copySchema } from \"./schema.js\";\n\nexport { default as rehypeAscribeAttributes } from \"./rehype.js\";\nexport { satteriAscribeAttributes } from \"./satteri.js\";\n\n/** The virtual module `content.ts` reads the resolved site output root from. */\nconst SITE_MODULE = \"virtual:ascribe/site\";\n\n/** Options of the integration. */\nexport interface AscribeOptions {\n /** The build whose site output is the collection (a name in `ascribe.toml`). */\n build: string;\n /** The directory holding `ascribe.toml`, relative to the Astro root. Default: the root. */\n project?: string;\n /**\n * The `ascribe` binary, relative to the Astro root. Default: `ASCRIBE_BIN`, then\n * the platform binary installed with `@ascribed/cli`.\n */\n binary?: string;\n /**\n * Mark each block of the site output with the source lines it came from\n * (source anchors), for review: `\"dev\"` in `astro dev` only, `true` always.\n * Default: `false`, though `review` has them in `astro dev`.\n */\n anchors?: boolean | \"dev\";\n /**\n * Review in the site preview: an **Ascribe review** app in `astro dev`'s\n * toolbar, which marks the page's changes and shows the pull request's\n * comments, with source anchors in `astro dev`. Default `true`; `false`\n * removes the app. `astro build` never has it.\n */\n review?: boolean;\n /**\n * Show a code block's title (`title=\"…\"` in its info string, as `@snippet`\n * writes it) above it, with a Shiki transformer that puts the block in a\n * `<figure class=\"code-title\">` with a `<figcaption>`. Default `true`.\n */\n codeTitles?: boolean;\n}\n\n/** The toolbar app's icon: a speech bubble over a page. */\nconst ICON =\n '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M6 3h8l4 4v6\"/><path d=\"M14 3v4h4\"/><path d=\"M6 3v15\"/><path d=\"M11 14h10v6h-6l-3 2v-2h-1z\"/></svg>';\n\n/**\n * Runs `ascribe build`, checks the site's routing, adds the markdown plugin, adds a page for each\n * permalink's address, and serves published files, and, with `[consumer] agents = true`, `llms.txt`\n * and each page's Markdown.\n */\nexport default function ascribe(options: AscribeOptions): AstroIntegration {\n let siteRoot = \"\";\n /** The plain output's root, when the project publishes it for agents. */\n let agentsRoot: string | undefined;\n let devProject: ReturnType<typeof readProject> | undefined;\n let devConfig:\n | {\n base: string;\n trailingSlash: string;\n site: string | undefined;\n }\n | undefined;\n let rebuild: (() => Promise<void>) | undefined;\n /** The dev server's sources as its first build read them. */\n const seen = new SeenFiles();\n let binary: string | undefined;\n let reviewing = false;\n let devHttps = false;\n let review: ReviewServer | undefined;\n return {\n name: \"@ascribed/astro\",\n hooks: {\n \"astro:config:setup\": async ({\n config,\n command,\n logger,\n updateConfig,\n addDevToolbarApp,\n createCodegenDir,\n injectRoute,\n }) => {\n const root = fileURLToPath(config.root);\n const project = readProject(path.resolve(root, options.project ?? \".\"));\n // An unknown build is `ascribe build`'s to report: it knows the implicit `site` build.\n siteRoot = project.siteRoot(options.build);\n if (project.consumer.agents) agentsRoot = project.plainRoot(options.build);\n\n // `ascribe.toml`'s routing must be Astro's, or every link Ascribe writes is wrong.\n const problems = consumerMismatches(project, {\n base: config.base,\n trailingSlash: config.trailingSlash,\n site: config.site,\n });\n if (problems.length > 0) {\n throw new Error(\n `@ascribed/astro: ascribe.toml and astro.config disagree:\\n- ${problems.join(\"\\n- \")}`,\n );\n }\n\n // Where a markdown plugin goes depends on the processor Astro 7.3 runs.\n const processor = config.markdown.processor;\n const pluginLists = processor.options as {\n rehypePlugins?: unknown[];\n hastPlugins?: unknown[];\n };\n if (processor.name === \"satteri\" && pluginLists.hastPlugins !== undefined) {\n pluginLists.hastPlugins.push(satteriAscribeAttributes());\n } else if (processor.name === \"unified\" && pluginLists.rehypePlugins !== undefined) {\n pluginLists.rehypePlugins.push(rehypeAscribeAttributes);\n } else {\n throw new Error(\n `@ascribed/astro: the markdown processor \"${processor.name}\" is not one it can add its plugin to. ` +\n \"Use Astro's default (satteri) or unified(), or add `rehypeAscribeAttributes` to your own.\",\n );\n }\n\n devConfig = {\n base: config.base,\n trailingSlash: config.trailingSlash,\n site: config.site,\n };\n\n reviewing = reviewFor(options.review, command);\n if (reviewing) {\n addDevToolbarApp({\n id: APP_ID,\n name: \"Ascribe review\",\n icon: ICON,\n entrypoint: new URL(\"./toolbar/app.js\", import.meta.url),\n });\n }\n\n if (command !== \"preview\") {\n const found = findBinary({ binary: options.binary, root });\n binary = found;\n // The schema's copy inside the Astro root, for content.config.ts to import.\n const codegen = fileURLToPath(createCodegenDir());\n rebuild = async () => {\n logger.info(`running ${path.basename(found)} build for \"${options.build}\"`);\n const result = await runBuild({\n binary: found,\n configPath: project.configPath,\n build: options.build,\n cwd: project.dir,\n anchors: anchorsFor(options.anchors, command, reviewing),\n outputs: [\n \"site\",\n // `llms.txt` and each page's Markdown, which agents read.\n ...(agentsRoot === undefined ? [] : ([\"plain\"] as const)),\n // While review is on, the JSON output says which page is at each route.\n ...(review?.active ? ([\"json\"] as const) : []),\n ],\n });\n if (hasWarnings(result.diagnostics)) logger.warn(result.diagnostics);\n else if (result.diagnostics !== \"\") logger.info(result.diagnostics);\n if (result.summary !== \"\") logger.info(result.summary);\n copySchema(siteRoot, codegen);\n };\n // The sources as the first build reads them: the watcher may report their edits late.\n if (command === \"dev\") seen.prime([project.contentRoot, project.configPath]);\n await rebuild();\n if (command === \"dev\") devProject = project;\n\n // Each permalink's address, a page that forwards to its heading.\n const permalinks = readPermalinks(siteRoot);\n if (permalinks !== undefined) {\n injectRoute({\n pattern: permalinkPattern(permalinks.path, config.base),\n entrypoint: new URL(\"../src/Permalink.astro\", import.meta.url),\n prerender: true,\n });\n }\n }\n\n // Dev serving of `_ascribe/files/`, and of the plain output for agents; the build copies them in `astro:build:done`.\n updateConfig({\n ...(options.codeTitles === false\n ? {}\n : { markdown: { shikiConfig: { transformers: [codeTitles] } } }),\n vite: {\n // `Elements.astro` is Astro source, so Vite must compile it, not load it as a Node module.\n ssr: { noExternal: [\"@ascribed/astro\"] },\n plugins: [\n {\n name: \"@ascribed/astro:site\",\n resolveId: (id) => (id === SITE_MODULE ? `\\0${SITE_MODULE}` : undefined),\n load: (id) =>\n id === `\\0${SITE_MODULE}`\n ? `export const siteRoot = ${JSON.stringify(siteRoot)};\\n` +\n `export const home = ${JSON.stringify(homeRoute(config.base, config.trailingSlash))};`\n : undefined,\n },\n {\n name: \"@ascribed/astro:files\",\n configureServer(server) {\n const base = config.base.endsWith(\"/\") ? config.base : `${config.base}/`;\n server.middlewares.use(filesMiddleware(siteRoot, base));\n if (agentsRoot !== undefined)\n server.middlewares.use(agentsMiddleware(agentsRoot, base));\n },\n },\n ],\n },\n });\n },\n \"astro:server:setup\": ({ server, refreshContent, logger, toolbar }) => {\n if (devProject && rebuild && devConfig) {\n if (!refreshContent)\n throw new Error(\"@ascribed/astro: this Astro version does not support refreshContent.\");\n const project = devProject;\n devHttps = Boolean(server.config.server.https);\n const builds = watchDev({\n server,\n seen,\n refreshContent: () => refreshContent({}),\n logger,\n project,\n astro: devConfig,\n build: options.build,\n rebuild,\n onBuilt: () => review?.rebuilt(),\n });\n if (reviewing && binary !== undefined) {\n const found = binary;\n const run = { binary: found, configPath: project.configPath, cwd: project.dir };\n const jsonRoot = path.join(path.dirname(siteRoot), \"json\");\n review = new ReviewServer({\n channel: toolbar as ToolbarChannel,\n logger,\n build: options.build,\n contentRoot: project.contentRoot,\n channelProblem: channelProblem(server.config),\n diff: (base) => runDiff({ ...run, build: options.build, base }),\n prompt: (page, base) => runPrompt({ ...run, build: options.build, base, path: page }),\n projectDir: project.dir,\n connect: async () => (await import(\"./review/github.js\")).connect(project.dir),\n writeRoutes: () =>\n builds.inTurn(async () => {\n await runBuild({ ...run, build: options.build, outputs: [\"json\"] });\n }),\n readRoutes: () => readRoutes(jsonRoot),\n });\n }\n }\n },\n \"astro:server:start\": ({ address, logger }) => {\n review?.listening(address.address);\n if (!devProject || !devConfig) return;\n const dir = devProject.dir;\n try {\n writeDevFile(dir, {\n url: siteUrl(address, { https: devHttps, base: devConfig.base }),\n build: options.build,\n pid: process.pid,\n });\n // Vite exits on a signal before `astro:server:done` runs.\n process.once(\"exit\", () => removeDevFile(dir, process.pid));\n } catch (error) {\n logger.warn(\n `couldn't write .ascribe/dev.json, which the editor's Open Site Preview reads: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n },\n \"astro:server:done\": () => {\n review?.dispose();\n review = undefined;\n if (devProject) removeDevFile(devProject.dir, process.pid);\n },\n \"astro:build:done\": async ({ dir, logger }) => {\n if (await copyPublishedFiles(siteRoot, dir)) logger.info(\"copied _ascribe/files/\");\n if (agentsRoot !== undefined && (await copyAgentFiles(agentsRoot, dir)))\n logger.info(\"copied llms.txt and each page's Markdown\");\n },\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/** `_ascribe/permalinks.json`. */
|
|
2
|
+
export interface PermalinkList {
|
|
3
|
+
version: number;
|
|
4
|
+
/** Where the addresses are published, from the site's root, ending in `/`. */
|
|
5
|
+
path: string;
|
|
6
|
+
/** The content model's dimensions, each with its values: the parameters an address takes. */
|
|
7
|
+
dimensions?: Record<string, string[]>;
|
|
8
|
+
/**
|
|
9
|
+
* Each permalink's destination. An alias names the permalink it leads to,
|
|
10
|
+
* and a retired permalink that leads somewhere its reason.
|
|
11
|
+
*/
|
|
12
|
+
permalinks: Record<string, {
|
|
13
|
+
route: string;
|
|
14
|
+
fragment?: string;
|
|
15
|
+
source: string;
|
|
16
|
+
alias?: string;
|
|
17
|
+
retired?: string;
|
|
18
|
+
}>;
|
|
19
|
+
/** The retired permalinks with nowhere to lead in this build, each with why. */
|
|
20
|
+
removed?: Record<string, {
|
|
21
|
+
reason: string;
|
|
22
|
+
}>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* What an address's page needs: where it sends the reader, and the
|
|
26
|
+
* parameters it takes; or, for a retired permalink with nowhere to lead,
|
|
27
|
+
* why it was removed.
|
|
28
|
+
*/
|
|
29
|
+
export type Forwarding = {
|
|
30
|
+
to: string;
|
|
31
|
+
dimensions: Record<string, string[]>;
|
|
32
|
+
} | {
|
|
33
|
+
reason: string;
|
|
34
|
+
};
|
|
35
|
+
/** The list's text in a build's site output, or `undefined` when there isn't one. */
|
|
36
|
+
export declare function permalinksText(siteRoot: string): string | undefined;
|
|
37
|
+
/** The list in a build's site output, or `undefined` when there isn't one. */
|
|
38
|
+
export declare function readPermalinks(siteRoot: string): PermalinkList | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* The route pattern for the addresses, as Astro takes it: under `base`, which
|
|
41
|
+
* Astro adds. Throws when the list's path isn't under `base`, since an Astro
|
|
42
|
+
* site serves nothing outside it.
|
|
43
|
+
*/
|
|
44
|
+
export declare function permalinkPattern(listPath: string, base: string): string;
|
|
45
|
+
/**
|
|
46
|
+
* `getStaticPaths` for the route: one page for each permalink in the site
|
|
47
|
+
* output's list, and one for each retired permalink it lists as removed.
|
|
48
|
+
*/
|
|
49
|
+
export declare function forwardingPaths(siteRoot: string): {
|
|
50
|
+
params: {
|
|
51
|
+
id: string;
|
|
52
|
+
};
|
|
53
|
+
props: Forwarding;
|
|
54
|
+
}[];
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Permalinks' addresses: a page at `<path><id>` for each permalink the build
|
|
2
|
+
// publishes, which sends the reader on to its heading, and for each retired
|
|
3
|
+
// one with nowhere to lead, which says why it was removed. Ascribe writes the list,
|
|
4
|
+
// `_ascribe/permalinks.json` in the site output (output-layout contract §1.2);
|
|
5
|
+
// the integration adds the route (`Permalink.astro`) and builds its pages
|
|
6
|
+
// from it, in `astro build` and `astro dev`.
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { normalizeBase } from "./project.js";
|
|
10
|
+
/** The list's format version this reads. */
|
|
11
|
+
const VERSION = 1;
|
|
12
|
+
/** The list's text in a build's site output, or `undefined` when there isn't one. */
|
|
13
|
+
export function permalinksText(siteRoot) {
|
|
14
|
+
try {
|
|
15
|
+
return readFileSync(listFile(siteRoot), "utf8");
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function listFile(siteRoot) {
|
|
22
|
+
return path.join(siteRoot, "_ascribe", "permalinks.json");
|
|
23
|
+
}
|
|
24
|
+
/** The list in a build's site output, or `undefined` when there isn't one. */
|
|
25
|
+
export function readPermalinks(siteRoot) {
|
|
26
|
+
const file = listFile(siteRoot);
|
|
27
|
+
const text = permalinksText(siteRoot);
|
|
28
|
+
if (text === undefined)
|
|
29
|
+
return undefined;
|
|
30
|
+
const list = JSON.parse(text);
|
|
31
|
+
if (list.version !== VERSION) {
|
|
32
|
+
throw new Error(`@ascribed/astro: ${file} is version ${String(list.version)} of the permalinks list, and this version of the integration reads version ${VERSION}. Update @ascribed/astro and @ascribed/cli together.`);
|
|
33
|
+
}
|
|
34
|
+
return list;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The route pattern for the addresses, as Astro takes it: under `base`, which
|
|
38
|
+
* Astro adds. Throws when the list's path isn't under `base`, since an Astro
|
|
39
|
+
* site serves nothing outside it.
|
|
40
|
+
*/
|
|
41
|
+
export function permalinkPattern(listPath, base) {
|
|
42
|
+
const root = normalizeBase(base);
|
|
43
|
+
const at = normalizeBase(listPath);
|
|
44
|
+
if (!at.startsWith(root)) {
|
|
45
|
+
throw new Error(`@ascribed/astro: permalinks are published at "${at}", which isn't under Astro's \`base\`, "${base}", and an Astro site serves nothing outside it. Set [consumer] permalink-path in ascribe.toml to a path under "${root}".`);
|
|
46
|
+
}
|
|
47
|
+
return `/${at.slice(root.length)}[id]`;
|
|
48
|
+
}
|
|
49
|
+
/** Where a permalink leads: its page's route, and the heading's anchor. */
|
|
50
|
+
function destination(entry) {
|
|
51
|
+
return entry.fragment === undefined ? entry.route : `${entry.route}#${entry.fragment}`;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* `getStaticPaths` for the route: one page for each permalink in the site
|
|
55
|
+
* output's list, and one for each retired permalink it lists as removed.
|
|
56
|
+
*/
|
|
57
|
+
export function forwardingPaths(siteRoot) {
|
|
58
|
+
const list = readPermalinks(siteRoot);
|
|
59
|
+
if (list === undefined)
|
|
60
|
+
return [];
|
|
61
|
+
const dimensions = list.dimensions ?? {};
|
|
62
|
+
const forwarding = Object.entries(list.permalinks).map(([id, entry]) => ({
|
|
63
|
+
params: { id },
|
|
64
|
+
props: { to: destination(entry), dimensions },
|
|
65
|
+
}));
|
|
66
|
+
const removed = Object.entries(list.removed ?? {}).map(([id, entry]) => ({
|
|
67
|
+
params: { id },
|
|
68
|
+
props: { reason: entry.reason },
|
|
69
|
+
}));
|
|
70
|
+
return [...forwarding, ...removed];
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=permalinks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permalinks.js","sourceRoot":"","sources":["../src/permalinks.ts"],"names":[],"mappings":"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","sourcesContent":["// Permalinks' addresses: a page at `<path><id>` for each permalink the build\n// publishes, which sends the reader on to its heading, and for each retired\n// one with nowhere to lead, which says why it was removed. Ascribe writes the list,\n// `_ascribe/permalinks.json` in the site output (output-layout contract §1.2);\n// the integration adds the route (`Permalink.astro`) and builds its pages\n// from it, in `astro build` and `astro dev`.\nimport { readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { normalizeBase } from \"./project.js\";\n\n/** The list's format version this reads. */\nconst VERSION = 1;\n\n/** `_ascribe/permalinks.json`. */\nexport interface PermalinkList {\n version: number;\n /** Where the addresses are published, from the site's root, ending in `/`. */\n path: string;\n /** The content model's dimensions, each with its values: the parameters an address takes. */\n dimensions?: Record<string, string[]>;\n /**\n * Each permalink's destination. An alias names the permalink it leads to,\n * and a retired permalink that leads somewhere its reason.\n */\n permalinks: Record<\n string,\n { route: string; fragment?: string; source: string; alias?: string; retired?: string }\n >;\n /** The retired permalinks with nowhere to lead in this build, each with why. */\n removed?: Record<string, { reason: string }>;\n}\n\n/**\n * What an address's page needs: where it sends the reader, and the\n * parameters it takes; or, for a retired permalink with nowhere to lead,\n * why it was removed.\n */\nexport type Forwarding = { to: string; dimensions: Record<string, string[]> } | { reason: string };\n\n/** The list's text in a build's site output, or `undefined` when there isn't one. */\nexport function permalinksText(siteRoot: string): string | undefined {\n try {\n return readFileSync(listFile(siteRoot), \"utf8\");\n } catch {\n return undefined;\n }\n}\n\nfunction listFile(siteRoot: string): string {\n return path.join(siteRoot, \"_ascribe\", \"permalinks.json\");\n}\n\n/** The list in a build's site output, or `undefined` when there isn't one. */\nexport function readPermalinks(siteRoot: string): PermalinkList | undefined {\n const file = listFile(siteRoot);\n const text = permalinksText(siteRoot);\n if (text === undefined) return undefined;\n const list = JSON.parse(text) as PermalinkList;\n if (list.version !== VERSION) {\n throw new Error(\n `@ascribed/astro: ${file} is version ${String(list.version)} of the permalinks list, and this version of the integration reads version ${VERSION}. Update @ascribed/astro and @ascribed/cli together.`,\n );\n }\n return list;\n}\n\n/**\n * The route pattern for the addresses, as Astro takes it: under `base`, which\n * Astro adds. Throws when the list's path isn't under `base`, since an Astro\n * site serves nothing outside it.\n */\nexport function permalinkPattern(listPath: string, base: string): string {\n const root = normalizeBase(base);\n const at = normalizeBase(listPath);\n if (!at.startsWith(root)) {\n throw new Error(\n `@ascribed/astro: permalinks are published at \"${at}\", which isn't under Astro's \\`base\\`, \"${base}\", and an Astro site serves nothing outside it. Set [consumer] permalink-path in ascribe.toml to a path under \"${root}\".`,\n );\n }\n return `/${at.slice(root.length)}[id]`;\n}\n\n/** Where a permalink leads: its page's route, and the heading's anchor. */\nfunction destination(entry: { route: string; fragment?: string }): string {\n return entry.fragment === undefined ? entry.route : `${entry.route}#${entry.fragment}`;\n}\n\n/**\n * `getStaticPaths` for the route: one page for each permalink in the site\n * output's list, and one for each retired permalink it lists as removed.\n */\nexport function forwardingPaths(siteRoot: string): { params: { id: string }; props: Forwarding }[] {\n const list = readPermalinks(siteRoot);\n if (list === undefined) return [];\n const dimensions = list.dimensions ?? {};\n const forwarding = Object.entries(list.permalinks).map(([id, entry]) => ({\n params: { id },\n props: { to: destination(entry), dimensions } as Forwarding,\n }));\n const removed = Object.entries(list.removed ?? {}).map(([id, entry]) => ({\n params: { id },\n props: { reason: entry.reason } as Forwarding,\n }));\n return [...forwarding, ...removed];\n}\n"]}
|
package/dist/project.d.ts
CHANGED
|
@@ -25,6 +25,12 @@ export interface ProjectInfo {
|
|
|
25
25
|
export declare function readProject(dir: string): ProjectInfo;
|
|
26
26
|
/** A base path with a leading and a trailing `/`, as Ascribe's router has it. */
|
|
27
27
|
export declare function normalizeBase(base: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* The route of the site's root, Astro's `base`, ending the way `trailingSlash`
|
|
30
|
+
* says the site's routes do. (`import.meta.env.BASE_URL` isn't replaced in a
|
|
31
|
+
* package's components.)
|
|
32
|
+
*/
|
|
33
|
+
export declare function homeRoute(base: string, trailingSlash: "always" | "never" | "ignore"): string;
|
|
28
34
|
/**
|
|
29
35
|
* The ways `ascribe.toml`'s `[consumer]` disagrees with Astro's configuration,
|
|
30
36
|
* as sentences. Ascribe writes every link with its own settings, so a site
|
package/dist/project.js
CHANGED
|
@@ -50,6 +50,15 @@ export function normalizeBase(base) {
|
|
|
50
50
|
result += "/";
|
|
51
51
|
return result;
|
|
52
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* The route of the site's root, Astro's `base`, ending the way `trailingSlash`
|
|
55
|
+
* says the site's routes do. (`import.meta.env.BASE_URL` isn't replaced in a
|
|
56
|
+
* package's components.)
|
|
57
|
+
*/
|
|
58
|
+
export function homeRoute(base, trailingSlash) {
|
|
59
|
+
const root = normalizeBase(base);
|
|
60
|
+
return trailingSlash === "never" && root !== "/" ? root.slice(0, -1) : root;
|
|
61
|
+
}
|
|
53
62
|
/**
|
|
54
63
|
* The ways `ascribe.toml`'s `[consumer]` disagrees with Astro's configuration,
|
|
55
64
|
* as sentences. Ascribe writes every link with its own settings, so a site
|
package/dist/project.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,uEAAuE;AACvE,oDAAoD;AACpD,yBAAyB;AACzB,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AA0BlC,kFAAkF;AAClF,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;IAClD,IAAI,KAA8B,CAAC;IACnC,IAAI,CAAC;QACH,KAAK,GAAG,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;IAClD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACb,8BAA8B,UAAU,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EACrG,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IAC1C,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IAC5C,MAAM,SAAS,GACb,OAAO,OAAO,CAAC,YAAY,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC;IACvF,MAAM,UAAU,GAAG,OAAO,OAAO,CAAC,cAAc,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAClG,MAAM,aAAa,GAAG,QAAQ,CAAC,gBAAgB,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC;IAClF,OAAO;QACL,GAAG;QACH,UAAU;QACV,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,UAAU,CAAC;QAC1C,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,SAAS,CAAC;QACxC,QAAQ,EAAE;YACR,IAAI,EAAE,OAAO,QAAQ,CAAC,MAAM,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS;YACzE,QAAQ,EAAE,aAAa,CACrB,OAAO,QAAQ,CAAC,WAAW,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,CACxE;YACD,aAAa;YACb,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC,KAAK,IAAI;SACpC;QACD,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,CAAC;QAChE,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC;KACnE,CAAC;AACJ,CAAC;AAED,SAAS,OAAO,CAAC,KAA8B,EAAE,IAAY;IAC3D,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACzE,CAAC,CAAE,KAAiC;QACpC,CAAC,CAAC,EAAE,CAAC;AACT,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,IAAI,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IACzB,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;IACnD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,GAAG,CAAC;IACzC,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAAoB,EACpB,KAAwE;IAExE,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC;IAC3D,IAAI,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC3C,QAAQ,CAAC,IAAI,CACX,4BAA4B,QAAQ,+BAA+B,KAAK,CAAC,IAAI,+BAA+B,CAC7G,CAAC;IACJ,CAAC;IACD,0EAA0E;IAC1E,IAAI,KAAK,CAAC,aAAa,KAAK,QAAQ,IAAI,KAAK,CAAC,aAAa,KAAK,aAAa,EAAE,CAAC;QAC9E,QAAQ,CAAC,IAAI,CACX,iCAAiC,aAAa,wCAAwC,KAAK,CAAC,aAAa,IAAI,CAC9G,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1F,QAAQ,CAAC,IAAI,CAAC,uBAAuB,IAAI,+BAA+B,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC;IAC1F,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,MAAM,CAAC,GAAW;IACzB,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC;IACb,CAAC;AACH,CAAC","sourcesContent":["// What the integration needs from `ascribe.toml`: where a build's site output\n// goes, and the consumer settings that must agree with `astro.config`.\n// The compiler validates the whole file; this reads\n// only what it compiled.\nimport { readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { parse } from \"smol-toml\";\n\n/** The parts of an Ascribe project the integration reads. */\nexport interface ProjectInfo {\n /** The directory holding `ascribe.toml`. */\n dir: string;\n /** The path of `ascribe.toml`. */\n configPath: string;\n /** Source files and assets watched during development. */\n contentRoot: string;\n /** Generated output, never treated as a source change. */\n outputRoot: string;\n /** `[consumer]`, with the profile's defaults. */\n consumer: {\n site: string | undefined;\n basePath: string;\n trailingSlash: \"always\" | \"never\";\n /** Whether builds publish what agents read: `llms.txt` and each page's Markdown, in the plain output. */\n agents: boolean;\n };\n /** The site output's root for a build: `<output-dir>/<build>/site`. */\n siteRoot(build: string): string;\n /** The plain output's root for a build: `<output-dir>/<build>/plain`. */\n plainRoot(build: string): string;\n}\n\n/** Reads `ascribe.toml` in `dir`. Throws a readable error if it can't be read. */\nexport function readProject(dir: string): ProjectInfo {\n const configPath = path.join(dir, \"ascribe.toml\");\n let table: Record<string, unknown>;\n try {\n table = parse(readFileSync(configPath, \"utf8\"));\n } catch (error) {\n throw new Error(\n `@ascribed/astro can't read ${configPath}: ${error instanceof Error ? error.message : String(error)}`,\n { cause: error },\n );\n }\n const project = section(table, \"project\");\n const consumer = section(table, \"consumer\");\n const outputDir =\n typeof project[\"output-dir\"] === \"string\" ? project[\"output-dir\"] : \".ascribe/build\";\n const contentDir = typeof project[\"content-root\"] === \"string\" ? project[\"content-root\"] : \"docs\";\n const trailingSlash = consumer[\"trailing-slash\"] === \"never\" ? \"never\" : \"always\";\n return {\n dir,\n configPath,\n contentRoot: path.resolve(dir, contentDir),\n outputRoot: path.resolve(dir, outputDir),\n consumer: {\n site: typeof consumer[\"site\"] === \"string\" ? consumer[\"site\"] : undefined,\n basePath: normalizeBase(\n typeof consumer[\"base-path\"] === \"string\" ? consumer[\"base-path\"] : \"/\",\n ),\n trailingSlash,\n agents: consumer[\"agents\"] === true,\n },\n siteRoot: (build) => path.resolve(dir, outputDir, build, \"site\"),\n plainRoot: (build) => path.resolve(dir, outputDir, build, \"plain\"),\n };\n}\n\nfunction section(table: Record<string, unknown>, name: string): Record<string, unknown> {\n const value = table[name];\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {};\n}\n\n/** A base path with a leading and a trailing `/`, as Ascribe's router has it. */\nexport function normalizeBase(base: string): string {\n let result = base.trim();\n if (!result.startsWith(\"/\")) result = `/${result}`;\n if (!result.endsWith(\"/\")) result += \"/\";\n return result;\n}\n\n/**\n * The ways `ascribe.toml`'s `[consumer]` disagrees with Astro's configuration,\n * as sentences. Ascribe writes every link with its own settings, so a site\n * whose routes differ would have broken links.\n */\nexport function consumerMismatches(\n project: ProjectInfo,\n astro: { base: string; trailingSlash: string; site: string | undefined },\n): string[] {\n const problems: string[] = [];\n const { basePath, trailingSlash, site } = project.consumer;\n if (normalizeBase(astro.base) !== basePath) {\n problems.push(\n `[consumer] base-path is \"${basePath}\", but Astro's \\`base\\` is \"${astro.base}\". Set them to the same path.`,\n );\n }\n // Astro's \"ignore\" serves both forms, so Ascribe's links work either way.\n if (astro.trailingSlash !== \"ignore\" && astro.trailingSlash !== trailingSlash) {\n problems.push(\n `[consumer] trailing-slash is \"${trailingSlash}\", but Astro's \\`trailingSlash\\` is \"${astro.trailingSlash}\".`,\n );\n }\n if (site !== undefined && astro.site !== undefined && origin(site) !== origin(astro.site)) {\n problems.push(`[consumer] site is \"${site}\", but Astro's \\`site\\` is \"${astro.site}\".`);\n }\n return problems;\n}\n\nfunction origin(url: string): string {\n try {\n return new URL(url).origin;\n } catch {\n return url;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"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","sourcesContent":["// What the integration needs from `ascribe.toml`: where a build's site output\n// goes, and the consumer settings that must agree with `astro.config`.\n// The compiler validates the whole file; this reads\n// only what it compiled.\nimport { readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { parse } from \"smol-toml\";\n\n/** The parts of an Ascribe project the integration reads. */\nexport interface ProjectInfo {\n /** The directory holding `ascribe.toml`. */\n dir: string;\n /** The path of `ascribe.toml`. */\n configPath: string;\n /** Source files and assets watched during development. */\n contentRoot: string;\n /** Generated output, never treated as a source change. */\n outputRoot: string;\n /** `[consumer]`, with the profile's defaults. */\n consumer: {\n site: string | undefined;\n basePath: string;\n trailingSlash: \"always\" | \"never\";\n /** Whether builds publish what agents read: `llms.txt` and each page's Markdown, in the plain output. */\n agents: boolean;\n };\n /** The site output's root for a build: `<output-dir>/<build>/site`. */\n siteRoot(build: string): string;\n /** The plain output's root for a build: `<output-dir>/<build>/plain`. */\n plainRoot(build: string): string;\n}\n\n/** Reads `ascribe.toml` in `dir`. Throws a readable error if it can't be read. */\nexport function readProject(dir: string): ProjectInfo {\n const configPath = path.join(dir, \"ascribe.toml\");\n let table: Record<string, unknown>;\n try {\n table = parse(readFileSync(configPath, \"utf8\"));\n } catch (error) {\n throw new Error(\n `@ascribed/astro can't read ${configPath}: ${error instanceof Error ? error.message : String(error)}`,\n { cause: error },\n );\n }\n const project = section(table, \"project\");\n const consumer = section(table, \"consumer\");\n const outputDir =\n typeof project[\"output-dir\"] === \"string\" ? project[\"output-dir\"] : \".ascribe/build\";\n const contentDir = typeof project[\"content-root\"] === \"string\" ? project[\"content-root\"] : \"docs\";\n const trailingSlash = consumer[\"trailing-slash\"] === \"never\" ? \"never\" : \"always\";\n return {\n dir,\n configPath,\n contentRoot: path.resolve(dir, contentDir),\n outputRoot: path.resolve(dir, outputDir),\n consumer: {\n site: typeof consumer[\"site\"] === \"string\" ? consumer[\"site\"] : undefined,\n basePath: normalizeBase(\n typeof consumer[\"base-path\"] === \"string\" ? consumer[\"base-path\"] : \"/\",\n ),\n trailingSlash,\n agents: consumer[\"agents\"] === true,\n },\n siteRoot: (build) => path.resolve(dir, outputDir, build, \"site\"),\n plainRoot: (build) => path.resolve(dir, outputDir, build, \"plain\"),\n };\n}\n\nfunction section(table: Record<string, unknown>, name: string): Record<string, unknown> {\n const value = table[name];\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {};\n}\n\n/** A base path with a leading and a trailing `/`, as Ascribe's router has it. */\nexport function normalizeBase(base: string): string {\n let result = base.trim();\n if (!result.startsWith(\"/\")) result = `/${result}`;\n if (!result.endsWith(\"/\")) result += \"/\";\n return result;\n}\n\n/**\n * The route of the site's root, Astro's `base`, ending the way `trailingSlash`\n * says the site's routes do. (`import.meta.env.BASE_URL` isn't replaced in a\n * package's components.)\n */\nexport function homeRoute(base: string, trailingSlash: \"always\" | \"never\" | \"ignore\"): string {\n const root = normalizeBase(base);\n return trailingSlash === \"never\" && root !== \"/\" ? root.slice(0, -1) : root;\n}\n\n/**\n * The ways `ascribe.toml`'s `[consumer]` disagrees with Astro's configuration,\n * as sentences. Ascribe writes every link with its own settings, so a site\n * whose routes differ would have broken links.\n */\nexport function consumerMismatches(\n project: ProjectInfo,\n astro: { base: string; trailingSlash: string; site: string | undefined },\n): string[] {\n const problems: string[] = [];\n const { basePath, trailingSlash, site } = project.consumer;\n if (normalizeBase(astro.base) !== basePath) {\n problems.push(\n `[consumer] base-path is \"${basePath}\", but Astro's \\`base\\` is \"${astro.base}\". Set them to the same path.`,\n );\n }\n // Astro's \"ignore\" serves both forms, so Ascribe's links work either way.\n if (astro.trailingSlash !== \"ignore\" && astro.trailingSlash !== trailingSlash) {\n problems.push(\n `[consumer] trailing-slash is \"${trailingSlash}\", but Astro's \\`trailingSlash\\` is \"${astro.trailingSlash}\".`,\n );\n }\n if (site !== undefined && astro.site !== undefined && origin(site) !== origin(astro.site)) {\n problems.push(`[consumer] site is \"${site}\", but Astro's \\`site\\` is \"${astro.site}\".`);\n }\n return problems;\n}\n\nfunction origin(url: string): string {\n try {\n return new URL(url).origin;\n } catch {\n return url;\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.13",
|
|
4
4
|
"description": "The Astro integration for Ascribe.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MPL-2.0",
|
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
"files": [
|
|
16
16
|
"dist",
|
|
17
17
|
"src/Elements.astro",
|
|
18
|
-
"src/Availability.astro"
|
|
18
|
+
"src/Availability.astro",
|
|
19
|
+
"src/Permalink.astro",
|
|
20
|
+
"src/MissingPermalink.astro"
|
|
19
21
|
],
|
|
20
22
|
"exports": {
|
|
21
23
|
".": {
|
|
@@ -32,18 +34,23 @@
|
|
|
32
34
|
},
|
|
33
35
|
"./Elements.astro": "./src/Elements.astro",
|
|
34
36
|
"./Availability.astro": "./src/Availability.astro",
|
|
37
|
+
"./MissingPermalink.astro": "./src/MissingPermalink.astro",
|
|
35
38
|
"./content": {
|
|
36
39
|
"types": "./dist/content.d.ts",
|
|
37
40
|
"default": "./dist/content.js"
|
|
41
|
+
},
|
|
42
|
+
"./permalinks": {
|
|
43
|
+
"types": "./dist/permalinks.d.ts",
|
|
44
|
+
"default": "./dist/permalinks.js"
|
|
38
45
|
}
|
|
39
46
|
},
|
|
40
47
|
"engines": {
|
|
41
48
|
"node": ">=24"
|
|
42
49
|
},
|
|
43
50
|
"dependencies": {
|
|
44
|
-
"@ascribed/cli": "0.2.1-next.
|
|
45
|
-
"@ascribed/elements": "0.2.1-next.
|
|
46
|
-
"@ascribed/review": "0.2.1-next.
|
|
51
|
+
"@ascribed/cli": "0.2.1-next.13",
|
|
52
|
+
"@ascribed/elements": "0.2.1-next.13",
|
|
53
|
+
"@ascribed/review": "0.2.1-next.13",
|
|
47
54
|
"property-information": "^7.2.0",
|
|
48
55
|
"smol-toml": "^1.9.0"
|
|
49
56
|
},
|
|
@@ -78,7 +85,7 @@
|
|
|
78
85
|
"publishConfig": {
|
|
79
86
|
"access": "public"
|
|
80
87
|
},
|
|
81
|
-
"gitHead": "
|
|
88
|
+
"gitHead": "42f00096ceb0f91a736b6a3d485eb38a91fbcc48",
|
|
82
89
|
"scripts": {
|
|
83
90
|
"build": "tsc -p tsconfig.build.json",
|
|
84
91
|
"typecheck": "tsc --noEmit",
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
// For a site's not-found page: on an address under the permalinks' path, it
|
|
3
|
+
// says the link came from a product that expected a section of these docs
|
|
4
|
+
// there, and that these docs don't have it. Anywhere else it shows nothing. A
|
|
5
|
+
// static host serves one not-found page for every missing address, so its
|
|
6
|
+
// script decides from the address, and without a script it stays hidden.
|
|
7
|
+
//
|
|
8
|
+
// import MissingPermalink from "@ascribed/astro/MissingPermalink.astro";
|
|
9
|
+
// <MissingPermalink />
|
|
10
|
+
//
|
|
11
|
+
// `home` is where it sends the reader, the site's root by default. Children
|
|
12
|
+
// replace its last paragraph, which offers the home page and searching.
|
|
13
|
+
import { home as root, siteRoot } from "virtual:ascribe/site";
|
|
14
|
+
import { readPermalinks } from "@ascribed/astro/permalinks";
|
|
15
|
+
|
|
16
|
+
interface Props {
|
|
17
|
+
home?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const { home = root } = Astro.props;
|
|
21
|
+
const path = readPermalinks(siteRoot)?.path;
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
{
|
|
25
|
+
path !== undefined && (
|
|
26
|
+
<section data-permalinks={path} hidden>
|
|
27
|
+
<p>
|
|
28
|
+
This address is a permalink, <code />: a name a product links to for a section of its
|
|
29
|
+
documentation. These docs have no section by that name. The link may come from a newer or
|
|
30
|
+
older version of the product than they describe.
|
|
31
|
+
</p>
|
|
32
|
+
<slot>
|
|
33
|
+
<p>Start from the <a href={home}>home page</a>, or search the docs for what you need.</p>
|
|
34
|
+
</slot>
|
|
35
|
+
</section>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
<script>
|
|
40
|
+
for (const section of document.querySelectorAll<HTMLElement>("section[data-permalinks]")) {
|
|
41
|
+
const path = section.dataset["permalinks"] ?? "";
|
|
42
|
+
let address: string;
|
|
43
|
+
try {
|
|
44
|
+
address = decodeURIComponent(location.pathname);
|
|
45
|
+
} catch {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (!address.startsWith(path)) continue;
|
|
49
|
+
// One segment after the path, as every permalink's address has.
|
|
50
|
+
const name = address.slice(path.length).replace(/\/$/, "");
|
|
51
|
+
if (name === "" || name.includes("/")) continue;
|
|
52
|
+
const code = section.querySelector("code");
|
|
53
|
+
if (code !== null) code.textContent = name;
|
|
54
|
+
section.hidden = false;
|
|
55
|
+
}
|
|
56
|
+
</script>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
---
|
|
2
|
+
// A permalink's address: a page that sends the reader on to the heading it
|
|
3
|
+
// names, wherever that is now. The integration adds it as a route at
|
|
4
|
+
// `<path>[id]`, one page for each entry of the site output's
|
|
5
|
+
// `_ascribe/permalinks.json`. It forwards without a script, links to the same
|
|
6
|
+
// place for a browser that doesn't follow, and isn't indexed. Its script
|
|
7
|
+
// first takes the reader's choices from the address's parameters
|
|
8
|
+
// (`?platform=macos`), as if they'd picked those tabs, then forwards.
|
|
9
|
+
//
|
|
10
|
+
// A retired permalink with nowhere to lead gets a page that says what was
|
|
11
|
+
// here was removed, gives the reason as its authors wrote it, as text, and
|
|
12
|
+
// links to the docs' home.
|
|
13
|
+
import { home, siteRoot } from "virtual:ascribe/site";
|
|
14
|
+
import { forwardingPaths, type Forwarding } from "@ascribed/astro/permalinks";
|
|
15
|
+
|
|
16
|
+
export function getStaticPaths() {
|
|
17
|
+
return forwardingPaths(siteRoot);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const props = Astro.props as Forwarding;
|
|
21
|
+
const to = "to" in props ? props.to : undefined;
|
|
22
|
+
const dimensions = "to" in props ? props.dimensions : {};
|
|
23
|
+
const reason = "reason" in props ? props.reason : undefined;
|
|
24
|
+
const canonical =
|
|
25
|
+
to === undefined || Astro.site === undefined ? to : new URL(to, Astro.site).href;
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
<!doctype html>
|
|
29
|
+
<html lang="en">
|
|
30
|
+
<head>
|
|
31
|
+
<meta charset="utf-8" />
|
|
32
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
33
|
+
{to === undefined ? <title>Removed from these docs</title> : <title>Redirecting to {to}</title>}
|
|
34
|
+
<meta name="robots" content="noindex" />
|
|
35
|
+
{to !== undefined && <meta http-equiv="refresh" content={`0;url=${to}`} />}
|
|
36
|
+
{canonical !== undefined && <link rel="canonical" href={canonical} />}
|
|
37
|
+
</head>
|
|
38
|
+
<body>
|
|
39
|
+
{
|
|
40
|
+
to === undefined ? (
|
|
41
|
+
<main>
|
|
42
|
+
<h1>Removed from these docs</h1>
|
|
43
|
+
<p>What this link led to was removed from these docs.</p>
|
|
44
|
+
<p data-reason>{reason}</p>
|
|
45
|
+
<p>
|
|
46
|
+
<a href={home}>Go to the docs' home page</a>
|
|
47
|
+
</p>
|
|
48
|
+
</main>
|
|
49
|
+
) : (
|
|
50
|
+
<p>
|
|
51
|
+
This link leads to <a href={to} data-dimensions={JSON.stringify(dimensions)}>{to}</a>.
|
|
52
|
+
</p>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
<script>
|
|
56
|
+
import { choose } from "@ascribed/elements";
|
|
57
|
+
|
|
58
|
+
// A parameter that names one of the content model's dimensions, with one of
|
|
59
|
+
// its values, is the reader's choice for it. Anything else is ignored, so a
|
|
60
|
+
// product sends what it knows without asking what the docs accept.
|
|
61
|
+
const link = document.querySelector<HTMLAnchorElement>("a[data-dimensions]");
|
|
62
|
+
if (link !== null) {
|
|
63
|
+
const dimensions = JSON.parse(link.dataset["dimensions"] ?? "{}") as Record<string, string[]>;
|
|
64
|
+
const chosen = new Set<string>();
|
|
65
|
+
for (const [name, value] of new URLSearchParams(location.search)) {
|
|
66
|
+
if (chosen.has(name) || !Object.hasOwn(dimensions, name)) continue;
|
|
67
|
+
if (dimensions[name]?.includes(value)) {
|
|
68
|
+
chosen.add(name);
|
|
69
|
+
choose(name, value);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Sooner than the refresh, which waits for the page to load.
|
|
73
|
+
location.replace(link.href);
|
|
74
|
+
}
|
|
75
|
+
</script>
|
|
76
|
+
</body>
|
|
77
|
+
</html>
|