Skip to content

Commit

Permalink
refactor(next): send IntegrationRouteData to integrations (#11864)
Browse files Browse the repository at this point in the history
Co-authored-by: Luiz Ferraz <[email protected]>
Co-authored-by: Alexander Niebuhr <[email protected]>
Co-authored-by: Florian Lefebvre <[email protected]>
  • Loading branch information
4 people authored Sep 13, 2024
1 parent d813262 commit ee38b3a
Show file tree
Hide file tree
Showing 9 changed files with 203 additions and 9 deletions.
21 changes: 21 additions & 0 deletions .changeset/brave-elephants-fly.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
'astro': major
---

### [changed]: `entryPoint` type inside the hook `astro:build:ssr`
In Astro v4.x, the `entryPoint` type was `RouteData`.

Astro v5.0 the `entryPoint` type is `IntegrationRouteData`, which contains a subset of the `RouteData` type. The fields `isIndex` and `fallbackRoutes` were removed.

#### What should I do?
Update your adapter to change the type of `entryPoint` from `RouteData` to `IntegrationRouteData`.

```diff
-import type {RouteData} from 'astro';
+import type {IntegrationRouteData} from "astro"

-function useRoute(route: RouteData) {
+function useRoute(route: IntegrationRouteData) {

}
```
21 changes: 21 additions & 0 deletions .changeset/fuzzy-pugs-live.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
'astro': major
---

### [changed]: `routes` type inside the hook `astro:build:done`
In Astro v4.x, the `routes` type was `RouteData`.

Astro v5.0 the `routes` type is `IntegrationRouteData`, which contains a subset of the `RouteData` type. The fields `isIndex` and `fallbackRoutes` were removed.

#### What should I do?
Update your adapter to change the type of `routes` from `RouteData` to `IntegrationRouteData`.

```diff
-import type {RouteData} from 'astro';
+import type {IntegrationRouteData} from "astro"

-function useRoute(route: RouteData) {
+function useRoute(route: IntegrationRouteData) {

}
```
24 changes: 24 additions & 0 deletions .changeset/ten-walls-tap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
'astro': major
---

### [changed]: `RouteData.distURL` is now an array
In Astro v4.x, `RouteData.distURL` was `undefined` or a `URL`

Astro v5.0, `RouteData.distURL` is `undefined` or an array of `URL`. This was a bug, because a route can generate multiple files on disk, especially when using dynamic routes such as `[slug]` or `[...slug]`.

#### What should I do?
Update your code to handle `RouteData.distURL` as an array.

```diff
if (route.distURL) {
- if (route.distURL.endsWith('index.html')) {
- // do something
- }
+ for (const url of route.distURL) {
+ if (url.endsWith('index.html')) {
+ // do something
+ }
+ }
}
```
6 changes: 5 additions & 1 deletion packages/astro/src/core/build/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,11 @@ async function generatePath(

const outFolder = getOutFolder(pipeline.settings, pathname, route);
const outFile = getOutFile(config, outFolder, pathname, route);
route.distURL = outFile;
if (route.distURL) {
route.distURL.push(outFile);
} else {
route.distURL = [outFile];
}

await fs.promises.mkdir(outFolder, { recursive: true });
await fs.promises.writeFile(outFile, body);
Expand Down
3 changes: 3 additions & 0 deletions packages/astro/src/core/routing/manifest/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ function createFileBasedRoutes(
pathname: pathname || undefined,
prerender,
fallbackRoutes: [],
distURL: [],
});
}
}
Expand Down Expand Up @@ -318,6 +319,7 @@ function createInjectedRoutes({ settings, cwd }: CreateRouteManifestParams): Rou
pathname: pathname || void 0,
prerender: prerenderInjected ?? prerender,
fallbackRoutes: [],
distURL: [],
});
}

Expand Down Expand Up @@ -386,6 +388,7 @@ function createRedirectRoutes(
redirect: to,
redirectRoute: routeMap.get(destination),
fallbackRoutes: [],
distURL: [],
});
}

