Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(next): send IntegrationRouteData to integrations #11864

Merged
merged 16 commits into from
Sep 13, 2024
Merged
22 changes: 22 additions & 0 deletions .changeset/brave-elephants-fly.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
'@astrojs/vercel': major
'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) {

}
```
22 changes: 22 additions & 0 deletions .changeset/fuzzy-pugs-live.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
'@astrojs/vercel': major
'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
+ }
+ }
}
```
17 changes: 8 additions & 9 deletions packages/astro/src/content/vite-plugin-content-virtual-mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,20 +254,19 @@ export async function generateContentEntryFile({
}

let virtualModContents: string;
if(isClient) {
if (isClient) {
throw new AstroError({
...AstroErrorData.ServerOnlyModule,
message: AstroErrorData.ServerOnlyModule.message('astro:content'),
});
} else {
virtualModContents =
nodeFs
.readFileSync(contentPaths.virtualModTemplate, 'utf-8')
.replace('@@CONTENT_DIR@@', relContentDir)
.replace("'@@CONTENT_ENTRY_GLOB_PATH@@'", contentEntryGlobResult)
.replace("'@@DATA_ENTRY_GLOB_PATH@@'", dataEntryGlobResult)
.replace("'@@RENDER_ENTRY_GLOB_PATH@@'", renderEntryGlobResult)
.replace('/* @@LOOKUP_MAP_ASSIGNMENT@@ */', `lookupMap = ${JSON.stringify(lookupMap)};`);
virtualModContents = nodeFs
.readFileSync(contentPaths.virtualModTemplate, 'utf-8')
.replace('@@CONTENT_DIR@@', relContentDir)
.replace("'@@CONTENT_ENTRY_GLOB_PATH@@'", contentEntryGlobResult)
.replace("'@@DATA_ENTRY_GLOB_PATH@@'", dataEntryGlobResult)
.replace("'@@RENDER_ENTRY_GLOB_PATH@@'", renderEntryGlobResult)
.replace('/* @@LOOKUP_MAP_ASSIGNMENT@@ */', `lookupMap = ${JSON.stringify(lookupMap)};`);
}

return virtualModContents;
Expand Down
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 @@ -468,7 +468,11 @@ async function generatePath(

const outFolder = getOutFolder(config, pathname, route);
const outFile = getOutFile(config, outFolder, pathname, route);
route.distURL = outFile;
if (route.distURL) {
ematipico marked this conversation as resolved.
Show resolved Hide resolved
route.distURL.push(outFile);
} else {
route.distURL = [outFile];
}

await fs.promises.mkdir(outFolder, { recursive: true });
await fs.promises.writeFile(outFile, body);
Expand Down
2 changes: 1 addition & 1 deletion packages/astro/src/core/middleware/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ function createContext({
generator: `Astro v${ASTRO_VERSION}`,
props: {},
rewrite,
routePattern: "",
routePattern: '',
redirect(path, status) {
return new Response(null, {
status: status || 302,
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 @@ -252,6 +252,7 @@ function createFileBasedRoutes(
pathname: pathname || undefined,
prerender,
fallbackRoutes: [],
distURL: [],
});
}
}
Expand Down Expand Up @@ -322,6 +323,7 @@ function createInjectedRoutes({ settings, cwd }: CreateRouteManifestParams): Rou
pathname: pathname || void 0,
prerender: prerenderInjected ?? prerender,
fallbackRoutes: [],
distURL: [],
});
}

Expand Down Expand Up @@ -390,6 +392,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 @@ -18,6 +18,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 @@ -502,14 +503,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 @@ -560,7 +565,7 @@ export async function runHookBuildDone({
}: RunHookBuildDone) {
const dir = isServerLikeOutput(config) ? config.build.client : config.outDir;
await fsMod.promises.mkdir(dir, { recursive: true });

const integrationRoutes = routes.map(toIntegrationRouteData);
for (const integration of config.integrations) {
if (integration?.hooks?.['astro:build:done']) {
const logger = getLogger(integration, logging);
Expand All @@ -571,7 +576,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 @@ -621,3 +626,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,
};
}
16 changes: 13 additions & 3 deletions packages/astro/src/types/public/integrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import type { AstroIntegrationLogger } from '../../core/logger/core.js';
import type { getToolbarServerCommunicationHelpers } from '../../integrations/hooks.js';
import type { DeepPartial } from '../../type-utils.js';
import type { AstroConfig } from './config.js';
import type { RouteData } from './internal.js';
import type { DevToolbarAppEntry } from './toolbar.js';
import type { RouteData } from './internal.js';

export interface RouteOptions {
/**
Expand Down Expand Up @@ -199,7 +199,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 @@ -221,7 +221,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 @@ -239,3 +239,13 @@ 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
};
85 changes: 83 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,102 @@ 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be worth documenting what value will be here in case of redirect and fallback routes since this is not optional and yet those types don't necessarily have a component.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Fryuni what would you propose then?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is for next major I think it would be better to change the name of this field to something like subject and then document what it means for different route types.

  • For page routes, it is the Astro component to be rendered.
  • For endpoint routes, it is the file declaring the method handlers.
  • For redirect routes, it is the target mask of the redirect.
  • For fallback routes, it is the subject of the route used as fallback.

*/
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 the index
florian-lefebvre marked this conversation as resolved.
Show resolved Hide resolved
*/
isIndex: boolean;
}

Expand Down
Loading
Loading