cloudflare-docs/astro.config.ts

274 lines
7 KiB
TypeScript

import { defineConfig } from "astro/config";
import starlight from "@astrojs/starlight";
import starlightDocSearch from "@astrojs/starlight-docsearch";
import starlightImageZoom from "starlight-image-zoom";
import liveCode from "astro-live-code";
import starlightLinksValidator from "starlight-links-validator";
import starlightScrollToTop from "starlight-scroll-to-top";
import icon from "astro-icon";
import sitemap from "@astrojs/sitemap";
import react from "@astrojs/react";
import { readdir } from "fs/promises";
import { fileURLToPath } from "url";
import { execSync } from "child_process";
import { existsSync } from "fs";
import remarkValidateImages from "./src/plugins/remark/validate-images";
import rehypeTitleFigure from "rehype-title-figure";
import rehypeMermaid from "./src/plugins/rehype/mermaid.ts";
import rehypeAutolinkHeadings from "./src/plugins/rehype/autolink-headings.ts";
import rehypeExternalLinks from "./src/plugins/rehype/external-links.ts";
import rehypeHeadingSlugs from "./src/plugins/rehype/heading-slugs.ts";
import rehypeShiftHeadings from "./src/plugins/rehype/shift-headings.ts";
async function autogenSections() {
const sections = (
await readdir("./src/content/docs/", {
withFileTypes: true,
})
)
.filter((x) => x.isDirectory())
.map((x) => x.name);
return sections.map((x) => {
return {
label: x,
autogenerate: {
directory: x,
collapsed: true,
},
};
});
}
async function autogenStyles() {
const styles = (
await readdir("./src/styles/", {
withFileTypes: true,
recursive: true,
})
)
.filter((x) => x.isFile())
.map((x) => x.parentPath + x.name)
.sort((a) => (a === "./src/styles/tailwind.css" ? -1 : 1));
return styles;
}
const sidebar = await autogenSections();
const customCss = await autogenStyles();
const runLinkCheck = process.env.RUN_LINK_CHECK || false;
/**
* Get the last Git modification date for a file
* @param filePath - Absolute path to the file
* @returns ISO date string or null if not available
*/
function getGitLastModified(filePath: string): string | null {
try {
const result = execSync(`git log -1 --format=%cI -- "${filePath}"`, {
encoding: "utf-8",
}).trim();
return result || null;
} catch (_error) {
return null;
}
}
/**
* Convert a sitemap URL to the corresponding source file path
* @param url - The full URL from the sitemap
* @returns Absolute file path or null if not found
*/
function urlToFilePath(url: string): string | null {
try {
const urlObj = new URL(url);
const pathname = urlObj.pathname.replace(/\/$/, ""); // Remove trailing slash
// Try different file extensions and paths
const possiblePaths = [
`./src/content/docs${pathname}.md`,
`./src/content/docs${pathname}.mdx`,
`./src/content/docs${pathname}/index.md`,
`./src/content/docs${pathname}/index.mdx`,
];
for (const path of possiblePaths) {
if (existsSync(path)) {
return path;
}
}
return null;
} catch (_error) {
return null;
}
}
// https://astro.build/config
export default defineConfig({
site: "https://developers.cloudflare.com",
markdown: {
smartypants: false,
remarkPlugins: [remarkValidateImages],
rehypePlugins: [
rehypeMermaid,
rehypeExternalLinks,
rehypeHeadingSlugs,
rehypeAutolinkHeadings,
// @ts-expect-error plugins types are outdated but functional
rehypeTitleFigure,
rehypeShiftHeadings,
],
},
image: {
service: {
entrypoint: "astro/assets/services/sharp",
config: {
limitInputPixels: false,
},
},
},
experimental: {
contentIntellisense: true,
},
server: {
port: 1111,
},
integrations: [
starlight({
title: "Cloudflare Docs",
logo: {
src: "./src/assets/logo.svg",
},
favicon: "/favicon.png",
social: [
{
label: "GitHub",
icon: "github",
href: "https://github.com/cloudflare/cloudflare-docs",
},
{ label: "X.com", icon: "x.com", href: "https://x.com/cloudflare" },
{
label: "YouTube",
icon: "youtube",
href: "https://www.youtube.com/cloudflare",
},
],
editLink: {
baseUrl:
"https://github.com/cloudflare/cloudflare-docs/edit/production/",
},
components: {
Banner: "./src/components/overrides/Banner.astro",
Footer: "./src/components/overrides/Footer.astro",
Head: "./src/components/overrides/Head.astro",
Header: "./src/components/overrides/Header.astro",
Hero: "./src/components/overrides/Hero.astro",
MarkdownContent: "./src/components/overrides/MarkdownContent.astro",
Sidebar: "./src/components/overrides/Sidebar.astro",
PageTitle: "./src/components/overrides/PageTitle.astro",
TableOfContents: "./src/components/overrides/TableOfContents.astro",
},
sidebar,
customCss,
pagination: false,
plugins: [
...(runLinkCheck
? [
starlightLinksValidator({
errorOnInvalidHashes: false,
errorOnLocalLinks: false,
exclude: [
"/api/",
"/api/**",
"/changelog/**",
"/http/resources/**",
"/llms.txt",
"/llms-full.txt",
"{props.*}",
"/",
"/glossary/",
"/directory/",
"/rules/snippets/examples/?operation=*",
"/rules/transform/examples/?operation=*",
"/ruleset-engine/rules-language/fields/reference/**",
"/workers/examples/?languages=*",
"/workers/examples/?tags=*",
"/workers/llms-full.txt",
"/workers-ai/models/**",
"**index.md",
"/markdown.zip",
"/style-guide/index.md",
"/style-guide/fixtures/markdown/index.md",
],
}),
]
: []),
starlightDocSearch({
clientOptionsModule: "./src/plugins/docsearch/index.ts",
}),
starlightImageZoom(),
starlightScrollToTop({
tooltipText: "Back to top",
showTooltip: true,
svgPath: "M12 6L6 12M12 6L18 12M12 12L6 18M12 12L18 18",
showProgressRing: true,
progressRingColor: "white",
showOnHomepage: false, // Hide on homepage (default)
}),
],
lastUpdated: true,
markdown: {
headingLinks: false,
},
routeMiddleware: "./src/plugins/starlight/route-data.ts",
disable404Route: true,
}),
liveCode({}),
icon(),
sitemap({
filter(page) {
if (page.includes("/style-guide/")) {
return false;
}
if (page.endsWith("/404/")) {
return false;
}
return true;
},
serialize(item) {
const filePath = urlToFilePath(item.url);
if (filePath) {
const gitDate = getGitLastModified(filePath);
if (gitDate) {
item.lastmod = gitDate;
}
} else {
console.warn(
`[sitemap] Could not find last modified for ${item.url} - setting to now`,
);
item.lastmod = new Date().toISOString();
}
return item;
},
}),
react(),
],
vite: {
resolve: {
alias: {
"./Page.astro": fileURLToPath(
new URL("./src/components/overrides/Page.astro", import.meta.url),
),
"../components/Page.astro": fileURLToPath(
new URL("./src/components/overrides/Page.astro", import.meta.url),
),
},
},
},
});