Skip to content

Commit ee38b3a

Browse files
ematipicoFryunialexanderniebuhrflorian-lefebvre
authored
refactor(next): send IntegrationRouteData to integrations (#11864)
Co-authored-by: Luiz Ferraz <[email protected]> Co-authored-by: Alexander Niebuhr <[email protected]> Co-authored-by: Florian Lefebvre <[email protected]>
1 parent d813262 commit ee38b3a

File tree

9 files changed

+203
-9
lines changed

9 files changed

+203
-9
lines changed

.changeset/brave-elephants-fly.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
'astro': major
3+
---
4+
5+
### [changed]: `entryPoint` type inside the hook `astro:build:ssr`
6+
In Astro v4.x, the `entryPoint` type was `RouteData`.
7+
8+
Astro v5.0 the `entryPoint` type is `IntegrationRouteData`, which contains a subset of the `RouteData` type. The fields `isIndex` and `fallbackRoutes` were removed.
9+
10+
#### What should I do?
11+
Update your adapter to change the type of `entryPoint` from `RouteData` to `IntegrationRouteData`.
12+
13+
```diff
14+
-import type {RouteData} from 'astro';
15+
+import type {IntegrationRouteData} from "astro"
16+
17+
-function useRoute(route: RouteData) {
18+
+function useRoute(route: IntegrationRouteData) {
19+
20+
}
21+
```

.changeset/fuzzy-pugs-live.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
'astro': major
3+
---
4+
5+
### [changed]: `routes` type inside the hook `astro:build:done`
6+
In Astro v4.x, the `routes` type was `RouteData`.
7+
8+
Astro v5.0 the `routes` type is `IntegrationRouteData`, which contains a subset of the `RouteData` type. The fields `isIndex` and `fallbackRoutes` were removed.
9+
10+
#### What should I do?
11+
Update your adapter to change the type of `routes` from `RouteData` to `IntegrationRouteData`.
12+
13+
```diff
14+
-import type {RouteData} from 'astro';
15+
+import type {IntegrationRouteData} from "astro"
16+
17+
-function useRoute(route: RouteData) {
18+
+function useRoute(route: IntegrationRouteData) {
19+
20+
}
21+
```

.changeset/ten-walls-tap.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
'astro': major
3+
---
4+
5+
### [changed]: `RouteData.distURL` is now an array
6+
In Astro v4.x, `RouteData.distURL` was `undefined` or a `URL`
7+
8+
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]`.
9+
10+
#### What should I do?
11+
Update your code to handle `RouteData.distURL` as an array.
12+
13+
```diff
14+
if (route.distURL) {
15+
- if (route.distURL.endsWith('index.html')) {
16+
- // do something
17+
- }
18+
+ for (const url of route.distURL) {
19+
+ if (url.endsWith('index.html')) {
20+
+ // do something
21+
+ }
22+
+ }
23+
}
24+
```

packages/astro/src/core/build/generate.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,11 @@ async function generatePath(
466466

467467
const outFolder = getOutFolder(pipeline.settings, pathname, route);
468468
const outFile = getOutFile(config, outFolder, pathname, route);
469-
route.distURL = outFile;
469+
if (route.distURL) {
470+
route.distURL.push(outFile);
471+
} else {
472+
route.distURL = [outFile];
473+
}
470474

471475
await fs.promises.mkdir(outFolder, { recursive: true });
472476
await fs.promises.writeFile(outFile, body);

packages/astro/src/core/routing/manifest/create.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ function createFileBasedRoutes(
254254
pathname: pathname || undefined,
255255
prerender,
256256
fallbackRoutes: [],
257+
distURL: [],
257258
});
258259
}
259260
}
@@ -318,6 +319,7 @@ function createInjectedRoutes({ settings, cwd }: CreateRouteManifestParams): Rou
318319
pathname: pathname || void 0,
319320
prerender: prerenderInjected ?? prerender,
320321
fallbackRoutes: [],
322+
distURL: [],
321323
});
322324
}
323325

@@ -386,6 +388,7 @@ function createRedirectRoutes(
386388
redirect: to,
387389
redirectRoute: routeMap.get(destination),
388390
fallbackRoutes: [],
391+
distURL: [],
389392
});
390393
}
391394

packages/astro/src/integrations/hooks.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import type {
2525
AstroIntegration,
2626
AstroRenderer,
2727
HookParameters,
28+
IntegrationRouteData,
2829
RouteOptions,
2930
} from '../types/public/integrations.js';
3031
import type { RouteData } from '../types/public/internal.js';
@@ -532,14 +533,18 @@ export async function runHookBuildSsr({
532533
entryPoints,
533534
middlewareEntryPoint,
534535
}: RunHookBuildSsr) {
536+
const entryPointsMap = new Map();
537+
for (const [key, value] of entryPoints) {
538+
entryPointsMap.set(toIntegrationRouteData(key), value);
539+
}
535540
for (const integration of config.integrations) {
536541
if (integration?.hooks?.['astro:build:ssr']) {
537542
await withTakingALongTimeMsg({
538543
name: integration.name,
539544
hookName: 'astro:build:ssr',
540545
hookResult: integration.hooks['astro:build:ssr']({
541546
manifest,
542-
entryPoints,
547+
entryPoints: entryPointsMap,
543548
middlewareEntryPoint,
544549
logger: getLogger(integration, logger),
545550
}),
@@ -592,7 +597,7 @@ export async function runHookBuildDone({
592597
const dir =
593598
settings.buildOutput === 'server' ? settings.config.build.client : settings.config.outDir;
594599
await fsMod.promises.mkdir(dir, { recursive: true });
595-
600+
const integrationRoutes = routes.map(toIntegrationRouteData);
596601
for (const integration of settings.config.integrations) {
597602
if (integration?.hooks?.['astro:build:done']) {
598603
const logger = getLogger(integration, logging);
@@ -603,7 +608,7 @@ export async function runHookBuildDone({
603608
hookResult: integration.hooks['astro:build:done']({
604609
pages: pages.map((p) => ({ pathname: p })),
605610
dir,
606-
routes,
611+
routes: integrationRoutes,
607612
logger,
608613
cacheManifest,
609614
}),
@@ -653,3 +658,20 @@ export async function runHookRouteSetup({
653658
);
654659
}
655660
}
661+
662+
function toIntegrationRouteData(route: RouteData): IntegrationRouteData {
663+
return {
664+
route: route.route,
665+
component: route.component,
666+
generate: route.generate,
667+
params: route.params,
668+
pathname: route.pathname,
669+
segments: route.segments,
670+
prerender: route.prerender,
671+
redirect: route.redirect,
672+
redirectRoute: route.redirectRoute ? toIntegrationRouteData(route.redirectRoute) : undefined,
673+
type: route.type,
674+
pattern: route.pattern,
675+
distURL: route.distURL,
676+
};
677+
}

packages/astro/src/types/public/integrations.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ export interface BaseIntegrationHooks {
206206
* This maps a {@link RouteData} to an {@link URL}, this URL represents
207207
* the physical file you should import.
208208
*/
209-
entryPoints: Map<RouteData, URL>;
209+
entryPoints: Map<IntegrationRouteData, URL>;
210210
/**
211211
* File path of the emitted middleware
212212
*/
@@ -228,7 +228,7 @@ export interface BaseIntegrationHooks {
228228
'astro:build:done': (options: {
229229
pages: { pathname: string }[];
230230
dir: URL;
231-
routes: RouteData[];
231+
routes: IntegrationRouteData[];
232232
logger: AstroIntegrationLogger;
233233
cacheManifest: boolean;
234234
}) => void | Promise<void>;
@@ -246,3 +246,16 @@ export interface AstroIntegration {
246246
[K in keyof Astro.IntegrationHooks]?: Astro.IntegrationHooks[K];
247247
} & Partial<Record<string, unknown>>;
248248
}
249+
250+
/**
251+
* A smaller version of the {@link RouteData} that is used in the integrations.
252+
*/
253+
export type IntegrationRouteData = Omit<
254+
RouteData,
255+
'isIndex' | 'fallbackRoutes' | 'redirectRoute'
256+
> & {
257+
/**
258+
* {@link RouteData.redirectRoute}
259+
*/
260+
redirectRoute?: IntegrationRouteData;
261+
};

packages/astro/src/types/public/internal.ts

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,21 +36,105 @@ export interface SSRLoadedRendererValue {
3636
renderHydrationScript?: () => string;
3737
}
3838

39+
/**
40+
* It contains the information about a route
41+
*/
3942
export interface RouteData {
43+
/**
44+
* The current **pattern** of the route. For example:
45+
* - `src/pages/index.astro` has a pattern of `/`
46+
* - `src/pages/blog/[...slug].astro` has a pattern of `/blog/[...slug]`
47+
* - `src/pages/site/[blog]/[...slug].astro` has a pattern of `/site/[blog]/[...slug]`
48+
*/
4049
route: string;
50+
/**
51+
* Source component URL
52+
*/
4153
component: string;
54+
/**
55+
* @param {any} data The optional parameters of the route
56+
*
57+
* @description
58+
* A function that accepts a list of params, interpolates them with the route pattern, and returns the path name of the route.
59+
*
60+
* ## Example
61+
*
62+
* For a route such as `/blog/[...id].astro`, the `generate` function would return something like this:
63+
*
64+
* ```js
65+
* console.log(generate({ id: 'presentation' })) // will log `/blog/presentation`
66+
* ```
67+
*/
4268
generate: (data?: any) => string;
69+
/**
70+
* Dynamic and spread route params
71+
* ex. "/pages/[lang]/[...slug].astro" will output the params ['lang', '...slug']
72+
*/
4373
params: string[];
74+
/**
75+
* Output URL pathname where this route will be served
76+
* note: will be undefined for [dynamic] and [...spread] routes
77+
*/
4478
pathname?: string;
45-
// expose the real path name on SSG
46-
distURL?: URL;
79+
/**
80+
* 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.
81+
*/
82+
distURL?: URL[];
83+
/**
84+
*
85+
* regex used for matching an input URL against a requested route
86+
* ex. "[fruit]/about.astro" will generate the pattern: /^\/([^/]+?)\/about\/?$/
87+
* where pattern.test("banana/about") is "true"
88+
*
89+
* ## Example
90+
*
91+
* ```js
92+
* if (route.pattern.test('/blog')) {
93+
* // do something
94+
* }
95+
* ```
96+
*/
4797
pattern: RegExp;
98+
/**
99+
* Similar to the "params" field, but with more associated metadata. For example, for `/site/[blog]/[...slug].astro`, the segments are:
100+
*
101+
* 1. `{ content: 'site', dynamic: false, spread: false }`
102+
* 2. `{ content: 'blog', dynamic: true, spread: false }`
103+
* 3. `{ content: '...slug', dynamic: true, spread: true }`
104+
*/
48105
segments: RoutePart[][];
106+
/**
107+
*
108+
* The type of the route. It can be:
109+
* - `page`: a route that lives in the file system, usually an Astro component
110+
* - `endpoint`: a route that lives in the file system, usually a JS file that exposes endpoints methods
111+
* - `redirect`: a route points to another route that lives in the file system
112+
* - `fallback`: a route that doesn't exist in the file system that needs to be handled with other means, usually the middleware
113+
*/
49114
type: RouteType;
115+
/**
116+
* Whether the route is prerendered or not
117+
*/
50118
prerender: boolean;
119+
/**
120+
* The route to redirect to. It holds information regarding the status code and its destination.
121+
*/
51122
redirect?: RedirectConfig;
123+
/**
124+
* The {@link RouteData} to redirect to. It's present when `RouteData.type` is `redirect`.
125+
*/
52126
redirectRoute?: RouteData;
127+
/**
128+
* A list of {@link RouteData} to fallback to. They are present when `i18n.fallback` has a list of locales.
129+
*/
53130
fallbackRoutes: RouteData[];
131+
132+
/**
133+
* If this route is a directory index
134+
* For example:
135+
* - src/pages/index.astro
136+
* - src/pages/blog/index.astro
137+
*/
54138
isIndex: boolean;
55139
}
56140

packages/astro/test/static-build-page-dist-url.test.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ describe('Static build: pages routes have distURL', () => {
2525
it('Pages routes have distURL', async () => {
2626
assert.equal(checkRoutes.length > 0, true, 'Pages not found: build end hook not being called');
2727
checkRoutes.forEach((p) => {
28-
assert.equal(p.distURL instanceof URL, true, `${p.pathname} doesn't include distURL`);
28+
p.distURL.forEach((distURL) => {
29+
assert.equal(distURL instanceof URL, true, `${p.pathname} doesn't include distURL`);
30+
});
2931
});
3032
});
3133
});

0 commit comments

Comments
 (0)