mirror of
https://github.com/withastro/astro.git
synced 2025-03-10 23:01:26 -05:00
feat(next): make defineConfig generic (#12243)
Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>
This commit is contained in:
parent
166ea961fe
commit
eb41d13162
10 changed files with 127 additions and 84 deletions
5
.changeset/forty-trains-notice.md
Normal file
5
.changeset/forty-trains-notice.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': minor
|
||||
---
|
||||
|
||||
Improves `defineConfig` type safety. TypeScript will now error if a group of related configuration options do not have consistent types. For example, you will now see an error if your language set for `i18n.defaultLocale` is not one of the supported locales specified in `i18n.locales`.
|
40
packages/astro/config.d.ts
vendored
40
packages/astro/config.d.ts
vendored
|
@ -1,40 +0,0 @@
|
|||
/// <reference path='./client.d.ts' />
|
||||
type ViteUserConfig = import('vite').UserConfig;
|
||||
type ViteUserConfigFn = import('vite').UserConfigFn;
|
||||
type AstroUserConfig = import('./dist/types/public/config.js').AstroUserConfig;
|
||||
type AstroInlineConfig = import('./dist/types/public/config.js').AstroInlineConfig;
|
||||
type ImageServiceConfig = import('./dist/types/public/config.js').ImageServiceConfig;
|
||||
type SharpImageServiceConfig = import('./dist/assets/services/sharp.js').SharpImageServiceConfig;
|
||||
type EnvField = typeof import('./dist/env/config.js').envField;
|
||||
|
||||
/**
|
||||
* See the full Astro Configuration API Documentation
|
||||
* https://astro.build/config
|
||||
*/
|
||||
export function defineConfig(config: AstroUserConfig): AstroUserConfig;
|
||||
|
||||
/**
|
||||
* Use Astro to generate a fully resolved Vite config
|
||||
*/
|
||||
export function getViteConfig(
|
||||
config: ViteUserConfig,
|
||||
inlineAstroConfig?: AstroInlineConfig,
|
||||
): ViteUserConfigFn;
|
||||
|
||||
/**
|
||||
* Return the configuration needed to use the Sharp-based image service
|
||||
*/
|
||||
export function sharpImageService(config?: SharpImageServiceConfig): ImageServiceConfig;
|
||||
|
||||
/**
|
||||
* Return the configuration needed to use the passthrough image service. This image services does not perform
|
||||
* any image transformations, and is mainly useful when your platform does not support other image services, or you are
|
||||
* not using Astro's built-in image processing.
|
||||
* See: https://docs.astro.build/en/guides/images/#configure-no-op-passthrough-service
|
||||
*/
|
||||
export function passthroughImageService(): ImageServiceConfig;
|
||||
|
||||
/**
|
||||
* Return a valid env field to use in this Astro config for `experimental.env.schema`.
|
||||
*/
|
||||
export declare const envField: EnvField;
|
|
@ -1,16 +0,0 @@
|
|||
export { defineConfig, getViteConfig } from './dist/config/index.js';
|
||||
export { envField } from './dist/env/config.js';
|
||||
|
||||
export function sharpImageService(config = {}) {
|
||||
return {
|
||||
entrypoint: 'astro/assets/services/sharp',
|
||||
config,
|
||||
};
|
||||
}
|
||||
|
||||
export function passthroughImageService() {
|
||||
return {
|
||||
entrypoint: 'astro/assets/services/noop',
|
||||
config: {},
|
||||
};
|
||||
}
|
|
@ -46,10 +46,7 @@
|
|||
},
|
||||
"./compiler-runtime": "./dist/runtime/compiler/index.js",
|
||||
"./runtime/*": "./dist/runtime/*",
|
||||
"./config": {
|
||||
"types": "./config.d.ts",
|
||||
"default": "./config.mjs"
|
||||
},
|
||||
"./config": "./dist/config/entrypoint.js",
|
||||
"./container": {
|
||||
"types": "./dist/container/index.d.ts",
|
||||
"default": "./dist/container/index.js"
|
||||
|
@ -93,8 +90,6 @@
|
|||
"types",
|
||||
"astro.js",
|
||||
"index.d.ts",
|
||||
"config.d.ts",
|
||||
"config.mjs",
|
||||
"zod.d.ts",
|
||||
"zod.mjs",
|
||||
"env.d.ts",
|
||||
|
|
30
packages/astro/src/config/entrypoint.ts
Normal file
30
packages/astro/src/config/entrypoint.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
// IMPORTANT: this file is the entrypoint for "astro/config". Keep it as light as possible!
|
||||
|
||||
import type { SharpImageServiceConfig } from '../assets/services/sharp.js';
|
||||
import type { ImageServiceConfig } from '../types/public/index.js';
|
||||
|
||||
export { defineConfig, getViteConfig } from './index.js';
|
||||
export { envField } from '../env/config.js';
|
||||
|
||||
/**
|
||||
* Return the configuration needed to use the Sharp-based image service
|
||||
*/
|
||||
export function sharpImageService(config: SharpImageServiceConfig = {}): ImageServiceConfig {
|
||||
return {
|
||||
entrypoint: 'astro/assets/services/sharp',
|
||||
config,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configuration needed to use the passthrough image service. This image services does not perform
|
||||
* any image transformations, and is mainly useful when your platform does not support other image services, or you are
|
||||
* not using Astro's built-in image processing.
|
||||
* See: https://docs.astro.build/en/guides/images/#configure-no-op-passthrough-service
|
||||
*/
|
||||
export function passthroughImageService(): ImageServiceConfig {
|
||||
return {
|
||||
entrypoint: 'astro/assets/services/noop',
|
||||
config: {},
|
||||
};
|
||||
}
|
|
@ -1,13 +1,22 @@
|
|||
import type { UserConfig as ViteUserConfig, UserConfigFn as ViteUserConfigFn } from 'vite';
|
||||
import { Logger } from '../core/logger/core.js';
|
||||
import { createRouteManifest } from '../core/routing/index.js';
|
||||
import type { AstroInlineConfig, AstroUserConfig } from '../types/public/config.js';
|
||||
import type { AstroInlineConfig, AstroUserConfig, Locales } from '../types/public/config.js';
|
||||
import { createDevelopmentManifest } from '../vite-plugin-astro-server/plugin.js';
|
||||
|
||||
export function defineConfig(config: AstroUserConfig) {
|
||||
/**
|
||||
* See the full Astro Configuration API Documentation
|
||||
* https://astro.build/config
|
||||
*/
|
||||
export function defineConfig<const TLocales extends Locales = never>(
|
||||
config: AstroUserConfig<TLocales>,
|
||||
) {
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use Astro to generate a fully resolved Vite config
|
||||
*/
|
||||
export function getViteConfig(
|
||||
userViteConfig: ViteUserConfig,
|
||||
inlineAstroConfig: AstroInlineConfig = {},
|
||||
|
|
|
@ -1160,8 +1160,8 @@ export const UnhandledRejection = {
|
|||
* import { defineConfig } from 'astro'
|
||||
* export default defineConfig({
|
||||
* i18n: {
|
||||
* defaultLocale: 'en',
|
||||
* locales: ['en', 'fr'],
|
||||
* defaultLocale: 'en',
|
||||
* },
|
||||
* })
|
||||
* ```
|
||||
|
|
2
packages/astro/src/env/config.ts
vendored
2
packages/astro/src/env/config.ts
vendored
|
@ -10,7 +10,7 @@ import type {
|
|||
} from './schema.js';
|
||||
|
||||
/**
|
||||
* Return a valid env field to use in this Astro config for `experimental.env.schema`.
|
||||
* Return a valid env field to use in this Astro config for `env.schema`.
|
||||
*/
|
||||
export const envField = {
|
||||
string: (options: StringFieldInput): StringField => ({
|
||||
|
|
|
@ -16,6 +16,14 @@ import type { AstroIntegration } from './integrations.js';
|
|||
|
||||
export type Locales = (string | { codes: string[]; path: string })[];
|
||||
|
||||
type NormalizeLocales<T extends Locales> = {
|
||||
[K in keyof T]: T[K] extends string
|
||||
? T[K]
|
||||
: T[K] extends { codes: Array<string> }
|
||||
? T[K]['codes'][number]
|
||||
: never;
|
||||
}[number];
|
||||
|
||||
export interface ImageServiceConfig<T extends Record<string, any> = Record<string, any>> {
|
||||
entrypoint: 'astro/assets/services/sharp' | (string & {});
|
||||
config?: T;
|
||||
|
@ -101,8 +109,9 @@ export interface ViteUserConfig extends OriginalViteUserConfig {
|
|||
/**
|
||||
* Astro User Config
|
||||
* Docs: https://docs.astro.build/reference/configuration-reference/
|
||||
*/
|
||||
export interface AstroUserConfig {
|
||||
*
|
||||
* Generics do not follow semver and may change at any time.
|
||||
*/ export interface AstroUserConfig<TLocales extends Locales = never> {
|
||||
/**
|
||||
* @docs
|
||||
* @kind heading
|
||||
|
@ -1203,18 +1212,6 @@ export interface AstroUserConfig {
|
|||
* See our guide for more information on [internationalization in Astro](/en/guides/internationalization/)
|
||||
*/
|
||||
i18n?: {
|
||||
/**
|
||||
* @docs
|
||||
* @name i18n.defaultLocale
|
||||
* @type {string}
|
||||
* @version 3.5.0
|
||||
* @description
|
||||
*
|
||||
* The default locale of your website/application. This is a required field.
|
||||
*
|
||||
* No particular language format or syntax is enforced, but we suggest using lower-case and hyphens as needed (e.g. "es", "pt-br") for greatest compatibility.
|
||||
*/
|
||||
defaultLocale: string;
|
||||
/**
|
||||
* @docs
|
||||
* @name i18n.locales
|
||||
|
@ -1222,13 +1219,26 @@ export interface AstroUserConfig {
|
|||
* @version 3.5.0
|
||||
* @description
|
||||
*
|
||||
* A list of all locales supported by the website, including the `defaultLocale`. This is a required field.
|
||||
* A list of all locales supported by the website. This is a required field.
|
||||
*
|
||||
* Languages can be listed either as individual codes (e.g. `['en', 'es', 'pt-br']`) or mapped to a shared `path` of codes (e.g. `{ path: "english", codes: ["en", "en-US"]}`). These codes will be used to determine the URL structure of your deployed site.
|
||||
*
|
||||
* No particular language code format or syntax is enforced, but your project folders containing your content files must match exactly the `locales` items in the list. In the case of multiple `codes` pointing to a custom URL path prefix, store your content files in a folder with the same name as the `path` configured.
|
||||
*/
|
||||
locales: Locales;
|
||||
locales: [TLocales] extends [never] ? Locales : TLocales;
|
||||
|
||||
/**
|
||||
* @docs
|
||||
* @name i18n.defaultLocale
|
||||
* @type {string}
|
||||
* @version 3.5.0
|
||||
* @description
|
||||
*
|
||||
* The default locale of your website/application, that is one of the specified `locales`. This is a required field.
|
||||
*
|
||||
* No particular language format or syntax is enforced, but we suggest using lower-case and hyphens as needed (e.g. "es", "pt-br") for greatest compatibility.
|
||||
*/
|
||||
defaultLocale: [TLocales] extends [never] ? string : NormalizeLocales<NoInfer<TLocales>>;
|
||||
|
||||
/**
|
||||
* @docs
|
||||
|
@ -1258,7 +1268,14 @@ export interface AstroUserConfig {
|
|||
* })
|
||||
* ```
|
||||
*/
|
||||
fallback?: Record<string, string>;
|
||||
fallback?: [TLocales] extends [never]
|
||||
? Record<string, string>
|
||||
: {
|
||||
[Locale in NormalizeLocales<NoInfer<TLocales>>]?: Exclude<
|
||||
NormalizeLocales<NoInfer<TLocales>>,
|
||||
Locale
|
||||
>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @docs
|
||||
|
@ -1444,7 +1461,9 @@ export interface AstroUserConfig {
|
|||
*
|
||||
* See the [Internationalization Guide](https://docs.astro.build/en/guides/internationalization/#domains) for more details, including the limitations of this feature.
|
||||
*/
|
||||
domains?: Record<string, string>;
|
||||
domains?: [TLocales] extends [never]
|
||||
? Record<string, string>
|
||||
: Partial<Record<NormalizeLocales<NoInfer<TLocales>>, string>>;
|
||||
};
|
||||
|
||||
/** ! WARNING: SUBJECT TO CHANGE */
|
||||
|
|
41
packages/astro/test/types/define-config.ts
Normal file
41
packages/astro/test/types/define-config.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
import { describe, it } from 'node:test';
|
||||
import { defineConfig } from '../../dist/config/index.js';
|
||||
import type { AstroUserConfig } from '../../dist/types/public/index.js';
|
||||
import { expectTypeOf } from 'expect-type';
|
||||
|
||||
describe('defineConfig()', () => {
|
||||
it('Infers generics correctly', () => {
|
||||
const config_0 = defineConfig({});
|
||||
expectTypeOf(config_0).toEqualTypeOf<AstroUserConfig<never>>();
|
||||
expectTypeOf(config_0.i18n!.defaultLocale).toEqualTypeOf<string>();
|
||||
|
||||
const config_1 = defineConfig({
|
||||
i18n: {
|
||||
locales: ['en'],
|
||||
defaultLocale: 'en',
|
||||
},
|
||||
});
|
||||
expectTypeOf(config_1).toEqualTypeOf<AstroUserConfig<['en']>>();
|
||||
expectTypeOf(config_1.i18n!.defaultLocale).toEqualTypeOf<'en'>();
|
||||
|
||||
const config_2 = defineConfig({
|
||||
i18n: {
|
||||
locales: ['en', 'fr'],
|
||||
defaultLocale: 'fr',
|
||||
},
|
||||
});
|
||||
expectTypeOf(config_2).toEqualTypeOf<AstroUserConfig<['en', 'fr']>>();
|
||||
expectTypeOf(config_2.i18n!.defaultLocale).toEqualTypeOf<'en' | 'fr'>();
|
||||
|
||||
const config_3 = defineConfig({
|
||||
i18n: {
|
||||
locales: ['en', { path: 'french', codes: ['fr', 'fr-FR'] }],
|
||||
defaultLocale: 'en',
|
||||
},
|
||||
});
|
||||
expectTypeOf(config_3).toEqualTypeOf<
|
||||
AstroUserConfig<['en', { readonly path: 'french'; readonly codes: ['fr', 'fr-FR'] }]>
|
||||
>();
|
||||
expectTypeOf(config_3.i18n!.defaultLocale).toEqualTypeOf<'en' | 'fr' | 'fr-FR'>();
|
||||
});
|
||||
});
|
Loading…
Add table
Reference in a new issue