import react, { reactCompilerPreset } from "@vitejs/plugin-react" import babel from "@rolldown/plugin-babel" import tailwindcss from "@tailwindcss/vite" import { icones } from "@icones/vite" import { readFile } from "node:fs/promises" import expressiveCode from "satteri-expressive-code" import { fileURLToPath } from "node:url" import { defineConfig, type Plugin } from "vite" import { satteri } from "vite-plugin-satteri" import { parse } from "yaml" const packagesDirectory = fileURLToPath(new URL("../packages", import.meta.url)) /** * Vite 默认监听 docs 根目录。现有的外部文档与元数据会作为模块被监听,但新建 * package、meta.yaml 或 MDX 文件在首次进入模块图之前不会触发 glob 重新计算。 */ function watchPackageDocs(): Plugin { return { name: "watch-package-docs", configureServer(server) { server.watcher.add(packagesDirectory) }, } } function loadYamlModules(): Plugin { return { name: "load-yaml-modules", enforce: "pre", transform(source, id) { if (!/\/meta\.ya?ml(?:\?|$)/.test(id)) return try { return { code: `export default ${JSON.stringify(parse(source))}`, map: null, } } catch (error) { this.error( `Unable to parse ${id}: ${error instanceof Error ? error.message : String(error)}` ) } }, } } function loadDocSearchSources(): Plugin { const suffix = "?docs-search-raw" const virtualPrefix = "\0docs-search-raw:" return { name: "load-doc-search-sources", enforce: "pre", async resolveId(id, importer) { if (!id.endsWith(suffix)) return const sourceId = id.slice(0, -suffix.length) const resolved = await this.resolve(sourceId, importer, { skipSelf: true, }) const filename = resolved?.id ?? sourceId return `${virtualPrefix}${encodeURIComponent(filename)}.js` }, async load(id) { if (!id.startsWith(virtualPrefix)) return const sourceId = decodeURIComponent( id.slice(virtualPrefix.length, -".js".length) ) const filename = sourceId.startsWith("/@fs/") ? sourceId.slice("/@fs".length) : sourceId this.addWatchFile(filename) const source = await readFile(filename, "utf8") return `export default ${JSON.stringify(source)}` }, } } // https://vite.dev/config/ export default defineConfig({ plugins: [ watchPackageDocs(), loadYamlModules(), loadDocSearchSources(), satteri({ features: { frontmatter: true, gfm: true, headingAttributes: true, }, mdx: { jsxImportSource: "react", }, hastPlugins: [ expressiveCode({ frames: false, textMarkers: false, themes: ["github-light", "github-dark"], useDarkModeMediaQuery: false, themeCssSelector: (theme) => theme.type === "dark" ? ".dark" : ":root:not(.dark)", styleOverrides: { borderRadius: "0.75rem", borderWidth: "1px", codeFontSize: "0.875rem", codeLineHeight: "1.5rem", codePaddingInline: "1rem", }, }), ], }), tailwindcss(), icones({ mode: "symbol", emitData: false, }), react(), babel({ presets: [reactCompilerPreset()] }), ], })