diff --git a/.changeset/hip-vans-return.md b/.changeset/hip-vans-return.md new file mode 100644 index 000000000000..29853896b435 --- /dev/null +++ b/.changeset/hip-vans-return.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Add the "--experimental-integrations" flag to enable 3rd-party integrations. diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 988bec9b0e17..40bf6d93a569 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -34,6 +34,7 @@ export interface CLIFlags { /** @deprecated */ experimentalStaticBuild?: boolean; experimentalSsr?: boolean; + experimentalIntegrations?: boolean; legacyBuild?: boolean; drafts?: boolean; } @@ -417,6 +418,12 @@ export interface AstroUserConfig { trailingSlash?: 'always' | 'never' | 'ignore'; }; + /** + * Enable experimental support for 3rd-party integrations. + * Default: false + */ + experimentalIntegrations?: boolean; + /** * @docs * @name vite diff --git a/packages/astro/src/core/config.ts b/packages/astro/src/core/config.ts index 85f082274843..884edca71fef 100644 --- a/packages/astro/src/core/config.ts +++ b/packages/astro/src/core/config.ts @@ -73,14 +73,7 @@ export const AstroConfigSchema = z.object({ // preprocess (val) => (Array.isArray(val) ? val.flat(Infinity).filter(Boolean) : val), // validate - z - .array(z.object({ name: z.string(), hooks: z.object({}).passthrough().default({}) })) - .default([]) - // validate: first-party integrations only - // TODO: Add `To use 3rd-party integrations or to create your own, use the --experimental-integrations flag.`, - .refine((arr) => arr.every((integration) => integration.name.startsWith('@astrojs/')), { - message: `Astro integrations are still experimental, and only official integrations are currently supported`, - }) + z.array(z.object({ name: z.string(), hooks: z.object({}).passthrough().default({}) })).default([]) ), adapter: z.object({ name: z.string(), hooks: z.object({}).passthrough().default({}) }).optional(), styleOptions: z @@ -133,6 +126,7 @@ export const AstroConfigSchema = z.object({ }) .optional() .default({}), + experimentalIntegrations: z.boolean().optional().default(false), vite: z.any().optional().default({}), // TODO: we don’t need validation, but can we get better type inference? }); @@ -209,10 +203,24 @@ export async function validateConfig(userConfig: any, root: string): Promise int.name.startsWith('@astrojs/'))) { + throw new Error([ + `Astro integrations are still experimental.`, + ``, + `Only official "@astrojs/*" integrations are currently supported.`, + `To enable 3rd-party integrations, use the "--experimental-integrations" flag.`, + `Breaking changes may occur in this API before Astro v1.0 is released.`, + `` + ].join('\n')); + } + // If successful, return the result as a verified AstroConfig object. + return result; } /** Adds '/' to end of string but doesn’t double-up */ @@ -236,6 +244,7 @@ function resolveFlags(flags: Partial): CLIFlags { host: typeof flags.host === 'string' || typeof flags.host === 'boolean' ? flags.host : undefined, legacyBuild: typeof flags.legacyBuild === 'boolean' ? flags.legacyBuild : false, experimentalSsr: typeof flags.experimentalSsr === 'boolean' ? flags.experimentalSsr : false, + experimentalIntegrations: typeof flags.experimentalIntegrations === 'boolean' ? flags.experimentalIntegrations : false, drafts: typeof flags.drafts === 'boolean' ? flags.drafts : false, }; } @@ -256,6 +265,7 @@ function mergeCLIFlags(astroConfig: AstroUserConfig, flags: CLIFlags) { astroConfig.buildOptions.legacyBuild = false; } } + if (typeof flags.experimentalIntegrations === 'boolean') astroConfig.experimentalIntegrations = flags.experimentalIntegrations; if (typeof flags.drafts === 'boolean') astroConfig.buildOptions.drafts = flags.drafts; return astroConfig; } @@ -311,6 +321,7 @@ export async function loadConfig(configOptions: LoadConfigOptions): Promise { const mergedConfig = mergeCLIFlags(userConfig, flags); const validatedConfig = await validateConfig(mergedConfig, root); + return validatedConfig; } diff --git a/packages/astro/test/config-validate.test.js b/packages/astro/test/config-validate.test.js index b8ac459ba131..b1079b6f499b 100644 --- a/packages/astro/test/config-validate.test.js +++ b/packages/astro/test/config-validate.test.js @@ -64,11 +64,10 @@ describe('Config Validation', () => { }); it('blocks third-party "integration" values', async () => { const configError = await validateConfig({ integrations: [{ name: '@my-plugin/a' }] }, process.cwd()).catch((err) => err); - expect(configError instanceof z.ZodError).to.equal(true); - const formattedError = stripAnsi(formatConfigError(configError)); - expect(formattedError).to.equal( - `[config] Astro found issue(s) with your configuration: - ! integrations Astro integrations are still experimental, and only official integrations are currently supported.` - ); + expect(configError).to.be.instanceOf(Error); + expect(configError.message).to.include('Astro integrations are still experimental.'); + }); + it('allows third-party "integration" values with the --experimental-integrations flag', async () => { + await validateConfig({ integrations: [{ name: '@my-plugin/a' }], experimentalIntegrations: true }, process.cwd()).catch((err) => err); }); });