@ascribed/astro 0.3.3-next.24 → 0.3.3-next.25

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 CHANGED
@@ -67,11 +67,11 @@ const { entry } = Astro.props;
67
67
 
68
68
  Each build writes a record of every page, `_ascribe/pages.json`, and the integration writes from every project's records what search engines read: `Head.astro` gives a page its `<title>`, description, canonical link, `noindex` when it's `unlisted`, and `nosnippet` when it's `unquoted`, a link to its Markdown version, the Open Graph tags a link preview reads, with the site's name and a social card given as properties, and its breadcrumbs as structured data, and `astro dev` warns about a tag a layout writes again; and the site gets one `sitemap.xml`, at Astro's `base`, and one `robots.txt`, at the host's root, with each project's `[crawlers] lines` and a group for each kind of crawler it disallows, such as `training = "disallow"`, in `astro build` and `astro dev`; projects that disagree on a kind fail the build. A page with no record, such as an Astro page of the site's own, gets nothing from `Head.astro` and is in the sitemap as a page Astro built. A site with its own `public/robots.txt` or `public/sitemap.xml` keeps it, and a site whose `base` isn't `/` gets no `robots.txt`: the build prints the lines its host's needs. With no `site`, there's no sitemap.
69
69
 
70
- A versioned build (`versions` in `ascribe.toml`) is one collection with every live line's pages: the current line's at the base path, and an older line's under its number, `/docs/3.4/`, with the line in front of their entry ids. Each entry's data has `line` and `current`. `Versions.astro`, given a page's entry, lists the lines, each that publishes the page a link, with the page's own marked; `VersionBanner.astro` says on an older line's page which version it is and links to the current line's; `sidebar(nav, entries, { entry })` shows the pages of the entry's line. `astro dev` rebuilds every line. A permalink forwards to the current line, and each line's `llms.txt` and Markdown are published at its base path.
70
+ A versioned build (`versions` in `ascribe.toml`) is one collection with every live line's pages: the current line's at the base path, and an older line's under its number, `/docs/3.4/`, with the line in front of their entry ids. Each entry's data has `line` and `current`. `Versions.astro`, given a page's entry, lists the lines, each that publishes the page a link, with the page's own marked; `VersionBanner.astro` says on an older line's page which version it is and links to the current line's; `sidebar(entries, { entry })` shows the pages of the entry's line. `astro dev` rebuilds every line. A permalink forwards to the current line, and each line's `llms.txt` and Markdown are published at its base path.
71
71
 
72
72
  A page's Markdown version has no head, so its rules are response headers, which each build lists in `_ascribe/headers.json`: `X-Robots-Tag` when the page isn't listed, and a canonical `Link` to the page. `astro dev` sends them. With `[consumer] headers = "netlify"`, `astro build` writes them into the site's `_headers`; a site with its own `public/_headers` keeps it, and the build fails with the lines it lacks.
73
73
 
74
- `npx @ascribed/astro init` writes those files into an Astro project once, as yours from then on: the collection (`src/content.config.ts`), the route (`src/pages/[...slug].astro`), a layout that loads the elements (`src/layouts/Docs.astro`), and the sidebar's list (`src/nav.ts`). It never writes over a file. `--project <dir>` names the folder holding `ascribe.toml`, and `--build <name>` the build, when there are several. The layout's sidebar comes from `sidebar(nav, entries)` in `@ascribed/astro/content`, which gives each listed page its entry and address, and fails the build when the list leaves out a page or names one that isn't there.
74
+ `npx @ascribed/astro init` writes those files into an Astro project once, as yours from then on: the collection (`src/content.config.ts`), the route (`src/pages/[...slug].astro`), and a layout that loads the elements and draws the sidebar (`src/layouts/Docs.astro`). It never writes over a file. `--project <dir>` names the folder holding `ascribe.toml`, and `--build <name>` the build, when there are several. The layout's sidebar comes from `sidebar(entries, { entry })` in `@ascribed/astro/content`: the project's navigation, in `ascribe.nav`'s order or the default one, from the build's `_ascribe/nav.json`, with no list to keep. `neighbors(entry, entries)` gives a page's previous and next in the same order, for links at its foot. A site's own list, `sidebar(entries, { nav })`, overrides the navigation: its groups, in its order, and the build fails when the list leaves out a page or names one that isn't there; `neighbors` takes it too.
75
75
 
