UNPKG

astro

Version:

Astro is a modern site builder with web best practices, performance, and DX front-of-mind.

344 lines (343 loc) • 15 kB
import { markdownConfigDefaults, syntaxHighlightDefaults } from "@astrojs/markdown-remark"; import { bundledThemes } from "shiki"; import * as z from "zod/v4"; import { FontFamilySchema } from "../../../assets/fonts/config.js"; import { SvgOptimizerSchema } from "../../../assets/svg/config.js"; import { EnvSchema } from "../../../env/schema.js"; import { CacheSchema, RouteRulesSchema } from "../../cache/config.js"; import { allowedDirectivesSchema, cspAlgorithmSchema, cspHashSchema } from "../../csp/config.js"; import { SessionSchema } from "../../session/config.js"; const ASTRO_CONFIG_DEFAULTS = { root: ".", srcDir: "./src", publicDir: "./public", outDir: "./dist", cacheDir: "./node_modules/.astro", base: "/", trailingSlash: "ignore", build: { format: "directory", client: "./client/", server: "./server/", assets: "_astro", serverEntry: "entry.mjs", redirects: true, inlineStylesheets: "auto", concurrency: 1 }, image: { endpoint: { entrypoint: void 0, route: "/_image" }, service: { entrypoint: "astro/assets/services/sharp", config: {} }, dangerouslyProcessSVG: false, responsiveStyles: false }, devToolbar: { enabled: true }, compressHTML: true, server: { host: false, port: 4321, open: false, allowedHosts: [] }, integrations: [], markdown: markdownConfigDefaults, vite: {}, legacy: { collectionsBackwardsCompat: false }, redirects: {}, security: { checkOrigin: true, allowedDomains: [], csp: false, actionBodySizeLimit: 1024 * 1024, serverIslandBodySizeLimit: 1024 * 1024 }, env: { schema: {}, validateSecrets: false }, prerenderConflictBehavior: "warn", experimental: { advancedRouting: false, clientPrerender: false, contentIntellisense: false, chromeDevtoolsWorkspace: false, rustCompiler: false, queuedRendering: { enabled: false }, logger: { entrypoint: "astro/logger/node" } } }; const highlighterTypesSchema = z.union([z.literal("shiki"), z.literal("prism")]).default(syntaxHighlightDefaults.type); const quoteCharacterMapSchema = z.object({ double: z.string(), single: z.string() }); const smartypantsOptionsSchema = z.object({ backticks: z.union([z.boolean(), z.literal("all")]).default(true), closingQuotes: quoteCharacterMapSchema.default({ double: "\u201D", single: "\u2019" }), dashes: z.union([z.boolean(), z.literal("inverted"), z.literal("oldschool")]).default(true), ellipses: z.union([z.boolean(), z.literal("spaced"), z.literal("unspaced")]).default(true), openingQuotes: quoteCharacterMapSchema.default({ double: "\u201C", single: "\u2018" }), quotes: z.boolean().default(true) }); const AstroConfigSchema = z.object({ root: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.root).transform((val) => new URL(val)), srcDir: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.srcDir).transform((val) => new URL(val)), publicDir: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.publicDir).transform((val) => new URL(val)), outDir: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.outDir).transform((val) => new URL(val)), cacheDir: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.cacheDir).transform((val) => new URL(val)), site: z.string().url().optional(), compressHTML: z.union([z.boolean(), z.literal("jsx")]).optional().default(ASTRO_CONFIG_DEFAULTS.compressHTML), base: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.base), trailingSlash: z.union([z.literal("always"), z.literal("never"), z.literal("ignore")]).optional().default(ASTRO_CONFIG_DEFAULTS.trailingSlash), output: z.union([z.literal("static"), z.literal("server"), z.literal("hybrid")]).optional().default("static").refine((val) => val !== "hybrid", { message: 'The `output: "hybrid"` option has been removed. Use `output: "static"` (the default) instead, which now behaves the same way.' }), scopedStyleStrategy: z.union([z.literal("where"), z.literal("class"), z.literal("attribute")]).optional().default("attribute"), adapter: z.object({ name: z.string(), hooks: z.object({}).loose().default({}) }).optional(), integrations: z.preprocess( // preprocess (val) => Array.isArray(val) ? val.flat(Number.POSITIVE_INFINITY).filter(Boolean) : val, // validate z.array(z.object({ name: z.string(), hooks: z.object({}).loose().default({}) })) ).optional().default(ASTRO_CONFIG_DEFAULTS.integrations), build: z.object({ format: z.union([z.literal("file"), z.literal("directory"), z.literal("preserve")]).optional().default(ASTRO_CONFIG_DEFAULTS.build.format), client: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.client).transform((val) => new URL(val)), server: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.server).transform((val) => new URL(val)), assets: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.assets), assetsPrefix: z.string().optional().or(z.object({ fallback: z.string() }).and(z.record(z.string(), z.string()))).optional(), serverEntry: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.serverEntry), redirects: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.build.redirects), inlineStylesheets: z.enum(["always", "auto", "never"]).optional().default(ASTRO_CONFIG_DEFAULTS.build.inlineStylesheets), concurrency: z.number().min(1).optional().default(ASTRO_CONFIG_DEFAULTS.build.concurrency) }).prefault({}), server: z.preprocess( // preprocess // NOTE: Uses the "error" command here because this is overwritten by the // individualized schema parser with the correct command. (val) => typeof val === "function" ? val({ command: "error" }) : val, // validate z.object({ open: z.union([z.string(), z.boolean()]).optional().default(ASTRO_CONFIG_DEFAULTS.server.open), host: z.union([z.string(), z.boolean()]).optional().default(ASTRO_CONFIG_DEFAULTS.server.host), port: z.number().optional().default(ASTRO_CONFIG_DEFAULTS.server.port), headers: z.custom().optional(), allowedHosts: z.union([z.array(z.string()), z.literal(true)]).optional().default(ASTRO_CONFIG_DEFAULTS.server.allowedHosts) }) ).prefault({}), redirects: z.record( z.string(), z.union([ z.string(), z.object({ status: z.union([ z.literal(300), z.literal(301), z.literal(302), z.literal(303), z.literal(304), z.literal(307), z.literal(308) ]), destination: z.string() }) ]) ).default(ASTRO_CONFIG_DEFAULTS.redirects), prefetch: z.union([ z.boolean(), z.object({ prefetchAll: z.boolean().optional(), defaultStrategy: z.enum(["tap", "hover", "viewport", "load"]).optional() }) ]).optional(), image: z.object({ endpoint: z.object({ route: z.literal("/_image").or(z.string()).default(ASTRO_CONFIG_DEFAULTS.image.endpoint.route), entrypoint: z.string().optional() }).default(ASTRO_CONFIG_DEFAULTS.image.endpoint), service: z.object({ entrypoint: z.union([z.literal("astro/assets/services/sharp"), z.string()]).default(ASTRO_CONFIG_DEFAULTS.image.service.entrypoint), config: z.record(z.string(), z.any()).default({}) }).default(ASTRO_CONFIG_DEFAULTS.image.service), dangerouslyProcessSVG: z.boolean().default(ASTRO_CONFIG_DEFAULTS.image.dangerouslyProcessSVG), domains: z.array(z.string()).default([]), remotePatterns: z.array( z.object({ protocol: z.string().optional(), hostname: z.string().optional(), port: z.string().optional(), pathname: z.string().optional() }) ).default([]), layout: z.enum(["constrained", "fixed", "full-width", "none"]).optional(), objectFit: z.string().optional(), objectPosition: z.string().optional(), breakpoints: z.array(z.number()).optional(), responsiveStyles: z.boolean().default(ASTRO_CONFIG_DEFAULTS.image.responsiveStyles) }).prefault(ASTRO_CONFIG_DEFAULTS.image), devToolbar: z.object({ enabled: z.boolean().default(ASTRO_CONFIG_DEFAULTS.devToolbar.enabled), placement: z.enum(["bottom-left", "bottom-center", "bottom-right"]).optional() }).default(ASTRO_CONFIG_DEFAULTS.devToolbar), markdown: z.object({ syntaxHighlight: z.union([ z.object({ type: highlighterTypesSchema, excludeLangs: z.array(z.string()).optional() }).default(syntaxHighlightDefaults), highlighterTypesSchema, z.literal(false) ]).default(ASTRO_CONFIG_DEFAULTS.markdown.syntaxHighlight), shikiConfig: z.object({ langs: z.custom().array().transform((langs) => { for (const lang of langs) { if (typeof lang === "object") { if (lang.id) { lang.name = lang.id; } if (lang.grammar) { Object.assign(lang, lang.grammar); } } } return langs; }).default([]), langAlias: z.record(z.string(), z.string()).optional().default(ASTRO_CONFIG_DEFAULTS.markdown.shikiConfig.langAlias), theme: z.enum(Object.keys(bundledThemes)).or(z.custom()).default(ASTRO_CONFIG_DEFAULTS.markdown.shikiConfig.theme), themes: z.record( z.string(), z.enum(Object.keys(bundledThemes)).or(z.custom()) ).optional().default(ASTRO_CONFIG_DEFAULTS.markdown.shikiConfig.themes), defaultColor: z.union([z.literal("light"), z.literal("dark"), z.string(), z.literal(false)]).optional(), wrap: z.boolean().or(z.null()).default(ASTRO_CONFIG_DEFAULTS.markdown.shikiConfig.wrap), transformers: z.custom().array().default(ASTRO_CONFIG_DEFAULTS.markdown.shikiConfig.transformers) }).prefault({}), remarkPlugins: z.union([ z.string(), z.tuple([z.string(), z.any()]), z.custom((data) => typeof data === "function"), z.tuple([z.custom((data) => typeof data === "function"), z.any()]) ]).array().default(ASTRO_CONFIG_DEFAULTS.markdown.remarkPlugins), rehypePlugins: z.union([ z.string(), z.tuple([z.string(), z.any()]), z.custom((data) => typeof data === "function"), z.tuple([z.custom((data) => typeof data === "function"), z.any()]) ]).array().default(ASTRO_CONFIG_DEFAULTS.markdown.rehypePlugins), remarkRehype: z.custom((data) => data instanceof Object && !Array.isArray(data)).default(ASTRO_CONFIG_DEFAULTS.markdown.remarkRehype), gfm: z.boolean().default(ASTRO_CONFIG_DEFAULTS.markdown.gfm), smartypants: z.union([z.boolean(), smartypantsOptionsSchema]).transform((val) => { if (val === true) return smartypantsOptionsSchema.parse({}); return val; }).prefault(ASTRO_CONFIG_DEFAULTS.markdown.smartypants) }).prefault({}), vite: z.custom((data) => data instanceof Object && !Array.isArray(data)).default(ASTRO_CONFIG_DEFAULTS.vite), i18n: z.optional( z.object({ defaultLocale: z.string(), locales: z.array( z.union([ z.string(), z.object({ path: z.string(), codes: z.tuple([z.string()], z.string()) }) ]) ), domains: z.record( z.string(), z.string().url( "The domain value must be a valid URL, and it has to start with 'https' or 'http'." ) ).optional(), fallback: z.record(z.string(), z.string()).optional(), routing: z.literal("manual").or( z.object({ prefixDefaultLocale: z.boolean().optional().default(false), redirectToDefaultLocale: z.boolean().optional().default(false), fallbackType: z.enum(["redirect", "rewrite"]).optional().default("redirect") }) ).optional().prefault({}) }).optional() ), security: z.object({ checkOrigin: z.boolean().default(ASTRO_CONFIG_DEFAULTS.security.checkOrigin), allowedDomains: z.array( z.object({ hostname: z.string().optional(), protocol: z.string().optional(), port: z.string().optional() }) ).optional().default(ASTRO_CONFIG_DEFAULTS.security.allowedDomains), actionBodySizeLimit: z.number().optional().default(ASTRO_CONFIG_DEFAULTS.security.actionBodySizeLimit), serverIslandBodySizeLimit: z.number().optional().default(ASTRO_CONFIG_DEFAULTS.security.serverIslandBodySizeLimit), csp: z.union([ z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.security.csp), z.object({ algorithm: cspAlgorithmSchema, directives: z.array(allowedDirectivesSchema).optional(), styleDirective: z.object({ resources: z.array(z.string()).optional(), hashes: z.array(cspHashSchema).optional() }).optional(), scriptDirective: z.object({ resources: z.array(z.string()).optional(), hashes: z.array(cspHashSchema).optional(), strictDynamic: z.boolean().optional() }).optional() }) ]).optional().default(ASTRO_CONFIG_DEFAULTS.security.csp) }).optional().default(ASTRO_CONFIG_DEFAULTS.security), env: z.object({ schema: EnvSchema.optional().default(ASTRO_CONFIG_DEFAULTS.env.schema), validateSecrets: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.env.validateSecrets) }).strict().optional().default(ASTRO_CONFIG_DEFAULTS.env), session: SessionSchema.optional(), prerenderConflictBehavior: z.enum(["error", "warn", "ignore"]).optional().default(ASTRO_CONFIG_DEFAULTS.prerenderConflictBehavior), fonts: z.array(FontFamilySchema).optional(), experimental: z.strictObject({ advancedRouting: z.union([ z.boolean(), z.strictObject({ fetchFile: z.string().nullable().optional().default("app") }) ]).optional().default(ASTRO_CONFIG_DEFAULTS.experimental.advancedRouting), clientPrerender: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.clientPrerender), contentIntellisense: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.contentIntellisense), chromeDevtoolsWorkspace: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.chromeDevtoolsWorkspace), svgOptimizer: SvgOptimizerSchema.optional(), cache: CacheSchema.optional(), routeRules: RouteRulesSchema.optional(), rustCompiler: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.rustCompiler), queuedRendering: z.object({ enabled: z.boolean().optional().prefault(false), poolSize: z.number().int().nonnegative().optional(), contentCache: z.boolean().optional() }).optional().prefault(ASTRO_CONFIG_DEFAULTS.experimental.queuedRendering), logger: z.object({ entrypoint: z.string(), config: z.record(z.string(), z.any()).optional() }).optional() }).prefault({}), legacy: z.object({ collectionsBackwardsCompat: z.boolean().optional().default(false) }).prefault({}) }); export { ASTRO_CONFIG_DEFAULTS, AstroConfigSchema };