summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.changeset/brave-elephants-fly.md21
-rw-r--r--.changeset/fuzzy-pugs-live.md21
-rw-r--r--.changeset/ten-walls-tap.md24
-rw-r--r--packages/astro/src/core/build/generate.ts6
-rw-r--r--packages/astro/src/core/routing/manifest/create.ts3
-rw-r--r--packages/astro/src/integrations/hooks.ts28
-rw-r--r--packages/astro/src/types/public/integrations.ts17
-rw-r--r--packages/astro/src/types/public/internal.ts88
-rw-r--r--packages/astro/test/static-build-page-dist-url.test.js4
9 files changed, 203 insertions, 9 deletions
diff --git a/.changeset/brave-elephants-fly.md b/.changeset/brave-elephants-fly.md
new file mode 100644
index 000000000..0c7673027
--- /dev/null
+++ b/.changeset/brave-elephants-fly.md
@@ -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) {
+
+}
+```
diff --git a/.changeset/fuzzy-pugs-live.md b/.changeset/fuzzy-pugs-live.md
new file mode 100644
index 000000000..bf1bd39dc
--- /dev/null
+++ b/.changeset/fuzzy-pugs-live.md
@@ -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) {
+
+}
+```
diff --git a/.changeset/ten-walls-tap.md b/.changeset/ten-walls-tap.md
new file mode 100644
index 000000000..afc8c07ed
--- /dev/null
+++ b/.changeset/ten-walls-tap.md
@@ -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
++ }
++ }
+}
+```
diff --git a/packages/astro/src/core/build/generate.ts b/packages/astro/src/core/build/generate.ts
index 9eaf4ff1d..7c8776f1d 100644
--- a/packages/astro/src/core/build/generate.ts
+++ b/packages/astro/src/core/build/generate.ts
@@ -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);
diff --git a/packages/astro/src/core/routing/manifest/create.ts b/packages/astro/src/core/routing/manifest/create.ts
index 834c37efe..4e0fb964a 100644
--- a/packages/astro/src/core/routing/manifest/create.ts
+++ b/packages/astro/src/core/routing/manifest/create.ts
@@ -254,6 +254,7 @@ function createFileBasedRoutes(
pathname: pathname || undefined,
prerender,
fallbackRoutes: [],
+ distURL: [],
});
}
}
@@ -318,6 +319,7 @@ function createInjectedRoutes({ settings, cwd }: CreateRouteManifestParams): Rou
pathname: pathname || void 0,
prerender: prerenderInjected ?? prerender,
fallbackRoutes: [],
+ distURL: [],
});
}
@@ -386,6 +388,7 @@ function createRedirectRoutes(
redirect: to,
redirectRoute: routeMap.get(destination),
fallbackRoutes: [],
+ distURL: [],
});
}
diff --git a/packages/astro/src/integrations/hooks.ts b/packages/astro/src/integrations/hooks.ts
index 0c40e60cd..18544ae80 100644
--- a/packages/astro/src/integrations/hooks.ts
+++ b/packages/astro/src/integrations/hooks.ts
@@ -25,6 +25,7 @@ import type {
AstroIntegration,
AstroRenderer,
HookParameters,
+ IntegrationRouteData,
RouteOptions,
} from '../types/public/integrations.js';
import type { RouteData } from '../types/public/internal.js';
@@ -532,6 +533,10 @@ 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({
@@ -539,7 +544,7 @@ export async function runHookBuildSsr({
hookName: 'astro:build:ssr',
hookResult: integration.hooks['astro:build:ssr']({
manifest,
- entryPoints,
+ entryPoints: entryPointsMap,
middlewareEntryPoint,
logger: getLogger(integration, logger),
}),
@@ -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);
@@ -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,
}),
@@ -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,
+ };
+}
diff --git a/packages/astro/src/types/public/integrations.ts b/packages/astro/src/types/public/integrations.ts
index 0836bdc9e..1980c0ab1 100644
--- a/packages/astro/src/types/public/integrations.ts
+++ b/packages/astro/src/types/public/integrations.ts
@@ -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
*/
@@ -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>;
@@ -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;
+};
diff --git a/packages/astro/src/types/public/internal.ts b/packages/astro/src/types/public/internal.ts
index bee15c6f7..c970ab3d1 100644
--- a/packages/astro/src/types/public/internal.ts
+++ b/packages/astro/src/types/public/internal.ts
@@ -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;
}
diff --git a/packages/astro/test/static-build-page-dist-url.test.js b/packages/astro/test/static-build-page-dist-url.test.js
index 72602274b..ebfa75d0e 100644
--- a/packages/astro/test/static-build-page-dist-url.test.js
+++ b/packages/astro/test/static-build-page-dist-url.test.js
@@ -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`);
+ });
});
});
});