76
76
  The [Astro guide](https://ascribed-dev.com/go/astro-guide/) walks through a site: `ascribe.toml`, the collection, the route, the layout, [a home page in front of the docs](https://ascribed-dev.com/go/a-home-page-in-front-of-the-docs/), and [several projects in one site](https://ascribed-dev.com/go/several-projects-in-one-site/). The [`@ascribed/astro` reference](https://ascribed-dev.com/go/astro-reference/) has the options, what the integration does in `astro build` and `astro dev`, search engines and crawlers, and [review in the site preview](https://ascribed-dev.com/go/review-in-the-site-preview/). [Review](https://ascribed-dev.com/go/review-guide/) walks through reviewing a pull request on your site.
77
77
 
@@ -93,8 +93,8 @@ The [Astro guide](https://ascribed-dev.com/go/astro-guide/) walks through a site
93
93
  | File | Role |
94
94
  |---|---|
95
95
  | `src/index.ts` | The integration |
96
- | `src/content.ts` | `ascribeCollection`, for `content.config.ts`, and `sidebar`, for a layout |
97
- | `src/nav.ts` | The sidebar: entry ids, routes, and the check of the site's list against the collection |
96
+ | `src/content.ts` | `ascribeCollection`, for `content.config.ts`, and `sidebar` and `neighbors`, for a layout |
97
+ | `src/nav.ts` | The sidebar and the reading order: entry ids, routes, and the check of the site's list against the collection |
98
98
  | `src/init.ts`, `src/cli.ts`, `starter/` | `npx @ascribed/astro init`, the package's command (`ascribe-astro`), and the files it writes. `examples/astro-site`'s are what it writes for that project; `test/init.test.ts` holds that, and rewrites them with `ASCRIBE_BLESS=1` |
99
99
  | `src/Elements.astro` | Loads `@ascribed/elements` (stylesheet and script) |
100
100
  | `src/Availability.astro` | A page's availability as the element library's badge |
package/dist/content.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { glob } from "astro/loaders";
2
2
  import { type PageRecord } from "./records.js";
3
3
  import { type LineNotice, type ListedLine, type PageLine } from "./versions.js";
4
- import { type NavEntry, type NavGroup, type SidebarGroup } from "./nav.js";
5
- export type { NavGroup, SidebarGroup, SidebarPage } from "./nav.js";
4
+ import { type NavEntry, type NavGroup, type Neighbors, type SidebarGroup } from "./nav.js";
5
+ export type { Formatted, NavGroup, NavPage, NavSection, Navigation, Neighbors, SidebarGroup, SidebarPage, } from "./nav.js";
6
6
  export type { Listing, ListingFrom, PageRecord, Step } from "./records.js";
7
7
  export type { LineNotice } from "./versions.js";
8
8
  export { headTags, scriptJson, type Card, type HeadOptions, type HeadTag } from "./head.js";
@@ -33,25 +33,52 @@ export declare function ascribeCollection<S>(options: {
33
33
  schema: S;
34
34
  };
35
35
  /**
36
- * A layout's sidebar: `nav`'s groups, each page with its collection entry and
37
- * its address, which is the one Ascribe's links use. `entries` is the
38
- * collection (`await getCollection("docs")`), and `name` says which project's,
39
- * as for `ascribeCollection`; with several projects and no `name`, it's the one
40
- * the entries were loaded from. Throws, naming each, when `nav` leaves out a page
41
- * of the collection, names a file that isn't one, or names a page twice, so the
42
- * build fails until the list is complete.
36
+ * A layout's sidebar: the project's navigation, from the build's
37
+ * `_ascribe/nav.json`, each page with its collection entry, its address,
38
+ * which is the one Ascribe's links use, and its label. The root's pages are
39
+ * the first group, "Start", and each top section is a group of its own, with
40
+ * its nested sections in `sections`. `entries` is the collection (`await
41
+ * getCollection("docs")`), and `name` says which project's, as for
42
+ * `ascribeCollection`; with several projects and no `name`, it's the one the
43
+ * entries were loaded from. Throws when the navigation names a page the
44
+ * collection has no entry for.
45
+ *
46
+ * A site's own list, `nav`, overrides the project's: its groups, in its
47
+ * order, checked against the collection. It throws, naming each, when the
48
+ * list leaves out a page of the collection, names a file that isn't one, or
49
+ * names a page twice, so the build fails until the list is complete.
43
50
  *
44
51
  * With a versioned build, `entry`, the page the sidebar is on, says which
45
- * line's pages it shows: each page of the list that line has, so an older
46
- * line's sidebar stays in that line. The list is checked against every
47
- * line's pages. Without `entry`, it shows the current line's.
52
+ * line's pages it shows: that line's navigation, or each page of the list
53
+ * that line has, so an older line's sidebar stays in that line. A list is
54
+ * checked against every line's pages. Without `entry`, it shows the current
55
+ * line's.
48
56
  */
49
- export declare function sidebar<E extends NavEntry>(nav: readonly NavGroup[], entries: readonly E[], options?: {
57
+ export declare function sidebar<E extends NavEntry>(entries: readonly E[], options?: {
50
58
  name?: string;
51
59
  entry?: {
52
60
  data?: unknown;
53
61
  };
62
+ nav?: readonly NavGroup[];
54
63
  }): SidebarGroup<E>[];
64
+ /**
65
+ * A page's previous and next in the project's navigation, in reading order:
66
+ * a section's own page, then its pages, a nested section right after its
67
+ * own page beside the folder, depth first, as `sidebar` lists them. Each is
68
+ * the entry, its address, and its label, which is the page's `nav-label`
69
+ * when it has one; the first page has no `previous`, and the last no
70
+ * `next`. `entry` is the page's collection entry, `entries` the collection,
71
+ * and `name` as for `sidebar`. In a versioned build, they're in the line
72
+ * `entry` is on.
73
+ *
74
+ * With a site's own list, `nav`, they're the pages before and after it in
75
+ * the list's groups, in order. Neither, for a page the navigation leaves
76
+ * out, such as an `unlisted` one.
77
+ */
78
+ export declare function neighbors<E extends NavEntry>(entry: E, entries: readonly E[], options?: {
79
+ name?: string;
80
+ nav?: readonly NavGroup[];
81
+ }): Neighbors<E>;
55
82
  /**
56
83
  * A collection entry's page record, from its build's `_ascribe/pages.json`:
57
84
  * its route, title, description, canonical address, and listing, as every
package/dist/content.js CHANGED
@@ -14,17 +14,20 @@
14
14
  // docs: defineCollection(ascribeCollection({ schema: docs, name: "docs" })),
15
15
  // };
16
16
  //
17
- // A layout's sidebar, from the site's list of pages (`src/nav.ts`, which
18
- // `npx @ascribed/astro init` writes), checked against the collection:
17
+ // A layout's sidebar, from the project's navigation (`ascribe.nav`'s order,
18
+ // or the default), or from a site's own list of pages as `nav`, and a page's
19
+ // previous and next:
19
20
  //
20
- // const groups = sidebar(nav, await getCollection("docs"));
21
+ // const groups = sidebar(await getCollection("docs"), { entry });
22
+ // const { previous, next } = neighbors(entry, await getCollection("docs"));
23
+ import { readFileSync, statSync } from "node:fs";
21
24
  import { pathToFileURL } from "node:url";
22
25
  import path from "node:path";
23
26
  import { glob } from "astro/loaders";
24
27
  import { astroRoot, collections, routes, siteRoot } from "virtual:ascribe/site";
25
28
  import { recordFor } from "./records.js";
26
29
  import { lineNotice, linesOf, listedLines, pageLines, } from "./versions.js";
27
- import { pageRoute, sidebarGroups, } from "./nav.js";
30
+ import { navigationGroups, neighborsIn, pageRoute, readingPages, sidebarGroups, } from "./nav.js";
28
31
  export { headTags, scriptJson } from "./head.js";
29
32
  /**
30
33
  * The collection's loader and schema: the `glob` loader on the site output's
@@ -111,41 +114,120 @@ function lineStore(store, line, older) {
111
114
  });
112
115
  }
113
116
  /**
114
- * A layout's sidebar: `nav`'s groups, each page with its collection entry and
115
- * its address, which is the one Ascribe's links use. `entries` is the
116
- * collection (`await getCollection("docs")`), and `name` says which project's,
117
- * as for `ascribeCollection`; with several projects and no `name`, it's the one
118
- * the entries were loaded from. Throws, naming each, when `nav` leaves out a page
119
- * of the collection, names a file that isn't one, or names a page twice, so the
120
- * build fails until the list is complete.
117
+ * A layout's sidebar: the project's navigation, from the build's
118
+ * `_ascribe/nav.json`, each page with its collection entry, its address,
119
+ * which is the one Ascribe's links use, and its label. The root's pages are
120
+ * the first group, "Start", and each top section is a group of its own, with
121
+ * its nested sections in `sections`. `entries` is the collection (`await
122
+ * getCollection("docs")`), and `name` says which project's, as for
123
+ * `ascribeCollection`; with several projects and no `name`, it's the one the
124
+ * entries were loaded from. Throws when the navigation names a page the
125
+ * collection has no entry for.
126
+ *
127
+ * A site's own list, `nav`, overrides the project's: its groups, in its
128
+ * order, checked against the collection. It throws, naming each, when the
129
+ * list leaves out a page of the collection, names a file that isn't one, or
130
+ * names a page twice, so the build fails until the list is complete.
121
131
  *
122
132
  * With a versioned build, `entry`, the page the sidebar is on, says which
123
- * line's pages it shows: each page of the list that line has, so an older
124
- * line's sidebar stays in that line. The list is checked against every
125
- * line's pages. Without `entry`, it shows the current line's.
133
+ * line's pages it shows: that line's navigation, or each page of the list
134
+ * that line has, so an older line's sidebar stays in that line. A list is
135
+ * checked against every line's pages. Without `entry`, it shows the current
136
+ * line's.
137
+ */
138
+ export function sidebar(entries, options = {}) {
139
+ const at = scope(entries, options, "sidebar");
140
+ if (options.nav !== undefined) {
141
+ if (at.lines === undefined)
142
+ return sidebarGroups(options.nav, entries, at.route, (e) => sourceFile(at.root, e), undefined, at.title);
143
+ return sidebarGroups(options.nav, entries, at.route, at.source, { id: at.id, shown: (entry) => lineOf(entry) === at.shown }, at.title);
144
+ }
145
+ return navigationGroups(readNavigation(at.shownRoot), at.own(), (entry) => at.route(entry.id), at.source);
146
+ }
147
+ /**
148
+ * A page's previous and next in the project's navigation, in reading order:
149
+ * a section's own page, then its pages, a nested section right after its
150
+ * own page beside the folder, depth first, as `sidebar` lists them. Each is
151
+ * the entry, its address, and its label, which is the page's `nav-label`
152
+ * when it has one; the first page has no `previous`, and the last no
153
+ * `next`. `entry` is the page's collection entry, `entries` the collection,
154
+ * and `name` as for `sidebar`. In a versioned build, they're in the line
155
+ * `entry` is on.
156
+ *
157
+ * With a site's own list, `nav`, they're the pages before and after it in
158
+ * the list's groups, in order. Neither, for a page the navigation leaves
159
+ * out, such as an `unlisted` one.
160
+ */
161
+ export function neighbors(entry, entries, options = {}) {
162
+ const same = (page) => page.entry === entry || page.entry.id === entry.id;
163
+ const on = entry;
164
+ if (options.nav !== undefined) {
165
+ const groups = sidebar(entries, { ...options, entry: on });
166
+ return neighborsIn(groups.flatMap((group) => group.pages), same);
167
+ }
168
+ const at = scope(entries, { ...options, entry: on }, "neighbors");
169
+ const pages = readingPages(readNavigation(at.shownRoot), at.own(), (page) => at.route(page.id), at.source);
170
+ return neighborsIn(pages, same);
171
+ }
172
+ /**
173
+ * What `sidebar` and `neighbors` need to know about the collection: its
174
+ * site output, how a page is routed and named, and, in a versioned build,
175
+ * the line shown, the one `options.entry` is on, or the current line.
126
176
  */
127
- export function sidebar(nav, entries, options = {}) {
177
+ function scope(entries, options, caller) {
178
+ if (!Array.isArray(entries) || (entries.length > 0 && !("id" in entries[0]))) {
179
+ throw new Error(caller === "sidebar"
180
+ ? "@ascribed/astro: sidebar() takes the collection first, and a site's own list as an option: `sidebar(entries, { nav })`."
181
+ : "@ascribed/astro: neighbors() takes the page's entry, then the collection: `neighbors(entry, entries)`.");
182
+ }
128
183
  const root = sidebarRoot(entries, options.name);
129
184
  const at = routes.find((route) => route.siteRoot === root);
130
185
  const basePath = at?.basePath ?? "/";
131
186
  const trailingSlash = at?.trailingSlash ?? "always";
132
187
  const route = (id) => pageRoute(id, basePath, trailingSlash);
133
188
  const lines = linesOf(root);
134
- if (lines === undefined)
135
- return sidebarGroups(nav, entries, route, (e) => sourceFile(root, e));
136
- const current = lines.find((line) => line.status === "current")?.line;
137
- const shown = lineOf(options.entry) ?? current;
138
- const lineRoot = (entry) => lines.find((line) => line.line === lineOf(entry))?.siteRoot ?? root;
139
- return sidebarGroups(nav, entries, route, (entry) => sourceFile(lineRoot(entry), entry), {
140
- // An older line's ids have the line in front.
141
- id: (entry) => {
142
- const line = lineOf(entry);
143
- if (line === undefined || line === current)
144
- return entry.id;
145
- return entry.id === line ? "index" : entry.id.slice(line.length + 1);
146
- },
147
- shown: (entry) => lineOf(entry) === shown,
148
- });
189
+ const current = lines?.find((line) => line.status === "current")?.line;
190
+ const shown = lines === undefined ? undefined : (lineOf(options.entry) ?? current);
191
+ const lineRoot = (entry) => lines?.find((line) => line.line === lineOf(entry))?.siteRoot ?? root;
192
+ // An older line's ids have the line in front.
193
+ const id = (entry) => {
194
+ const line = lineOf(entry);
195
+ if (line === undefined || line === current)
196
+ return entry.id;
197
+ return entry.id === line ? "index" : entry.id.slice(line.length + 1);
198
+ };
199
+ const source = (entry) => sourceFile(lineRoot(entry), entry);
200
+ const title = (entry) => recordFor(lineRoot(entry), source(entry))?.title;
201
+ const shownRoot = lines?.find((line) => line.line === shown)?.siteRoot ?? root;
202
+ const own = () => entries.filter((entry) => lines === undefined || lineOf(entry) === shown);
203
+ return { root, route, lines, shown, id, source, title, shownRoot, own };
204
+ }
205
+ /** The navigation's format version this reads. */
206
+ const NAV_VERSION = 1;
207
+ /** A build's navigation by its site output root, as last read, and when its file was written. */
208
+ const navigations = new Map();
209
+ /**
210
+ * The navigation in a build's site output, `_ascribe/nav.json`. Read again
211
+ * when the file changes, as it does with each build in `astro dev`.
212
+ */
213
+ function readNavigation(siteRoot) {
214
+ const file = path.join(siteRoot, "_ascribe", "nav.json");
215
+ let written;
216
+ try {
217
+ written = statSync(file).mtimeMs;
218
+ }
219
+ catch {
220
+ throw new Error(`@ascribed/astro: ${file} isn't there. \`ascribe build\` writes it beside the page records; update @ascribed/cli and @ascribed/astro together, or pass the site's own list as \`sidebar(entries, { nav })\`.`);
221
+ }
222
+ const known = navigations.get(siteRoot);
223
+ if (known !== undefined && known.written === written)
224
+ return known.navigation;
225
+ const navigation = JSON.parse(readFileSync(file, "utf8"));
226
+ if (navigation.version !== NAV_VERSION) {
227
+ throw new Error(`@ascribed/astro: ${file} is version ${String(navigation.version)} of the navigation, and this version of the integration reads version ${NAV_VERSION}. Update @ascribed/astro and @ascribed/cli together.`);
228
+ }
229
+ navigations.set(siteRoot, { written, navigation });
230
+ return navigation;
149
231
  }
150
232
  /** The line an entry of a versioned build's collection is from, or `undefined`. */
151
233
  function lineOf(entry) {
@@ -1 +1 @@
1
- {"version":3,"file":"content.js","sourceRoot":"","sources":["../src/content.ts"],"names":[],"mappings":"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","sourcesContent":["// The content collection over a build's site output. Import it from\n// `content.config.ts`, which Vite loads, so it can read what the integration\n// resolved (`virtual:ascribe/site`) instead of finding the project itself.\n//\n// import { defineCollection } from \"astro:content\";\n// import { ascribeCollection } from \"@ascribed/astro/content\";\n// import { schema } from \"../.ascribe/build/site/site/_ascribe/schema.ts\";\n// export const collections = { docs: defineCollection(ascribeCollection({ schema })) };\n//\n// With several projects, each collection names its entry:\n//\n// import { schema as docs } from \"../.astro/integrations/_ascribed_astro/docs/schema.ts\";\n// export const collections = {\n// docs: defineCollection(ascribeCollection({ schema: docs, name: \"docs\" })),\n// };\n//\n// A layout's sidebar, from the site's list of pages (`src/nav.ts`, which\n// `npx @ascribed/astro init` writes), checked against the collection:\n//\n// const groups = sidebar(nav, await getCollection(\"docs\"));\nimport { pathToFileURL } from \"node:url\";\nimport path from \"node:path\";\nimport { glob, type LoaderContext } from \"astro/loaders\";\nimport { astroRoot, collections, routes, siteRoot } from \"virtual:ascribe/site\";\nimport { recordFor, type PageRecord } from \"./records.js\";\nimport {\n lineNotice,\n linesOf,\n listedLines,\n pageLines,\n type LineNotice,\n type ListedLine,\n type PageLine,\n} from \"./versions.js\";\nimport {\n pageRoute,\n sidebarGroups,\n type NavEntry,\n type NavGroup,\n type SidebarGroup,\n} from \"./nav.js\";\n\nexport type { NavGroup, SidebarGroup, SidebarPage } from \"./nav.js\";\nexport type { Listing, ListingFrom, PageRecord, Step } from \"./records.js\";\nexport type { LineNotice } from \"./versions.js\";\nexport { headTags, scriptJson, type Card, type HeadOptions, type HeadTag } from \"./head.js\";\n\n/**\n * The collection's loader and schema: the `glob` loader on the site output's\n * pages, with the generated Zod schema (`_ascribe/schema.ts`'s `schema`).\n * With several projects (`ascribe({ projects })`), `name` says which entry's\n * site output; it can be left out when there's one entry.\n *\n * Entry ids are Astro's own (`guides/my-setup` for `Guides/My Setup.md`, `index`\n * for the root page), which is what `AstroRouter` in `ascribe-resolve`\n * computes, so a page's route is the base path plus its id, and the root page is\n * at the base path itself.\n *\n * A versioned build's collection has every live line's pages: the current\n * line's with the ids above, and an older line's with the line in front\n * (`3.4/guides/my-setup`, and `3.4` for its root page), so a page's route is\n * still the base path plus its id. Each line's pages are `glob`'s over that\n * line's site output, and each entry's data has `line`, the line it's from,\n * and `current`, whether that's the current line. The schema doesn't type\n * them: `Versions.astro` and `sidebar` read them.\n */\nexport function ascribeCollection<S>(options: { schema: S; name?: string }): {\n loader: ReturnType<typeof glob>;\n schema: S;\n} {\n const root = rootFor(options.name);\n // Whether the build is versioned is known once it's built, so it's read when the collection loads.\n const loader: ReturnType<typeof glob> = {\n name: \"@ascribed/astro\",\n load: async (context) => {\n const lines = linesOf(root);\n if (lines === undefined) return pagesOf(root).load(context);\n const older = lines.filter((line) => line.status !== \"current\").map((line) => line.line);\n for (const line of lines) {\n const current = line.status === \"current\";\n await pagesOf(line.siteRoot).load({\n ...context,\n store: lineStore(context.store, current ? undefined : line.line, older),\n parseData: async (props) => {\n const data: Record<string, unknown> = await context.parseData(props);\n for (const key of [\"line\", \"current\"]) {\n if (key in data)\n throw new Error(\n `@ascribed/astro: ${props.filePath ?? props.id} has a frontmatter field \\`${key}\\`, and a versioned build's entries have \\`${key}\\` of their own. Rename the field.`,\n );\n }\n return { ...data, line: line.line, current } as unknown as typeof props.data;\n },\n });\n }\n },\n };\n return { loader, schema: options.schema };\n}\n\n/** `glob` over one site output's pages. `_ascribe/` holds the schema and published files, never pages. */\nfunction pagesOf(siteRoot: string): ReturnType<typeof glob> {\n return glob({ pattern: [\"**/*.md\", \"!_ascribe/**\"], base: pathToFileURL(siteRoot + path.sep) });\n}\n\n/**\n * The collection's store as one line's `glob` sees it: its own entries, by\n * the ids `glob` gives them, which the store keeps with the line in front.\n * `line` is `undefined` for the current line, whose ids are `glob`'s, and\n * whose entries are every one not under an older line, so an entry of a\n * line that's no longer live is removed with the current line's stale ones.\n */\nfunction lineStore(\n store: LoaderContext[\"store\"],\n line: string | undefined,\n older: readonly string[],\n): LoaderContext[\"store\"] {\n const under = (id: string, prefix: string) => id === prefix || id.startsWith(`${prefix}/`);\n const outer = (id: string) => (line === undefined ? id : id === \"index\" ? line : `${line}/${id}`);\n const inner = (id: string) =>\n line === undefined ? id : id === line ? \"index\" : id.slice(line.length + 1);\n const mine = (id: string) =>\n line === undefined ? !older.some((prefix) => under(id, prefix)) : under(id, line);\n const scoped: Partial<LoaderContext[\"store\"]> = {\n get: (id) => store.get(outer(id)),\n set: (entry) => store.set({ ...entry, id: outer(entry.id) }),\n delete: (id) => store.delete(outer(id)),\n has: (id) => store.has(outer(id)),\n keys: () => store.keys().filter(mine).map(inner),\n values: () => store.entries().flatMap(([id, entry]) => (mine(id) ? [entry] : [])),\n entries: () =>\n store.entries().flatMap(([id, entry]) => (mine(id) ? [[inner(id), entry] as const] : [])),\n clear: () => {\n for (const id of store.keys().filter(mine)) store.delete(id);\n },\n };\n // What `glob` uses beyond these (`addModuleImport`, `addAssetImports`) is by file, not by id.\n return new Proxy(store, {\n get: (target, key, receiver) =>\n key in scoped\n ? scoped[key as keyof typeof scoped]\n : (Reflect.get(target, key, receiver) as unknown),\n });\n}\n\n/**\n * A layout's sidebar: `nav`'s groups, each page with its collection entry and\n * its address, which is the one Ascribe's links use. `entries` is the\n * collection (`await getCollection(\"docs\")`), and `name` says which project's,\n * as for `ascribeCollection`; with several projects and no `name`, it's the one\n * the entries were loaded from. Throws, naming each, when `nav` leaves out a page\n * of the collection, names a file that isn't one, or names a page twice, so the\n * build fails until the list is complete.\n *\n * With a versioned build, `entry`, the page the sidebar is on, says which\n * line's pages it shows: each page of the list that line has, so an older\n * line's sidebar stays in that line. The list is checked against every\n * line's pages. Without `entry`, it shows the current line's.\n */\nexport function sidebar<E extends NavEntry>(\n nav: readonly NavGroup[],\n entries: readonly E[],\n options: { name?: string; entry?: { data?: unknown } } = {},\n): SidebarGroup<E>[] {\n const root = sidebarRoot(entries, options.name);\n const at = routes.find((route) => route.siteRoot === root);\n const basePath = at?.basePath ?? \"/\";\n const trailingSlash = at?.trailingSlash ?? \"always\";\n const route = (id: string) => pageRoute(id, basePath, trailingSlash);\n const lines = linesOf(root);\n if (lines === undefined) return sidebarGroups(nav, entries, route, (e) => sourceFile(root, e));\n const current = lines.find((line) => line.status === \"current\")?.line;\n const shown = lineOf(options.entry) ?? current;\n const lineRoot = (entry: NavEntry) =>\n lines.find((line) => line.line === lineOf(entry))?.siteRoot ?? root;\n return sidebarGroups(nav, entries, route, (entry) => sourceFile(lineRoot(entry), entry), {\n // An older line's ids have the line in front.\n id: (entry) => {\n const line = lineOf(entry);\n if (line === undefined || line === current) return entry.id;\n return entry.id === line ? \"index\" : entry.id.slice(line.length + 1);\n },\n shown: (entry) => lineOf(entry) === shown,\n });\n}\n\n/** The line an entry of a versioned build's collection is from, or `undefined`. */\nfunction lineOf(entry: object | undefined): string | undefined {\n const data = (entry as { data?: unknown } | undefined)?.data;\n const line = (data as { line?: unknown } | undefined)?.line;\n return typeof line === \"string\" ? line : undefined;\n}\n\n/**\n * A collection entry's page record, from its build's `_ascribe/pages.json`:\n * its route, title, description, canonical address, and listing, as every\n * output Ascribe writes has them. `undefined` for an entry that isn't from an\n * Ascribe collection, or whose page has no record. An entry of an older line\n * has that line's record.\n */\nexport function pageRecord(entry: {\n filePath?: string | undefined;\n data?: unknown;\n}): PageRecord | undefined {\n const found = entryRoot(entry);\n if (found === undefined) return undefined;\n return recordFor(found.lineRoot, found.source);\n}\n\n/** A line that publishes a page: its route there, and whether it's the line the page is from. */\nexport interface PageVersion extends PageLine {\n /** Whether this is the line of the entry asked about. */\n own: boolean;\n}\n\n/**\n * The lines of a versioned build that publish a collection entry's page,\n * newest first, each with the page's route in it and whether it's the\n * entry's own line, from `<output-dir>/_ascribe/versions.json`. A line that\n * doesn't have the page isn't listed. Empty for an entry of a build that\n * isn't versioned. `Versions.astro` writes them as a list of links.\n */\nexport function pageVersions(entry: {\n filePath?: string | undefined;\n data?: unknown;\n}): PageVersion[] {\n const found = entryRoot(entry);\n if (found === undefined) return [];\n const own = lineOf(entry);\n return pageLines(found.root, found.source).map((line) => ({\n ...line,\n own: own === undefined ? line.current : line.line === own,\n }));\n}\n\n/** A line of a versioned build, as a page's list of versions shows it. */\nexport interface LineVersion extends ListedLine {\n /** Whether this is the line of the entry asked about. */\n own: boolean;\n}\n\n/**\n * Every line of a versioned build, for the list of versions on a collection\n * entry's page, newest first: each live line, with the page's route in it,\n * or the page in its place, and no route when it has neither; then each\n * archived line that has an address, with that address. Empty for an entry\n * of a build that isn't versioned. `Versions.astro` writes them.\n */\nexport function lineVersions(entry: {\n filePath?: string | undefined;\n data?: unknown;\n}): LineVersion[] {\n const found = entryRoot(entry);\n if (found === undefined) return [];\n const own = lineOf(entry);\n return listedLines(found.root, found.source).map((line) => ({\n ...line,\n own: own === undefined ? line.status === \"current\" : line.line === own,\n }));\n}\n\n/**\n * What a collection entry's page of an older line says about the line: its\n * label and number, the last day it's supported, and the page's route in the\n * current line, or the page that takes its place there, or else the current\n * line's home. `undefined` on the current line, and for an entry of a build\n * that isn't versioned. `VersionBanner.astro` writes it.\n */\nexport function versionNotice(entry: {\n filePath?: string | undefined;\n data?: unknown;\n}): LineNotice | undefined {\n const found = entryRoot(entry);\n const line = lineOf(entry);\n if (found === undefined || line === undefined) return undefined;\n const at = routes.find((route) => route.siteRoot === found.root);\n const home = pageRoute(\"index\", at?.basePath ?? \"/\", at?.trailingSlash ?? \"always\");\n return lineNotice(found.root, found.source, line, home);\n}\n\n/**\n * The site output root of the entry's build (the current line's, for a\n * versioned build), the root of the line it's from, and its page's source\n * path, or `undefined` for an entry from none of the integration's\n * collections.\n */\nfunction entryRoot(entry: {\n filePath?: string | undefined;\n data?: unknown;\n}): { root: string; lineRoot: string; source: string } | undefined {\n if (entry.filePath === undefined) return undefined;\n const file = entry.filePath;\n for (const root of declaredRoots()) {\n const lineRoot = [root, ...(linesOf(root) ?? []).map((line) => line.siteRoot)].find(\n (candidate) => inside(candidate, file),\n );\n if (lineRoot === undefined) continue;\n const data = entry.data as { source?: unknown } | undefined;\n const source =\n typeof data?.source === \"string\"\n ? data.source\n : path.relative(lineRoot, path.resolve(astroRoot, file)).split(path.sep).join(\"/\");\n return { root, lineRoot, source };\n }\n return undefined;\n}\n\n/** The site output roots of the integration's entries. */\nfunction declaredRoots(): string[] {\n return [...Object.values(collections), ...(siteRoot === \"\" ? [] : [siteRoot])];\n}\n\n/**\n * The site output root of the collection a sidebar lists: the entry `name`\n * names, or, with several and no name, the one the entries were loaded from.\n */\nfunction sidebarRoot(entries: readonly NavEntry[], name: string | undefined): string {\n const first = entries[0];\n if (name === undefined && Object.keys(collections).length > 1 && first !== undefined) {\n const from = entryRoot(first)?.root;\n if (from !== undefined) return from;\n }\n return rootFor(name);\n}\n\n/** Whether `file`, from the Astro root as an entry's `filePath` is, is under `root`. */\nfunction inside(root: string, file: string): boolean {\n const relative = path.relative(root, path.resolve(astroRoot, file));\n return !relative.startsWith(\"..\") && !path.isAbsolute(relative);\n}\n\n/** An entry's source file, by its path in the site output, which mirrors the content root. */\nfunction sourceFile(root: string, entry: NavEntry): string {\n if (entry.filePath === undefined) return `the page ${entry.id}`;\n return inside(root, entry.filePath)\n ? path.relative(root, path.resolve(astroRoot, entry.filePath)).split(path.sep).join(\"/\")\n : entry.filePath;\n}\n\n/** The site output root of the entry `name` names. */\nfunction rootFor(name: string | undefined): string {\n const names = Object.keys(collections);\n if (names.length === 0) {\n if (name !== undefined) {\n throw new Error(\n `@ascribed/astro: ascribeCollection was given the name \"${name}\", but ascribe() was given one project, which has no name. Leave out \\`name\\`, or list the project in \\`projects\\`.`,\n );\n }\n return siteRoot;\n }\n if (name === undefined) {\n if (names.length === 1) return collections[names[0] as string] as string;\n throw new Error(\n `@ascribed/astro: ascribeCollection needs a \\`name\\` when ascribe() has several projects: one of ${names.map((n) => `\"${n}\"`).join(\", \")}.`,\n );\n }\n const root = collections[name];\n if (root === undefined) {\n throw new Error(\n `@ascribed/astro: ascribeCollection was given the name \"${name}\", and ascribe()'s projects are ${names.map((n) => `\"${n}\"`).join(\", \")}.`,\n );\n }\n return root;\n}\n"]}
1
+ {"version":3,"file":"content.js","sourceRoot":"","sources":["../src/content.ts"],"names":[],"mappings":"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","sourcesContent":["// The content collection over a build's site output. Import it from\n// `content.config.ts`, which Vite loads, so it can read what the integration\n// resolved (`virtual:ascribe/site`) instead of finding the project itself.\n//\n// import { defineCollection } from \"astro:content\";\n// import { ascribeCollection } from \"@ascribed/astro/content\";\n// import { schema } from \"../.ascribe/build/site/site/_ascribe/schema.ts\";\n// export const collections = { docs: defineCollection(ascribeCollection({ schema })) };\n//\n// With several projects, each collection names its entry:\n//\n// import { schema as docs } from \"../.astro/integrations/_ascribed_astro/docs/schema.ts\";\n// export const collections = {\n// docs: defineCollection(ascribeCollection({ schema: docs, name: \"docs\" })),\n// };\n//\n// A layout's sidebar, from the project's navigation (`ascribe.nav`'s order,\n// or the default), or from a site's own list of pages as `nav`, and a page's\n// previous and next:\n//\n// const groups = sidebar(await getCollection(\"docs\"), { entry });\n// const { previous, next } = neighbors(entry, await getCollection(\"docs\"));\nimport { readFileSync, statSync } from \"node:fs\";\nimport { pathToFileURL } from \"node:url\";\nimport path from \"node:path\";\nimport { glob, type LoaderContext } from \"astro/loaders\";\nimport { astroRoot, collections, routes, siteRoot } from \"virtual:ascribe/site\";\nimport { recordFor, type PageRecord } from \"./records.js\";\nimport {\n lineNotice,\n linesOf,\n listedLines,\n pageLines,\n type LineNotice,\n type ListedLine,\n type PageLine,\n} from \"./versions.js\";\nimport {\n navigationGroups,\n neighborsIn,\n pageRoute,\n readingPages,\n sidebarGroups,\n type NavEntry,\n type NavGroup,\n type Navigation,\n type Neighbors,\n type SidebarGroup,\n type SidebarPage,\n} from \"./nav.js\";\n\nexport type {\n Formatted,\n NavGroup,\n NavPage,\n NavSection,\n Navigation,\n Neighbors,\n SidebarGroup,\n SidebarPage,\n} from \"./nav.js\";\nexport type { Listing, ListingFrom, PageRecord, Step } from \"./records.js\";\nexport type { LineNotice } from \"./versions.js\";\nexport { headTags, scriptJson, type Card, type HeadOptions, type HeadTag } from \"./head.js\";\n\n/**\n * The collection's loader and schema: the `glob` loader on the site output's\n * pages, with the generated Zod schema (`_ascribe/schema.ts`'s `schema`).\n * With several projects (`ascribe({ projects })`), `name` says which entry's\n * site output; it can be left out when there's one entry.\n *\n * Entry ids are Astro's own (`guides/my-setup` for `Guides/My Setup.md`, `index`\n * for the root page), which is what `AstroRouter` in `ascribe-resolve`\n * computes, so a page's route is the base path plus its id, and the root page is\n * at the base path itself.\n *\n * A versioned build's collection has every live line's pages: the current\n * line's with the ids above, and an older line's with the line in front\n * (`3.4/guides/my-setup`, and `3.4` for its root page), so a page's route is\n * still the base path plus its id. Each line's pages are `glob`'s over that\n * line's site output, and each entry's data has `line`, the line it's from,\n * and `current`, whether that's the current line. The schema doesn't type\n * them: `Versions.astro` and `sidebar` read them.\n */\nexport function ascribeCollection<S>(options: { schema: S; name?: string }): {\n loader: ReturnType<typeof glob>;\n schema: S;\n} {\n const root = rootFor(options.name);\n // Whether the build is versioned is known once it's built, so it's read when the collection loads.\n const loader: ReturnType<typeof glob> = {\n name: \"@ascribed/astro\",\n load: async (context) => {\n const lines = linesOf(root);\n if (lines === undefined) return pagesOf(root).load(context);\n const older = lines.filter((line) => line.status !== \"current\").map((line) => line.line);\n for (const line of lines) {\n const current = line.status === \"current\";\n await pagesOf(line.siteRoot).load({\n ...context,\n store: lineStore(context.store, current ? undefined : line.line, older),\n parseData: async (props) => {\n const data: Record<string, unknown> = await context.parseData(props);\n for (const key of [\"line\", \"current\"]) {\n if (key in data)\n throw new Error(\n `@ascribed/astro: ${props.filePath ?? props.id} has a frontmatter field \\`${key}\\`, and a versioned build's entries have \\`${key}\\` of their own. Rename the field.`,\n );\n }\n return { ...data, line: line.line, current } as unknown as typeof props.data;\n },\n });\n }\n },\n };\n return { loader, schema: options.schema };\n}\n\n/** `glob` over one site output's pages. `_ascribe/` holds the schema and published files, never pages. */\nfunction pagesOf(siteRoot: string): ReturnType<typeof glob> {\n return glob({ pattern: [\"**/*.md\", \"!_ascribe/**\"], base: pathToFileURL(siteRoot + path.sep) });\n}\n\n/**\n * The collection's store as one line's `glob` sees it: its own entries, by\n * the ids `glob` gives them, which the store keeps with the line in front.\n * `line` is `undefined` for the current line, whose ids are `glob`'s, and\n * whose entries are every one not under an older line, so an entry of a\n * line that's no longer live is removed with the current line's stale ones.\n */\nfunction lineStore(\n store: LoaderContext[\"store\"],\n line: string | undefined,\n older: readonly string[],\n): LoaderContext[\"store\"] {\n const under = (id: string, prefix: string) => id === prefix || id.startsWith(`${prefix}/`);\n const outer = (id: string) => (line === undefined ? id : id === \"index\" ? line : `${line}/${id}`);\n const inner = (id: string) =>\n line === undefined ? id : id === line ? \"index\" : id.slice(line.length + 1);\n const mine = (id: string) =>\n line === undefined ? !older.some((prefix) => under(id, prefix)) : under(id, line);\n const scoped: Partial<LoaderContext[\"store\"]> = {\n get: (id) => store.get(outer(id)),\n set: (entry) => store.set({ ...entry, id: outer(entry.id) }),\n delete: (id) => store.delete(outer(id)),\n has: (id) => store.has(outer(id)),\n keys: () => store.keys().filter(mine).map(inner),\n values: () => store.entries().flatMap(([id, entry]) => (mine(id) ? [entry] : [])),\n entries: () =>\n store.entries().flatMap(([id, entry]) => (mine(id) ? [[inner(id), entry] as const] : [])),\n clear: () => {\n for (const id of store.keys().filter(mine)) store.delete(id);\n },\n };\n // What `glob` uses beyond these (`addModuleImport`, `addAssetImports`) is by file, not by id.\n return new Proxy(store, {\n get: (target, key, receiver) =>\n key in scoped\n ? scoped[key as keyof typeof scoped]\n : (Reflect.get(target, key, receiver) as unknown),\n });\n}\n\n/**\n * A layout's sidebar: the project's navigation, from the build's\n * `_ascribe/nav.json`, each page with its collection entry, its address,\n * which is the one Ascribe's links use, and its label. The root's pages are\n * the first group, \"Start\", and each top section is a group of its own, with\n * its nested sections in `sections`. `entries` is the collection (`await\n * getCollection(\"docs\")`), and `name` says which project's, as for\n * `ascribeCollection`; with several projects and no `name`, it's the one the\n * entries were loaded from. Throws when the navigation names a page the\n * collection has no entry for.\n *\n * A site's own list, `nav`, overrides the project's: its groups, in its\n * order, checked against the collection. It throws, naming each, when the\n * list leaves out a page of the collection, names a file that isn't one, or\n * names a page twice, so the build fails until the list is complete.\n *\n * With a versioned build, `entry`, the page the sidebar is on, says which\n * line's pages it shows: that line's navigation, or each page of the list\n * that line has, so an older line's sidebar stays in that line. A list is\n * checked against every line's pages. Without `entry`, it shows the current\n * line's.\n */\nexport function sidebar<E extends NavEntry>(\n entries: readonly E[],\n options: { name?: string; entry?: { data?: unknown }; nav?: readonly NavGroup[] } = {},\n): SidebarGroup<E>[] {\n const at = scope(entries, options, \"sidebar\");\n if (options.nav !== undefined) {\n if (at.lines === undefined)\n return sidebarGroups(\n options.nav,\n entries,\n at.route,\n (e) => sourceFile(at.root, e),\n undefined,\n at.title,\n );\n return sidebarGroups(\n options.nav,\n entries,\n at.route,\n at.source,\n { id: at.id, shown: (entry) => lineOf(entry) === at.shown },\n at.title,\n );\n }\n return navigationGroups<E>(\n readNavigation(at.shownRoot),\n at.own(),\n (entry) => at.route(entry.id),\n at.source,\n );\n}\n\n/**\n * A page's previous and next in the project's navigation, in reading order:\n * a section's own page, then its pages, a nested section right after its\n * own page beside the folder, depth first, as `sidebar` lists them. Each is\n * the entry, its address, and its label, which is the page's `nav-label`\n * when it has one; the first page has no `previous`, and the last no\n * `next`. `entry` is the page's collection entry, `entries` the collection,\n * and `name` as for `sidebar`. In a versioned build, they're in the line\n * `entry` is on.\n *\n * With a site's own list, `nav`, they're the pages before and after it in\n * the list's groups, in order. Neither, for a page the navigation leaves\n * out, such as an `unlisted` one.\n */\nexport function neighbors<E extends NavEntry>(\n entry: E,\n entries: readonly E[],\n options: { name?: string; nav?: readonly NavGroup[] } = {},\n): Neighbors<E> {\n const same = (page: SidebarPage<E>) => page.entry === entry || page.entry.id === entry.id;\n const on = entry as { data?: unknown };\n if (options.nav !== undefined) {\n const groups = sidebar(entries, { ...options, entry: on });\n return neighborsIn(\n groups.flatMap((group) => group.pages),\n same,\n );\n }\n const at = scope(entries, { ...options, entry: on }, \"neighbors\");\n const pages = readingPages<E>(\n readNavigation(at.shownRoot),\n at.own(),\n (page) => at.route(page.id),\n at.source,\n );\n return neighborsIn(pages, same);\n}\n\n/**\n * What `sidebar` and `neighbors` need to know about the collection: its\n * site output, how a page is routed and named, and, in a versioned build,\n * the line shown, the one `options.entry` is on, or the current line.\n */\nfunction scope<E extends NavEntry>(\n entries: readonly E[],\n options: { name?: string; entry?: { data?: unknown } },\n caller: \"sidebar\" | \"neighbors\",\n) {\n if (!Array.isArray(entries) || (entries.length > 0 && !(\"id\" in (entries[0] as object)))) {\n throw new Error(\n caller === \"sidebar\"\n ? \"@ascribed/astro: sidebar() takes the collection first, and a site's own list as an option: `sidebar(entries, { nav })`.\"\n : \"@ascribed/astro: neighbors() takes the page's entry, then the collection: `neighbors(entry, entries)`.\",\n );\n }\n const root = sidebarRoot(entries, options.name);\n const at = routes.find((route) => route.siteRoot === root);\n const basePath = at?.basePath ?? \"/\";\n const trailingSlash = at?.trailingSlash ?? \"always\";\n const route = (id: string) => pageRoute(id, basePath, trailingSlash);\n const lines = linesOf(root);\n const current = lines?.find((line) => line.status === \"current\")?.line;\n const shown = lines === undefined ? undefined : (lineOf(options.entry) ?? current);\n const lineRoot = (entry: NavEntry) =>\n lines?.find((line) => line.line === lineOf(entry))?.siteRoot ?? root;\n // An older line's ids have the line in front.\n const id = (entry: NavEntry) => {\n const line = lineOf(entry);\n if (line === undefined || line === current) return entry.id;\n return entry.id === line ? \"index\" : entry.id.slice(line.length + 1);\n };\n const source = (entry: NavEntry) => sourceFile(lineRoot(entry), entry);\n const title = (entry: E) => recordFor(lineRoot(entry), source(entry))?.title;\n const shownRoot = lines?.find((line) => line.line === shown)?.siteRoot ?? root;\n const own = () => entries.filter((entry) => lines === undefined || lineOf(entry) === shown);\n return { root, route, lines, shown, id, source, title, shownRoot, own };\n}\n\n/** The navigation's format version this reads. */\nconst NAV_VERSION = 1;\n\n/** A build's navigation by its site output root, as last read, and when its file was written. */\nconst navigations = new Map<string, { written: number; navigation: Navigation }>();\n\n/**\n * The navigation in a build's site output, `_ascribe/nav.json`. Read again\n * when the file changes, as it does with each build in `astro dev`.\n */\nfunction readNavigation(siteRoot: string): Navigation {\n const file = path.join(siteRoot, \"_ascribe\", \"nav.json\");\n let written: number;\n try {\n written = statSync(file).mtimeMs;\n } catch {\n throw new Error(\n `@ascribed/astro: ${file} isn't there. \\`ascribe build\\` writes it beside the page records; update @ascribed/cli and @ascribed/astro together, or pass the site's own list as \\`sidebar(entries, { nav })\\`.`,\n );\n }\n const known = navigations.get(siteRoot);\n if (known !== undefined && known.written === written) return known.navigation;\n const navigation = JSON.parse(readFileSync(file, \"utf8\")) as Navigation;\n if (navigation.version !== NAV_VERSION) {\n throw new Error(\n `@ascribed/astro: ${file} is version ${String(navigation.version)} of the navigation, and this version of the integration reads version ${NAV_VERSION}. Update @ascribed/astro and @ascribed/cli together.`,\n );\n }\n navigations.set(siteRoot, { written, navigation });\n return navigation;\n}\n\n/** The line an entry of a versioned build's collection is from, or `undefined`. */\nfunction lineOf(entry: object | undefined): string | undefined {\n const data = (entry as { data?: unknown } | undefined)?.data;\n const line = (data as { line?: unknown } | undefined)?.line;\n return typeof line === \"string\" ? line : undefined;\n}\n\n/**\n * A collection entry's page record, from its build's `_ascribe/pages.json`:\n * its route, title, description, canonical address, and listing, as every\n * output Ascribe writes has them. `undefined` for an entry that isn't from an\n * Ascribe collection, or whose page has no record. An entry of an older line\n * has that line's record.\n */\nexport function pageRecord(entry: {\n filePath?: string | undefined;\n data?: unknown;\n}): PageRecord | undefined {\n const found = entryRoot(entry);\n if (found === undefined) return undefined;\n return recordFor(found.lineRoot, found.source);\n}\n\n/** A line that publishes a page: its route there, and whether it's the line the page is from. */\nexport interface PageVersion extends PageLine {\n /** Whether this is the line of the entry asked about. */\n own: boolean;\n}\n\n/**\n * The lines of a versioned build that publish a collection entry's page,\n * newest first, each with the page's route in it and whether it's the\n * entry's own line, from `<output-dir>/_ascribe/versions.json`. A line that\n * doesn't have the page isn't listed. Empty for an entry of a build that\n * isn't versioned. `Versions.astro` writes them as a list of links.\n */\nexport function pageVersions(entry: {\n filePath?: string | undefined;\n data?: unknown;\n}): PageVersion[] {\n const found = entryRoot(entry);\n if (found === undefined) return [];\n const own = lineOf(entry);\n return pageLines(found.root, found.source).map((line) => ({\n ...line,\n own: own === undefined ? line.current : line.line === own,\n }));\n}\n\n/** A line of a versioned build, as a page's list of versions shows it. */\nexport interface LineVersion extends ListedLine {\n /** Whether this is the line of the entry asked about. */\n own: boolean;\n}\n\n/**\n * Every line of a versioned build, for the list of versions on a collection\n * entry's page, newest first: each live line, with the page's route in it,\n * or the page in its place, and no route when it has neither; then each\n * archived line that has an address, with that address. Empty for an entry\n * of a build that isn't versioned. `Versions.astro` writes them.\n */\nexport function lineVersions(entry: {\n filePath?: string | undefined;\n data?: unknown;\n}): LineVersion[] {\n const found = entryRoot(entry);\n if (found === undefined) return [];\n const own = lineOf(entry);\n return listedLines(found.root, found.source).map((line) => ({\n ...line,\n own: own === undefined ? line.status === \"current\" : line.line === own,\n }));\n}\n\n/**\n * What a collection entry's page of an older line says about the line: its\n * label and number, the last day it's supported, and the page's route in the\n * current line, or the page that takes its place there, or else the current\n * line's home. `undefined` on the current line, and for an entry of a build\n * that isn't versioned. `VersionBanner.astro` writes it.\n */\nexport function versionNotice(entry: {\n filePath?: string | undefined;\n data?: unknown;\n}): LineNotice | undefined {\n const found = entryRoot(entry);\n const line = lineOf(entry);\n if (found === undefined || line === undefined) return undefined;\n const at = routes.find((route) => route.siteRoot === found.root);\n const home = pageRoute(\"index\", at?.basePath ?? \"/\", at?.trailingSlash ?? \"always\");\n return lineNotice(found.root, found.source, line, home);\n}\n\n/**\n * The site output root of the entry's build (the current line's, for a\n * versioned build), the root of the line it's from, and its page's source\n * path, or `undefined` for an entry from none of the integration's\n * collections.\n */\nfunction entryRoot(entry: {\n filePath?: string | undefined;\n data?: unknown;\n}): { root: string; lineRoot: string; source: string } | undefined {\n if (entry.filePath === undefined) return undefined;\n const file = entry.filePath;\n for (const root of declaredRoots()) {\n const lineRoot = [root, ...(linesOf(root) ?? []).map((line) => line.siteRoot)].find(\n (candidate) => inside(candidate, file),\n );\n if (lineRoot === undefined) continue;\n const data = entry.data as { source?: unknown } | undefined;\n const source =\n typeof data?.source === \"string\"\n ? data.source\n : path.relative(lineRoot, path.resolve(astroRoot, file)).split(path.sep).join(\"/\");\n return { root, lineRoot, source };\n }\n return undefined;\n}\n\n/** The site output roots of the integration's entries. */\nfunction declaredRoots(): string[] {\n return [...Object.values(collections), ...(siteRoot === \"\" ? [] : [siteRoot])];\n}\n\n/**\n * The site output root of the collection a sidebar lists: the entry `name`\n * names, or, with several and no name, the one the entries were loaded from.\n */\nfunction sidebarRoot(entries: readonly NavEntry[], name: string | undefined): string {\n const first = entries[0];\n if (name === undefined && Object.keys(collections).length > 1 && first !== undefined) {\n const from = entryRoot(first)?.root;\n if (from !== undefined) return from;\n }\n return rootFor(name);\n}\n\n/** Whether `file`, from the Astro root as an entry's `filePath` is, is under `root`. */\nfunction inside(root: string, file: string): boolean {\n const relative = path.relative(root, path.resolve(astroRoot, file));\n return !relative.startsWith(\"..\") && !path.isAbsolute(relative);\n}\n\n/** An entry's source file, by its path in the site output, which mirrors the content root. */\nfunction sourceFile(root: string, entry: NavEntry): string {\n if (entry.filePath === undefined) return `the page ${entry.id}`;\n return inside(root, entry.filePath)\n ? path.relative(root, path.resolve(astroRoot, entry.filePath)).split(path.sep).join(\"/\")\n : entry.filePath;\n}\n\n/** The site output root of the entry `name` names. */\nfunction rootFor(name: string | undefined): string {\n const names = Object.keys(collections);\n if (names.length === 0) {\n if (name !== undefined) {\n throw new Error(\n `@ascribed/astro: ascribeCollection was given the name \"${name}\", but ascribe() was given one project, which has no name. Leave out \\`name\\`, or list the project in \\`projects\\`.`,\n );\n }\n return siteRoot;\n }\n if (name === undefined) {\n if (names.length === 1) return collections[names[0] as string] as string;\n throw new Error(\n `@ascribed/astro: ascribeCollection needs a \\`name\\` when ascribe() has several projects: one of ${names.map((n) => `\"${n}\"`).join(\", \")}.`,\n );\n }\n const root = collections[name];\n if (root === undefined) {\n throw new Error(\n `@ascribed/astro: ascribeCollection was given the name \"${name}\", and ascribe()'s projects are ${names.map((n) => `\"${n}\"`).join(\", \")}.`,\n );\n }\n return root;\n}\n"]}
package/dist/index.js CHANGED
@@ -23,9 +23,9 @@
23
23
  // import Head from "@ascribed/astro/Head.astro"; // <Head entry={entry} /> in <head>
24
24
  // import Versions from "@ascribed/astro/Versions.astro"; // the lines that have the page
25
25
  //
26
- // // the sidebar: src/nav.ts's list, checked against the collection
26
+ // // the sidebar: the project's navigation, joined to the collection
27
27
  // import { sidebar } from "@ascribed/astro/content";
28
- // const groups = sidebar(nav, await getCollection("docs"));
28
+ // const groups = sidebar(await getCollection("docs"));
29
29
  //
30
30
  // // src/pages/404.astro
31
31
  // import MissingPermalink from "@ascribed/astro/MissingPermalink.astro"; // a permalink that isn't here