From a039e65008d6dacefde6a63efab9942445a4b527 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Fri, 22 Nov 2024 17:16:19 +0100 Subject: [PATCH] feat(next): routes resolved hook (#10077) Co-authored-by: Sarah Rainsberger --- src/content/docs/en/guides/upgrade-to/v5.mdx | 40 ++++ .../en/reference/integrations-reference.mdx | 179 +++++++++++++++++- 2 files changed, 216 insertions(+), 3 deletions(-) diff --git a/src/content/docs/en/guides/upgrade-to/v5.mdx b/src/content/docs/en/guides/upgrade-to/v5.mdx index 845df7f78c599..213dc9b6f6e9c 100644 --- a/src/content/docs/en/guides/upgrade-to/v5.mdx +++ b/src/content/docs/en/guides/upgrade-to/v5.mdx @@ -365,6 +365,46 @@ export default function createIntegration() { Learn more about [the Adapter API](/en/reference/adapter-reference/) for building adapter integrations. +### Deprecated: `routes` on `astro:build:done` hook (Integration API) + + + +In Astro v4.x, integrations accessed routes from the `astro:build:done` hook. + +Astro v5.0 deprecates the `routes` array passed to this hook. Instead, it exposes a new `astro:routes:resolved` hook that runs before `astro:config:done`, and whenever a route changes in development. It has all the same properties of the deprecated `routes` list, except `distURL` which is only available during build. + +#### What should I do? + +Remove any instance of `routes` passed to `astro:build:done` and replace it with the new `astro:routes:resolved` hook. Access `distURL` on the newly exposed `assets` map: + +```js title="my-integration.mjs" ins={2,6-8,11,13-18} del={10} +const integration = () => { + let routes + return { + name: 'my-integration', + hooks: { + 'astro:routes:resolved': (params) => { + routes = params.routes + }, + 'astro:build:done': ({ + routes + assets + }) => { + for (const route of routes) { + const distURL = assets.get(route.pattern) + if (distURL) { + Object.assign(route, { distURL }) + } + } + console.log(routes) + } + } + } +} +``` + +Learn more about [the Integration API `astro:routes:resolved` hook](/en/reference/integrations-reference/#astroroutesresolved) for building integrations. + ## Removed The following features have now been entirely removed from the code base and can no longer be used. Some of these features may have continued to work in your project even after deprecation. Others may have silently had no effect. diff --git a/src/content/docs/en/reference/integrations-reference.mdx b/src/content/docs/en/reference/integrations-reference.mdx index 4b4943259aecd..855e713f1b842 100644 --- a/src/content/docs/en/reference/integrations-reference.mdx +++ b/src/content/docs/en/reference/integrations-reference.mdx @@ -39,6 +39,10 @@ interface AstroIntegration { createCodegenDir: () => URL; logger: AstroIntegrationLogger; }) => void | Promise; + 'astro:routes:resolved'?: (options: { + routes: IntegrationResolvedRoute[]; + logger: AstroIntegrationLogger; + }) => void | Promise; 'astro:config:done'?: (options: { config: AstroConfig; setAdapter: (adapter: AstroAdapter) => void; @@ -62,7 +66,13 @@ interface AstroIntegration { entryPoints: Map; logger: AstroIntegrationLogger; }) => void | Promise; - 'astro:build:done'?: (options: { dir: URL; routes: IntegrationRouteData[]; logger: AstroIntegrationLogger; }) => void | Promise; + 'astro:build:done'?: (options: { + dir: URL; + /** @deprecated Use the `assets` map and the new `astro:routes:resolved` hook */ + routes: IntegrationRouteData[]; + assets: Map; + logger: AstroIntegrationLogger; + }) => void | Promise; // ... any custom hooks from integrations }; @@ -452,10 +462,155 @@ const integration = { } ``` -### `astro:config:done` +### `astro:routes:resolved` + + **Previous hook:** [`astro:config:setup`](#astroconfigsetup) +**Next hook:** [`astro:config:done`](#astroconfigdone) + +**When:** In `astro dev`, it also runs if a file based route changes (added/removed/updated). + +**Why:** To access routes and their metadata + +```js +'astro:routes:resolved'?: (options: { + routes: IntegrationResolvedRoute[]; + logger: AstroIntegrationLogger; +}) => void | Promise; +``` + +#### `routes` option + +**Type:** [`IntegrationResolvedRoute[]`](#integrationresolvedroute-type-reference) + +A list of all routes with their associated metadata. + +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) + }, + } + } +} +``` + +##### `IntegrationResolvedRoute` type reference + +```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']; + + /** + * Whether the route is prerendered or not + */ + isPrerendered: RouteData['prerender']; + + /** + * The {@link IntegrationResolvedRoute} to redirect to. It's present when `IntegrationResolvedRoute.type` is `redirect`. + */ + redirectRoute?: IntegrationResolvedRoute; + + /** + * @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; + + /** + * Dynamic and spread route params + * ex. "/pages/[lang]/[...slug].astro" will output the params ['lang', '...slug'] + */ + params: string[]; + + /** + * Output URL pathname where this route will be served + * note: will be undefined for [dynamic] and [...spread] routes + */ + pathname?: string; + + /** + * 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; + + /** + * The route to redirect to. It holds information regarding the status code and its destination. + */ + redirect?: RedirectConfig; + + /** + * Whether the route comes from Astro core, an integration or the user's project + */ + origin: 'internal' | 'external' | 'project'; +} +``` + + +### `astro:config:done` + +**Previous hook:** [`astro:config:setup`](#astroroutesresolved) + **Next hook:** [`astro:server:setup`](#astroserversetup) when running in "dev" mode, or [`astro:build:start`](#astrobuildstart) during production builds **When:** After the Astro config has resolved and other integrations have run their `astro:config:setup` hooks. @@ -702,7 +857,13 @@ The address, family and port number supplied by the [Node.js Net module](https:/ **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: { dir: URL; routes: IntegrationRouteData[], pages: { pathname: string }[] }) => void | Promise; +'astro:build:done'?: (options: { + dir: URL; + /** @deprecated Use the `assets` map and the new `astro:routes:resolved` hook */ + routes: IntegrationRouteData[]; + assets: Map; + logger: AstroIntegrationLogger; +}) => void | Promise; ``` #### `dir` option @@ -731,6 +892,10 @@ export default function myIntegration() { #### `routes` option +:::caution +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. @@ -809,6 +974,14 @@ interface IntegrationRouteData { } ``` +#### `assets` option + + + +**Type:** `Map` + +Contains URLs to output files paths, grouped by [`IntegrationResolvedRoute`](#integrationresolvedroute-type-reference) `pattern` property. + #### `pages` option **Type:** `{ pathname: string }[]`