summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Florian Lefebvre <contact@florian-lefebvre.dev> 2024-11-21 12:30:49 +0100
committerGravatar GitHub <noreply@github.com> 2024-11-21 12:30:49 +0100
commit8309c61f0dfa5991d3f6c5c5fca4403794d6fda2 (patch)
treee91f9387ee8dca2c9ca922a8ea1321d37a72ca64
parent3f02d5f12b167514fff6eb9693b4e25c668e7a31 (diff)
downloadastro-8309c61f0dfa5991d3f6c5c5fca4403794d6fda2.tar.gz
astro-8309c61f0dfa5991d3f6c5c5fca4403794d6fda2.tar.zst
astro-8309c61f0dfa5991d3f6c5c5fca4403794d6fda2.zip
feat(next): astro:routes:resolved (#12329)
Co-authored-by: Luiz Ferraz <luiz@lferraz.com> Co-authored-by: Emanuele Stoppa <my.burning@gmail.com> Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>
Diffstat (limited to '')
-rw-r--r--.changeset/giant-ravens-look.md50
-rw-r--r--packages/astro/src/actions/integration.ts3
-rw-r--r--packages/astro/src/assets/endpoint/config.ts1
-rw-r--r--packages/astro/src/container/index.ts1
-rw-r--r--packages/astro/src/core/dev/container.ts4
-rw-r--r--packages/astro/src/core/routing/astro-designed-error-pages.ts2
-rw-r--r--packages/astro/src/core/routing/manifest/create.ts11
-rw-r--r--packages/astro/src/core/routing/manifest/serialization.ts1
-rw-r--r--packages/astro/src/core/server-islands/endpoint.ts1
-rw-r--r--packages/astro/src/integrations/hooks.ts50
-rw-r--r--packages/astro/src/types/astro.ts6
-rw-r--r--packages/astro/src/types/public/integrations.ts52
-rw-r--r--packages/astro/src/types/public/internal.ts18
-rw-r--r--packages/astro/src/vite-plugin-astro-server/plugin.ts48
-rw-r--r--packages/astro/src/vite-plugin-integrations-container/index.ts4
-rw-r--r--packages/astro/test/units/integrations/api.test.js217
16 files changed, 434 insertions, 35 deletions
diff --git a/.changeset/giant-ravens-look.md b/.changeset/giant-ravens-look.md
new file mode 100644
index 000000000..dfa5d6d95
--- /dev/null
+++ b/.changeset/giant-ravens-look.md
@@ -0,0 +1,50 @@
+---
+'astro': minor
+---
+
+Adds a new `astro:routes:resolved` hook to the Integration API. Also update the `astro:build:done` hook by deprecating `routes` and adding a new `assets` map.
+
+When building an integration, you can now get access to routes inside the `astro:routes:resolved` hook:
+
+```js
+const integration = () => {
+ return {
+ name: 'my-integration',
+ hooks: {
+ 'astro:routes:resolved': ({ routes }) => {
+ console.log(routes)
+ }
+ }
+ }
+}
+```
+
+This hook runs before `astro:config:done`, and whenever a route changes in development.
+
+The `routes` array from `astro:build:done` is now deprecated, and exposed properties are now available on `astro:routes:resolved`, except for `distURL`. For this, you can use the newly exposed `assets` map:
+
+```diff
+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)
+ }
+ }
+ }
+}
+``` \ No newline at end of file
diff --git a/packages/astro/src/actions/integration.ts b/packages/astro/src/actions/integration.ts
index d7c99fc42..13d76e8b6 100644
--- a/packages/astro/src/actions/integration.ts
+++ b/packages/astro/src/actions/integration.ts
@@ -18,10 +18,11 @@ export default function astroIntegrationActionsRouteHandler({
name: VIRTUAL_MODULE_ID,
hooks: {
async 'astro:config:setup'(params) {
- params.injectRoute({
+ settings.injectedRoutes.push({
pattern: ACTION_RPC_ROUTE_PATTERN,
entrypoint: 'astro/actions/runtime/route.js',
prerender: false,
+ origin: 'internal',
});
params.addMiddleware({
diff --git a/packages/astro/src/assets/endpoint/config.ts b/packages/astro/src/assets/endpoint/config.ts
index a910df25c..b9309d446 100644
--- a/packages/astro/src/assets/endpoint/config.ts
+++ b/packages/astro/src/assets/endpoint/config.ts
@@ -63,5 +63,6 @@ function getImageEndpointData(
pathname: settings.config.image.endpoint.route,
prerender: false,
fallbackRoutes: [],
+ origin: 'internal',
};
}
diff --git a/packages/astro/src/container/index.ts b/packages/astro/src/container/index.ts
index 7694f3afa..2564e2875 100644
--- a/packages/astro/src/container/index.ts
+++ b/packages/astro/src/container/index.ts
@@ -544,6 +544,7 @@ export class experimental_AstroContainer {
type,
fallbackRoutes: [],
isIndex: false,
+ origin: 'internal',
};
}
diff --git a/packages/astro/src/core/dev/container.ts b/packages/astro/src/core/dev/container.ts
index 3f9090498..0ca40cd74 100644
--- a/packages/astro/src/core/dev/container.ts
+++ b/packages/astro/src/core/dev/container.ts
@@ -7,6 +7,7 @@ import * as vite from 'vite';
import {
runHookConfigDone,
runHookConfigSetup,
+ runHookRoutesResolved,
runHookServerDone,
runHookServerStart,
} from '../../integrations/hooks.js';
@@ -83,10 +84,11 @@ export async function createContainer({
.filter(Boolean) as string[];
// Create the route manifest already outside of Vite so that `runHookConfigDone` can use it to inform integrations of the build output
- let manifest = await createRouteManifest({ settings, fsMod: fs }, logger);
+ let manifest = await createRouteManifest({ settings, fsMod: fs }, logger, { dev: true });
const devSSRManifest = createDevelopmentManifest(settings);
manifest = injectDefaultDevRoutes(settings, devSSRManifest, manifest);
+ await runHookRoutesResolved({ settings, logger, routes: manifest.routes });
await runHookConfigDone({ settings, logger, command: 'dev' });
diff --git a/packages/astro/src/core/routing/astro-designed-error-pages.ts b/packages/astro/src/core/routing/astro-designed-error-pages.ts
index 671221b5d..4348e6e97 100644
--- a/packages/astro/src/core/routing/astro-designed-error-pages.ts
+++ b/packages/astro/src/core/routing/astro-designed-error-pages.ts
@@ -15,6 +15,7 @@ export const DEFAULT_404_ROUTE: RouteData = {
route: '/404',
fallbackRoutes: [],
isIndex: false,
+ origin: 'internal',
};
export const DEFAULT_500_ROUTE: RouteData = {
@@ -29,6 +30,7 @@ export const DEFAULT_500_ROUTE: RouteData = {
route: '/500',
fallbackRoutes: [],
isIndex: false,
+ origin: 'internal',
};
export function ensure404Route(manifest: ManifestData) {
diff --git a/packages/astro/src/core/routing/manifest/create.ts b/packages/astro/src/core/routing/manifest/create.ts
index 33b521699..621164084 100644
--- a/packages/astro/src/core/routing/manifest/create.ts
+++ b/packages/astro/src/core/routing/manifest/create.ts
@@ -20,6 +20,7 @@ import { routeComparator } from '../priority.js';
import { getRouteGenerator } from './generator.js';
import { getPattern } from './pattern.js';
import { getRoutePrerenderOption } from './prerender.js';
+import { runHookRoutesResolved } from '../../../integrations/hooks.js';
const require = createRequire(import.meta.url);
interface Item {
@@ -255,6 +256,7 @@ function createFileBasedRoutes(
prerender,
fallbackRoutes: [],
distURL: [],
+ origin: 'project',
});
}
}
@@ -280,7 +282,7 @@ function createInjectedRoutes({ settings, cwd }: CreateRouteManifestParams): Rou
const routes: RouteData[] = [];
for (const injectedRoute of settings.injectedRoutes) {
- const { pattern: name, entrypoint, prerender: prerenderInjected } = injectedRoute;
+ const { pattern: name, entrypoint, prerender: prerenderInjected, origin } = injectedRoute;
const { resolved, component } = resolveInjectedRoute(entrypoint.toString(), config.root, cwd);
const segments = removeLeadingForwardSlash(name)
@@ -320,6 +322,7 @@ function createInjectedRoutes({ settings, cwd }: CreateRouteManifestParams): Rou
prerender: prerenderInjected ?? prerender,
fallbackRoutes: [],
distURL: [],
+ origin,
});
}
@@ -389,6 +392,7 @@ function createRedirectRoutes(
redirectRoute: routeMap.get(destination),
fallbackRoutes: [],
distURL: [],
+ origin: 'project',
});
}
@@ -480,6 +484,7 @@ function detectRouteCollision(a: RouteData, b: RouteData, _config: AstroConfig,
export async function createRouteManifest(
params: CreateRouteManifestParams,
logger: Logger,
+ { dev = false }: { dev?: boolean } = {},
): Promise<ManifestData> {
const { settings } = params;
const { config } = settings;
@@ -727,6 +732,10 @@ export async function createRouteManifest(
}
}
+ if (!dev) {
+ await runHookRoutesResolved({ routes, settings, logger });
+ }
+
return {
routes,
};
diff --git a/packages/astro/src/core/routing/manifest/serialization.ts b/packages/astro/src/core/routing/manifest/serialization.ts
index c0cf600f0..3d6214876 100644
--- a/packages/astro/src/core/routing/manifest/serialization.ts
+++ b/packages/astro/src/core/routing/manifest/serialization.ts
@@ -41,5 +41,6 @@ export function deserializeRouteData(rawRouteData: SerializedRouteData): RouteDa
return deserializeRouteData(fallback);
}),
isIndex: rawRouteData.isIndex,
+ origin: rawRouteData.origin,
};
}
diff --git a/packages/astro/src/core/server-islands/endpoint.ts b/packages/astro/src/core/server-islands/endpoint.ts
index 540f75b84..999826a6f 100644
--- a/packages/astro/src/core/server-islands/endpoint.ts
+++ b/packages/astro/src/core/server-islands/endpoint.ts
@@ -31,6 +31,7 @@ export function getServerIslandRouteData(config: ConfigFields) {
isIndex: false,
fallbackRoutes: [],
route: SERVER_ISLAND_ROUTE,
+ origin: 'internal',
};
return route;
}
diff --git a/packages/astro/src/integrations/hooks.ts b/packages/astro/src/integrations/hooks.ts
index 4c1d4a1ae..5a4b723b2 100644
--- a/packages/astro/src/integrations/hooks.ts
+++ b/packages/astro/src/integrations/hooks.ts
@@ -24,7 +24,9 @@ import type {
import type {
AstroIntegration,
AstroRenderer,
+ BaseIntegrationHooks,
HookParameters,
+ IntegrationResolvedRoute,
IntegrationRouteData,
RouteOptions,
} from '../types/public/integrations.js';
@@ -39,7 +41,7 @@ async function withTakingALongTimeMsg<T>({
logger,
}: {
name: string;
- hookName: string;
+ hookName: keyof BaseIntegrationHooks;
hookResult: T | Promise<T>;
timeoutMs?: number;
logger: Logger;
@@ -204,7 +206,7 @@ export async function runHookConfigSetup({
);
injectRoute.entrypoint = injectRoute.entryPoint as string;
}
- updatedSettings.injectedRoutes.push(injectRoute);
+ updatedSettings.injectedRoutes.push({ ...injectRoute, origin: 'external' });
},
addWatchFile: (path) => {
updatedSettings.watchFiles.push(path instanceof URL ? fileURLToPath(path) : path);
@@ -599,6 +601,9 @@ export async function runHookBuildDone({ settings, pages, routes, logging }: Run
pages: pages.map((p) => ({ pathname: p })),
dir,
routes: integrationRoutes,
+ assets: new Map(
+ routes.filter((r) => r.distURL !== undefined).map((r) => [r.route, r.distURL!]),
+ ),
logger,
}),
logger: logging,
@@ -648,6 +653,47 @@ export async function runHookRouteSetup({
}
}
+export async function runHookRoutesResolved({
+ routes,
+ settings,
+ logger,
+}: { routes: Array<RouteData>; settings: AstroSettings; logger: Logger }) {
+ for (const integration of settings.config.integrations) {
+ if (integration?.hooks?.['astro:routes:resolved']) {
+ const integrationLogger = getLogger(integration, logger);
+
+ await withTakingALongTimeMsg({
+ name: integration.name,
+ hookName: 'astro:routes:resolved',
+ hookResult: integration.hooks['astro:routes:resolved']({
+ routes: routes.map((route) => toIntegrationResolvedRoute(route)),
+ logger: integrationLogger,
+ }),
+ logger,
+ });
+ }
+ }
+}
+
+function toIntegrationResolvedRoute(route: RouteData): IntegrationResolvedRoute {
+ return {
+ isPrerendered: route.prerender,
+ entrypoint: route.component,
+ pattern: route.route,
+ params: route.params,
+ origin: route.origin,
+ generate: route.generate,
+ patternRegex: route.pattern,
+ segments: route.segments,
+ type: route.type,
+ pathname: route.pathname,
+ redirect: route.redirect,
+ redirectRoute: route.redirectRoute
+ ? toIntegrationResolvedRoute(route.redirectRoute)
+ : undefined,
+ };
+}
+
function toIntegrationRouteData(route: RouteData): IntegrationRouteData {
return {
route: route.route,
diff --git a/packages/astro/src/types/astro.ts b/packages/astro/src/types/astro.ts
index 7e2f2af7e..654652c42 100644
--- a/packages/astro/src/types/astro.ts
+++ b/packages/astro/src/types/astro.ts
@@ -10,12 +10,10 @@ import type { ContentEntryType, DataEntryType } from './public/content.js';
import type {
AstroAdapter,
AstroRenderer,
- InjectedRoute,
InjectedScriptStage,
InjectedType,
- ResolvedInjectedRoute,
} from './public/integrations.js';
-import type { RouteData } from './public/internal.js';
+import type { InternalInjectedRoute, RouteData, ResolvedInjectedRoute } from './public/internal.js';
import type { DevToolbarAppEntry } from './public/toolbar.js';
export type SerializedRouteData = Omit<
@@ -35,7 +33,7 @@ export interface AstroSettings {
config: AstroConfig;
adapter: AstroAdapter | undefined;
preferences: AstroPreferences;
- injectedRoutes: InjectedRoute[];
+ injectedRoutes: InternalInjectedRoute[];
resolvedInjectedRoutes: ResolvedInjectedRoute[];
pageExtensions: string[];
contentEntryTypes: ContentEntryType[];
diff --git a/packages/astro/src/types/public/integrations.ts b/packages/astro/src/types/public/integrations.ts
index 4d0709fab..2d0b13e96 100644
--- a/packages/astro/src/types/public/integrations.ts
+++ b/packages/astro/src/types/public/integrations.ts
@@ -8,7 +8,7 @@ import type { getToolbarServerCommunicationHelpers } from '../../integrations/ho
import type { DeepPartial } from '../../type-utils.js';
import type { AstroConfig } from './config.js';
import type { RefreshContentOptions } from './content.js';
-import type { RouteData } from './internal.js';
+import type { InternalInjectedRoute, RouteData } from './internal.js';
import type { DevToolbarAppEntry } from './toolbar.js';
export interface RouteOptions {
@@ -138,15 +138,7 @@ export type AstroAdapterFeatureMap = {
*/
export type InjectedScriptStage = 'before-hydration' | 'head-inline' | 'page' | 'page-ssr';
-export interface InjectedRoute {
- pattern: string;
- entrypoint: string | URL;
- prerender?: boolean;
-}
-
-export interface ResolvedInjectedRoute extends InjectedRoute {
- resolvedEntryPoint?: URL;
-}
+export type InjectedRoute = Omit<InternalInjectedRoute, 'origin'>;
export interface InjectedType {
filename: string;
@@ -225,13 +217,19 @@ export interface BaseIntegrationHooks {
'astro:build:done': (options: {
pages: { pathname: string }[];
dir: URL;
+ /** @deprecated Use the `assets` map and the new `astro:routes:resolved` hook */
routes: IntegrationRouteData[];
+ assets: Map<string, URL[]>;
logger: AstroIntegrationLogger;
}) => void | Promise<void>;
'astro:route:setup': (options: {
route: RouteOptions;
logger: AstroIntegrationLogger;
}) => void | Promise<void>;
+ 'astro:routes:resolved': (options: {
+ routes: IntegrationResolvedRoute[];
+ logger: AstroIntegrationLogger;
+ }) => void | Promise<void>;
}
export interface AstroIntegration {
@@ -245,13 +243,45 @@ export interface AstroIntegration {
/**
* A smaller version of the {@link RouteData} that is used in the integrations.
+ * @deprecated Use {@link IntegrationResolvedRoute}
*/
export type IntegrationRouteData = Omit<
RouteData,
- 'isIndex' | 'fallbackRoutes' | 'redirectRoute'
+ 'isIndex' | 'fallbackRoutes' | 'redirectRoute' | 'origin'
> & {
/**
* {@link RouteData.redirectRoute}
*/
redirectRoute?: IntegrationRouteData;
};
+
+export interface IntegrationResolvedRoute
+ extends Pick<
+ RouteData,
+ 'generate' | 'params' | 'pathname' | 'segments' | 'type' | 'redirect' | 'origin'
+ > {
+ /**
+ * {@link RouteData.route}
+ */
+ pattern: RouteData['route'];
+
+ /**
+ * {@link RouteData.pattern}
+ */
+ patternRegex: RouteData['pattern'];
+
+ /**
+ * {@link RouteData.component}
+ */
+ entrypoint: RouteData['component'];
+
+ /**
+ * {@link RouteData.prerender}
+ */
+ isPrerendered: RouteData['prerender'];
+
+ /**
+ * {@link RouteData.redirectRoute}
+ */
+ redirectRoute?: IntegrationResolvedRoute;
+}
diff --git a/packages/astro/src/types/public/internal.ts b/packages/astro/src/types/public/internal.ts
index c970ab3d1..a17ee7650 100644
--- a/packages/astro/src/types/public/internal.ts
+++ b/packages/astro/src/types/public/internal.ts
@@ -136,6 +136,11 @@ export interface RouteData {
* - src/pages/blog/index.astro
*/
isIndex: boolean;
+
+ /**
+ * Whether the route comes from Astro core, an integration or the user's project
+ */
+ origin: 'internal' | 'external' | 'project';
}
/**
@@ -284,3 +289,16 @@ export interface SSRMetadata {
}
export type SSRError = Error & ViteErrorPayload['err'];
+
+// `origin` is set within the hook, but the user doesn't have access to this property. That's why
+// we need an intermediary interface
+export interface InternalInjectedRoute {
+ pattern: string;
+ entrypoint: string | URL;
+ prerender?: boolean;
+ origin: RouteData['origin'];
+}
+
+export interface ResolvedInjectedRoute extends InternalInjectedRoute {
+ resolvedEntryPoint?: URL;
+}
diff --git a/packages/astro/src/vite-plugin-astro-server/plugin.ts b/packages/astro/src/vite-plugin-astro-server/plugin.ts
index 2a22db6c7..642565161 100644
--- a/packages/astro/src/vite-plugin-astro-server/plugin.ts
+++ b/packages/astro/src/vite-plugin-astro-server/plugin.ts
@@ -2,6 +2,7 @@ import { AsyncLocalStorage } from 'node:async_hooks';
import type fs from 'node:fs';
import { IncomingMessage } from 'node:http';
import type * as vite from 'vite';
+import { normalizePath } from 'vite';
import type { SSRManifest, SSRManifestI18n } from '../core/app/types.js';
import { warnMissingAdapter } from '../core/dev/adapter-validation.js';
import { createKey } from '../core/encryption.js';
@@ -21,6 +22,9 @@ import { recordServerError } from './error.js';
import { DevPipeline } from './pipeline.js';
import { handleRequest } from './request.js';
import { setRouteError } from './server-state.js';
+import { fileURLToPath } from 'node:url';
+import { getRoutePrerenderOption } from '../core/routing/manifest/prerender.js';
+import { runHookRoutesResolved } from '../integrations/hooks.js';
export interface AstroPluginOptions {
settings: AstroSettings;
@@ -50,26 +54,46 @@ export default function createVitePluginAstroServer({
const controller = createController({ loader });
const localStorage = new AsyncLocalStorage();
- /** rebuild the route cache + manifest, as needed. */
- async function rebuildManifest(needsManifestRebuild: boolean) {
+ /** rebuild the route cache + manifest */
+ async function rebuildManifest(path: string | null = null) {
pipeline.clearRouteCache();
- if (needsManifestRebuild) {
+
+ // If a route changes, we check if it's part of the manifest and check for its prerender value
+ if (path !== null) {
+ const route = routeManifest.routes.find(
+ (r) =>
+ normalizePath(path) ===
+ normalizePath(fileURLToPath(new URL(r.component, settings.config.root))),
+ );
+ if (!route) {
+ return;
+ }
+ if (route.type !== 'page' && route.type !== 'endpoint') return;
+
+ const routePath = fileURLToPath(new URL(route.component, settings.config.root));
+ try {
+ const content = await fsMod.promises.readFile(routePath, 'utf-8');
+ await getRoutePrerenderOption(content, route, settings, logger);
+ } catch (_) {}
+ } else {
routeManifest = injectDefaultDevRoutes(
settings,
devSSRManifest,
- await createRouteManifest({ settings, fsMod }, logger), // TODO: Handle partial updates to the manifest
+ await createRouteManifest({ settings, fsMod }, logger, { dev: true }),
);
- warnMissingAdapter(logger, settings);
- pipeline.manifest.checkOrigin =
- settings.config.security.checkOrigin && settings.buildOutput === 'server';
- pipeline.setManifestData(routeManifest);
}
+ await runHookRoutesResolved({ routes: routeManifest.routes, settings, logger });
+
+ warnMissingAdapter(logger, settings);
+ pipeline.manifest.checkOrigin =
+ settings.config.security.checkOrigin && settings.buildOutput === 'server';
+ pipeline.setManifestData(routeManifest);
}
- // Rebuild route manifest on file change, if needed.
- viteServer.watcher.on('add', rebuildManifest.bind(null, true));
- viteServer.watcher.on('unlink', rebuildManifest.bind(null, true));
- viteServer.watcher.on('change', rebuildManifest.bind(null, false));
+ // Rebuild route manifest on file change
+ viteServer.watcher.on('add', rebuildManifest.bind(null, null));
+ viteServer.watcher.on('unlink', rebuildManifest.bind(null, null));
+ viteServer.watcher.on('change', rebuildManifest);
function handleUnhandledRejection(rejection: any) {
const error = new AstroError({
diff --git a/packages/astro/src/vite-plugin-integrations-container/index.ts b/packages/astro/src/vite-plugin-integrations-container/index.ts
index 0af181e39..81b720aef 100644
--- a/packages/astro/src/vite-plugin-integrations-container/index.ts
+++ b/packages/astro/src/vite-plugin-integrations-container/index.ts
@@ -5,7 +5,7 @@ import type { AstroSettings } from '../types/astro.js';
import { normalizePath } from 'vite';
import { runHookServerSetup } from '../integrations/hooks.js';
-import type { InjectedRoute, ResolvedInjectedRoute } from '../types/public/integrations.js';
+import type { InternalInjectedRoute, ResolvedInjectedRoute } from '../types/public/internal.js';
/** Connect Astro integrations into Vite, as needed. */
export default function astroIntegrationsContainerPlugin({
@@ -33,7 +33,7 @@ export default function astroIntegrationsContainerPlugin({
async function resolveEntryPoint(
this: PluginContext,
- route: InjectedRoute,
+ route: InternalInjectedRoute,
): Promise<ResolvedInjectedRoute> {
const resolvedId = await this.resolve(route.entrypoint.toString())
.then((res) => res?.id)
diff --git a/packages/astro/test/units/integrations/api.test.js b/packages/astro/test/units/integrations/api.test.js
index f2365c006..97da20c19 100644
--- a/packages/astro/test/units/integrations/api.test.js
+++ b/packages/astro/test/units/integrations/api.test.js
@@ -7,7 +7,7 @@ import {
runHookBuildSetup,
runHookConfigSetup,
} from '../../../dist/integrations/hooks.js';
-import { defaultLogger } from '../test-utils.js';
+import { createFixture, defaultLogger, runInContainer } from '../test-utils.js';
const defaultConfig = {
root: new URL('./', import.meta.url),
@@ -131,6 +131,221 @@ describe('Integration API', () => {
assert.equal(updatedSettings.config.site, site);
assert.equal(updatedSettings.config.integrations.length, 2);
});
+
+ describe('Routes resolved hooks', () => {
+ it('should work in dev', async () => {
+ let routes = [];
+ const fixture = await createFixture({
+ '/src/pages/about.astro': '',
+ '/src/actions.ts': 'export const server = {}',
+ '/src/foo.astro': '',
+ });
+
+ await runInContainer(
+ {
+ inlineConfig: {
+ root: fixture.path,
+ integrations: [
+ {
+ name: 'test',
+ hooks: {
+ 'astro:config:setup': (params) => {
+ params.injectRoute({
+ entrypoint: './src/foo.astro',
+ pattern: '/foo',
+ });
+ },
+ 'astro:routes:resolved': (params) => {
+ routes = params.routes.map((r) => ({
+ isPrerendered: r.isPrerendered,
+ entrypoint: r.entrypoint,
+ pattern: r.pattern,
+ params: r.params,
+ origin: r.origin,
+ }));
+ routes.sort((a, b) => a.pattern.localeCompare(b.pattern));
+ },
+ },
+ },
+ ],
+ },
+ },
+ async (container) => {
+ assert.deepEqual(
+ routes,
+ [
+ {
+ isPrerendered: false,
+ entrypoint: '_server-islands.astro',
+ pattern: '/_server-islands/[name]',
+ params: ['name'],
+ origin: 'internal',
+ },
+ {
+ isPrerendered: false,
+ entrypoint: '../../../../dist/actions/runtime/route.js',
+ pattern: '/_actions/[...path]',
+ params: ['...path'],
+ origin: 'internal',
+ },
+ {
+ isPrerendered: true,
+ entrypoint: 'src/pages/about.astro',
+ pattern: '/about',
+ params: [],
+ origin: 'project',
+ },
+ {
+ isPrerendered: true,
+ entrypoint: 'src/foo.astro',
+ pattern: '/foo',
+ params: [],
+ origin: 'external',
+ },
+ {
+ isPrerendered: false,
+ entrypoint: '../../../../dist/assets/endpoint/node.js',
+ pattern: '/_image',
+ params: [],
+ origin: 'internal',
+ },
+ {
+ isPrerendered: false,
+ entrypoint: 'astro-default-404.astro',
+ pattern: '/404',
+ params: [],
+ origin: 'internal',
+ },
+ ].sort((a, b) => a.pattern.localeCompare(b.pattern)),
+ );
+
+ await fixture.writeFile('/src/pages/bar.astro', '');
+ container.viteServer.watcher.emit(
+ 'add',
+ fixture.getPath('/src/pages/bar.astro').replace(/\\/g, '/'),
+ );
+ await new Promise((r) => setTimeout(r, 100));
+
+ assert.deepEqual(
+ routes,
+ [
+ {
+ isPrerendered: false,
+ entrypoint: '_server-islands.astro',
+ pattern: '/_server-islands/[name]',
+ params: ['name'],
+ origin: 'internal',
+ },
+ {
+ isPrerendered: false,
+ entrypoint: '../../../../dist/actions/runtime/route.js',
+ pattern: '/_actions/[...path]',
+ params: ['...path'],
+ origin: 'internal',
+ },
+ {
+ isPrerendered: true,
+ entrypoint: 'src/pages/about.astro',
+ pattern: '/about',
+ params: [],
+ origin: 'project',
+ },
+ {
+ isPrerendered: true,
+ entrypoint: 'src/pages/bar.astro',
+ pattern: '/bar',
+ params: [],
+ origin: 'project',
+ },
+ {
+ isPrerendered: true,
+ entrypoint: 'src/foo.astro',
+ pattern: '/foo',
+ params: [],
+ origin: 'external',
+ },
+ {
+ isPrerendered: false,
+ entrypoint: '../../../../dist/assets/endpoint/node.js',
+ pattern: '/_image',
+ params: [],
+ origin: 'internal',
+ },
+ {
+ isPrerendered: false,
+ entrypoint: 'astro-default-404.astro',
+ pattern: '/404',
+ params: [],
+ origin: 'internal',
+ },
+ ].sort((a, b) => a.pattern.localeCompare(b.pattern)),
+ );
+
+ await fixture.writeFile('/src/pages/about.astro', '---\nexport const prerender=false\n');
+ container.viteServer.watcher.emit(
+ 'change',
+ fixture.getPath('/src/pages/about.astro').replace(/\\/g, '/'),
+ );
+ await new Promise((r) => setTimeout(r, 100));
+
+ assert.deepEqual(
+ routes,
+ [
+ {
+ isPrerendered: false,
+ entrypoint: '_server-islands.astro',
+ pattern: '/_server-islands/[name]',
+ params: ['name'],
+ origin: 'internal',
+ },
+ {
+ isPrerendered: false,
+ entrypoint: '../../../../dist/actions/runtime/route.js',
+ pattern: '/_actions/[...path]',
+ params: ['...path'],
+ origin: 'internal',
+ },
+ {
+ isPrerendered: false,
+ entrypoint: 'src/pages/about.astro',
+ pattern: '/about',
+ params: [],
+ origin: 'project',
+ },
+ {
+ isPrerendered: true,
+ entrypoint: 'src/pages/bar.astro',
+ pattern: '/bar',
+ params: [],
+ origin: 'project',
+ },
+ {
+ isPrerendered: true,
+ entrypoint: 'src/foo.astro',
+ pattern: '/foo',
+ params: [],
+ origin: 'external',
+ },
+ {
+ isPrerendered: false,
+ entrypoint: '../../../../dist/assets/endpoint/node.js',
+ pattern: '/_image',
+ params: [],
+ origin: 'internal',
+ },
+ {
+ isPrerendered: false,
+ entrypoint: 'astro-default-404.astro',
+ pattern: '/404',
+ params: [],
+ origin: 'internal',
+ },
+ ].sort((a, b) => a.pattern.localeCompare(b.pattern)),
+ );
+ },
+ );
+ });
+ });
});
describe('Astro feature map', function () {