From 9b0114c7d3f82914f4443c865ac38d5859fbbceb Mon Sep 17 00:00:00 2001 From: Chris Swithinbank Date: Wed, 27 Sep 2023 21:17:27 +0200 Subject: [PATCH] Support integrations added in updateConfig() in astro:config:setup (#8672) Co-authored-by: Sarah Rainsberger --- .changeset/cold-schools-yell.md | 34 +++++++++++ packages/astro/src/integrations/index.ts | 5 +- .../astro/test/units/integrations/api.test.js | 58 ++++++++++++++++++- 3 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 .changeset/cold-schools-yell.md diff --git a/.changeset/cold-schools-yell.md b/.changeset/cold-schools-yell.md new file mode 100644 index 000000000..7d6b4f381 --- /dev/null +++ b/.changeset/cold-schools-yell.md @@ -0,0 +1,34 @@ +--- +'astro': minor +--- + +Support adding integrations dynamically + +Astro integrations can now themselves dynamically add and configure additional integrations during set-up. This makes it possible for integration authors to bundle integrations more intelligently for their users. + +In the following example, a custom integration checks whether `@astrojs/sitemap` is already configured. If not, the integration adds Astro’s sitemap integration, passing any desired configuration options: + +```ts +import sitemap from '@astrojs/sitemap'; +import type { AstroIntegration } from 'astro'; + +const MyIntegration = (): AstroIntegration => { + return { + name: 'my-integration', + + 'astro:config:setup': ({ config, updateConfig }) => { + // Look for sitemap in user-configured integrations. + const userSitemap = config.integrations.find( + ({ name }) => name === '@astrojs/sitemap' + ); + + if (!userSitemap) { + // If sitemap wasn’t found, add it. + updateConfig({ + integrations: [sitemap({ /* opts */ }], + }); + } + }, + }; +}; +``` diff --git a/packages/astro/src/integrations/index.ts b/packages/astro/src/integrations/index.ts index c5c92a4e4..8d557cae7 100644 --- a/packages/astro/src/integrations/index.ts +++ b/packages/astro/src/integrations/index.ts @@ -75,7 +75,10 @@ export async function runHookConfigSetup({ let addedClientDirectives = new Map>(); let astroJSXRenderer: AstroRenderer | null = null; - for (const integration of settings.config.integrations) { + // eslint-disable-next-line @typescript-eslint/prefer-for-of -- We need a for loop to be able to read integrations pushed while the loop is running. + for (let i = 0; i < updatedConfig.integrations.length; i++) { + const integration = updatedConfig.integrations[i]; + /** * By making integration hooks optional, Astro can now ignore null or undefined Integrations * instead of giving an internal error most people can't read diff --git a/packages/astro/test/units/integrations/api.test.js b/packages/astro/test/units/integrations/api.test.js index 0a7d57929..0940a78f7 100644 --- a/packages/astro/test/units/integrations/api.test.js +++ b/packages/astro/test/units/integrations/api.test.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { runHookBuildSetup } from '../../../dist/integrations/index.js'; +import { runHookBuildSetup, runHookConfigSetup } from '../../../dist/integrations/index.js'; import { validateSupportedFeatures } from '../../../dist/integrations/astroFeaturesValidation.js'; import { defaultLogger } from '../test-utils.js'; @@ -29,6 +29,62 @@ describe('Integration API', () => { }); expect(updatedViteConfig).to.haveOwnProperty('define'); }); + + it('runHookConfigSetup can update Astro config', async () => { + const site = 'https://test.com/'; + const updatedSettings = await runHookConfigSetup({ + logger: defaultLogger, + settings: { + config: { + integrations: [ + { + name: 'test', + hooks: { + "astro:config:setup": ({ updateConfig }) => { + updateConfig({ site }); + } + }, + }, + ], + }, + }, + }); + expect(updatedSettings.config.site).to.equal(site); + }); + + it('runHookConfigSetup runs integrations added by another integration', async () => { + const site = 'https://test.com/'; + const updatedSettings = await runHookConfigSetup({ + logger: defaultLogger, + settings: { + config: { + integrations: [ + { + name: 'test', + hooks: { + "astro:config:setup": ({ updateConfig }) => { + updateConfig({ + integrations: [{ + name: 'dynamically-added', + hooks: { + // eslint-disable-next-line @typescript-eslint/no-shadow + "astro:config:setup": ({ updateConfig }) => { + updateConfig({ site }); + } + }, + }], + }); + } + }, + }, + ], + }, + }, + }); + expect(updatedSettings.config.site).to.equal(site); + expect(updatedSettings.config.integrations.length).to.equal(2); + }); + }); describe('Astro feature map', function () {