Repository navigation
Expand file tree
/
Copy pathastro.config.ts
More file actions
153 lines (148 loc) · 7.24 KB
/
Copy pathastro.config.ts
File metadata and controls
153 lines (148 loc) · 7.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import { readFileSync } from "node:fs";
import { writeFile } from "node:fs/promises";
import type { AstroUserConfig } from "astro";
import { defineConfig } from "astro/config";
import tailwindcss from "@tailwindcss/vite";
import nimbus, { defineConfig as defineNimbusConfig } from "@cloudflare/nimbus-docs";
import { tableScroll } from "@cloudflare/nimbus-docs/markdown";
import { resolveToken } from "./src/lib/design-checks.mjs";
import mkdocsHeadingIds from "./src/lib/mkdocs-heading-ids.mjs";
import { mermaidFence } from "./src/lib/mermaid-fence";
import { slashlessHref } from "./src/lib/slashless-href.mjs";
import { slashlessLinks } from "./src/lib/slashless-links";
import { DEPRECATED, googleAnalytics, iconHead, SITE_DESCRIPTION, SITE_TITLE, STABLE_VERSION } from "./src/lib/site.mjs";
import codeTheme from "./src/styles/code-theme.json" with { type: "json" };
import { redirects, sidebars, versions } from "./src/versions.generated.mjs";
/** The type of a Shiki theme in the Astro config. A JSON import has wider types (a string, not "dark"). */
type ShikiTheme = NonNullable<NonNullable<NonNullable<AstroUserConfig["markdown"]>["shikiConfig"]>["theme"]>;
const nimbusConfig = defineNimbusConfig({
site: "https://docs.phalcon.io",
title: SITE_TITLE,
description: SITE_DESCRIPTION,
locale: "en",
github: "https://github.com/phalcon/documentation",
socialImageAlt: "Phalcon documentation preview",
// The icons of phalcon/assets (copies in public/; nimbus writes the SVG tag
// from public/favicon.svg), with the browser bar color of the tokens, and
// Google Analytics (an empty id disables it).
head: [
...iconHead(resolveToken(readFileSync(new URL("./src/styles/tokens.css", import.meta.url), "utf8"), "--ph-dark-bg")),
...googleAnalytics("G-JR4REKBKH3"),
] as { tag: "link" | "meta" | "script"; attrs?: Record<string, string>; content?: string }[],
// Every published version is a `docs-<version>` collection listed in
// `others`. The primary `docs` collection stays empty; `current` is only
// the label for it. `deprecated` adds the deprecation banner, the sidebar
// badge and the Pagefind `status:deprecated` facet.
versions: {
current: "latest",
others: versions,
deprecated: DEPRECATED,
},
// Each version route builds its own rail from src/sidebar/<version>.mjs
// (generated by resources/nimbus/convert.py from that version's
// mkdocs.yml). This global config is the newest version's navigation and
// serves nimbus's own helpers (breadcrumbs, llms.txt).
sidebar: {
items: (sidebars as Record<string, unknown>)[versions[0]] as typeof sidebars[keyof typeof sidebars],
scope: "full",
defaultCollapsed: true,
},
});
/**
* Cloudflare Pages reads `_redirects` from the build root and serves real
* 301s from it at the edge. Written here rather than committed to public/
* so the stable version comes from src/lib/site.mjs and cannot go stale.
*
* These two routes were Cloudflare zone rules, which intercept before Pages
* ever sees the request. Zone rules therefore win over this file: the rules
* have to be deactivated for these lines to take effect, and this file has
* to be deployed before they are, or /latest/* has nothing to fall back on.
*/
const cloudflareRedirects = {
name: "cloudflare-redirects",
hooks: {
"astro:build:done": async ({ dir, logger }: { dir: URL; logger: { info: (message: string) => void } }) => {
// First match wins, so the exact paths precede the wildcard. Bare
// `/latest` needs its own line: the Cloudflare rule this replaces
// matched `/latest/*`, which never covered it, and it 404s. `/latest/`
// is listed too, or the wildcard sends it to `/${STABLE_VERSION}/`,
// which is only a meta-refresh stub and costs an extra hop.
const body = [
`/ /${STABLE_VERSION}/introduction 301`,
`/latest /${STABLE_VERSION}/introduction 301`,
`/latest/ /${STABLE_VERSION}/introduction 301`,
`/latest/* /${STABLE_VERSION}/:splat 301`,
].join("\n");
await writeFile(new URL("_redirects", dir), `${body}\n`, "utf8");
logger.info(`_redirects written, /latest -> /${STABLE_VERSION}`);
},
},
};
export default defineConfig({
output: "static",
// Slashless URLs. Pages still emit as `x/index.html` (the `directory`
// build format — `file` would leak ".html" into Astro.url.pathname and
// from there into canonicals, breadcrumbs and pagination), but every URL
// the site hands out is extensionless and slashless.
trailingSlash: "never",
// The primary collection holds no documentation: the root sends readers
// to the stable version, as the MkDocs site did. Each version brings the
// redirects of its MkDocs stubs (generated by resources/nimbus/convert.py).
redirects: {
"/": `/${STABLE_VERSION}/introduction`,
// The generated maps are the MkDocs-era URLs, which carried a trailing
// slash on both sides. The keys keep theirs — they are the old links
// still in the wild, and they are what an incoming request looks like
// — while the targets move to the slashless form the site now uses.
...Object.fromEntries(
Object.entries(redirects as Record<string, string>).map(([from, to]) => [from, slashlessHref(to)]),
),
},
// Tailwind v4 via its Vite plugin (the integration Astro recommends for
// Tailwind v4 — replaces the PostCSS plugin, which doesn't build under
// Astro 7's Vite 8 bundler).
vite: {
plugins: [tailwindcss()],
},
// Code blocks use the shared code theme in both modes: src/styles/code-theme.json,
// a copy of phalcon/css/code-theme.json in phalcon/assets. Its colors are
// --code-<role> variables, and src/styles/globals.css gives them the syntax
// tokens of each mode. With a theme of its own, nimbus does not set its two
// GitHub themes.
markdown: {
shikiConfig: {
theme: codeTheme as unknown as ShikiTheme,
},
},
// Hover-prefetch link targets so full-page navigations feel instant without
// a client-side router.
prefetch: {
prefetchAll: true,
defaultStrategy: "hover",
},
integrations: [
cloudflareRedirects,
nimbus(nimbusConfig, {
// Authoring rules are opt-in by design — your repo, your taste. The
// two below are the load-bearing pair: frontmatter has to validate
// against the content schema for the page to render properly, and
// broken internal links are 404s for your readers. Add the others
// (heading hierarchy, code-block language, style, etc.) when you're
// ready to enforce them — see `nimbus-docs lint --help`.
rules: {
"nimbus/frontmatter-shape": "error",
"nimbus/internal-link": "error",
},
// Wrap wide tables so they scroll instead of overflowing the page
// (styled by `.nb-table-scroll` in src/styles/prose.css).
markdown: {
// Heading ids as MkDocs generated them, so that old anchors keep
// working. mermaidFence turns highlighted ```mermaid fences back
// into <pre class="mermaid"> for the client-side renderer.
// slashlessLinks drops the trailing slash the converted content
// still carries on its internal links.
hastPlugins: [tableScroll(), mkdocsHeadingIds(), mermaidFence(), slashlessLinks()],
},
}),
],
});