mirror of
https://github.com/withastro/astro.git
synced 2024-12-16 21:46:22 -05:00
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>
This commit is contained in:
parent
3f02d5f12b
commit
8309c61f0d
16 changed files with 434 additions and 35 deletions
50
.changeset/giant-ravens-look.md
Normal file
50
.changeset/giant-ravens-look.md
Normal file
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
|
@ -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({
|
||||
|
|
|
@ -63,5 +63,6 @@ function getImageEndpointData(
|
|||
pathname: settings.config.image.endpoint.route,
|
||||
prerender: false,
|
||||
fallbackRoutes: [],
|
||||
origin: 'internal',
|
||||
};
|
||||
}
|
||||
|
|
|
@ -544,6 +544,7 @@ export class experimental_AstroContainer {
|
|||
type,
|
||||
fallbackRoutes: [],
|
||||
isIndex: false,
|
||||
origin: 'internal',
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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' });
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -41,5 +41,6 @@ export function deserializeRouteData(rawRouteData: SerializedRouteData): RouteDa
|
|||
return deserializeRouteData(fallback);
|
||||
}),
|
||||
isIndex: rawRouteData.isIndex,
|
||||
origin: rawRouteData.origin,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ export function getServerIslandRouteData(config: ConfigFields) {
|
|||
isIndex: false,
|
||||
fallbackRoutes: [],
|
||||
route: SERVER_ISLAND_ROUTE,
|
||||
origin: 'internal',
|
||||
};
|
||||
return route;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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[];
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 () {
|
||||
|
|
Loading…
Reference in a new issue