summaryrefslogtreecommitdiff
path: root/packages/integrations/vercel/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/integrations/vercel/src')
-rw-r--r--packages/integrations/vercel/src/image/shared.ts5
-rw-r--r--packages/integrations/vercel/src/image/squoosh-dev-service.ts31
-rw-r--r--packages/integrations/vercel/src/lib/prerender.ts5
-rw-r--r--packages/integrations/vercel/src/lib/redirects.ts34
-rw-r--r--packages/integrations/vercel/src/serverless/adapter.ts146
-rw-r--r--packages/integrations/vercel/src/serverless/entrypoint.ts15
-rw-r--r--packages/integrations/vercel/src/serverless/middleware.ts2
-rw-r--r--packages/integrations/vercel/src/serverless/polyfill.ts3
-rw-r--r--packages/integrations/vercel/src/static/adapter.ts13
9 files changed, 77 insertions, 177 deletions
diff --git a/packages/integrations/vercel/src/image/shared.ts b/packages/integrations/vercel/src/image/shared.ts
index dfae0d06f..21f101912 100644
--- a/packages/integrations/vercel/src/image/shared.ts
+++ b/packages/integrations/vercel/src/image/shared.ts
@@ -13,7 +13,7 @@ export function isESMImportedImage(src: ImageMetadata | string): src is ImageMet
return typeof src === 'object';
}
-export type DevImageService = 'sharp' | 'squoosh' | (string & {});
+export type DevImageService = 'sharp' | (string & {});
// https://vercel.com/docs/build-output-api/v3/configuration#images
type ImageFormat = 'image/avif' | 'image/webp';
@@ -76,9 +76,6 @@ export function getAstroImageConfig(
case 'sharp':
devService = '@astrojs/vercel/dev-image-service';
break;
- case 'squoosh':
- devService = '@astrojs/vercel/squoosh-dev-image-service';
- break;
default:
if (typeof devImageService === 'string') {
devService = devImageService;
diff --git a/packages/integrations/vercel/src/image/squoosh-dev-service.ts b/packages/integrations/vercel/src/image/squoosh-dev-service.ts
deleted file mode 100644
index d3b05bb11..000000000
--- a/packages/integrations/vercel/src/image/squoosh-dev-service.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import type { LocalImageService } from 'astro';
-import squooshService from 'astro/assets/services/squoosh';
-import { baseDevService } from './shared-dev-service.js';
-
-const service: LocalImageService = {
- ...baseDevService,
- getHTMLAttributes(options, serviceOptions) {
- const { inputtedWidth, ...props } = options;
-
- // If `validateOptions` returned a different width than the one of the image, use it for attributes
- if (inputtedWidth) {
- props.width = inputtedWidth;
- }
-
- return squooshService.getHTMLAttributes
- ? squooshService.getHTMLAttributes(props, serviceOptions)
- : {};
- },
- transform(inputBuffer, transform, serviceOptions) {
- // NOTE: Hardcoding webp here isn't accurate to how the Vercel Image Optimization API works, normally what we should
- // do is setup a custom endpoint that sniff the user's accept-content header and serve the proper format based on the
- // user's Vercel config. However, that's: a lot of work for: not much. The dev service is inaccurate to the prod service
- // in many more ways, this is one of the less offending cases and is, imo, okay, erika - 2023-04-27
- transform.format = transform.src.endsWith('svg') ? 'svg' : 'webp';
-
- // The base squoosh service works the same way as the Vercel Image Optimization API, so it's a safe fallback in local
- return squooshService.transform(inputBuffer, transform, serviceOptions);
- },
-};
-
-export default service;
diff --git a/packages/integrations/vercel/src/lib/prerender.ts b/packages/integrations/vercel/src/lib/prerender.ts
deleted file mode 100644
index f69f3b5d4..000000000
--- a/packages/integrations/vercel/src/lib/prerender.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import type { AstroConfig } from 'astro';
-
-export function isServerLikeOutput(config: AstroConfig) {
- return config.output === 'server' || config.output === 'hybrid';
-}
diff --git a/packages/integrations/vercel/src/lib/redirects.ts b/packages/integrations/vercel/src/lib/redirects.ts
index 59a006886..b11a5b2cc 100644
--- a/packages/integrations/vercel/src/lib/redirects.ts
+++ b/packages/integrations/vercel/src/lib/redirects.ts
@@ -1,6 +1,6 @@
import nodePath from 'node:path';
import { appendForwardSlash, removeLeadingForwardSlash } from '@astrojs/internal-helpers/path';
-import type { AstroConfig, RouteData, RoutePart } from 'astro';
+import type { AstroConfig, IntegrationRouteData, RoutePart } from 'astro';
const pathJoin = nodePath.posix.join;
@@ -49,19 +49,19 @@ function getMatchPattern(segments: RoutePart[][]) {
return segment[0].spread
? '(?:\\/(.*?))?'
: segment
- .map((part) => {
- if (part)
- return part.dynamic
- ? '([^/]+?)'
- : part.content
- .normalize()
- .replace(/\?/g, '%3F')
- .replace(/#/g, '%23')
- .replace(/%5B/g, '[')
- .replace(/%5D/g, ']')
- .replace(/[*+?^${}()|[\]\\]/g, '\\$&');
- })
- .join('');
+ .map((part) => {
+ if (part)
+ return part.dynamic
+ ? '([^/]+?)'
+ : part.content
+ .normalize()
+ .replace(/\?/g, '%3F')
+ .replace(/#/g, '%23')
+ .replace(/%5B/g, '[')
+ .replace(/%5D/g, ']')
+ .replace(/[*+?^${}()|[\]\\]/g, '\\$&');
+ })
+ .join('');
})
.join('/');
}
@@ -85,7 +85,7 @@ function getReplacePattern(segments: RoutePart[][]) {
return result;
}
-function getRedirectLocation(route: RouteData, config: AstroConfig): string {
+function getRedirectLocation(route: IntegrationRouteData, config: AstroConfig): string {
if (route.redirectRoute) {
const pattern = getReplacePattern(route.redirectRoute.segments);
const path = config.trailingSlash === 'always' ? appendForwardSlash(pattern) : pattern;
@@ -99,7 +99,7 @@ function getRedirectLocation(route: RouteData, config: AstroConfig): string {
}
}
-function getRedirectStatus(route: RouteData): number {
+function getRedirectStatus(route: IntegrationRouteData): number {
if (typeof route.redirect === 'object') {
return route.redirect.status;
}
@@ -116,7 +116,7 @@ export function escapeRegex(content: string) {
return `^/${getMatchPattern(segments)}$`;
}
-export function getRedirects(routes: RouteData[], config: AstroConfig): VercelRoute[] {
+export function getRedirects(routes: IntegrationRouteData[], config: AstroConfig): VercelRoute[] {
const redirects: VercelRoute[] = [];
for (const route of routes) {
diff --git a/packages/integrations/vercel/src/serverless/adapter.ts b/packages/integrations/vercel/src/serverless/adapter.ts
index e50bd4190..f45743b03 100644
--- a/packages/integrations/vercel/src/serverless/adapter.ts
+++ b/packages/integrations/vercel/src/serverless/adapter.ts
@@ -7,7 +7,7 @@ import type {
AstroConfig,
AstroIntegration,
AstroIntegrationLogger,
- RouteData,
+ IntegrationRouteData,
} from 'astro';
import { AstroError } from 'astro/errors';
import glob from 'fast-glob';
@@ -70,12 +70,10 @@ const SUPPORTED_NODE_VERSIONS: Record<
function getAdapter({
edgeMiddleware,
- functionPerRoute,
middlewareSecret,
skewProtection,
}: {
edgeMiddleware: boolean;
- functionPerRoute: boolean;
middlewareSecret: string;
skewProtection: boolean;
}): AstroAdapter {
@@ -86,19 +84,15 @@ function getAdapter({
args: { middlewareSecret, skewProtection },
adapterFeatures: {
edgeMiddleware,
- functionPerRoute,
+ buildOutput: 'server',
},
supportedAstroFeatures: {
hybridOutput: 'stable',
staticOutput: 'stable',
serverOutput: 'stable',
- assets: {
- supportKind: 'stable',
- isSharpCompatible: true,
- isSquooshCompatible: true,
- },
+ sharpImageService: 'stable',
i18nDomains: 'experimental',
- envGetSecret: 'experimental',
+ envGetSecret: 'stable',
},
};
}
@@ -134,12 +128,6 @@ export interface VercelServerlessConfig {
/** Whether to create the Vercel Edge middleware from an Astro middleware in your code base. */
edgeMiddleware?: boolean;
- /**
- * Whether to split builds into a separate function for each route.
- * @deprecated `functionPerRoute` is deprecated and will be removed in the next major release of the adapter.
- */
- functionPerRoute?: boolean;
-
/** The maximum duration (in seconds) that Serverless Functions can run before timing out. See the [Vercel documentation](https://vercel.com/docs/functions/serverless-functions/runtimes#maxduration) for the default and maximum limit for your account plan. */
maxDuration?: number;
@@ -186,7 +174,6 @@ export default function vercelServerless({
imageService,
imagesConfig,
devImageService = 'sharp',
- functionPerRoute = false,
edgeMiddleware = false,
maxDuration,
isr = false,
@@ -206,7 +193,7 @@ export default function vercelServerless({
let _config: AstroConfig;
let _buildTempFolder: URL;
let _serverEntry: string;
- let _entryPoints: Map<RouteData, URL>;
+ let _entryPoints: Map<IntegrationRouteData, URL>;
let _middlewareEntryPoint: URL | undefined;
// Extra files to be merged with `includeFiles` during build
const extraFilesToInclude: URL[] = [];
@@ -246,10 +233,10 @@ export default function vercelServerless({
if (vercelConfig.trailingSlash === true && config.trailingSlash === 'always') {
logger.warn(
'\n' +
- `\tYour "vercel.json" \`trailingSlash\` configuration (set to \`true\`) will conflict with your Astro \`trailinglSlash\` configuration (set to \`"always"\`).\n` +
- // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
- `\tThis would cause infinite redirects under certain conditions and throw an \`ERR_TOO_MANY_REDIRECTS\` error.\n` +
- `\tTo prevent this, your Astro configuration is updated to \`"ignore"\` during builds.\n`
+ `\tYour "vercel.json" \`trailingSlash\` configuration (set to \`true\`) will conflict with your Astro \`trailinglSlash\` configuration (set to \`"always"\`).\n` +
+ // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
+ `\tThis would cause infinite redirects under certain conditions and throw an \`ERR_TOO_MANY_REDIRECTS\` error.\n` +
+ `\tTo prevent this, your Astro configuration is updated to \`"ignore"\` during builds.\n`
);
updateConfig({
trailingSlash: 'ignore',
@@ -270,10 +257,7 @@ export default function vercelServerless({
vite: {
...getSpeedInsightsViteConfig(speedInsights?.enabled),
ssr: {
- external: [
- '@vercel/nft',
- ...((await shouldExternalizeAstroEnvSetup()) ? ['astro/env/setup'] : []),
- ],
+ external: ['@vercel/nft'],
},
},
...getAstroImageConfig(
@@ -285,39 +269,12 @@ export default function vercelServerless({
),
});
},
- 'astro:config:done': ({ setAdapter, config, logger }) => {
- if (functionPerRoute === true) {
- logger.warn(
- // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
- `\n` +
- `\tVercel's hosting plans might have limits to the number of functions you can create.\n` +
- // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
- `\tMake sure to check your plan carefully to avoid incurring additional costs.\n` +
- // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
- `\tYou can set functionPerRoute: false to prevent surpassing the limit.\n`
- );
-
- logger.warn(
- // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
- `\n` +
- // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
- `\t\`functionPerRoute\` is deprecated and will be removed in a future version of the adapter.\n`
- );
- }
-
- setAdapter(
- getAdapter({ functionPerRoute, edgeMiddleware, middlewareSecret, skewProtection })
- );
+ 'astro:config:done': ({ setAdapter, config }) => {
+ setAdapter(getAdapter({ edgeMiddleware, middlewareSecret, skewProtection }));
_config = config;
_buildTempFolder = config.build.server;
_serverEntry = config.build.serverEntry;
-
- if (config.output === 'static') {
- throw new AstroError(
- '`output: "server"` or `output: "hybrid"` is required to use the serverless adapter.'
- );
- }
},
'astro:build:ssr': async ({ entryPoints, middlewareEntryPoint }) => {
_entryPoints = new Map(
@@ -357,7 +314,8 @@ export default function vercelServerless({
// Multiple entrypoint support
if (_entryPoints.size) {
- const getRouteFuncName = (route: RouteData) => route.component.replace('src/pages/', '');
+ const getRouteFuncName = (route: IntegrationRouteData) =>
+ route.component.replace('src/pages/', '');
const getFallbackFuncName = (entryFile: URL) =>
basename(entryFile.toString())
@@ -426,31 +384,31 @@ export default function vercelServerless({
...routeDefinitions,
...(fourOhFourRoute
? [
- {
- src: '/.*',
- dest: fourOhFourRoute.prerender
- ? '/404.html'
- : _middlewareEntryPoint
- ? MIDDLEWARE_PATH
- : NODE_PATH,
- status: 404,
- },
- ]
+ {
+ src: '/.*',
+ dest: fourOhFourRoute.prerender
+ ? '/404.html'
+ : _middlewareEntryPoint
+ ? MIDDLEWARE_PATH
+ : NODE_PATH,
+ status: 404,
+ },
+ ]
: []),
],
...(imageService || imagesConfig
? {
- images: imagesConfig
- ? {
- ...imagesConfig,
- domains: [...imagesConfig.domains, ..._config.image.domains],
- remotePatterns: [
- ...(imagesConfig.remotePatterns ?? []),
- ..._config.image.remotePatterns,
- ],
- }
- : getDefaultImageConfig(_config.image),
- }
+ images: imagesConfig
+ ? {
+ ...imagesConfig,
+ domains: [...imagesConfig.domains, ..._config.image.domains],
+ remotePatterns: [
+ ...(imagesConfig.remotePatterns ?? []),
+ ..._config.image.remotePatterns,
+ ],
+ }
+ : getDefaultImageConfig(_config.image),
+ }
: {}),
});
@@ -463,16 +421,6 @@ export default function vercelServerless({
type Runtime = `nodejs${string}.x`;
-// TODO: remove once we don't use a TLA anymore
-async function shouldExternalizeAstroEnvSetup() {
- try {
- await import('astro/env/setup');
- return false;
- } catch {
- return true;
- }
-}
-
class VercelBuilder {
readonly NTF_CACHE = {};
@@ -483,7 +431,7 @@ class VercelBuilder {
readonly logger: AstroIntegrationLogger,
readonly maxDuration?: number,
readonly runtime = getRuntime(process, logger)
- ) {}
+ ) { }
async buildServerlessFolder(entry: URL, functionName: string, root: URL) {
const { config, includeFiles, excludeFiles, logger, NTF_CACHE, runtime, maxDuration } = this;
@@ -561,14 +509,14 @@ function getRuntime(process: NodeJS.Process, logger: AstroIntegrationLogger): Ru
const support = SUPPORTED_NODE_VERSIONS[major];
if (support === undefined) {
logger.warn(
- // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
// biome-ignore lint/style/useTemplate: <explanation>
+ // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
`\n` +
- `\tThe local Node.js version (${major}) is not supported by Vercel Serverless Functions.\n` +
- // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
- `\tYour project will use Node.js 18 as the runtime instead.\n` +
- // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
- `\tConsider switching your local version to 18.\n`
+ `\tThe local Node.js version (${major}) is not supported by Vercel Serverless Functions.\n` +
+ // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
+ `\tYour project will use Node.js 18 as the runtime instead.\n` +
+ // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
+ `\tConsider switching your local version to 18.\n`
);
return 'nodejs18.x';
}
@@ -594,13 +542,13 @@ function getRuntime(process: NodeJS.Process, logger: AstroIntegrationLogger): Ru
support.removal
);
logger.warn(
- // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
// biome-ignore lint/style/useTemplate: <explanation>
+ // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
`\n` +
- `\tYour project is being built for Node.js ${major} as the runtime.\n` +
- `\tThis version is deprecated by Vercel Serverless Functions, and scheduled to be disabled on ${removeDate}.\n` +
- // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
- `\tConsider upgrading your local version to 18.\n`
+ `\tYour project is being built for Node.js ${major} as the runtime.\n` +
+ `\tThis version is deprecated by Vercel Serverless Functions, and scheduled to be disabled on ${removeDate}.\n` +
+ // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
+ `\tConsider upgrading your local version to 18.\n`
);
return `nodejs${major}.x`;
}
diff --git a/packages/integrations/vercel/src/serverless/entrypoint.ts b/packages/integrations/vercel/src/serverless/entrypoint.ts
index 31f78bb8a..01279a2b4 100644
--- a/packages/integrations/vercel/src/serverless/entrypoint.ts
+++ b/packages/integrations/vercel/src/serverless/entrypoint.ts
@@ -1,6 +1,10 @@
+// Keep at the top
+import './polyfill.js';
+
import type { IncomingMessage, ServerResponse } from 'node:http';
import type { SSRManifest } from 'astro';
-import { NodeApp, applyPolyfills } from 'astro/app/node';
+import { NodeApp } from 'astro/app/node';
+import { setGetEnv } from 'astro/env/setup';
import {
ASTRO_LOCALS_HEADER,
ASTRO_MIDDLEWARE_SECRET_HEADER,
@@ -8,14 +12,7 @@ import {
ASTRO_PATH_PARAM,
} from './adapter.js';
-// Run polyfills immediately so any dependent code can use the globals
-applyPolyfills();
-
-// Won't throw if the virtual module is not available because it's not supported in
-// the users's astro version or if astro:env is not enabled in the project
-await import('astro/env/setup')
- .then((mod) => mod.setGetEnv((key) => process.env[key]))
- .catch(() => {});
+setGetEnv((key) => process.env[key]);
export const createExports = (
manifest: SSRManifest,
diff --git a/packages/integrations/vercel/src/serverless/middleware.ts b/packages/integrations/vercel/src/serverless/middleware.ts
index ca84bff33..973df238f 100644
--- a/packages/integrations/vercel/src/serverless/middleware.ts
+++ b/packages/integrations/vercel/src/serverless/middleware.ts
@@ -98,7 +98,7 @@ export default async function middleware(request, context) {
request,
params: {}
});
- ctx.locals = { vercel: { edge: context }, ...${handlerTemplateCall} };
+ Object.assign(ctx.locals, { vercel: { edge: context }, ...${handlerTemplateCall} });
const { origin } = new URL(request.url);
const next = async () => {
const { vercel, ...locals } = ctx.locals;
diff --git a/packages/integrations/vercel/src/serverless/polyfill.ts b/packages/integrations/vercel/src/serverless/polyfill.ts
new file mode 100644
index 000000000..dc00f45d7
--- /dev/null
+++ b/packages/integrations/vercel/src/serverless/polyfill.ts
@@ -0,0 +1,3 @@
+import { applyPolyfills } from 'astro/app/node';
+
+applyPolyfills();
diff --git a/packages/integrations/vercel/src/static/adapter.ts b/packages/integrations/vercel/src/static/adapter.ts
index 4a5bdf966..458143215 100644
--- a/packages/integrations/vercel/src/static/adapter.ts
+++ b/packages/integrations/vercel/src/static/adapter.ts
@@ -7,7 +7,6 @@ import {
getAstroImageConfig,
getDefaultImageConfig,
} from '../image/shared.js';
-import { isServerLikeOutput } from '../lib/prerender.js';
import { getRedirects } from '../lib/redirects.js';
import {
type VercelSpeedInsightsConfig,
@@ -24,19 +23,15 @@ function getAdapter(): AstroAdapter {
return {
name: PACKAGE_NAME,
supportedAstroFeatures: {
- assets: {
- supportKind: 'stable',
- isSquooshCompatible: true,
- isSharpCompatible: true,
- },
+ sharpImageService: 'stable',
staticOutput: 'stable',
serverOutput: 'unsupported',
hybridOutput: 'unsupported',
envGetSecret: 'unsupported',
},
adapterFeatures: {
+ buildOutput: 'static',
edgeMiddleware: false,
- functionPerRoute: false,
},
};
}
@@ -102,10 +97,6 @@ export default function vercelStatic({
'astro:config:done': ({ setAdapter, config }) => {
setAdapter(getAdapter());
_config = config;
-
- if (isServerLikeOutput(config)) {
- throw new Error(`${PACKAGE_NAME} should be used with output: 'static'`);
- }
},
'astro:build:start': async () => {
// Ensure to have `.vercel/output` empty.