Skip to content
6 changes: 6 additions & 0 deletions .changeset/beige-clowns-read.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@astrojs/vercel': major
'astro': major
---

TODO: entrypoints
6 changes: 6 additions & 0 deletions .changeset/cuddly-worlds-beam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@astrojs/sitemap': major
'astro': major
---

TODO: routes
4 changes: 1 addition & 3 deletions packages/astro/src/core/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import { AstroError, AstroErrorData } from '../errors/index.js';
import type { Logger } from '../logger/core.js';
import { levels, timerMessage } from '../logger/core.js';
import { createRoutesList } from '../routing/index.js';
import { getServerIslandRouteData } from '../server-islands/endpoint.js';
import { clearContentLayerCache } from '../sync/index.js';
import { ensureProcessNodeEnv } from '../util.js';
import { collectPagesData } from './page-data.js';
Expand Down Expand Up @@ -242,8 +241,7 @@ class AstroBuilder {
pages: pageNames,
routes: Object.values(allPages)
.flat()
.map((pageData) => pageData.route)
.concat(hasServerIslands ? getServerIslandRouteData(this.settings.config) : []),
.map((pageData) => pageData.route),
logger: this.logger,
});

Expand Down
2 changes: 0 additions & 2 deletions packages/astro/src/core/build/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ export interface BuildInternals {
// The SSR manifest entry chunk.
manifestEntryChunk?: Rollup.OutputChunk;
manifestFileName?: string;
entryPoints: Map<RouteData, URL>;
componentMetadata: SSRResult['componentMetadata'];
middlewareEntryPoint: URL | undefined;
astroActionsEntryPoint: URL | undefined;
Expand Down Expand Up @@ -121,7 +120,6 @@ export function createBuildInternals(): BuildInternals {
discoveredScripts: new Set(),
staticFiles: new Set(),
componentMetadata: new Map(),
entryPoints: new Map(),
prerenderOnlyChunks: [],
astroActionsEntryPoint: undefined,
middlewareEntryPoint: undefined,
Expand Down
1 change: 0 additions & 1 deletion packages/astro/src/core/build/plugins/plugin-manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,6 @@ export function pluginManifest(
config: options.settings.config,
manifest,
logger: options.logger,
entryPoints: internals.entryPoints,
middlewareEntryPoint: shouldPassMiddlewareEntryPoint
? internals.middlewareEntryPoint
: undefined,
Expand Down
28 changes: 0 additions & 28 deletions packages/astro/src/integrations/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import type {
BaseIntegrationHooks,
HookParameters,
IntegrationResolvedRoute,
IntegrationRouteData,
RouteOptions,
RouteToHeaders,
} from '../types/public/integrations.js';
Expand Down Expand Up @@ -548,29 +547,22 @@ type RunHookBuildSsr = {
config: AstroConfig;
manifest: SerializedSSRManifest;
logger: Logger;
entryPoints: Map<RouteData, URL>;
middlewareEntryPoint: URL | undefined;
};

export async function runHookBuildSsr({
config,
manifest,
logger,
entryPoints,
middlewareEntryPoint,
}: RunHookBuildSsr) {
const entryPointsMap = new Map();
for (const [key, value] of entryPoints) {
entryPointsMap.set(toIntegrationRouteData(key), value);
}
for (const integration of config.integrations) {
await runHookInternal({
integration,
hookName: 'astro:build:ssr',
logger,
params: () => ({
manifest,
entryPoints: entryPointsMap,
middlewareEntryPoint,
}),
});
Expand Down Expand Up @@ -602,15 +594,13 @@ export async function runHookBuildGenerated({
type RunHookBuildDone = {
settings: AstroSettings;
pages: string[];
// TODO: remove in Astro 6
routes: RouteData[];
logger: Logger;
};

export async function runHookBuildDone({ settings, pages, routes, logger }: RunHookBuildDone) {
const dir = getClientOutputDirectory(settings);
await fsMod.promises.mkdir(dir, { recursive: true });
const integrationRoutes = routes.map(toIntegrationRouteData);

for (const integration of settings.config.integrations) {
await runHookInternal({
Expand All @@ -620,7 +610,6 @@ export async function runHookBuildDone({ settings, pages, routes, logger }: RunH
params: () => ({
pages: pages.map((p) => ({ pathname: p })),
dir,
routes: integrationRoutes,
assets: new Map(
routes.filter((r) => r.distURL !== undefined).map((r) => [r.route, r.distURL!]),
),
Expand Down Expand Up @@ -701,20 +690,3 @@ export function toIntegrationResolvedRoute(route: RouteData): IntegrationResolve
: undefined,
};
}

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,
};
}
8 changes: 0 additions & 8 deletions packages/astro/src/types/public/integrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,12 +211,6 @@ export interface BaseIntegrationHooks {
'astro:server:done': (options: { logger: AstroIntegrationLogger }) => void | Promise<void>;
'astro:build:ssr': (options: {
manifest: SerializedSSRManifest;
/**
* This maps a {@link RouteData} to an {@link URL}, this URL represents
* the physical file you should import.
*/
// TODO: Change in Astro 6.0
entryPoints: Map<IntegrationRouteData, URL>;
/**
* File path of the emitted middleware
*/
Expand All @@ -239,8 +233,6 @@ 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>;
Expand Down
13 changes: 9 additions & 4 deletions packages/integrations/sitemap/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import type { AstroConfig, AstroIntegration } from 'astro';
import type { AstroConfig, AstroIntegration, IntegrationResolvedRoute } from 'astro';
import type { EnumChangefreq, LinkItem as LinkItemBase, SitemapItemLoose } from 'sitemap';
import { ZodError } from 'zod';

Expand Down Expand Up @@ -76,17 +76,22 @@ const isStatusCodePage = (locales: string[]) => {
};
};
const createPlugin = (options?: SitemapOptions): AstroIntegration => {
let _routes: Array<IntegrationResolvedRoute>
let config: AstroConfig;

return {
name: PKG_NAME,

hooks: {
'astro:routes:resolved': ({ routes }) => {
_routes = routes;
},

'astro:config:done': async ({ config: cfg }) => {
config = cfg;
},

'astro:build:done': async ({ dir, routes, pages, logger }) => {
'astro:build:done': async ({ dir, pages, logger }) => {
try {
if (!config.site) {
logger.warn(
Expand All @@ -112,15 +117,15 @@ const createPlugin = (options?: SitemapOptions): AstroIntegration => {
return new URL(fullPath, finalSiteUrl).href;
});

const routeUrls = routes.reduce<string[]>((urls, r) => {
const routeUrls = _routes.reduce<string[]>((urls, r) => {
// Only expose pages, not endpoints or redirects
if (r.type !== 'page') return urls;

/**
* Dynamic URLs have entries with `undefined` pathnames
*/
if (r.pathname) {
if (shouldIgnoreStatus(r.pathname ?? r.route)) return urls;
if (shouldIgnoreStatus(r.pathname ?? r.pattern)) return urls;

// `finalSiteUrl` may end with a trailing slash
// or not because of base paths.
Expand Down
140 changes: 52 additions & 88 deletions packages/integrations/vercel/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { cpSync, existsSync, mkdirSync, readFileSync } from 'node:fs';
import { basename } from 'node:path';
import { pathToFileURL } from 'node:url';
import { emptyDir, removeDir, writeJson } from '@astrojs/internal-helpers/fs';
import {
Expand Down Expand Up @@ -230,7 +229,6 @@ export default function vercelAdapter({
let _config: AstroConfig;
let _buildTempFolder: URL;
let _serverEntry: string;
let _entryPoints: Map<Pick<IntegrationResolvedRoute, 'entrypoint' | 'patternRegex'>, URL>;
let _middlewareEntryPoint: URL | undefined;
let _routeToHeaders: RouteToHeaders | undefined = undefined;
// Extra files to be merged with `includeFiles` during build
Expand Down Expand Up @@ -358,18 +356,7 @@ export default function vercelAdapter({
// Ensure to have `.vercel/output` empty.
await emptyDir(new URL('./.vercel/output/', _config.root));
},
'astro:build:ssr': async ({ entryPoints, middlewareEntryPoint }) => {
_entryPoints = new Map(
Array.from(entryPoints)
.filter(([routeData]) => !routeData.prerender)
.map(([routeData, url]) => [
{
entrypoint: routeData.component,
patternRegex: routeData.pattern,
},
url,
]),
);
'astro:build:ssr': async ({ middlewareEntryPoint }) => {
_middlewareEntryPoint = middlewareEntryPoint;
},

Expand Down Expand Up @@ -432,92 +419,69 @@ export default function vercelAdapter({
maxDuration,
);

// Multiple entrypoint support
if (_entryPoints.size) {
const getRouteFuncName = (route: Pick<IntegrationResolvedRoute, 'entrypoint'>) =>
route.entrypoint.replace('src/pages/', '');

const getFallbackFuncName = (entryFile: URL) =>
basename(entryFile.toString())
.replace('entry.', '')
.replace(/\.mjs$/, '');

for (const [route, entryFile] of _entryPoints) {
const func = route.entrypoint.startsWith('src/pages/')
? getRouteFuncName(route)
: getFallbackFuncName(entryFile);
const entryFile = new URL(_serverEntry, _buildTempFolder);
if (isr) {
const isrConfig = typeof isr === 'object' ? isr : {};
await builder.buildServerlessFolder(entryFile, NODE_PATH, _config.root);
if (isrConfig.exclude?.length) {
const expandedExclusions = isrConfig.exclude.reduce<string[]>((acc, exclusion) => {
if (exclusion instanceof RegExp) {
return [
...acc,
...routes
.filter((route) => exclusion.test(route.pattern))
.map((route) => route.pattern),
];
}

await builder.buildServerlessFolder(entryFile, func, _config.root);
return [...acc, exclusion];
}, []);

routeDefinitions.push({
src: route.patternRegex.source,
dest: func,
});
const dest = _middlewareEntryPoint ? MIDDLEWARE_PATH : NODE_PATH;
for (const route of expandedExclusions) {
// vercel interprets src as a regex pattern, so we need to escape it
routeDefinitions.push({
src: escapeRegex(route),
dest,
});
}
}
} else {
const entryFile = new URL(_serverEntry, _buildTempFolder);
if (isr) {
const isrConfig = typeof isr === 'object' ? isr : {};
await builder.buildServerlessFolder(entryFile, NODE_PATH, _config.root);
if (isrConfig.exclude?.length) {
const expandedExclusions = isrConfig.exclude.reduce<string[]>((acc, exclusion) => {
if (exclusion instanceof RegExp) {
return [
...acc,
...routes
.filter((route) => exclusion.test(route.pattern))
.map((route) => route.pattern),
];
}

return [...acc, exclusion];
}, []);

const dest = _middlewareEntryPoint ? MIDDLEWARE_PATH : NODE_PATH;
for (const route of expandedExclusions) {
// vercel interprets src as a regex pattern, so we need to escape it
routeDefinitions.push({
src: escapeRegex(route),
dest,
});
await builder.buildISRFolder(entryFile, '_isr', isrConfig, _config.root);
for (const route of routes) {
// Do not create _isr route entries for excluded routes
const excludeRouteFromIsr = isrConfig.exclude?.some((exclusion) => {
if (exclusion instanceof RegExp) {
return exclusion.test(route.pattern);
}
}
await builder.buildISRFolder(entryFile, '_isr', isrConfig, _config.root);
for (const route of routes) {
// Do not create _isr route entries for excluded routes
const excludeRouteFromIsr = isrConfig.exclude?.some((exclusion) => {
if (exclusion instanceof RegExp) {
return exclusion.test(route.pattern);
}

return exclusion === route.pattern;
});

if (!excludeRouteFromIsr) {
const src = route.patternRegex.source;
const dest =
src.startsWith('^\\/_image') || src.startsWith('^\\/_server-islands')
? NODE_PATH
: ISR_PATH;
if (!route.isPrerendered)
routeDefinitions.push({
src,
dest,
});
}
}
} else {
await builder.buildServerlessFolder(entryFile, NODE_PATH, _config.root);
const dest = _middlewareEntryPoint ? MIDDLEWARE_PATH : NODE_PATH;
for (const route of routes) {
return exclusion === route.pattern;
});

if (!excludeRouteFromIsr) {
const src = route.patternRegex.source;
const dest =
src.startsWith('^\\/_image') || src.startsWith('^\\/_server-islands')
? NODE_PATH
: ISR_PATH;
if (!route.isPrerendered)
routeDefinitions.push({
src: route.patternRegex.source,
src,
dest,
});
}
}
} else {
await builder.buildServerlessFolder(entryFile, NODE_PATH, _config.root);
const dest = _middlewareEntryPoint ? MIDDLEWARE_PATH : NODE_PATH;
for (const route of routes) {
if (!route.isPrerendered)
routeDefinitions.push({
src: route.patternRegex.source,
dest,
});
}
}

if (_middlewareEntryPoint) {
await builder.buildMiddlewareFolder(
_middlewareEntryPoint,
Expand Down
Loading