From 3c83116f4c7f5f1a1a620e8c570c0afe65f651d5 Mon Sep 17 00:00:00 2001 From: Luiz Ferraz Date: Fri, 28 Jun 2024 02:47:09 -0300 Subject: [PATCH 1/8] Document how to extend integration hooks --- .../en/reference/integrations-reference.mdx | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/src/content/docs/en/reference/integrations-reference.mdx b/src/content/docs/en/reference/integrations-reference.mdx index 9e5580fbc305a..9bd00ef99ce67 100644 --- a/src/content/docs/en/reference/integrations-reference.mdx +++ b/src/content/docs/en/reference/integrations-reference.mdx @@ -54,6 +54,8 @@ interface AstroIntegration { logger: AstroIntegrationLogger; }) => void | Promise; 'astro:build:done'?: (options: { dir: URL; routes: RouteData[]; logger: AstroIntegrationLogger; }) => void | Promise; + + // ... other integration-defined hooks }; } ``` @@ -613,6 +615,90 @@ A list of all generated pages. It is an object with one property. - `pathname` - the finalized path of the page. +### Defining your own hooks for other integrations + +You can extend the hooks defined for Astro integration by provide the following type augmentation along with your integration code: + +```ts +// your-integration/types.ts +declare global { + namespace Astro { + interface IntegrationHooks { + 'myLib:eventHappened': (your: string, parameters: number) => Promise; + } + } +} +``` + +#### Calling custom hooks on other integrations + +Custom hooks implemented by all the integrations in use can be accessed from the `astro:config:setup` and `astro:config:done` hooks. + +```ts +// your-integration/index.ts +import './types.ts'; + +export default (): AstroIntegration => { + return { + name: 'your-integration', + hooks: { + 'astro:config:setup': async ({ config }) => { + for (const integration of config.integrations) { + await integration.hooks['myLib:eventHappened'].?('your values', 123); + } + }, + } + } +} +``` + +Custom hooks don't need to be triggered inside of those hooks, they can be stored and called later: + +```ts +// your-integration/index.ts +import './types.ts'; + +export default (): AstroIntegration => { + let integrations: AstroIntegration[] = []; + + return { + name: 'your-integration', + hooks: { + 'astro:config:done': async ({ config }) => { + // Retrieve the integration from one of the hooks they are given + integrations = config.integrations; + }, + 'astro:build:done': async () => { + // Calls the custom hook at a different phase of the integration + for (const integration of config.integrations) { + await integration.hooks['myLib:eventHappened'].?('your values', 123); + } + }, + } + } +} +``` + +#### Using a custom hook from another integration + +Integrations can declare hooks defined by other integrations by importing their types: + +```ts +// your-integration/index.ts +import 'other-integration/types.ts'; + +export default (): AstroIntegration => { + return { + name: 'your-integration', + hooks: { + 'otherLib:eventHappened': async (your, values) => { + // ... + }, + } + } +} +``` + ## Allow installation with `astro add` [The `astro add` command](/en/reference/cli-reference/#astro-add) allows users to easily add integrations and adapters to their project. If you want _your_ integration to be installable with this tool, **add `astro-integration` to the `keywords` field in your `package.json`**: From aa8b2307212a91570b51d97b080569e5f05d9de9 Mon Sep 17 00:00:00 2001 From: Luiz Ferraz Date: Tue, 2 Jul 2024 14:48:40 -0300 Subject: [PATCH 2/8] Apply suggestions from code review [skip ci] Co-authored-by: Chris Swithinbank --- .../en/reference/integrations-reference.mdx | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/content/docs/en/reference/integrations-reference.mdx b/src/content/docs/en/reference/integrations-reference.mdx index 9bd00ef99ce67..a6d42c37848a7 100644 --- a/src/content/docs/en/reference/integrations-reference.mdx +++ b/src/content/docs/en/reference/integrations-reference.mdx @@ -54,8 +54,6 @@ interface AstroIntegration { logger: AstroIntegrationLogger; }) => void | Promise; 'astro:build:done'?: (options: { dir: URL; routes: RouteData[]; logger: AstroIntegrationLogger; }) => void | Promise; - - // ... other integration-defined hooks }; } ``` @@ -615,9 +613,13 @@ A list of all generated pages. It is an object with one property. - `pathname` - the finalized path of the page. -### Defining your own hooks for other integrations +### Defining custom hooks + +As an integration author, you can define custom hooks that your integration will call. This allows your integration to interact with other integrations. Other integrations can provide values for your custom hooks and your integration can call those at the appropriate moment in the integration lifecycle. + +#### Typing a custom integration hook -You can extend the hooks defined for Astro integration by provide the following type augmentation along with your integration code: +Provide the type for your custom hook by augmenting Astro’s `IntegrationHooks` interface in a types file in your integration: ```ts // your-integration/types.ts @@ -632,9 +634,9 @@ declare global { #### Calling custom hooks on other integrations -Custom hooks implemented by all the integrations in use can be accessed from the `astro:config:setup` and `astro:config:done` hooks. +Other integrations in use can be accessed from the `astro:config:setup` and `astro:config:done` hooks. Loop over these to call your custom hook on each if available: -```ts +```ts {9-11} // your-integration/index.ts import './types.ts'; @@ -652,9 +654,9 @@ export default (): AstroIntegration => { } ``` -Custom hooks don't need to be triggered inside of those hooks, they can be stored and called later: +To call custom hooks outside of `astro:config:setup` and `astro:config:done`, store the integrations array and use it later: -```ts +```ts {5,11-12,15-18} // your-integration/index.ts import './types.ts'; @@ -665,11 +667,11 @@ export default (): AstroIntegration => { name: 'your-integration', hooks: { 'astro:config:done': async ({ config }) => { - // Retrieve the integration from one of the hooks they are given + // Store the integrations when available integrations = config.integrations; }, 'astro:build:done': async () => { - // Calls the custom hook at a different phase of the integration + // Loop over the stored integrations in a later hook for (const integration of config.integrations) { await integration.hooks['myLib:eventHappened'].?('your values', 123); } @@ -683,7 +685,7 @@ export default (): AstroIntegration => { Integrations can declare hooks defined by other integrations by importing their types: -```ts +```ts {2,8-10} // your-integration/index.ts import 'other-integration/types.ts'; From 4a73d52370f4f0c2575cc934ccb76cc15e33b436 Mon Sep 17 00:00:00 2001 From: Luiz Ferraz Date: Tue, 2 Jul 2024 20:08:44 -0300 Subject: [PATCH 3/8] Update src/content/docs/en/reference/integrations-reference.mdx [skip ci] Co-authored-by: Chris Swithinbank --- src/content/docs/en/reference/integrations-reference.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/docs/en/reference/integrations-reference.mdx b/src/content/docs/en/reference/integrations-reference.mdx index a6d42c37848a7..87ce51c99ab52 100644 --- a/src/content/docs/en/reference/integrations-reference.mdx +++ b/src/content/docs/en/reference/integrations-reference.mdx @@ -683,7 +683,7 @@ export default (): AstroIntegration => { #### Using a custom hook from another integration -Integrations can declare hooks defined by other integrations by importing their types: +An integration that uses your custom hook will need to import your types file. It can then provide a value for your hook in `hooks` with type checking and editor hints. ```ts {2,8-10} // your-integration/index.ts From 7cab01050e438f6b877d80ee00d767d70e1c03b6 Mon Sep 17 00:00:00 2001 From: Luiz Ferraz Date: Tue, 2 Jul 2024 21:09:18 -0300 Subject: [PATCH 4/8] Improve (hopefully) the explanation --- .../en/reference/integrations-reference.mdx | 67 +++++++++++++------ 1 file changed, 47 insertions(+), 20 deletions(-) diff --git a/src/content/docs/en/reference/integrations-reference.mdx b/src/content/docs/en/reference/integrations-reference.mdx index 87ce51c99ab52..edb81094df5a8 100644 --- a/src/content/docs/en/reference/integrations-reference.mdx +++ b/src/content/docs/en/reference/integrations-reference.mdx @@ -615,14 +615,14 @@ A list of all generated pages. It is an object with one property. ### Defining custom hooks -As an integration author, you can define custom hooks that your integration will call. This allows your integration to interact with other integrations. Other integrations can provide values for your custom hooks and your integration can call those at the appropriate moment in the integration lifecycle. +As an integration author, you can define custom hooks that your integration will call. This allows for runtime interactions between your integration and other integrations. Other integrations can provide implementations for your custom hooks and your integration can call those at the appropriate moment in your integration's lifecycle. -#### Typing a custom integration hook +#### Defining hook types -Provide the type for your custom hook by augmenting Astro’s `IntegrationHooks` interface in a types file in your integration: +Provide the type for your custom hook by augmenting Astro’s `IntegrationHooks` interface within your integration: -```ts -// your-integration/types.ts +```ts title="types.ts" +// on an implementation file declare global { namespace Astro { interface IntegrationHooks { @@ -632,14 +632,12 @@ declare global { } ``` -#### Calling custom hooks on other integrations +#### Calling custom hooks -Other integrations in use can be accessed from the `astro:config:setup` and `astro:config:done` hooks. Loop over these to call your custom hook on each if available: +Installed integrations can be accessed from the `astro:config:setup` and `astro:config:done` hooks. Loop over these to call your custom hook on each if available: -```ts {9-11} +```ts {7-9} // your-integration/index.ts -import './types.ts'; - export default (): AstroIntegration => { return { name: 'your-integration', @@ -654,12 +652,10 @@ export default (): AstroIntegration => { } ``` -To call custom hooks outside of `astro:config:setup` and `astro:config:done`, store the integrations array and use it later: +To call custom hooks outside of `astro:config:setup` and `astro:config:done`, store the integrations array and use it later. `astro:config:done` should be preferred to work with integrations that get added by other integrations during `astro:config:setup`. -```ts {5,11-12,15-18} +```ts {3,9-10,13-16} // your-integration/index.ts -import './types.ts'; - export default (): AstroIntegration => { let integrations: AstroIntegration[] = []; @@ -685,15 +681,13 @@ export default (): AstroIntegration => { An integration that uses your custom hook will need to import your types file. It can then provide a value for your hook in `hooks` with type checking and editor hints. -```ts {2,8-10} -// your-integration/index.ts -import 'other-integration/types.ts'; - +```ts {6-8} +// other-integration/index.ts export default (): AstroIntegration => { return { - name: 'your-integration', + name: 'other-integration', hooks: { - 'otherLib:eventHappened': async (your, values) => { + 'myLib:eventHappened': async (your, values) => { // ... }, } @@ -701,6 +695,39 @@ export default (): AstroIntegration => { } ``` +Make sure that your type augmentation can be loaded by other integrations; otherwise, their hook implementations won't be properly typed. The easiest way to ensure this is to either have that declaration in the same file that exports your integration or import it from that file: + +```ts {2-3} {5-12} +// your-integration/index.ts +// Import the types +import './types.ts'; + +// Or declare it in the same file +declare global { + namespace Astro { + interface IntegrationHooks { + 'myLib:eventHappened': (your: string, parameters: number) => Promise; + } + } +} + +export default (): AstroIntegration => { + return { + name: 'your-integration', + hooks: { /* ... */ } + } +} +``` + +In either case, other integrations can load your types by importing your integration or declaring it as a reference: + +```ts +// other-integration/index.ts +import yourIntegration from 'your-integration'; +// or +/// +``` + ## Allow installation with `astro add` [The `astro add` command](/en/reference/cli-reference/#astro-add) allows users to easily add integrations and adapters to their project. If you want _your_ integration to be installable with this tool, **add `astro-integration` to the `keywords` field in your `package.json`**: From 551d3629efc6e6b3c64d3d532e65c189789c77c8 Mon Sep 17 00:00:00 2001 From: Luiz Ferraz Date: Mon, 8 Jul 2024 07:43:05 -0300 Subject: [PATCH 5/8] Less guidy and more referency --- .../en/reference/integrations-reference.mdx | 117 ++---------------- 1 file changed, 12 insertions(+), 105 deletions(-) diff --git a/src/content/docs/en/reference/integrations-reference.mdx b/src/content/docs/en/reference/integrations-reference.mdx index edb81094df5a8..8e1f0b4556ca1 100644 --- a/src/content/docs/en/reference/integrations-reference.mdx +++ b/src/content/docs/en/reference/integrations-reference.mdx @@ -54,12 +54,18 @@ interface AstroIntegration { logger: AstroIntegrationLogger; }) => void | Promise; 'astro:build:done'?: (options: { dir: URL; routes: RouteData[]; logger: AstroIntegrationLogger; }) => void | Promise; + + // ... integration-defined hooks gets added here }; } ``` ## Hooks +Astro defines hooks that integrations can implement to execute during certain parts of Astro's lifecycle and to extend its functionality. The Astro hooks are defined in the `IntegrationHooks` interface, which is part of the global `Astro` namespace. + +The following hooks are built-in to Astro: + ### `astro:config:setup` **Next hook:** [`astro:config:done`](#astroconfigdone) @@ -613,120 +619,21 @@ A list of all generated pages. It is an object with one property. - `pathname` - the finalized path of the page. -### Defining custom hooks - -As an integration author, you can define custom hooks that your integration will call. This allows for runtime interactions between your integration and other integrations. Other integrations can provide implementations for your custom hooks and your integration can call those at the appropriate moment in your integration's lifecycle. - -#### Defining hook types - -Provide the type for your custom hook by augmenting Astro’s `IntegrationHooks` interface within your integration: - -```ts title="types.ts" -// on an implementation file -declare global { - namespace Astro { - interface IntegrationHooks { - 'myLib:eventHappened': (your: string, parameters: number) => Promise; - } - } -} -``` - -#### Calling custom hooks +### Custom hooks -Installed integrations can be accessed from the `astro:config:setup` and `astro:config:done` hooks. Loop over these to call your custom hook on each if available: +Since the `IntegrationHooks` is a global interface, custom hooks can be added to integrations by extending the `IntegrationHooks` interface through [global augmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#global-augmentation). -```ts {7-9} -// your-integration/index.ts -export default (): AstroIntegration => { - return { - name: 'your-integration', - hooks: { - 'astro:config:setup': async ({ config }) => { - for (const integration of config.integrations) { - await integration.hooks['myLib:eventHappened'].?('your values', 123); - } - }, - } - } -} -``` - -To call custom hooks outside of `astro:config:setup` and `astro:config:done`, store the integrations array and use it later. `astro:config:done` should be preferred to work with integrations that get added by other integrations during `astro:config:setup`. - -```ts {3,9-10,13-16} -// your-integration/index.ts -export default (): AstroIntegration => { - let integrations: AstroIntegration[] = []; - - return { - name: 'your-integration', - hooks: { - 'astro:config:done': async ({ config }) => { - // Store the integrations when available - integrations = config.integrations; - }, - 'astro:build:done': async () => { - // Loop over the stored integrations in a later hook - for (const integration of config.integrations) { - await integration.hooks['myLib:eventHappened'].?('your values', 123); - } - }, - } - } -} -``` - -#### Using a custom hook from another integration - -An integration that uses your custom hook will need to import your types file. It can then provide a value for your hook in `hooks` with type checking and editor hints. - -```ts {6-8} -// other-integration/index.ts -export default (): AstroIntegration => { - return { - name: 'other-integration', - hooks: { - 'myLib:eventHappened': async (your, values) => { - // ... - }, - } - } -} -``` - -Make sure that your type augmentation can be loaded by other integrations; otherwise, their hook implementations won't be properly typed. The easiest way to ensure this is to either have that declaration in the same file that exports your integration or import it from that file: - -```ts {2-3} {5-12} -// your-integration/index.ts -// Import the types -import './types.ts'; - -// Or declare it in the same file +```ts declare global { namespace Astro { - interface IntegrationHooks { - 'myLib:eventHappened': (your: string, parameters: number) => Promise; + export interface IntegrationHook { + 'your:hook': (params: YourHookParameters) => Promise } } } - -export default (): AstroIntegration => { - return { - name: 'your-integration', - hooks: { /* ... */ } - } -} ``` -In either case, other integrations can load your types by importing your integration or declaring it as a reference: - -```ts -// other-integration/index.ts -import yourIntegration from 'your-integration'; -// or -/// -``` +Astro reserves the `astro:` prefix for future built-in hooks, defining a hook with that prefix may break in future versions. ## Allow installation with `astro add` From 3e4f0409918d0bdad8eb139f16ffd9b82285a59f Mon Sep 17 00:00:00 2001 From: Luiz Ferraz Date: Mon, 8 Jul 2024 15:06:08 -0300 Subject: [PATCH 6/8] Apply suggestions from code review Co-authored-by: Sarah Rainsberger --- src/content/docs/en/reference/integrations-reference.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/content/docs/en/reference/integrations-reference.mdx b/src/content/docs/en/reference/integrations-reference.mdx index 8e1f0b4556ca1..575b07a38aec8 100644 --- a/src/content/docs/en/reference/integrations-reference.mdx +++ b/src/content/docs/en/reference/integrations-reference.mdx @@ -621,7 +621,7 @@ A list of all generated pages. It is an object with one property. ### Custom hooks -Since the `IntegrationHooks` is a global interface, custom hooks can be added to integrations by extending the `IntegrationHooks` interface through [global augmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#global-augmentation). +Custom hooks can be added to integrations by extending the `IntegrationHooks` interface through [global augmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#global-augmentation). ```ts declare global { @@ -633,7 +633,7 @@ declare global { } ``` -Astro reserves the `astro:` prefix for future built-in hooks, defining a hook with that prefix may break in future versions. +Astro reserves the `astro:` prefix for future built-in hooks. Please choose a different prefix when naming your custom hook. ## Allow installation with `astro add` From 9a35fcac610733797594f4c6e077a8af96ae69d7 Mon Sep 17 00:00:00 2001 From: Sarah Rainsberger Date: Fri, 12 Jul 2024 08:07:10 -0300 Subject: [PATCH 7/8] tiny edit --- src/content/docs/en/reference/integrations-reference.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/docs/en/reference/integrations-reference.mdx b/src/content/docs/en/reference/integrations-reference.mdx index 575b07a38aec8..139b892b527a5 100644 --- a/src/content/docs/en/reference/integrations-reference.mdx +++ b/src/content/docs/en/reference/integrations-reference.mdx @@ -62,7 +62,7 @@ interface AstroIntegration { ## Hooks -Astro defines hooks that integrations can implement to execute during certain parts of Astro's lifecycle and to extend its functionality. The Astro hooks are defined in the `IntegrationHooks` interface, which is part of the global `Astro` namespace. +Astro provides hooks that integrations can implement to execute during certain parts of Astro's lifecycle. Astro hooks are defined in the `IntegrationHooks` interface, which is part of the global `Astro` namespace. The following hooks are built-in to Astro: From 6dc6fa670161a401656acd275b14e09857684fe5 Mon Sep 17 00:00:00 2001 From: Luiz Ferraz Date: Fri, 12 Jul 2024 21:06:55 -0300 Subject: [PATCH 8/8] Update src/content/docs/en/reference/integrations-reference.mdx Co-authored-by: Sarah Rainsberger --- src/content/docs/en/reference/integrations-reference.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/docs/en/reference/integrations-reference.mdx b/src/content/docs/en/reference/integrations-reference.mdx index 139b892b527a5..26d079398978a 100644 --- a/src/content/docs/en/reference/integrations-reference.mdx +++ b/src/content/docs/en/reference/integrations-reference.mdx @@ -55,7 +55,7 @@ interface AstroIntegration { }) => void | Promise; 'astro:build:done'?: (options: { dir: URL; routes: RouteData[]; logger: AstroIntegrationLogger; }) => void | Promise; - // ... integration-defined hooks gets added here + // ... any custom hooks from integrations }; } ```