diff --git a/src/content/docs/en/guides/upgrade-to/v5.mdx b/src/content/docs/en/guides/upgrade-to/v5.mdx index b73305552be6c..8ce843e6df9d7 100644 --- a/src/content/docs/en/guides/upgrade-to/v5.mdx +++ b/src/content/docs/en/guides/upgrade-to/v5.mdx @@ -1140,14 +1140,14 @@ If you were using the deprecated shape, update your dev toolbar app to use the n ```js title="my-integration.mjs" del={1-2} ins={4-10} // Old shape -addDevToolbarApp("./my-app.js"); +addDevToolbarApp("./my-dev-toolbar-app.mjs"); // New shape addDevToolbarApp({ id: "my-app", name: "My App", icon: "...", - entrypoint: "./my-app.js", + entrypoint: "./my-dev-toolbar-app.mjs", }); ``` diff --git a/src/content/docs/en/reference/integrations-reference.mdx b/src/content/docs/en/reference/integrations-reference.mdx index da92b747c5ee5..8013912697ff2 100644 --- a/src/content/docs/en/reference/integrations-reference.mdx +++ b/src/content/docs/en/reference/integrations-reference.mdx @@ -33,12 +33,16 @@ interface AstroIntegration { addWatchFile: (path: URL | string) => void; addClientDirective: (directive: ClientDirectiveConfig) => void; addMiddleware: (middleware: AstroIntegrationMiddleware) => void; - addDevToolbarApp: (pluginEntrypoint: string) => void; + addDevToolbarApp: (entrypoint: DevToolbarAppEntry) => void; injectScript: (stage: InjectedScriptStage, content: string) => void; - injectRoute: (injectedRoute: { pattern: string; entrypoint: string; prerender?: boolean }) => void; + injectRoute: (injectedRoute: InjectedRoute) => void; createCodegenDir: () => URL; logger: AstroIntegrationLogger; }) => void | Promise; + 'astro:route:setup'?: (options: { + route: RouteOptions; + logger: AstroIntegrationLogger; + }) => void | Promise; 'astro:routes:resolved'?: (options: { routes: IntegrationResolvedRoute[]; logger: AstroIntegrationLogger; @@ -46,14 +50,26 @@ interface AstroIntegration { 'astro:config:done'?: (options: { config: AstroConfig; setAdapter: (adapter: AstroAdapter) => void; - injectTypes: (injectedType: { filename: string; content: string }) => URL; + injectTypes: (injectedType: InjectedType) => URL; + logger: AstroIntegrationLogger; + buildOutput: 'static' | 'server'; + }) => void | Promise; + 'astro:server:setup'?: (options: { + server: vite.ViteDevServer; + logger: AstroIntegrationLogger; + toolbar: ReturnType; + refreshContent?: (options: RefreshContentOptions) => Promise; + }) => void | Promise; + 'astro:server:start'?: (options: { + address: AddressInfo; + logger: AstroIntegrationLogger; + }) => void | Promise; + 'astro:server:done'?: (options: { logger: AstroIntegrationLogger; - }) => void | Promise; - 'astro:route:setup'?: (options: { route: RouteOptions; logger: AstroIntegrationLogger; }) => void | Promise; - 'astro:server:setup'?: (options: { server: vite.ViteDevServer; logger: AstroIntegrationLogger; }) => void | Promise; - 'astro:server:start'?: (options: { address: AddressInfo; logger: AstroIntegrationLogger; }) => void | Promise; - 'astro:server:done'?: (options: { logger: AstroIntegrationLogger; }) => void | Promise; - 'astro:build:start'?: (options: { logger: AstroIntegrationLogger; }) => void | Promise; + }) => void | Promise; + 'astro:build:start'?: (options: { + logger: AstroIntegrationLogger; + }) => void | Promise; 'astro:build:setup'?: (options: { vite: vite.InlineConfig; pages: Map; @@ -61,16 +77,19 @@ interface AstroIntegration { updateConfig: (newConfig: vite.InlineConfig) => void; logger: AstroIntegrationLogger; }) => void | Promise; - 'astro:build:generated'?: (options: { dir: URL; logger: AstroIntegrationLogger; }) => void | Promise; 'astro:build:ssr'?: (options: { - manifest: SerializedSSRManifest; - entryPoints: Map; - logger: AstroIntegrationLogger; + manifest: SerializedSSRManifest; + entryPoints: Map; + middlewareEntryPoint: URL | undefined; + logger: AstroIntegrationLogger; + }) => void | Promise; + 'astro:build:generated'?: (options: { + dir: URL; + logger: AstroIntegrationLogger; }) => void | Promise; 'astro:build:done'?: (options: { + pages: { pathname: string }[]; dir: URL; - /** @deprecated Use the `assets` map and the new `astro:routes:resolved` hook */ - routes: IntegrationRouteData[]; assets: Map; logger: AstroIntegrationLogger; }) => void | Promise; @@ -82,13 +101,13 @@ interface AstroIntegration { ## Hooks -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. +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. Each hook has a [`logger` option](#astrointegrationlogger) that allows you to use the Astro logger to write logs. The following hooks are built in to Astro: ### `astro:config:setup` -**Next hook:** [`astro:config:done`](#astroconfigdone) +**Next hook:** [`astro:route:setup`](#astroroutesetup) **When:** On initialization, before either the [Vite](https://vite.dev/config/) or [Astro config](/en/reference/configuration-reference/) have resolved. @@ -103,10 +122,10 @@ The following hooks are built in to Astro: addRenderer: (renderer: AstroRenderer) => void; addClientDirective: (directive: ClientDirectiveConfig) => void; addMiddleware: (middleware: AstroIntegrationMiddleware) => void; - addDevToolbarApp: (pluginEntrypoint: string) => void; + addDevToolbarApp: (entrypoint: DevToolbarAppEntry) => void; addWatchFile: (path: URL | string) => void; injectScript: (stage: InjectedScriptStage, content: string) => void; - injectRoute: (injectedRoute: { pattern: string; entrypoint: string; prerender?: boolean }) => void; + injectRoute: (injectedRoute: InjectedRoute) => void; createCodegenDir: () => URL; logger: AstroIntegrationLogger; }) => void | Promise; @@ -114,13 +133,19 @@ The following hooks are built in to Astro: #### `config` option +

+ **Type:** `AstroConfig` +

A read-only copy of the user-supplied [Astro config](/en/reference/configuration-reference/). This is resolved _before_ any other integrations have run. If you need a copy of the config after all integrations have completed their config updates, [see the `astro:config:done` hook](#astroconfigdone). #### `command` option +

+ **Type:** `'dev' | 'build' | 'preview' | 'sync'` +

- `dev` - Project is executed with `astro dev` - `build` - Project is executed with `astro build` @@ -129,13 +154,20 @@ A read-only copy of the user-supplied [Astro config](/en/reference/configuration #### `isRestart` option -**Type:** `boolean` +

+ +**Type:** `boolean`
+ +

`false` when the dev server starts, `true` when a reload is triggered. Useful to detect when this function is called more than once. -#### `updateConfig` option +#### `updateConfig()` option + +

**Type:** `(newConfig: DeepPartial) => AstroConfig;` +

A callback function to update the user-supplied [Astro config](/en/reference/configuration-reference/). Any config you provide **will be merged with the user config + other integration config updates,** so you are free to omit keys! @@ -158,10 +190,13 @@ export default { } ``` -#### `addRenderer` option +#### `addRenderer()` option + +

-**Type:** `(renderer:` [`AstroRenderer`](https://github.com/withastro/astro/blob/fdd607c5755034edf262e7b275732519328a33b2/packages/astro/src/%40types/astro.ts#L872-L883) `) => void;` +**Type:** `(renderer:` [`AstroRenderer`](https://github.com/withastro/astro/blob/fdd607c5755034edf262e7b275732519328a33b2/packages/astro/src/%40types/astro.ts#L872-L883) `) => void;`
**Examples:** [`svelte`](https://github.com/withastro/astro/blob/main/packages/integrations/svelte/src/index.ts), [`react`](https://github.com/withastro/astro/blob/main/packages/integrations/react/src/index.ts), [`preact`](https://github.com/withastro/astro/blob/main/packages/integrations/preact/src/index.ts), [`vue`](https://github.com/withastro/astro/blob/main/packages/integrations/vue/src/index.ts), [`solid`](https://github.com/withastro/astro/blob/main/packages/integrations/solid/src/index.ts) +

A callback function to add a component framework renderer (i.e. React, Vue, Svelte, etc). You can browse the examples and type definition above for more advanced options, but here are the 2 main options to be aware of: @@ -172,9 +207,13 @@ A callback function to add a component framework renderer (i.e. React, Vue, Svel The functions `clientEntrypoint` and `serverEntrypoint` accept a `URL`. -#### `addWatchFile` option +#### `addWatchFile()` option -**Type:** `URL | string` +

+ +**Type:** `(path: URL | string) => void`
+ +

If your integration depends on some configuration file that Vite doesn't watch and/or needs a full dev server restart to take effect, add it with `addWatchFile`. Whenever that file changes, the Astro dev server will be reloaded (you can check when a reload happens with `isRestart`). @@ -186,11 +225,13 @@ addWatchFile('/home/user/.../my-config.json'); addWatchFile(new URL('./tailwind.config.js', config.root)); ``` -#### `addClientDirective` option +#### `addClientDirective()` option -

+

-**Type:** `(directive:` [`ClientDirectiveConfig`](https://github.com/withastro/astro/blob/00327c213f74627ac9ca1dec774efa5bf71e9375/packages/astro/src/%40types/astro.ts#L1872-L1875) `) => void;` +**Type:** `(directive:` [`ClientDirectiveConfig`](https://github.com/withastro/astro/blob/00327c213f74627ac9ca1dec774efa5bf71e9375/packages/astro/src/%40types/astro.ts#L1872-L1875) `) => void;`
+ +

Adds a [custom client directive](/en/reference/directives-reference/#custom-client-directives) to be used in `.astro` files. @@ -251,11 +292,13 @@ declare module 'astro' { } ``` -#### `addDevToolbarApp` option +#### `addDevToolbarApp()` option -

+

-**Type:** `(pluginEntrypoint: string) => void;` +**Type:** `(entrypoint: DevToolbarAppEntry) => void;`
+ +

Adds a [custom dev toolbar app](/en/reference/dev-toolbar-app-reference/). @@ -281,7 +324,11 @@ export default () => ({ name: "dev-toolbar-app", hooks: { "astro:config:setup": ({ addDevToolbarApp }) => { - addDevToolbarApp("./astro-dev-toolbar-app/plugin.js"); + addDevToolbarApp({ + entrypoint: "./astro-dev-toolbar-app/plugin.js", + id: "my-plugin", + name: "My Plugin" + }); }, }, }); @@ -301,11 +348,13 @@ export default { }, }; ``` -#### `addMiddleware` option +#### `addMiddleware()` option -

+

-**Type:** `(middleware:` [`AstroIntegrationMiddleware`](https://github.com/withastro/astro/blob/852ac0f75dfca1b2602e9cdbfa0447d9998e2449/packages/astro/src/%40types/astro.ts#L2124-L2127) `) => void;` +**Type:** `(middleware:` [`AstroIntegrationMiddleware`](https://github.com/withastro/astro/blob/852ac0f75dfca1b2602e9cdbfa0447d9998e2449/packages/astro/src/%40types/astro.ts#L2124-L2127) `) => void;`
+ +

Adds [middleware](/en/guides/middleware/) to run on each request. Takes the `entrypoint` module that contains the middleware, and an `order` to specify whether it should run before (`pre`) other middleware or after (`post`). @@ -317,10 +366,10 @@ export default () => ({ name: "my-middleware-package", hooks: { "astro:config:setup": ({ addMiddleware }) => { - addMiddleware({ - entrypoint: '@my-package/middleware', - order: 'pre' - }); + addMiddleware({ + entrypoint: '@my-package/middleware', + order: 'pre' + }); }, }, }); @@ -354,18 +403,21 @@ export default () => ({ name: "my-middleware-package", hooks: { "astro:config:setup": ({ addMiddleware }) => { - addMiddleware({ - entrypoint: new URL('./middleware.js', import.meta.url), - order: 'pre' - }); + addMiddleware({ + entrypoint: new URL('./middleware.js', import.meta.url), + order: 'pre' + }); }, }, }); ``` -#### `injectRoute` option +#### `injectRoute()` option + +

-**Type:** `({ pattern: string; entrypoint: string; pattern?: boolean }) => void;` +**Type:** `({ pattern: string; entrypoint: string | URL; prerender?: boolean }) => void;` +

A callback function to inject routes into an Astro project. Injected routes can be [`.astro` pages](/en/basics/astro-pages/) or [`.js` and `.ts` route handlers](/en/guides/endpoints/#static-file-endpoints). @@ -419,9 +471,12 @@ injectRoute({ }); ``` -#### `injectScript` option +#### `injectScript()` option + +

**Type:** `(stage: InjectedScriptStage, content: string) => void;` +

A callback function to inject a string of JavaScript content onto every page. @@ -453,164 +508,137 @@ It allows you to have a dedicated folder, avoiding conflicts with another integr import { writeFileSync } from 'node:fs' const integration = { - name: 'my-integration', - hooks: { - 'astro:config:setup': ({ createCodegenDir }) => { - const codegenDir = createCodegenDir() - writeFileSync(new URL('cache.json', codegenDir), '{}', 'utf-8') - } + name: 'my-integration', + hooks: { + 'astro:config:setup': ({ createCodegenDir }) => { + const codegenDir = createCodegenDir() + writeFileSync(new URL('cache.json', codegenDir), '{}', 'utf-8') } + } } ``` -### `astro:routes:resolved` +### `astro:route:setup` - +

**Previous hook:** [`astro:config:setup`](#astroconfigsetup) -**Next hook:** [`astro:config:done`](#astroconfigdone) +**Next hook:** [`astro:routes:resolved`](#astroroutesresolved) -**When:** In `astro dev`, it also runs if a file based route changes (added/removed/updated). +**When:** In `astro build`, before bundling starts. In `astro dev`, while building the module graph and on every change to a file based route (added/removed/updated). -**Why:** To access routes and their metadata +**Why:** To set options for a route at build or request time, such as enabling [on-demand server rendering](/en/guides/on-demand-rendering/#enabling-on-demand-rendering). ```js -'astro:routes:resolved'?: (options: { - routes: IntegrationResolvedRoute[]; +'astro:route:setup'?: (options: { + route: RouteOptions; logger: AstroIntegrationLogger; }) => void | Promise; ``` -#### `routes` option +#### `route` option -**Type:** [`IntegrationResolvedRoute[]`](#integrationresolvedroute-type-reference) +

-A list of all routes with their associated metadata. +**Type:** [`RouteOptions`](https://github.com/withastro/astro/blob/3b10b97a4fecd1dfd959b160a07b5b8427fe40a7/packages/astro/src/types/public/integrations.ts#L14-L27) +

-Example use: +An object with a `component` property to identify the route and the following additional values to allow you to configure the generated route: `prerender`. -```js title="my-integration.mjs" -const integration = () => { - return { - name: 'my-integration', - hooks: { - 'astro:routes:resolved': ({ routes }) => { - const projectRoutes = routes.filter(r => r.origin === 'project').map(r => r.pattern) - - console.log(projectRoutes) - }, +##### `route.component` + +

+**Type:** `string`
+ +

+ +The `component` property indicates the entrypoint that will be rendered on the route. You can access this value before the routes are built to configure on-demand server rendering for that page. + +##### `route.prerender` + +

+**Type:** `boolean`
+**Default:** `undefined`
+ +

+ +The `prerender` property is used to configure [on-demand server rendering](/en/guides/on-demand-rendering/#enabling-on-demand-rendering) for a route. If the route file contains an explicit `export const prerender` value, the value will be used as the default instead of `undefined`. + +```js title="astro.config.mjs" +import { defineConfig } from 'astro/config'; + +export default defineConfig({ + integrations: [setPrerender()], +}); + +function setPrerender() { + return { + name: 'set-prerender', + hooks: { + 'astro:route:setup': ({ route }) => { + if (route.component.endsWith('/blog/[slug].astro')) { + route.prerender = true; } - } + }, + }, + }; } ``` + +If the final value after running all the hooks is `undefined`, the route will fall back to a prerender default based on the [`output` option](/en/reference/configuration-reference/#output): prerendered for `static` mode, and on-demand rendered for `server` mode. -##### `IntegrationResolvedRoute` type reference +### `astro:routes:resolved` -```ts -interface IntegrationResolvedRoute { - /** - * The current **pattern** of the route. For example: - * - `src/pages/index.astro` has a pattern of `/` - * - `src/pages/blog/[...slug].astro` has a pattern of `/blog/[...slug]` - * - `src/pages/site/[blog]/[...slug].astro` has a pattern of `/site/[blog]/[...slug]` - */ - pattern: RouteData['route']; +

- /** - * - * regex used for matching an input URL against a requested route - * ex. "[fruit]/about.astro" will generate the pattern: /^\/([^/]+?)\/about\/?$/ - * where pattern.test("banana/about") is "true" - * - * ## Example - * - * ```js - * if (route.pattern.test('/blog')) { - * // do something - * } - * ``` - */ - patternRegex: RouteData['pattern']; + +

- /** - * Source component URL - */ - entrypoint: RouteData['component']; +**Previous hook:** [`astro:route:setup`](#astroroutesetup) - /** - * Whether the route is prerendered or not - */ - isPrerendered: RouteData['prerender']; +**Next hook:** [`astro:config:done`](#astroconfigdone) (only during setup) - /** - * The {@link IntegrationResolvedRoute} to redirect to. It's present when `IntegrationResolvedRoute.type` is `redirect`. - */ - redirectRoute?: IntegrationResolvedRoute; +**When:** In `astro dev`, it also runs on every change to a file based route (added/removed/updated). - /** - * @param {any} data The optional parameters of the route - * - * @description - * A function that accepts a list of params, interpolates them with the route pattern, and returns the path name of the route. - * - * ## Example - * - * For a route such as `/blog/[...id].astro`, the `generate` function would return something like this: - * - * ```js - * console.log(generate({ id: 'presentation' })) // will log `/blog/presentation` - * ``` - */ - generate: (data?: any) => string; +**Why:** To access routes and their metadata - /** - * Dynamic and spread route params - * ex. "/pages/[lang]/[...slug].astro" will output the params ['lang', '...slug'] - */ - params: string[]; +```js +'astro:routes:resolved'?: (options: { + routes: IntegrationResolvedRoute[]; + logger: AstroIntegrationLogger; +}) => void | Promise; +``` - /** - * Output URL pathname where this route will be served - * note: will be undefined for [dynamic] and [...spread] routes - */ - pathname?: string; +#### `routes` option - /** - * Similar to the "params" field, but with more associated metadata. For example, for `/site/[blog]/[...slug].astro`, the segments are: - * - * 1. `{ content: 'site', dynamic: false, spread: false }` - * 2. `{ content: 'blog', dynamic: true, spread: false }` - * 3. `{ content: '...slug', dynamic: true, spread: true }` - */ - segments: RoutePart[][]; +

- /** - * - * The type of the route. It can be: - * - `page`: a route that lives in the file system, usually an Astro component - * - `endpoint`: a route that lives in the file system, usually a JS file that exposes endpoints methods - * - `redirect`: a route points to another route that lives in the file system - * - `fallback`: a route that doesn't exist in the file system that needs to be handled with other means, usually the middleware - */ - type: RouteType; +**Type:** [`IntegrationResolvedRoute[]`](#integrationresolvedroute-type-reference) +

- /** - * The route to redirect to. It holds information regarding the status code and its destination. - */ - redirect?: RedirectConfig; +A list of all routes with their associated metadata. - /** - * Whether the route comes from Astro core, an integration or the user's project - */ - origin: 'internal' | 'external' | 'project'; +Example use: + +```js title="my-integration.mjs" +const integration = () => { + return { + name: 'my-integration', + hooks: { + 'astro:routes:resolved': ({ routes }) => { + const projectRoutes = routes.filter(r => r.origin === 'project').map(r => r.pattern) + + console.log(projectRoutes) + }, + } + } } ``` - ### `astro:config:done` -**Previous hook:** [`astro:config:setup`](#astroroutesresolved) +**Previous hook:** [`astro:routes:resolved`](#astroroutesresolved) **Next hook:** [`astro:server:setup`](#astroserversetup) when running in "dev" mode, or [`astro:build:start`](#astrobuildstart) during production builds @@ -622,28 +650,37 @@ interface IntegrationResolvedRoute { 'astro:config:done'?: (options: { config: AstroConfig; setAdapter: (adapter: AstroAdapter) => void; - injectTypes: (injectedType: { filename: string; content: string }) => URL; + injectTypes: (injectedType: InjectedType) => URL; logger: AstroIntegrationLogger; + buildOutput: 'static' | 'server'; }) => void | Promise; ``` #### `config` option +

+ **Type:** `AstroConfig` +

A read-only copy of the user-supplied [Astro config](/en/reference/configuration-reference/). This is resolved _after_ other integrations have run. -#### `setAdapter` option +#### `setAdapter()` option + +

**Type:** `(adapter: AstroAdapter) => void;` +

Makes the integration an adapter. Read more in the [adapter API](/en/reference/adapter-reference/). -#### `injectTypes` options +#### `injectTypes()` option -

+

-**Type:** `(injectedType: { filename: string; content: string }) => URL` +**Type:** `(injectedType: { filename: string; content: string }) => URL`
+ +

Allows you to inject types into your user's project by adding a new `*.d.ts` file. @@ -661,6 +698,16 @@ const path = injectTypes({ console.log(path) // URL ``` +#### `buildOutput` option + +

+ +**Type:** `'static' | 'server'`
+ +

+ +Allows you to adapt the logic of your integration depending on the user's project output. + ### `astro:server:setup` **Previous hook:** [`astro:config:done`](#astroconfigdone) @@ -674,15 +721,21 @@ console.log(path) // URL ```js 'astro:server:setup'?: (options: { server: vite.ViteDevServer; + logger: AstroIntegrationLogger; + toolbar: ReturnType; refreshContent: (options: { loaders?: Array; context?: Record; }) => Promise; }) => void | Promise; ``` + #### `server` option +

+ **Type:** [`ViteDevServer`](https://vite.dev/guide/api-javascript.html#vitedevserver) +

A mutable instance of the Vite server used in "dev" mode. For instance, this is [used by our Partytown integration](/en/guides/integrations-guide/partytown/) to inject the Partytown server as middleware: @@ -701,7 +754,53 @@ export default { } ``` -#### `refreshContent` option +#### `toolbar` option + +

+ +**Type:** `ReturnType`
+ +

+ +An object providing callback functions to interact with the [dev toolbar](/en/reference/dev-toolbar-app-reference/): + +##### `on()` + +

+ +**Type:** `(event: string, callback: (data: T) => void) => void`
+

+ +A function that takes an event name as first argument and a callback function as second argument. This allows you to receive a message from a dev toolbar app with data associated to that event. + +##### `onAppInitialized()` + +

+ +**Type:** `(appId: string, callback: (data: Record) => void) => void`
+

+ +A function fired when a dev toolbar app is initialized. The first argument is the id of the app that was initialized. The second argument is a callback function to run when the app is initialized. + +##### `onAppToggled()` + +

+ +**Type:** `(appId: string, callback: (data: { state: boolean; }) => void) => void`
+

+ +A function fired when a dev toolbar app is toggled on or off. The first argument is the id of the app that was toggled. The second argument is a callback function providing the state to execute when the application is toggled. + +##### `send()` + +

+ +**Type:** `(event: string, payload: T) => void`
+

+ +A function that sends a message to the dev toolbar that an app can listen for. This takes an event name as the first argument and a payload as the second argument which can be any serializable data. + +#### `refreshContent()` option

@@ -716,44 +815,43 @@ By default, `refreshContent` will refresh all collections. You can optionally pa You can also pass a `context` object to the loaders. This can be used to pass arbitrary data such as the webhook body, or an event from the websocket. ```ts title=my-integration.ts {19-22} - { - name: 'my-integration', - hooks: { - 'astro:server:setup': async ({ server, refreshContent }) => { - // Register a dev server webhook endpoint - server.middlewares.use('/_refresh', async (req, res) => { - if(req.method !== 'POST') { - res.statusCode = 405 - res.end('Method Not Allowed'); - return - } - let body = ''; - req.on('data', chunk => { - body += chunk.toString(); - }); - req.on('end', async () => { - try { - const webhookBody = JSON.parse(body); - await refreshContent({ - context: { webhookBody }, - loaders: ['my-loader'] - }); - res.writeHead(200, { 'Content-Type': 'application/json' }); - res.end(JSON.stringify({ message: 'Content refreshed successfully' })); - } catch (error) { - res.writeHead(500, { 'Content-Type': 'application/json' }); - res.end(JSON.stringify({ error: 'Failed to refresh content: ' + error.message })); - } - }); - }); +{ + name: 'my-integration', + hooks: { + 'astro:server:setup': async ({ server, refreshContent }) => { + // Register a dev server webhook endpoint + server.middlewares.use('/_refresh', async (req, res) => { + if(req.method !== 'POST') { + res.statusCode = 405 + res.end('Method Not Allowed'); + return } + let body = ''; + req.on('data', chunk => { + body += chunk.toString(); + }); + req.on('end', async () => { + try { + const webhookBody = JSON.parse(body); + await refreshContent({ + context: { webhookBody }, + loaders: ['my-loader'] + }); + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ message: 'Content refreshed successfully' })); + } catch (error) { + res.writeHead(500, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ error: 'Failed to refresh content: ' + error.message })); + } + }); + }); } + } } ``` The loader can then access the `refreshContextData` property to get the webhook body. See the [`refreshContextData`](/en/reference/content-loader-reference/#refreshcontextdata) property for more information. - ### `astro:server:start` **Previous hook:** [`astro:server:setup`](#astroserversetup) @@ -765,12 +863,18 @@ The loader can then access the `refreshContextData` property to get the webhook **Why:** To intercept network requests at the specified address. If you intend to use this address for middleware, consider using `astro:server:setup` instead. ```js -'astro:server:start'?: (options: { address: AddressInfo }) => void | Promise; +'astro:server:start'?: (options: { + address: AddressInfo; + logger: AstroIntegrationLogger; +}) => void | Promise; ``` #### `address` option +

+ **Type:** [`AddressInfo`](https://microsoft.github.io/PowerBI-JavaScript/interfaces/_node_modules__types_node_net_d_._net_.addressinfo.html) +

The address, family and port number supplied by the [Node.js Net module](https://nodejs.org/api/net.html). @@ -783,7 +887,9 @@ The address, family and port number supplied by the [Node.js Net module](https:/ **Why:** To run any cleanup events you may trigger during the `astro:server:setup` or `astro:server:start` hooks. ```js -'astro:server:done'?: () => void | Promise; +'astro:server:done'?: (options: { + logger: AstroIntegrationLogger; +}) => void | Promise; ``` ### `astro:build:start` @@ -797,7 +903,9 @@ The address, family and port number supplied by the [Node.js Net module](https:/ **Why:** To set up any global objects or clients needed during a production build. This can also extend the build configuration options in the [adapter API](/en/reference/adapter-reference/). ```js -'astro:build:start'?: () => void | Promise; +'astro:build:start'?: (options: { + logger: AstroIntegrationLogger; +}) => void | Promise; ``` ### `astro:build:setup` @@ -821,46 +929,249 @@ The address, family and port number supplied by the [Node.js Net module](https:/ ``` -### `astro:build:generated` +#### `vite` option -**Previous hook:** [`astro:build:setup`](#astrobuildsetup) +

-**When:** After a static production build has finished generating routes and assets. +**Type:** [`InlineConfig`](https://vite.dev/guide/api-javascript.html#inlineconfig) +

-**Why:** To access generated routes and assets **before** build artifacts are cleaned up. This is a very uncommon use case. We recommend using [`astro:build:done`](#astrobuilddone) unless you really need to access the generated files before cleanup. +An object that allows you to access the Vite configuration used in the build. + +This can be useful if you need to access configuration options in your integration: ```js -'astro:build:generated'?: (options: { dir: URL }) => void | Promise; +export default { + name: 'my-integration', + hooks: { + 'astro:build:setup': ({ vite }) => { + const { publicDir, root } = vite; + }, + } +} ``` -### `astro:build:ssr` +#### `pages` option -**Previous hook:** [`astro:build:setup`](#astrobuildsetup) +

-**When:** After a production SSR build has completed. +**Type:** Map\PageBuildData\> +

-**Why:** To access the SSR manifest and map of the emitted entry points. This is useful when creating custom SSR builds in plugins or integrations. -- `entryPoints` maps a page route to the physical file emitted after the build; -- `middlewareEntryPoint` is the file system path of the middleware file; +A `Map` with a list of pages as key and their build data as value. + +This can be used to perform an action if a route matches a criteria: ```js -'astro:build:ssr'?: (options: { - manifest: SerializedSSRManifest, - entryPoints: Map, - middlewareEntryPoint: URL -}) => void | Promise; +export default { + name: 'my-integration', + hooks: { + 'astro:build:setup': ({ pages }) => { + pages.forEach((data) => { + if (data.route.pattern.test("/blog")) { + console.log(data.route.type); + } + }); + }, + } +} ``` -### `astro:build:done` +#### `target` option + +

+ +**Type:** `'client' | 'server'` +

+ +Builds are separated in two distinct phases: `client` and `server`. This option allow you to determine the current build phase. + +This can be used to perform an action only in a specific phase: + +```js +export default { + name: 'my-integration', + hooks: { + 'astro:build:setup': ({ target }) => { + if (target === "server") { + // do something in server build phase + } + }, + } +} +``` + +#### `updateConfig()` option + +

+ +**Type:** (newConfig: InlineConfig) => void +

+ +A callback function to update the [Vite](https://vite.dev/) options used in the build. Any config you provide **will be merged with the user config + other integration config updates**, so you are free to omit keys! + +For example, this can be used to supply a plugin to the user's project: + +```js +import awesomeCssPlugin from 'awesome-css-vite-plugin'; + +export default { + name: 'my-integration', + hooks: { + 'astro:build:setup': ({ updateConfig }) => { + updateConfig({ + plugins: [awesomeCssPlugin()], + }) + } + } +} +``` + +### `astro:build:ssr` + +**Previous hook:** [`astro:build:setup`](#astrobuildsetup) + +**Next hook:** [`astro:build:generated`](#astrobuildgenerated) + +**When:** After a production SSR build has completed. + +**Why:** To access the SSR manifest and map of the emitted entry points. This is useful when creating custom SSR builds in plugins or integrations. +- `entryPoints` maps a page route to the physical file emitted after the build; +- `middlewareEntryPoint` is the file system path of the middleware file; + +```js +'astro:build:ssr'?: (options: { + manifest: SerializedSSRManifest; + entryPoints: Map; + middlewareEntryPoint: URL | undefined; + logger: AstroIntegrationLogger; +}) => void | Promise; +``` + +#### `manifest` option + +

+ +**Type:** [`SerializedSSRManifest`](https://github.com/withastro/astro/blob/3b10b97a4fecd1dfd959b160a07b5b8427fe40a7/packages/astro/src/core/app/types.ts#L91-L109) +

+ +Allows you to create a custom build by accessing the SSR manifest. + +```js +export default { + name: 'my-integration', + hooks: { + 'astro:build:ssr': ({ manifest }) => { + const { i18n } = manifest; + if (i18n?.strategy === "domains-prefix-always") { + // do something + } + }, + }, +} +``` + +#### `entryPoints` option + +

+ +**Type:** Map\<IntegrationRouteData, URL\>
+ +

+ +A `Map` of the emitted entry points with the `IntegrationRouteData` as key and the physical file URL as value. + +```js +export default { + name: 'my-integration', + hooks: { + 'astro:build:ssr': ({ entryPoints }) => { + entryPoints.forEach((url) => { + console.log(url.href); + }); + }, + }, +} +``` + +#### `middlewareEntryPoint` option + +

+ +**Type:** `URL | undefined`
+ +

+ +Exposes the [middleware](/en/guides/middleware/) file path. + +```js +export default { + name: 'my-integration', + hooks: { + 'astro:build:ssr': ({ middlewareEntryPoint }) => { + if (middlewareEntryPoint) { + // do some operations if a middleware exist + } + }, + }, +} +``` + +### `astro:build:generated` + +

+ + +

**Previous hook:** [`astro:build:ssr`](#astrobuildssr) +**Next hook:** [`astro:build:done`](#astrobuilddone) + +**When:** After a static production build has finished generating routes and assets. + +**Why:** To access generated routes and assets **before** build artifacts are cleaned up. This is a very uncommon use case. We recommend using [`astro:build:done`](#astrobuilddone) unless you really need to access the generated files before cleanup. + +```js +'astro:build:generated'?: (options: { + dir: URL; + logger: AstroIntegrationLogger; +}) => void | Promise; +``` + +#### `dir` option + +

+ +**Type:** [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) +

+ +A URL path to the build output directory. Note that if you need a valid absolute path string, you should use Node's built-in [`fileURLToPath`](https://nodejs.org/api/url.html#urlfileurltopathurl) utility. + +```js +import { fileURLToPath } from 'node:url'; + +export default { + name: 'my-integration', + hooks: { + 'astro:build:generated': ({ dir }) => { + const outFile = fileURLToPath(new URL('./my-integration.json', dir)); + } + } +} +``` + +### `astro:build:done` + +**Previous hook:** [`astro:build:generated`](#astrobuildgenerated) + **When:** After a production build (SSG or SSR) has completed. **Why:** To access generated routes and assets for extension (ex. copy content into the generated `/assets` directory). If you plan to transform generated assets, we recommend exploring the [Vite Plugin API](https://vite.dev/guide/api-plugin.html) and [configuring via `astro:config:setup`](#updateconfig-option) instead. ```js 'astro:build:done'?: (options: { + pages: { pathname: string }[]; dir: URL; /** @deprecated Use the `assets` map and the new `astro:routes:resolved` hook */ routes: IntegrationRouteData[]; @@ -871,7 +1182,10 @@ The address, family and port number supplied by the [Node.js Net module](https:/ #### `dir` option +

+ **Type:** [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) +

A URL path to the build output directory. Note that if you need a valid absolute path string, you should use Node's built-in [`fileURLToPath`](https://nodejs.org/api/url.html#urlfileurltopathurl) utility. @@ -899,7 +1213,10 @@ export default function myIntegration() { This property is deprecated since v5.0. Check the [migration guide](/en/guides/upgrade-to/v5/#deprecated-routes-on-astrobuilddone-hook-integration-api). ::: +

+ **Type:** [`IntegrationRouteData[]`](#integrationroutedata-type-reference) +

A list of all generated routes alongside their associated metadata. @@ -908,159 +1225,107 @@ You can reference the full `IntegrationRouteData` type below, but the most commo - `component` - the input file path relative to the project root - `pathname` - the output file URL (undefined for routes using `[dynamic]` and `[...spread]` params) -##### `IntegrationRouteData` type reference - -```ts -interface IntegrationRouteData { - /** - * The type of the route. It can be: - * - * - `page`: a route that lives in the file system, usually an Astro component - * - `endpoint`: a route that lives in the file system, usually a JS file that exposes endpoints methods - * - `redirect`: a route that points to another route that lives in the file system - * - `fallback`: a route that doesn't exist in the file system and needs to be handled with other means, usually middleware - */ - type: 'page' | 'endpoint' | 'redirect' | 'fallback'; - /** Source component URL */ - component: string; - /** - * Output URL pathname where this route will be served - * note: will be undefined for [dynamic] and [...spread] routes - */ - pathname?: string; - /** - * regex used for matching an input URL against a requested route - * ex. "[fruit]/about.astro" will generate the pattern: /^\/([^/]+?)\/about\/?$/ - * where pattern.test("banana/about") is "true" - */ - pattern: RegExp; - /** - * Dynamic and spread route params - * ex. "/pages/[lang]/[..slug].astro" will output the params ['lang', '...slug'] - */ - params: string[]; - /** - * Similar to the "params" field, but with more associated metadata - * ex. "/pages/[lang]/index.astro" will output the segments - * [[ { content: 'lang', dynamic: true, spread: false } ]] - */ - segments: { content: string; dynamic: boolean; spread: boolean; }[][]; - /** - * A function that accepts a list of params, interpolates them with the route pattern, and returns the path name of the route. - * - * ## Example - * - * For a route such as `/blog/[...id].astro`, the `generate` function would return something like this: - * - * ```js - * console.log(generate({ id: 'presentation' })) // will log `/blog/presentation` - * ``` - * This is typically for internal use, so call with caution! - */ - generate: (data?: any) => string; - /** - * Whether the route is prerendered or not. - */ - prerender: boolean; - /** - * The paths of the physical files emitted by this route. When a route **isn't** prerendered, the value is either `undefined` or an empty array. - */ - distURL?: URL[]; - /** - * The route to redirect to. It holds information regarding the status code and its destination. - */ - redirect?: RedirectConfig; - /** - * The `IntegrationRouteData` to redirect to. It's present when `RouteData.type` is `redirect`. - */ - redirectRoute?: IntegrationRouteData; -} -``` - #### `assets` option - +

-**Type:** `Map` +**Type:** `Map`
+ +

Contains URLs to output files paths, grouped by [`IntegrationResolvedRoute`](#integrationresolvedroute-type-reference) `pattern` property. #### `pages` option +

+ **Type:** `{ pathname: string }[]` +

A list of all generated pages. It is an object with one property. - `pathname` - the finalized path of the page. -### `astro:route:setup` - -

- -**When:** In `astro dev`, whenever a route is requested. In `astro build`, while bundling and transforming a route file. +### Custom hooks -**Why:** To set options for a route at build or request time, such as enabling [on-demand server rendering](/en/guides/on-demand-rendering/#enabling-on-demand-rendering). +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). -```js -'astro:route:setup'?: (options: { route: RouteOptions }) => void | Promise; +```ts +declare global { + namespace Astro { + export interface IntegrationHook { + 'your:hook': (params: YourHookParameters) => Promise + } + } +} ``` -#### `route` option - -**Type:** `RouteOptions` - -An object with a `component` property to identify the route and the following additional values to allow you to configure the generated route: `prerender`. +Astro reserves the `astro:` prefix for future built-in hooks. Please choose a different prefix when naming your custom hook. -The `component` property is a readonly string path relative to the project root, e.g `"src/pages/blog/[slug].astro"`. +## Integration types reference -##### `route.prerender` +### `AstroIntegrationLogger` -

-**Type:** `boolean`
-**Default:** `undefined`
- -

- -The `prerender` property is used to configure [on-demand server rendering](/en/guides/on-demand-rendering/#enabling-on-demand-rendering) for a route. If the route file contains an explicit `export const prerender` value, the value will be used as the default instead of `undefined`. +An instance of the Astro logger, useful to write logs. This logger uses the same [log level](/en/reference/cli-reference/#--verbose) +configured via CLI. -```js title="astro.config.mjs" -import { defineConfig } from 'astro/config'; +**Methods available** to write to terminal: +- `logger.info("Message")`; +- `logger.warn("Message")`; +- `logger.error("Message")`; +- `logger.debug("Message")`; -export default defineConfig({ - integrations: [setPrerender()], -}); +All the messages are prepended with a label that has the same value as the name of the integration. -function setPrerender() { +```ts title="integration.ts" {8} +import type { AstroIntegration } from "astro"; +export function formatIntegration(): AstroIntegration { return { - name: 'set-prerender', + name: "astro-format", hooks: { - 'astro:route:setup': ({ route }) => { - if (route.component.endsWith('/blog/[slug].astro')) { - route.prerender = true; - } - }, - }, - }; + "astro:build:done": ({ logger }) => { + // do something + logger.info("Integration ready."); + } + } + } } ``` - -If the final value after running all the hooks is `undefined`, the route will fall back to a prerender default based on the [`output` option](/en/reference/configuration-reference/#output): prerendered for `static` mode, and on-demand rendered for `server` mode. -### Custom hooks +The example above will log a message that includes the provided `info` message: -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). +```shell +[astro-format] Integration ready. +``` -```ts -declare global { - namespace Astro { - export interface IntegrationHook { - 'your:hook': (params: YourHookParameters) => Promise +To log some messages with a different label, use the `.fork` method to specify an alternative to the default `name`: + +```ts title="integration.ts" ".fork" +import type { AstroIntegration } from "astro"; +export function formatIntegration(): AstroIntegration { + return { + name: "astro-format", + hooks: { + "astro:config:done": ({ logger }) => { + // do something + logger.info("Integration ready."); + }, + "astro:build:done": ({ logger }) => { + const buildLogger = logger.fork("astro-format/build"); + // do something + buildLogger.info("Build finished.") + } } } } ``` -Astro reserves the `astro:` prefix for future built-in hooks. Please choose a different prefix when naming your custom hook. +The example above will produce logs with `[astro-format]` by default, and `[astro-format/build]` when specified: + +```shell +[astro-format] Integration ready. +[astro-format/build] Build finished. +``` ### `HookParameters` @@ -1074,96 +1339,330 @@ function mySetup(options: HookParameters<'astro:config:setup'>) { } ``` -## 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`**: +### `IntegrationResolvedRoute` type reference -```json -{ - "name": "example", - "keywords": ["astro-integration"], +```ts +interface IntegrationResolvedRoute { + pattern: RouteData['route']; + patternRegex: RouteData['pattern']; + entrypoint: RouteData['component']; + isPrerendered: RouteData['prerender']; + redirectRoute?: IntegrationResolvedRoute; + generate: (data?: any) => string; + params: string[]; + pathname?: string; + segments: RoutePart[][]; + type: RouteType; + redirect?: RedirectConfig; + origin: 'internal' | 'external' | 'project'; } ``` -Once you [publish your integration to npm](https://docs.npmjs.com/cli/v8/commands/npm-publish), running `astro add example` will install your package with any peer dependencies specified in your `package.json`. This will also apply your integration to the user's `astro.config` like so: +#### `pattern` -```diff -// astro.config.mjs -import { defineConfig } from 'astro/config'; -+ import example from 'example'; +

-export default defineConfig({ -+ integrations: [example()], -}) +**Type:** `string` +

+ +Allows you to identify the type of route based on its path. Here are some examples of paths associated with their pattern: +* `src/pages/index.astro` will be `/` +* `src/pages/blog/[...slug].astro` will be `/blog/[...slug]` +* `src/pages/site/[blog]/[...slug].astro` will be `/site/[blog]/[...slug]` + +#### `patternRegex` + +

+ +**Type:** `RegExp` +

+ +Allows you to access a regex used for matching an input URL against a requested route. + +For example, given a `[fruit]/about.astro` path, the regex will be `/^\/([^/]+?)\/about\/?$/`. Using `pattern.test("banana/about")` will return `true`. + +#### `entrypoint` + +

+ +**Type:** `string` +

+ +The URL pathname of the source component. + +#### `isPrerendered` + +

+ +**Type:** `boolean` +

+ +Determines whether the route use [on demand rendering](/en/guides/on-demand-rendering/). The value will be `true` for projects configured with: +* `output: 'static'` when the route does not export `const prerender = true` +* `output: 'server'` when the route exports `const prerender = false` + +#### `redirectRoute` + +

+ +**Type:** `IntegrationResolvedRoute | undefined` +

+ +When the value of `IntegrationResolvedRoute.type` is `redirect`, the value will be the `IntegrationResolvedRoute` to redirect to. Otherwise, the value will be undefined. + +#### `generate()` + +

+ +**Type:** `(data?: any) => string` +

+ +A function that provides the optional parameters of the route, interpolates them with the route pattern, and returns the path name of the route. + +For example, with a route such as `/blog/[...id].astro`, the `generate` function could return: + +```js +console.log(generate({ id: 'presentation' })) // will log `/blog/presentation` ``` -:::caution -This assumes your integration definition is 1) a `default` export and 2) a function. Ensure this is true before adding the `astro-integration` keyword! -::: +#### `params` -## `AstroIntegrationLogger` +

-An instance of the Astro logger, useful to write logs. This logger uses the same [log level](/en/reference/cli-reference/#--verbose) -configured via CLI. +**Type:** `string[]` +

-**Methods available** to write to terminal: -- `logger.info("Message")`; -- `logger.warn("Message")`; -- `logger.error("Message")`; -- `logger.debug("Message")`; +Allows you to access the route `params`. For example, when a project uses the following [dynamic routes](/en/guides/routing/#dynamic-routes) `/pages/[lang]/[...slug].astro`, the value will be `['lang', '...slug']`. -All the messages are prepended with a label that has the same value of the integration. +#### `pathname` -```ts title="integration.ts" {8} -import type { AstroIntegration } from "astro"; -export function formatIntegration(): AstroIntegration { - return { - name: "astro-format", - hooks: { - "astro:build:done": ({ logger }) => { - // do something - logger.info("Integration ready."); - } - } - } +

+ +**Type:** `string | undefined` +

+ +For regular routes, the value will be the URL pathname where this route will be served. When the project uses [dynamic routes](/en/guides/routing/#dynamic-routes) (ie. `[dynamic]` or `[...spread]`), the pathname will be undefined. + +#### `segments` + +

+ +**Type:** RoutePart[][] +

+ +Allows you to access the route [`params`](#params) with additional metadata. Each object contains the following properties: +* `content`: the `param` name, +* `dynamic`: wether the route is dynamic or not, +* `spread`: whether the dynamic route uses the spread syntax or not. + +For example, the following route `/pages/[blog]/[...slug].astro` will output the segments: + +```js +[ + [ { content: 'pages', dynamic: false, spread: false } ], + [ { content: 'blog', dynamic: true, spread: false } ], + [ { content: '...slug', dynamic: true, spread: true } ] +] +``` + +#### `type` + +

+ +**Type:** `RouteType` +

+ +Allows you to identify the type of route. Possible values are: +* `page`: a route that lives in the file system, usually an Astro component +* `endpoint`: a route that lives in the file system, usually a JS file that exposes endpoints methods +* `redirect`: a route points to another route that lives in the file system +* `fallback`: a route that doesn't exist in the file system that needs to be handled with other means, usually the middleware + +#### `redirect` + +

+ +**Type:** RedirectConfig | undefined +

+ +Allows you to access the route to redirect to. This can be a string or an object containing information about the status code and its destination. + +#### `origin` + +

+ +**Type:** `'internal' | 'external' | 'project'` +

+ +Determines if a route comes from Astro core (`internal`), an integration (`external`) or the user's project (`project`). + +### `IntegrationRouteData` type reference + +:::caution +This type is deprecated since v5.0. Use [`IntegrationResolvedRoute`](#integrationresolvedroute-type-reference) instead. +::: + +A smaller version of the `RouteData` that is used in the integrations. + +```ts +interface IntegrationRouteData { + type: RouteType; + component: string; + pathname?: string; + pattern: RegExp; + params: string[]; + segments: { content: string; dynamic: boolean; spread: boolean; }[][]; + generate: (data?: any) => string; + prerender: boolean; + distURL?: URL[]; + redirect?: RedirectConfig; + redirectRoute?: IntegrationRouteData; } ``` -The example above will log a message that includes the provided `info` message: +#### `type` -```shell -[astro-format] Integration ready. +

+ +**Type:** `RouteType` +

+ +Allows you to identify the type of the route. The value can be: +- `page`: a route that lives in the file system, usually an Astro component +- `endpoint`: a route that lives in the file system, usually a JS file that exposes endpoints methods +- `redirect`: a route that points to another route that lives in the file system +- `fallback`: a route that doesn't exist in the file system and needs to be handled with other means, usually middleware + +#### `component` + +

+ +**Type:** `string` +

+ +Allows you to access the source component URL pathname. + +#### `pathname` + +

+ +**Type:** `string | undefined` +

+ +For regular routes, the value will be the URL pathname where this route will be served. When the project uses [dynamic routes](/en/guides/routing/#dynamic-routes) (ie. `[dynamic]` or `[...spread]`), the pathname will be undefined. + +#### `pattern` + +

+ +**Type:** `RegExp` +

+ +Allows you to access a regex used for matching an input URL against a requested route. + +For example, given a `[fruit]/about.astro` path, the regex will be `/^\/([^/]+?)\/about\/?$/`. Using `pattern.test("banana/about")` will return `true`. + +#### `params` + +

+ +**Type:** `string[]` +

+ +Allows you to access the route `params`. For example, when a project uses the following [dynamic routes](/en/guides/routing/#dynamic-routes) `/pages/[lang]/[...slug].astro`, the value will be `['lang', '...slug']`. + +#### `segments` + +

+ +**Type:** `{ content: string; dynamic: boolean; spread: boolean; }[][]` +

+ +Allows you to access the route [`params`](#params-1) with additional metadata. Each object contains the following properties: +* `content`: the `param`, +* `dynamic`: wether the route is dynamic or not, +* `spread`: whether the dynamic route uses the spread syntax or not. + +For example, the following route `/pages/[lang]/index.astro` will output the segments `[[ { content: 'lang', dynamic: true, spread: false } ]]`. + +#### `generate()` + +

+ +**Type:** `(data?: any) => string` +

+ +A function that provides the optional parameters of the route, interpolates them with the route pattern, and returns the path name of the route. + +For example, with a route such as `/blog/[...id].astro`, the `generate` function could return: + +```js +console.log(generate({ id: 'presentation' })) // will log `/blog/presentation` ``` -To log some messages with a different label, use the `.fork` method to specify an alternative to the default `name`: +#### `prerender` -```ts title="integration.ts" ".fork" -import type { AstroIntegration } from "astro"; -export function formatIntegration(): AstroIntegration { - return { - name: "astro-format", - hooks: { - "astro:config:done": ({ logger }) => { - // do something - logger.info("Integration ready."); - }, - "astro:build:done": ({ logger }) => { - const buildLogger = logger.fork("astro-format/build"); - // do something - buildLogger.info("Build finished.") - } - } - } +

+ +**Type:** `boolean` +

+ +Determines whether the route is prerendered or not. + +#### `distURL` + +

+ +**Type:** `URL[] | undefined` +

+ +The paths of the physical files emitted by this route. When a route **isn't** prerendered, the value is either `undefined` or an empty array. + +#### `redirect` + +

+ +**Type:** RedirectConfig | undefined +

+ +Allows you to access the route to redirect to. This can be a string or an object containing information about the status code and its destination. + +#### `redirectRoute` + +

+ +**Type:** `IntegrationRouteData | undefined` +

+ +When the value of `RouteData.type` is `redirect`, the value will contains the `IntegrationRouteData` of the route to redirect to. Otherwise, the value will be undefined. + +## 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`**: + +```json +{ + "name": "example", + "keywords": ["astro-integration"], } ``` -The example above will produce logs with `[astro-format]` by default, and `[astro-format/build]` when specified: +Once you [publish your integration to npm](https://docs.npmjs.com/cli/v8/commands/npm-publish), running `astro add example` will install your package with any peer dependencies specified in your `package.json`. This will also apply your integration to the user's `astro.config.*` like so: -```shell -[astro-format] Integration ready. -[astro-format/build] Build finished. +```js ins={3,6} +// astro.config.mjs +import { defineConfig } from 'astro/config'; +import example from 'example'; + +export default defineConfig({ + integrations: [example()], +}) ``` +:::caution +This assumes your integration definition is 1) a `default` export and 2) a function. Ensure this is true before adding the `astro-integration` keyword! +::: + ## Integration Ordering All integrations are run in the order that they are configured. For instance, for the array `[react(), svelte()]` in a user's `astro.config.*`, `react` will run before `svelte`.