Expand Down
28 changes: 25 additions & 3 deletions packages/astro/src/integrations/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import type {
AstroIntegration,
AstroRenderer,
HookParameters,
IntegrationRouteData,
RouteOptions,
} from '../types/public/integrations.js';
import type { RouteData } from '../types/public/internal.js';
Expand Down Expand Up @@ -532,14 +533,18 @@ export async function runHookBuildSsr({
entryPoints,
middlewareEntryPoint,
}: RunHookBuildSsr) {
const entryPointsMap = new Map();
for (const [key, value] of entryPoints) {
entryPointsMap.set(toIntegrationRouteData(key), value);
}
for (const integration of config.integrations) {
if (integration?.hooks?.['astro:build:ssr']) {
await withTakingALongTimeMsg({
name: integration.name,
hookName: 'astro:build:ssr',
hookResult: integration.hooks['astro:build:ssr']({
manifest,
entryPoints,
entryPoints: entryPointsMap,
middlewareEntryPoint,
logger: getLogger(integration, logger),
}),
Expand Down Expand Up @@ -592,7 +597,7 @@ export async function runHookBuildDone({
const dir =
settings.buildOutput === 'server' ? settings.config.build.client : settings.config.outDir;
await fsMod.promises.mkdir(dir, { recursive: true });

const integrationRoutes = routes.map(toIntegrationRouteData);
for (const integration of settings.config.integrations) {
if (integration?.hooks?.['astro:build:done']) {
const logger = getLogger(integration, logging);
Expand All @@ -603,7 +608,7 @@ export async function runHookBuildDone({
hookResult: integration.hooks['astro:build:done']({
pages: pages.map((p) => ({ pathname: p })),
dir,
routes,
routes: integrationRoutes,
logger,
cacheManifest,
}),
Expand Down Expand Up @@ -653,3 +658,20 @@ export async function runHookRouteSetup({
);
}
}

function toIntegrationRouteData(route: RouteData): IntegrationRouteData {
return {
route: route.route,
component: route.component,
generate: route.generate,
params: route.params,
pathname: route.pathname,
segments: route.segments,
prerender: route.prerender,
redirect: route.redirect,
redirectRoute: route.redirectRoute ? toIntegrationRouteData(route.redirectRoute) : undefined,
type: route.type,
pattern: route.pattern,
distURL: route.distURL,
};
}
17 changes: 15 additions & 2 deletions packages/astro/src/types/public/integrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ export interface BaseIntegrationHooks {
* This maps a {@link RouteData} to an {@link URL}, this URL represents
* the physical file you should import.
*/
entryPoints: Map<RouteData, URL>;
entryPoints: Map<IntegrationRouteData, URL>;
/**
* File path of the emitted middleware
*/
Expand All @@ -228,7 +228,7 @@ export interface BaseIntegrationHooks {
'astro:build:done': (options: {
pages: { pathname: string }[];
dir: URL;
routes: RouteData[];
routes: IntegrationRouteData[];
logger: AstroIntegrationLogger;
cacheManifest: boolean;
}) => void | Promise<void>;
Expand All @@ -246,3 +246,16 @@ export interface AstroIntegration {
[K in keyof Astro.IntegrationHooks]?: Astro.IntegrationHooks[K];
} & Partial<Record<string, unknown>>;
}

/**
* A smaller version of the {@link RouteData} that is used in the integrations.
*/
export type IntegrationRouteData = Omit<
RouteData,
'isIndex' | 'fallbackRoutes' | 'redirectRoute'
> & {
/**
* {@link RouteData.redirectRoute}
*/
redirectRoute?: IntegrationRouteData;
};
88 changes: 86 additions & 2 deletions packages/astro/src/types/public/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,105 @@ export interface SSRLoadedRendererValue {
renderHydrationScript?: () => string;
}

/**
* It contains the information about a route
*/
export interface RouteData {
/**
* 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]`
*/
route: string;
/**
* Source component URL
*/
component: string;
/**
* @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;
// expose the real path name on SSG
distURL?: URL;
/**
* 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[];
/**
*
* 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
* }
* ```
*/
pattern: RegExp;
/**
* 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;
/**
* Whether the route is prerendered or not
*/
prerender: boolean;
/**
* The route to redirect to. It holds information regarding the status code and its destination.
*/
redirect?: RedirectConfig;
/**
* The {@link RouteData} to redirect to. It's present when `RouteData.type` is `redirect`.
*/
redirectRoute?: RouteData;
/**
* A list of {@link RouteData} to fallback to. They are present when `i18n.fallback` has a list of locales.
*/
fallbackRoutes: RouteData[];

/**
* If this route is a directory index
* For example:
* - src/pages/index.astro
* - src/pages/blog/index.astro
*/
isIndex: boolean;
}

Expand Down
4 changes: 3 additions & 1 deletion packages/astro/test/static-build-page-dist-url.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ describe('Static build: pages routes have distURL', () => {
it('Pages routes have distURL', async () => {
assert.equal(checkRoutes.length > 0, true, 'Pages not found: build end hook not being called');
checkRoutes.forEach((p) => {
assert.equal(p.distURL instanceof URL, true, `${p.pathname} doesn't include distURL`);
p.distURL.forEach((distURL) => {
assert.equal(distURL instanceof URL, true, `${p.pathname} doesn't include distURL`);
});
});
});
});

0 comments on commit ee38b3a

Please sign in to comment.