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

fix: prioritize dynamic prerendered routes over dynamic server routes #7235

Merged
merged 13 commits into from May 30, 2023
5 changes: 5 additions & 0 deletions .changeset/hungry-peaches-speak.md
@@ -0,0 +1,5 @@
---
'astro': patch
---

Prioritize dynamic prerendered routes over dynamic server routes
3 changes: 1 addition & 2 deletions packages/astro/src/core/render/dev/index.ts
@@ -1,4 +1,3 @@
import { fileURLToPath } from 'url';
import type {
AstroMiddlewareInstance,
AstroSettings,
Expand Down Expand Up @@ -65,7 +64,7 @@ export async function preload({

try {
// Load the module from the Vite SSR Runtime.
const mod = (await env.loader.import(fileURLToPath(filePath))) as ComponentInstance;
const mod = (await env.loader.import(viteID(filePath))) as ComponentInstance;

return [renderers, mod];
} catch (error) {
Expand Down
1 change: 1 addition & 0 deletions packages/astro/src/core/routing/manifest/create.ts
Expand Up @@ -173,6 +173,7 @@ function comparator(a: Item, b: Item) {
}
}

// endpoints are prioritized over pages
if (a.isPage !== b.isPage) {
return a.isPage ? 1 : -1;
}
Expand Down
67 changes: 67 additions & 0 deletions packages/astro/src/prerender/routing.ts
@@ -0,0 +1,67 @@
import type { AstroSettings, RouteData } from '../@types/astro';
import { preload, type DevelopmentEnvironment } from '../core/render/dev/index.js';
import { getPrerenderStatus } from './metadata.js';

type GetSortedPreloadedMatchesParams = {
env: DevelopmentEnvironment;
matches: RouteData[];
settings: AstroSettings;
};
export async function getSortedPreloadedMatches({
env,
matches,
settings,
}: GetSortedPreloadedMatchesParams) {
return (
await preloadAndSetPrerenderStatus({
env,
matches,
settings,
})
).sort((a, b) => prioritizePrerenderedMatchesComparator(a.route, b.route));
}

type PreloadAndSetPrerenderStatusParams = {
env: DevelopmentEnvironment;
matches: RouteData[];
settings: AstroSettings;
};
async function preloadAndSetPrerenderStatus({
env,
matches,
settings,
}: PreloadAndSetPrerenderStatusParams) {
const preloaded = await Promise.all(
matches.map(async (route) => {
MoustaphaDev marked this conversation as resolved.
Show resolved Hide resolved
const filePath = new URL(`./${route.component}`, settings.config.root);
const preloadedComponent = await preload({ env, filePath });

// gets the prerender metadata set by the `astro:scanner` vite plugin
const prerenderStatus = getPrerenderStatus({
filePath,
loader: env.loader,
});

if (prerenderStatus !== undefined) {
route.prerender = prerenderStatus;
}

return { preloadedComponent, route, filePath } as const;
})
);
return preloaded;
}

function prioritizePrerenderedMatchesComparator(a: RouteData, b: RouteData): number {
if (areRegexesEqual(a.pattern, b.pattern)) {
if (a.prerender !== b.prerender) {
return a.prerender ? -1 : 1;
}
return a.component < b.component ? -1 : 1;
}
return 0;
}

function areRegexesEqual(regexp1: RegExp, regexp2: RegExp) {
return regexp1.source === regexp2.source && regexp1.global === regexp2.global;
}
22 changes: 5 additions & 17 deletions packages/astro/src/vite-plugin-astro-server/route.ts
Expand Up @@ -16,10 +16,10 @@ import { preload, renderPage } from '../core/render/dev/index.js';
import { getParamsAndProps, GetParamsAndPropsError } from '../core/render/index.js';
import { createRequest } from '../core/request.js';
import { matchAllRoutes } from '../core/routing/index.js';
import { getPrerenderStatus } from '../prerender/metadata.js';
import { isHybridOutput } from '../prerender/utils.js';
import { log404 } from './common.js';
import { handle404Response, writeSSRResult, writeWebResponse } from './response.js';
import { getSortedPreloadedMatches } from '../prerender/routing.js';

type AsyncReturnType<T extends (...args: any) => Promise<any>> = T extends (
...args: any
Expand Down Expand Up @@ -47,24 +47,12 @@ export async function matchRoute(
): Promise<MatchedRoute | undefined> {
const { logging, settings, routeCache } = env;
const matches = matchAllRoutes(pathname, manifest);
const preloadedMatches = await getSortedPreloadedMatches({ env, matches, settings });

for await (const maybeRoute of matches) {
const filePath = new URL(`./${maybeRoute.component}`, settings.config.root);
const preloadedComponent = await preload({ env, filePath });

// gets the prerender metadata set by the `astro:scanner` vite plugin
const prerenderStatus = getPrerenderStatus({
filePath,
loader: env.loader,
});

if (prerenderStatus !== undefined) {
maybeRoute.prerender = prerenderStatus;
}

const [, mod] = preloadedComponent;
for await (const { preloadedComponent, route: maybeRoute, filePath } of preloadedMatches) {
// attempt to get static paths
// if this fails, we have a bad URL match!
const [, mod] = preloadedComponent;
const paramsAndPropsRes = await getParamsAndProps({
mod,
route: maybeRoute,
Expand Down Expand Up @@ -210,7 +198,7 @@ export async function handleRoute(
await writeWebResponse(res, result.response);
} else {
let contentType = 'text/plain';
// Dynamic routes dont include `route.pathname`, so synthesize a path for these (e.g. 'src/pages/[slug].svg')
// Dynamic routes don't include `route.pathname`, so synthesize a path for these (e.g. 'src/pages/[slug].svg')
const filepath =
route.pathname ||
route.segments.map((segment) => segment.map((p) => p.content).join('')).join('/');
Expand Down