Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/astro/src/core/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ import { mergeConfig } from './merge.js';
import { validateConfig } from './validate.js';
import { loadConfigWithVite } from './vite-load.js';

/**
* Resolve the project root directory to an absolute path.
* Falls back to `process.cwd()` when no directory is given.
* @param cwd A relative or absolute path, or a `file://` URL.
*/
export function resolveRoot(cwd?: string | URL): string {
if (cwd instanceof URL) {
cwd = fileURLToPath(cwd);
Expand Down
7 changes: 7 additions & 0 deletions packages/astro/src/core/config/merge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ function mergeConfigRecursively(
return merged;
}

/**
* Deep-merge two Astro config objects. Arrays are concatenated, objects are
* recursed, and special keys (`vite`, `server`, `markdown.processor`) receive
* bespoke handling so that overrides behave predictably.
* @param defaults The base config (typically from the user's config file).
* @param overrides Partial overrides (e.g. from integrations or inline config).
*/
export function mergeConfig<C extends AstroConfig | AstroInlineConfig>(
defaults: C,
overrides: DeepPartial<C>,
Expand Down
3 changes: 3 additions & 0 deletions packages/astro/src/core/config/schemas/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export type RemarkRehype = ComplexifyWithOmit<_RemarkRehype>;
/** @lintignore */
export type Smartypants = ComplexifyWithOmit<_Smartypants>;

/** Default values for every Astro config field, used as fallbacks during validation. */
export const ASTRO_CONFIG_DEFAULTS = {
root: '.',
srcDir: './src',
Expand Down Expand Up @@ -152,6 +153,7 @@ const smartypantsOptionsSchema: z.ZodType<Smartypants> = z.object({
quotes: z.boolean().default(true),
});

/** Base Zod schema for the full Astro user configuration. Paths are raw strings at this stage; the relative schema applies file-system transforms. */
export const AstroConfigSchema = z.object({
root: z
.string()
Expand Down Expand Up @@ -610,4 +612,5 @@ export const AstroConfigSchema = z.object({
.prefault({}),
});

/** Inferred TypeScript type produced by parsing `AstroConfigSchema`. */
export type AstroConfigType = z.infer<typeof AstroConfigSchema>;
6 changes: 6 additions & 0 deletions packages/astro/src/core/config/schemas/refined.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ import {
validateRemotePatterns,
} from './refined-validators.js';

/**
* Second-pass validation schema that runs cross-field semantic checks on a
* fully-parsed `AstroConfig` (e.g. i18n consistency, asset prefix shape,
* font CSS variable format). Used after the base schema and the relative
* transforms have both completed.
*/
export const AstroConfigRefinedSchema = z.custom<AstroConfig>().superRefine((config, ctx) => {
let issues: ConfigValidationIssue[] = [];
issues = issues.concat(
Expand Down
9 changes: 9 additions & 0 deletions packages/astro/src/core/config/schemas/relative.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ function resolveDirAsUrl(dir: string, root: string) {
return pathToFileURL(resolvedDir);
}

/**
* Extend the base `AstroConfigSchema` with path transforms that resolve
* directory strings (`root`, `srcDir`, `publicDir`, `outDir`, `cacheDir`,
* `build.client`, `build.server`) relative to a given filesystem root.
* Also normalizes `base` and `image.endpoint.route` according to
* `trailingSlash` and handles `server` as a function-or-object union.
* @param cmd The running Astro command (e.g. `'dev'`, `'build'`).
* @param fileProtocolRoot Absolute filesystem root used for path resolution.
*/
export function createRelativeSchema(cmd: string, fileProtocolRoot: string) {
let originalBuildClient: string;
let originalBuildServer: string;
Expand Down
14 changes: 14 additions & 0 deletions packages/astro/src/core/config/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ import {
import { AstroTimer } from './timer.js';
import { loadTSConfig } from './tsconfig.js';

/**
* Create an `AstroSettings` object from a fully-resolved `AstroConfig`,
* populating default content-entry types, client directives, and other
* runtime bookkeeping without loading a tsconfig.
* @param config Resolved Astro configuration.
* @param logLevel Logging verbosity for the current session.
*/
export function createBaseSettings(
config: AstroConfig,
logLevel: AstroInlineConfig['logLevel'],
Expand Down Expand Up @@ -165,6 +172,13 @@ export function createBaseSettings(
};
}

/**
* Create an `AstroSettings` object from a fully-resolved `AstroConfig`,
* including tsconfig loading and watch-file registration.
* @param config Resolved Astro configuration.
* @param logLevel Logging verbosity for the current session.
* @param cwd Optional working directory used to locate `tsconfig.json`.
*/
export async function createSettings(
config: AstroConfig,
logLevel: AstroInlineConfig['logLevel'],
Expand Down
15 changes: 15 additions & 0 deletions packages/astro/src/core/config/tsconfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@ import { readTsconfig, type TsconfigResult } from 'get-tsconfig';
import { parse as parseJsonc, type ParseError } from 'jsonc-parser';
import type { CompilerOptions, TypeAcquisition } from 'typescript';

/** Default tsconfig that extends Astro's base configuration. */
export const defaultTSConfig: TSConfig = { extends: 'astro/tsconfigs/base' };

/** Frameworks that require custom TypeScript compiler settings for JSX support. */
export type frameworkWithTSSettings = 'vue' | 'react' | 'preact' | 'solid-js';
// The following presets unfortunately cannot be inside the specific integrations, as we need
// them even in cases where the integrations are not installed
/**
* Per-framework tsconfig presets merged when a framework integration is detected.
* Each entry maps to compiler options needed for that framework's JSX transform.
*/
export const presets = new Map<frameworkWithTSSettings, TSConfig>([
[
'vue', // Settings needed for template intellisense when using Volar
Expand Down Expand Up @@ -47,6 +53,7 @@ export const presets = new Map<frameworkWithTSSettings, TSConfig>([
],
]);

/** Result of successfully loading and resolving a tsconfig/jsconfig file. */
export interface TSConfigLoadedResult {
error?: undefined;
/** Absolute path of the root tsconfig/jsconfig file that was loaded. */
Expand All @@ -62,6 +69,7 @@ export interface TSConfigLoadedResult {
sources: string[];
}

/** Discriminated union of tsconfig load outcomes: success, parse error, or missing file. */
export type TSConfigResult =
| TSConfigLoadedResult
| { error: 'invalid-config'; message: string }
Expand Down Expand Up @@ -137,6 +145,12 @@ export async function loadTSConfig(root: string | undefined): Promise<TSConfigRe
};
}

/**
* Merge framework-specific TypeScript compiler options into an existing tsconfig.
* Returns the target unchanged if the framework has no preset.
* @param target The tsconfig to augment.
* @param framework The framework whose preset should be merged in.
*/
export function updateTSConfigForFramework(
target: TSConfig,
framework: frameworkWithTSSettings,
Expand Down Expand Up @@ -190,6 +204,7 @@ type StripEnums<T extends Record<string, any>> = {
: any;
};

/** Subset of `tsconfig.json` fields that Astro reads and manipulates. */
export interface TSConfig {
compilerOptions?: StripEnums<CompilerOptions>;
compileOnSave?: boolean;
Expand Down
8 changes: 8 additions & 0 deletions packages/astro/src/core/config/vite-load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ interface LoadConfigWithViteOptions {
fs: typeof fsType;
}

/**
* Load an Astro config file using Vite's module runner as a fallback when
* native Node.js `import()` fails (e.g. for `.ts` files). Closes the
* temporary Vite dev server after loading.
* @param options.root Project root directory.
* @param options.configPath Absolute path to the config file.
* @param options.fs Filesystem module to use for file operations.
*/
export async function loadConfigWithVite({
configPath,
fs,
Expand Down
Loading