summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/integrations/vercel/CHANGELOG.md68
-rw-r--r--packages/integrations/vercel/package.json16
-rw-r--r--packages/integrations/vercel/src/image/shared.ts7
-rw-r--r--packages/integrations/vercel/src/image/squoosh-dev-service.ts31
-rw-r--r--packages/integrations/vercel/src/index.ts622
-rw-r--r--packages/integrations/vercel/src/lib/prerender.ts5
-rw-r--r--packages/integrations/vercel/src/lib/redirects.ts8
-rw-r--r--packages/integrations/vercel/src/lib/speed-insights.ts30
-rw-r--r--packages/integrations/vercel/src/serverless/adapter.ts616
-rw-r--r--packages/integrations/vercel/src/serverless/entrypoint.ts16
-rw-r--r--packages/integrations/vercel/src/serverless/middleware.ts4
-rw-r--r--packages/integrations/vercel/src/serverless/polyfill.ts3
-rw-r--r--packages/integrations/vercel/src/speed-insights.ts68
-rw-r--r--packages/integrations/vercel/src/static/adapter.ts167
-rw-r--r--packages/integrations/vercel/test/fixtures/basic/astro.config.mjs4
-rw-r--r--packages/integrations/vercel/test/fixtures/basic/package.json2
-rw-r--r--packages/integrations/vercel/test/fixtures/functionPerRoute/astro.config.mjs9
-rw-r--r--packages/integrations/vercel/test/fixtures/functionPerRoute/package.json9
-rw-r--r--packages/integrations/vercel/test/fixtures/functionPerRoute/src/pages/one.astro8
-rw-r--r--packages/integrations/vercel/test/fixtures/functionPerRoute/src/pages/prerender.astro12
-rw-r--r--packages/integrations/vercel/test/fixtures/functionPerRoute/src/pages/two.astro8
-rw-r--r--packages/integrations/vercel/test/fixtures/image/package.json2
-rw-r--r--packages/integrations/vercel/test/fixtures/isr/package.json2
-rw-r--r--packages/integrations/vercel/test/fixtures/max-duration/package.json2
-rw-r--r--packages/integrations/vercel/test/fixtures/middleware-with-edge-file/package.json2
-rw-r--r--packages/integrations/vercel/test/fixtures/middleware-without-edge-file/package.json2
-rw-r--r--packages/integrations/vercel/test/fixtures/no-output/package.json2
-rw-r--r--packages/integrations/vercel/test/fixtures/prerendered-error-pages/package.json2
-rw-r--r--packages/integrations/vercel/test/fixtures/redirects-serverless/astro.config.mjs2
-rw-r--r--packages/integrations/vercel/test/fixtures/redirects-serverless/package.json2
-rw-r--r--packages/integrations/vercel/test/fixtures/redirects/package.json2
-rw-r--r--packages/integrations/vercel/test/fixtures/server-islands/astro.config.mjs5
-rw-r--r--packages/integrations/vercel/test/fixtures/server-islands/package.json2
-rw-r--r--packages/integrations/vercel/test/fixtures/serverless-prerender/package.json2
-rw-r--r--packages/integrations/vercel/test/fixtures/serverless-prerender/src/pages/index.astro2
-rw-r--r--packages/integrations/vercel/test/fixtures/serverless-with-dynamic-routes/astro.config.mjs1
-rw-r--r--packages/integrations/vercel/test/fixtures/serverless-with-dynamic-routes/package.json2
-rw-r--r--packages/integrations/vercel/test/fixtures/serverless-with-dynamic-routes/src/pages/index.astro2
-rw-r--r--packages/integrations/vercel/test/fixtures/static-assets/package.json2
-rw-r--r--packages/integrations/vercel/test/fixtures/static/package.json2
-rw-r--r--packages/integrations/vercel/test/fixtures/streaming/package.json2
-rw-r--r--packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-server/astro.config.mjs10
-rw-r--r--packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-server/package.json9
-rw-r--r--packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-server/src/pages/one.astro8
-rw-r--r--packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-server/src/pages/two.astro8
-rw-r--r--packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-static/astro.config.mjs10
-rw-r--r--packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-static/package.json9
-rw-r--r--packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-static/src/pages/one.astro8
-rw-r--r--packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-static/src/pages/two.astro8
-rw-r--r--packages/integrations/vercel/test/fixtures/with-web-analytics-enabled/output-as-static/package.json2
-rw-r--r--packages/integrations/vercel/test/hosted/hosted-astro-project/package.json2
-rw-r--r--packages/integrations/vercel/test/isr.test.js8
-rw-r--r--packages/integrations/vercel/test/no-output.test.js25
-rw-r--r--packages/integrations/vercel/test/prerendered-error-pages.test.js13
-rw-r--r--packages/integrations/vercel/test/serverless-prerender.test.js2
-rw-r--r--packages/integrations/vercel/test/serverless-with-dynamic-routes.test.js9
-rw-r--r--packages/integrations/vercel/test/speed-insights.test.js47
-rw-r--r--packages/integrations/vercel/test/split.test.js32
58 files changed, 769 insertions, 1196 deletions
diff --git a/packages/integrations/vercel/CHANGELOG.md b/packages/integrations/vercel/CHANGELOG.md
index ac8d71fd1..cb2556add 100644
--- a/packages/integrations/vercel/CHANGELOG.md
+++ b/packages/integrations/vercel/CHANGELOG.md
@@ -1,5 +1,25 @@
# @astrojs/vercel
+## 8.0.0-beta.2
+
+### Major Changes
+
+- [#375](https://github.com/withastro/adapters/pull/375) [`e7881f7`](https://github.com/withastro/adapters/commit/e7881f7928c6ca62d43c763033f9ed065a907f3b) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Updates internal code to works with Astro 5 changes to hybrid rendering. No changes are necessary to your project, apart from using Astro 5
+
+- [#397](https://github.com/withastro/adapters/pull/397) [`776a266`](https://github.com/withastro/adapters/commit/776a26670cf483e37ec0e6eba27a0bde09db0146) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Welcome to the Astro 5 beta! This release has no changes from the latest alpha of this package, but it does bring us one step closer to the final, stable release.
+
+ Starting from this release, no breaking changes will be introduced unless absolutely necessary.
+
+ To learn how to upgrade, check out the [Astro v5.0 upgrade guide in our beta docs site](https://5-0-0-beta.docs.astro.build/en/guides/upgrade-to/v5/).
+
+- [#377](https://github.com/withastro/adapters/pull/377) [`b77f99c`](https://github.com/withastro/adapters/commit/b77f99c92dae715033ebcee2af25f0f1054572b8) Thanks [@alexanderniebuhr](https://github.com/alexanderniebuhr)! - Updates the adapter to use new `IntegrationRouteData` type
+
+- [#392](https://github.com/withastro/adapters/pull/392) [`3a49eb7`](https://github.com/withastro/adapters/commit/3a49eb7802c44212ccfab06034b7dc5f2b060e94) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Updates internal code for Astro 5 changes. No changes is required to your project, apart from using Astro 5
+
+### Minor Changes
+
+- [#385](https://github.com/withastro/adapters/pull/385) [`bb725b7`](https://github.com/withastro/adapters/commit/bb725b7a430a01a3cd197e3e84381be4fa0c945c) Thanks [@florian-lefebvre](https://github.com/florian-lefebvre)! - Cleans up `astro:env` support
+
## 7.8.2
### Patch Changes
@@ -12,6 +32,54 @@
- [#381](https://github.com/withastro/adapters/pull/381) [`46fbb26`](https://github.com/withastro/adapters/commit/46fbb26175ab09d12f95dba63cfe76bdcc25ef59) Thanks [@matthewp](https://github.com/matthewp)! - Prevent crawling for dependencies outside of the workspace root
+## 8.0.0-alpha.1
+
+### Major Changes
+
+- [#11679](https://github.com/withastro/astro/pull/11679) [`ea71b90`](https://github.com/withastro/astro/commit/ea71b90c9c08ddd1d3397c78e2e273fb799f7dbd) Thanks [@florian-lefebvre](https://github.com/florian-lefebvre)! - Adds stable support for `astro:env`
+
+- [#11770](https://github.com/withastro/astro/pull/11770) [`cfa6a47`](https://github.com/withastro/astro/commit/cfa6a47ac7a541f99fdad46a68d0cca6e5816cd5) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Removed support for the Squoosh image service. As the underlying library `libsquoosh` is no longer maintained, and the image service sees very little usage we have decided to remove it from Astro.
+
+ Our recommendation is to use the base Sharp image service, which is more powerful, faster, and more actively maintained.
+
+ ```diff
+ - import { squooshImageService } from "astro/config";
+ import { defineConfig } from "astro/config";
+
+ export default defineConfig({
+ - image: {
+ - service: squooshImageService()
+ - }
+ });
+ ```
+
+ If you are using this service, and cannot migrate to the base Sharp image service, a third-party extraction of the previous service is available here: https://github.com/Princesseuh/astro-image-service-squoosh
+
+### Patch Changes
+
+- [#11783](https://github.com/withastro/astro/pull/11783) [`fc81b01`](https://github.com/withastro/astro/commit/fc81b01bcdd43646bcc615b16bf0400a646445c8) Thanks [@matthewp](https://github.com/matthewp)! - Prevent race condition with Node 18
+
+ Using Node 18 there can be a race condition where polyfill for the `crypto` global is not applied in time. This change ensures the polyfills run first.
+
+## 8.0.0-alpha.0
+
+### Major Changes
+
+- [#11714](https://github.com/withastro/astro/pull/11714) [`8a53517`](https://github.com/withastro/astro/commit/8a5351737d6a14fc55f1dafad8f3b04079e81af6) Thanks [@matthewp](https://github.com/matthewp)! - Remove support for functionPerRoute
+
+ This change removes support for the `functionPerRoute` option both in Astro and `@astrojs/vercel`.
+
+ This option made it so that each route got built as separate entrypoints so that they could be loaded as separate functions. The hope was that by doing this it would decrease the size of each function. However in practice routes use most of the same code, and increases in function size limitations made the potential upsides less important.
+
+ Additionally there are downsides to functionPerRoute, such as hitting limits on the number of functions per project. The feature also never worked with some Astro features like i18n domains and request rewriting.
+
+ Given this, the feature has been removed from Astro.
+
+### Patch Changes
+
+- Updated dependencies [[`b6fbdaa`](https://github.com/withastro/astro/commit/b6fbdaa94a9ecec706a99e1938fbf5cd028c72e0), [`89bab1e`](https://github.com/withastro/astro/commit/89bab1e70786123fbe933a9d7a1b80c9334dcc5f), [`d74617c`](https://github.com/withastro/astro/commit/d74617cbd3278feba05909ec83db2d73d57a153e), [`e90f559`](https://github.com/withastro/astro/commit/e90f5593d23043579611452a84b9e18ad2407ef9), [`2df49a6`](https://github.com/withastro/astro/commit/2df49a6fb4f6d92fe45f7429430abe63defeacd6), [`8a53517`](https://github.com/withastro/astro/commit/8a5351737d6a14fc55f1dafad8f3b04079e81af6)]:
+ - astro@5.0.0-alpha.0
+
## 7.8.0
### Minor Changes
diff --git a/packages/integrations/vercel/package.json b/packages/integrations/vercel/package.json
index 25fdc8a16..4f9ca6c55 100644
--- a/packages/integrations/vercel/package.json
+++ b/packages/integrations/vercel/package.json
@@ -1,7 +1,7 @@
{
"name": "@astrojs/vercel",
"description": "Deploy your site to Vercel",
- "version": "7.8.2",
+ "version": "8.0.0-beta.2",
"type": "module",
"author": "withastro",
"license": "MIT",
@@ -14,16 +14,13 @@
"bugs": "https://github.com/withastro/adapters/issues",
"homepage": "https://docs.astro.build/en/guides/integrations-guide/vercel/",
"exports": {
- ".": {
- "types": "./types.d.ts"
- },
+ ".": "./dist/index.js",
+ "./entrypoint": "./dist/serverless/entrypoint.js",
"./serverless": "./dist/serverless/adapter.js",
"./serverless/entrypoint": "./dist/serverless/entrypoint.js",
"./static": "./dist/static/adapter.js",
- "./speed-insights": "./dist/speed-insights.js",
"./build-image-service": "./dist/image/build-service.js",
"./dev-image-service": "./dist/image/dev-service.js",
- "./squoosh-dev-service": "./dist/image/squoosh-dev-service.js",
"./package.json": "./package.json"
},
"typesVersions": {
@@ -44,15 +41,14 @@
"@vercel/edge": "^1.1.2",
"@vercel/nft": "^0.27.5",
"esbuild": "^0.21.5",
- "fast-glob": "^3.3.2",
- "web-vitals": "^3.5.2"
+ "fast-glob": "^3.3.2"
},
"peerDependencies": {
- "astro": "^4.2.0"
+ "astro": "^5.0.0-alpha.8"
},
"devDependencies": {
"@astrojs/test-utils": "workspace:*",
- "astro": "^4.16.8",
+ "astro": "^5.0.0-alpha.8",
"astro-scripts": "workspace:*",
"cheerio": "1.0.0"
},
diff --git a/packages/integrations/vercel/src/image/shared.ts b/packages/integrations/vercel/src/image/shared.ts
index dfae0d06f..cacb09192 100644
--- a/packages/integrations/vercel/src/image/shared.ts
+++ b/packages/integrations/vercel/src/image/shared.ts
@@ -13,12 +13,12 @@ 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';
-type RemotePattern = {
+export type RemotePattern = {
protocol?: 'http' | 'https';
hostname: string;
port?: string;
@@ -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/index.ts b/packages/integrations/vercel/src/index.ts
new file mode 100644
index 000000000..f6d71661b
--- /dev/null
+++ b/packages/integrations/vercel/src/index.ts
@@ -0,0 +1,622 @@
+import { existsSync, readFileSync } from 'node:fs';
+import { basename } from 'node:path';
+import { pathToFileURL } from 'node:url';
+import { emptyDir, removeDir, writeJson } from '@astrojs/internal-helpers/fs';
+import type {
+ AstroAdapter,
+ AstroConfig,
+ AstroIntegration,
+ AstroIntegrationLogger,
+ IntegrationRouteData,
+} from 'astro';
+import glob from 'fast-glob';
+import {
+ type DevImageService,
+ type VercelImageConfig,
+ getAstroImageConfig,
+ getDefaultImageConfig,
+} from './image/shared.js';
+import type { RemotePattern } from './image/shared.js';
+import { copyDependenciesToFunction } from './lib/nft.js';
+import { escapeRegex, getRedirects } from './lib/redirects.js';
+import {
+ type VercelWebAnalyticsConfig,
+ getInjectableWebAnalyticsContent,
+} from './lib/web-analytics.js';
+import { generateEdgeMiddleware } from './serverless/middleware.js';
+
+const PACKAGE_NAME = '@astrojs/vercel';
+
+/**
+ * The edge function calls the node server at /_render,
+ * with the original path as the value of this header.
+ */
+export const ASTRO_PATH_HEADER = 'x-astro-path';
+export const ASTRO_PATH_PARAM = 'x_astro_path';
+
+/**
+ * The edge function calls the node server at /_render,
+ * with the locals serialized into this header.
+ */
+export const ASTRO_LOCALS_HEADER = 'x-astro-locals';
+export const ASTRO_MIDDLEWARE_SECRET_HEADER = 'x-astro-middleware-secret';
+export const VERCEL_EDGE_MIDDLEWARE_FILE = 'vercel-edge-middleware';
+
+// Vercel routes the folder names to a path on the deployed website.
+// We attempt to avoid interfering by prefixing with an underscore.
+export const NODE_PATH = '_render';
+const MIDDLEWARE_PATH = '_middleware';
+
+// This isn't documented by vercel anywhere, but unlike serverless
+// and edge functions, isr functions are not passed the original path.
+// Instead, we have to use $0 to refer to the regex match from "src".
+const ISR_PATH = `/_isr?${ASTRO_PATH_PARAM}=$0`;
+
+// https://vercel.com/docs/concepts/functions/serverless-functions/runtimes/node-js#node.js-version
+const SUPPORTED_NODE_VERSIONS: Record<
+ string,
+ | { status: 'default' }
+ | { status: 'beta' }
+ | { status: 'retiring'; removal: Date | string; warnDate: Date }
+ | { status: 'deprecated'; removal: Date }
+> = {
+ 18: { status: 'retiring', removal: 'Early 2025', warnDate: new Date('October 1 2024') },
+ 20: { status: 'default' },
+};
+
+function getAdapter({
+ edgeMiddleware,
+ middlewareSecret,
+ skewProtection,
+ buildOutput,
+}: {
+ buildOutput: 'server' | 'static';
+ edgeMiddleware: boolean;
+ middlewareSecret: string;
+ skewProtection: boolean;
+}): AstroAdapter {
+ return {
+ name: PACKAGE_NAME,
+ serverEntrypoint: `${PACKAGE_NAME}/entrypoint`,
+ exports: ['default'],
+ args: { middlewareSecret, skewProtection },
+ adapterFeatures: {
+ edgeMiddleware,
+ buildOutput,
+ },
+ supportedAstroFeatures: {
+ hybridOutput: 'stable',
+ staticOutput: 'stable',
+ serverOutput: 'stable',
+ sharpImageService: 'stable',
+ i18nDomains: 'experimental',
+ envGetSecret: 'stable',
+ },
+ };
+}
+
+export interface VercelServerlessConfig {
+ /** Configuration for [Vercel Web Analytics](https://vercel.com/docs/concepts/analytics). */
+ webAnalytics?: VercelWebAnalyticsConfig;
+
+ /** Force files to be bundled with your function. This is helpful when you notice missing files. */
+ includeFiles?: string[];
+
+ /** Exclude any files from the bundling process that would otherwise be included. */
+ excludeFiles?: string[];
+
+ /** When enabled, an Image Service powered by the Vercel Image Optimization API will be automatically configured and used in production. In development, the image service specified by devImageService will be used instead. */
+ imageService?: boolean;
+
+ /** Configuration options for [Vercel’s Image Optimization API](https://vercel.com/docs/concepts/image-optimization). See [Vercel’s image configuration documentation](https://vercel.com/docs/build-output-api/v3/configuration#images) for a complete list of supported parameters. */
+ imagesConfig?: VercelImageConfig;
+
+ /** Allows you to configure which image service to use in development when imageService is enabled. */
+ devImageService?: DevImageService;
+
+ /** Whether to create the Vercel Edge middleware from an Astro middleware in your code base. */
+ edgeMiddleware?: 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;
+
+ /** Whether to cache on-demand rendered pages in the same way as static files. */
+ isr?: boolean | VercelISRConfig;
+ /**
+ * It enables Vercel skew protection: https://vercel.com/docs/deployments/skew-protection
+ */
+ skewProtection?: boolean;
+}
+
+interface VercelISRConfig {
+ /**
+ * A secret random string that you create.
+ * Its presence in the `__prerender_bypass` cookie will result in fresh responses being served, bypassing the cache. See Vercel’s documentation on [Draft Mode](https://vercel.com/docs/build-output-api/v3/features#draft-mode) for more information.
+ * Its presence in the `x-prerender-revalidate` header will result in a fresh response which will then be cached for all future requests to be used. See Vercel’s documentation on [On-Demand Incremental Static Regeneration (ISR)](https://vercel.com/docs/build-output-api/v3/features#on-demand-incremental-static-regeneration-isr) for more information.
+ *
+ * @default `undefined`
+ */
+ bypassToken?: string;
+
+ /**
+ * Expiration time (in seconds) before the pages will be re-generated.
+ *
+ * Setting to `false` means that the page will stay cached as long as the current deployment is in production.
+ *
+ * @default `false`
+ */
+ expiration?: number | false;
+
+ /**
+ * Paths that will always be served by a serverless function instead of an ISR function.
+ *
+ * @default `[]`
+ */
+ exclude?: string[];
+}
+
+export default function vercelAdapter({
+ webAnalytics,
+ includeFiles: _includeFiles = [],
+ excludeFiles: _excludeFiles = [],
+ imageService,
+ imagesConfig,
+ devImageService = 'sharp',
+ edgeMiddleware = false,
+ maxDuration,
+ isr = false,
+ skewProtection = false,
+}: VercelServerlessConfig = {}): AstroIntegration {
+ if (maxDuration) {
+ if (typeof maxDuration !== 'number') {
+ // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
+ throw new TypeError(`maxDuration must be a number`, { cause: maxDuration });
+ }
+ if (maxDuration <= 0) {
+ // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
+ throw new TypeError(`maxDuration must be a positive number`, { cause: maxDuration });
+ }
+ }
+
+ let _config: AstroConfig;
+ let _buildTempFolder: URL;
+ let _serverEntry: string;
+ let _entryPoints: Map<IntegrationRouteData, URL>;
+ let _middlewareEntryPoint: URL | undefined;
+ // Extra files to be merged with `includeFiles` during build
+ const extraFilesToInclude: URL[] = [];
+ // Secret used to verify that the caller is the astro-generated edge middleware and not a third-party
+ const middlewareSecret = crypto.randomUUID();
+
+ let buildOutput: 'server' | 'static';
+
+ return {
+ name: PACKAGE_NAME,
+ hooks: {
+ 'astro:config:setup': async ({ command, config, updateConfig, injectScript, logger }) => {
+ buildOutput = config.output;
+ if (webAnalytics?.enabled) {
+ injectScript(
+ 'head-inline',
+ await getInjectableWebAnalyticsContent({
+ mode: command === 'dev' ? 'development' : 'production',
+ })
+ );
+ }
+
+ if (buildOutput === 'server') {
+ if (maxDuration && maxDuration > 900) {
+ logger.warn(
+ `maxDuration is set to ${maxDuration} seconds, which is longer than the maximum allowed duration of 900 seconds.`
+ );
+ logger.warn(
+ // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
+ `Please make sure that your plan allows for this duration. See https://vercel.com/docs/functions/serverless-functions/runtimes#maxduration for more information.`
+ );
+ }
+
+ const vercelConfigPath = new URL('vercel.json', config.root);
+ if (existsSync(vercelConfigPath)) {
+ try {
+ const vercelConfig = JSON.parse(readFileSync(vercelConfigPath, 'utf-8'));
+ 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`
+ );
+ updateConfig({
+ trailingSlash: 'ignore',
+ });
+ }
+ } catch (_err) {
+ logger.warn(`Your "vercel.json" config is not a valid json file.`);
+ }
+ }
+
+ updateConfig({
+ outDir: new URL('./.vercel/output/', config.root),
+ build: {
+ client: new URL('./.vercel/output/static/', config.root),
+ server: new URL('./.vercel/output/_functions/', config.root),
+ redirects: false,
+ },
+ vite: {
+ ssr: {
+ external: ['@vercel/nft'],
+ },
+ },
+ ...getAstroImageConfig(
+ imageService,
+ imagesConfig,
+ command,
+ devImageService,
+ config.image
+ ),
+ });
+ } else {
+ const outDir = new URL('./.vercel/output/static/', config.root);
+ updateConfig({
+ outDir,
+ build: {
+ format: 'directory',
+ redirects: false,
+ },
+ ...getAstroImageConfig(
+ imageService,
+ imagesConfig,
+ command,
+ devImageService,
+ config.image
+ ),
+ });
+ }
+ },
+ 'astro:config:done': ({ setAdapter, config }) => {
+ if (buildOutput === 'server') {
+ setAdapter(getAdapter({ buildOutput, edgeMiddleware, middlewareSecret, skewProtection }));
+ } else {
+ setAdapter(
+ getAdapter({
+ edgeMiddleware: false,
+ middlewareSecret: '',
+ skewProtection,
+ buildOutput,
+ })
+ );
+ }
+ _config = config;
+ _buildTempFolder = config.build.server;
+ _serverEntry = config.build.serverEntry;
+ },
+ 'astro:build:start': async () => {
+ if (buildOutput !== 'static') {
+ // Ensure to have `.vercel/output` empty.
+ // This is because, when building to static, outDir = .vercel/output/static/,
+ // so .vercel/output itself won't get cleaned.
+ await emptyDir(new URL('./.vercel/output/', _config.root));
+ }
+ },
+ 'astro:build:ssr': async ({ entryPoints, middlewareEntryPoint }) => {
+ _entryPoints = new Map(
+ Array.from(entryPoints).filter(([routeData]) => !routeData.prerender)
+ );
+ _middlewareEntryPoint = middlewareEntryPoint;
+ },
+ 'astro:build:done': async ({ routes, logger }) => {
+ const routeDefinitions: Array<{
+ src: string;
+ dest: string;
+ middlewarePath?: string;
+ }> = [];
+
+ if (buildOutput === 'server') {
+ // Merge any includes from `vite.assetsInclude
+ if (_config.vite.assetsInclude) {
+ const mergeGlobbedIncludes = (globPattern: unknown) => {
+ if (typeof globPattern === 'string') {
+ const entries = glob.sync(globPattern).map((p) => pathToFileURL(p));
+ extraFilesToInclude.push(...entries);
+ } else if (Array.isArray(globPattern)) {
+ for (const pattern of globPattern) {
+ mergeGlobbedIncludes(pattern);
+ }
+ }
+ };
+
+ mergeGlobbedIncludes(_config.vite.assetsInclude);
+ }
+
+ const includeFiles = _includeFiles
+ .map((file) => new URL(file, _config.root))
+ .concat(extraFilesToInclude);
+ const excludeFiles = _excludeFiles.map((file) => new URL(file, _config.root));
+
+ const builder = new VercelBuilder(
+ _config,
+ excludeFiles,
+ includeFiles,
+ logger,
+ maxDuration
+ );
+
+ // Multiple entrypoint support
+ if (_entryPoints.size) {
+ const getRouteFuncName = (route: IntegrationRouteData) =>
+ route.component.replace('src/pages/', '');
+
+ const getFallbackFuncName = (entryFile: URL) =>
+ basename(entryFile.toString())
+ .replace('entry.', '')
+ .replace(/\.mjs$/, '');
+
+ for (const [route, entryFile] of _entryPoints) {
+ const func = route.component.startsWith('src/pages/')
+ ? getRouteFuncName(route)
+ : getFallbackFuncName(entryFile);
+
+ await builder.buildServerlessFolder(entryFile, func, _config.root);
+
+ routeDefinitions.push({
+ src: route.pattern.source,
+ dest: func,
+ });
+ }
+ } 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 dest = _middlewareEntryPoint ? MIDDLEWARE_PATH : NODE_PATH;
+ for (const route of isrConfig.exclude) {
+ // 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) {
+ const src = route.pattern.source;
+ const dest = src.startsWith('^\\/_image') ? NODE_PATH : ISR_PATH;
+ if (!route.prerender) 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) {
+ if (!route.prerender) routeDefinitions.push({ src: route.pattern.source, dest });
+ }
+ }
+ }
+ if (_middlewareEntryPoint) {
+ await builder.buildMiddlewareFolder(
+ _middlewareEntryPoint,
+ MIDDLEWARE_PATH,
+ middlewareSecret
+ );
+ }
+ }
+ const fourOhFourRoute = routes.find((route) => route.pathname === '/404');
+ const destination =
+ buildOutput === 'server'
+ ? new URL('./config.json', _config.outDir)
+ : new URL('./.vercel/output/config.json', _config.root);
+ const finalRoutes = [
+ ...getRedirects(routes, _config),
+ {
+ src: `^/${_config.build.assets}/(.*)$`,
+ headers: { 'cache-control': 'public, max-age=31536000, immutable' },
+ continue: true,
+ },
+ { handle: 'filesystem' },
+ ];
+ if (buildOutput === 'server') {
+ finalRoutes.push(...routeDefinitions);
+ }
+
+ if (fourOhFourRoute) {
+ if (buildOutput === 'server') {
+ finalRoutes.push({
+ src: '/.*',
+ dest: fourOhFourRoute.prerender
+ ? '/404.html'
+ : _middlewareEntryPoint
+ ? MIDDLEWARE_PATH
+ : NODE_PATH,
+ status: 404,
+ });
+ } else {
+ finalRoutes.push({
+ src: '/.*',
+ dest: '/404.html',
+ status: 404,
+ });
+ }
+ }
+
+ let images: VercelImageConfig | undefined;
+ if (imageService || imagesConfig) {
+ if (imagesConfig) {
+ images = {
+ ...imagesConfig,
+ domains: [...imagesConfig.domains, ..._config.image.domains],
+ remotePatterns: [...(imagesConfig.remotePatterns ?? [])],
+ };
+ const remotePatterns = _config.image.remotePatterns;
+ for (const pattern of remotePatterns) {
+ if (isAcceptedPattern(pattern)) {
+ images.remotePatterns?.push(pattern);
+ }
+ }
+ } else {
+ images = getDefaultImageConfig(_config.image);
+ }
+ }
+
+ // Output configuration
+ // https://vercel.com/docs/build-output-api/v3#build-output-configuration
+ await writeJson(destination, {
+ version: 3,
+ routes: finalRoutes,
+ images,
+ });
+
+ // Remove temporary folder
+ if (buildOutput === 'server') {
+ await removeDir(_buildTempFolder);
+ }
+ },
+ },
+ };
+}
+
+function isAcceptedPattern(pattern: any): pattern is RemotePattern {
+ if (pattern == null) {
+ return false;
+ }
+ if (!pattern.hostname) {
+ return false;
+ }
+ if (pattern.protocol && (pattern.protocol !== 'http' || pattern.protocol !== 'https')) {
+ return false;
+ }
+ return true;
+}
+
+type Runtime = `nodejs${string}.x`;
+
+class VercelBuilder {
+ readonly NTF_CACHE = {};
+
+ constructor(
+ readonly config: AstroConfig,
+ readonly excludeFiles: URL[],
+ readonly includeFiles: URL[],
+ 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;
+ // .vercel/output/functions/<name>.func/
+ const functionFolder = new URL(`./functions/${functionName}.func/`, config.outDir);
+ const packageJson = new URL(`./functions/${functionName}.func/package.json`, config.outDir);
+ const vcConfig = new URL(`./functions/${functionName}.func/.vc-config.json`, config.outDir);
+
+ // Copy necessary files (e.g. node_modules/)
+ const { handler } = await copyDependenciesToFunction(
+ {
+ entry,
+ outDir: functionFolder,
+ includeFiles,
+ excludeFiles,
+ logger,
+ root,
+ },
+ NTF_CACHE
+ );
+
+ // Enable ESM
+ // https://aws.amazon.com/blogs/compute/using-node-js-es-modules-and-top-level-await-in-aws-lambda/
+ await writeJson(packageJson, { type: 'module' });
+
+ // Serverless function config
+ // https://vercel.com/docs/build-output-api/v3#vercel-primitives/serverless-functions/configuration
+ await writeJson(vcConfig, {
+ runtime,
+ handler: handler.replaceAll('\\', '/'),
+ launcherType: 'Nodejs',
+ maxDuration,
+ supportsResponseStreaming: true,
+ });
+ }
+
+ async buildISRFolder(entry: URL, functionName: string, isr: VercelISRConfig, root: URL) {
+ await this.buildServerlessFolder(entry, functionName, root);
+ const prerenderConfig = new URL(
+ `./functions/${functionName}.prerender-config.json`,
+ this.config.outDir
+ );
+ // https://vercel.com/docs/build-output-api/v3/primitives#prerender-configuration-file
+ await writeJson(prerenderConfig, {
+ expiration: isr.expiration ?? false,
+ bypassToken: isr.bypassToken,
+ allowQuery: [ASTRO_PATH_PARAM],
+ passQuery: true,
+ });
+ }
+
+ async buildMiddlewareFolder(entry: URL, functionName: string, middlewareSecret: string) {
+ const functionFolder = new URL(`./functions/${functionName}.func/`, this.config.outDir);
+
+ await generateEdgeMiddleware(
+ entry,
+ this.config.root,
+ new URL(VERCEL_EDGE_MIDDLEWARE_FILE, this.config.srcDir),
+ new URL('./middleware.mjs', functionFolder),
+ middlewareSecret,
+ this.logger
+ );
+
+ // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
+ await writeJson(new URL(`./.vc-config.json`, functionFolder), {
+ runtime: 'edge',
+ entrypoint: 'middleware.mjs',
+ });
+ }
+}
+
+function getRuntime(process: NodeJS.Process, logger: AstroIntegrationLogger): Runtime {
+ const version = process.version.slice(1); // 'v18.19.0' --> '18.19.0'
+ const major = version.split('.')[0]; // '18.19.0' --> '18'
+ const support = SUPPORTED_NODE_VERSIONS[major];
+ if (support === undefined) {
+ logger.warn(
+ // 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`
+ );
+ return 'nodejs18.x';
+ }
+ if (support.status === 'default') {
+ return `nodejs${major}.x`;
+ }
+ if (support.status === 'retiring') {
+ if (support.warnDate && new Date() >= support.warnDate) {
+ logger.warn(
+ `Your project is being built for Node.js ${major} as the runtime, which is retiring by ${support.removal}.`
+ );
+ }
+ return `nodejs${major}.x`;
+ }
+ if (support.status === 'beta') {
+ logger.warn(
+ `Your project is being built for Node.js ${major} as the runtime, which is currently in beta for Vercel Serverless Functions.`
+ );
+ return `nodejs${major}.x`;
+ }
+ if (support.status === 'deprecated') {
+ const removeDate = new Intl.DateTimeFormat(undefined, { dateStyle: 'long' }).format(
+ support.removal
+ );
+ logger.warn(
+ // 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`
+ );
+ return `nodejs${major}.x`;
+ }
+ return 'nodejs18.x';
+}
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 e6d991429..7ef4d5045 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;
@@ -91,7 +91,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;
@@ -105,7 +105,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;
}
@@ -122,7 +122,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/lib/speed-insights.ts b/packages/integrations/vercel/src/lib/speed-insights.ts
deleted file mode 100644
index 033a705c5..000000000
--- a/packages/integrations/vercel/src/lib/speed-insights.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-export type VercelSpeedInsightsConfig = {
- enabled: boolean;
-};
-
-export function getSpeedInsightsViteConfig(enabled?: boolean) {
- if (enabled) {
- return {
- define: exposeEnv(['VERCEL_ANALYTICS_ID']),
- };
- }
-
- return {};
-}
-
-/**
- * While Vercel adds the `PUBLIC_` prefix for their `VERCEL_` env vars by default, some env vars
- * like `VERCEL_ANALYTICS_ID` aren't, so handle them here so that it works correctly in runtime.
- */
-export function exposeEnv(envs: string[]): Record<string, unknown> {
- const mapped: Record<string, unknown> = {};
-
- // biome-ignore lint/complexity/noForEach: <explanation>
- envs
- .filter((env) => process.env[env])
- .forEach((env) => {
- mapped[`import.meta.env.PUBLIC_${env}`] = JSON.stringify(process.env[env]);
- });
-
- return mapped;
-}
diff --git a/packages/integrations/vercel/src/serverless/adapter.ts b/packages/integrations/vercel/src/serverless/adapter.ts
index e50bd4190..86464f89b 100644
--- a/packages/integrations/vercel/src/serverless/adapter.ts
+++ b/packages/integrations/vercel/src/serverless/adapter.ts
@@ -1,608 +1,10 @@
-import { existsSync, readFileSync } from 'node:fs';
-import { basename } from 'node:path';
-import { pathToFileURL } from 'node:url';
-import { removeDir, writeJson } from '@astrojs/internal-helpers/fs';
-import type {
- AstroAdapter,
- AstroConfig,
- AstroIntegration,
- AstroIntegrationLogger,
- RouteData,
-} from 'astro';
-import { AstroError } from 'astro/errors';
-import glob from 'fast-glob';
-import {
- type DevImageService,
- type VercelImageConfig,
- getAstroImageConfig,
- getDefaultImageConfig,
-} from '../image/shared.js';
-import { copyDependenciesToFunction } from '../lib/nft.js';
-import { escapeRegex, getRedirects } from '../lib/redirects.js';
-import {
- type VercelSpeedInsightsConfig,
- getSpeedInsightsViteConfig,
-} from '../lib/speed-insights.js';
-import {
- type VercelWebAnalyticsConfig,
- getInjectableWebAnalyticsContent,
-} from '../lib/web-analytics.js';
-import { generateEdgeMiddleware } from './middleware.js';
-
-const PACKAGE_NAME = '@astrojs/vercel/serverless';
-
-/**
- * The edge function calls the node server at /_render,
- * with the original path as the value of this header.
- */
-export const ASTRO_PATH_HEADER = 'x-astro-path';
-export const ASTRO_PATH_PARAM = 'x_astro_path';
-
-/**
- * The edge function calls the node server at /_render,
- * with the locals serialized into this header.
- */
-export const ASTRO_LOCALS_HEADER = 'x-astro-locals';
-export const ASTRO_MIDDLEWARE_SECRET_HEADER = 'x-astro-middleware-secret';
-export const VERCEL_EDGE_MIDDLEWARE_FILE = 'vercel-edge-middleware';
-
-// Vercel routes the folder names to a path on the deployed website.
-// We attempt to avoid interfering by prefixing with an underscore.
-export const NODE_PATH = '_render';
-const MIDDLEWARE_PATH = '_middleware';
-
-// This isn't documented by vercel anywhere, but unlike serverless
-// and edge functions, isr functions are not passed the original path.
-// Instead, we have to use $0 to refer to the regex match from "src".
-const ISR_PATH = `/_isr?${ASTRO_PATH_PARAM}=$0`;
-
-// https://vercel.com/docs/concepts/functions/serverless-functions/runtimes/node-js#node.js-version
-const SUPPORTED_NODE_VERSIONS: Record<
- string,
- | { status: 'default' }
- | { status: 'beta' }
- | { status: 'retiring'; removal: Date | string; warnDate: Date }
- | { status: 'deprecated'; removal: Date }
-> = {
- 18: { status: 'retiring', removal: 'Early 2025', warnDate: new Date('October 1 2024') },
- 20: { status: 'default' },
-};
-
-function getAdapter({
- edgeMiddleware,
- functionPerRoute,
- middlewareSecret,
- skewProtection,
-}: {
- edgeMiddleware: boolean;
- functionPerRoute: boolean;
- middlewareSecret: string;
- skewProtection: boolean;
-}): AstroAdapter {
- return {
- name: PACKAGE_NAME,
- serverEntrypoint: `${PACKAGE_NAME}/entrypoint`,
- exports: ['default'],
- args: { middlewareSecret, skewProtection },
- adapterFeatures: {
- edgeMiddleware,
- functionPerRoute,
- },
- supportedAstroFeatures: {
- hybridOutput: 'stable',
- staticOutput: 'stable',
- serverOutput: 'stable',
- assets: {
- supportKind: 'stable',
- isSharpCompatible: true,
- isSquooshCompatible: true,
- },
- i18nDomains: 'experimental',
- envGetSecret: 'experimental',
- },
- };
-}
-
-export interface VercelServerlessConfig {
- /** Configuration for [Vercel Web Analytics](https://vercel.com/docs/concepts/analytics). */
- webAnalytics?: VercelWebAnalyticsConfig;
-
- /**
- * @deprecated This option lets you configure the legacy speed insights API which is now deprecated by Vercel.
- *
- * See [Vercel Speed Insights Quickstart](https://vercel.com/docs/speed-insights/quickstart) for instructions on how to use the library instead.
- *
- * https://vercel.com/docs/speed-insights/quickstart
- */
- speedInsights?: VercelSpeedInsightsConfig;
-
- /** Force files to be bundled with your function. This is helpful when you notice missing files. */
- includeFiles?: string[];
-
- /** Exclude any files from the bundling process that would otherwise be included. */
- excludeFiles?: string[];
-
- /** When enabled, an Image Service powered by the Vercel Image Optimization API will be automatically configured and used in production. In development, the image service specified by devImageService will be used instead. */
- imageService?: boolean;
-
- /** Configuration options for [Vercel’s Image Optimization API](https://vercel.com/docs/concepts/image-optimization). See [Vercel’s image configuration documentation](https://vercel.com/docs/build-output-api/v3/configuration#images) for a complete list of supported parameters. */
- imagesConfig?: VercelImageConfig;
-
- /** Allows you to configure which image service to use in development when imageService is enabled. */
- devImageService?: DevImageService;
-
- /** 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;
-
- /** Whether to cache on-demand rendered pages in the same way as static files. */
- isr?: boolean | VercelISRConfig;
- /**
- * It enables Vercel skew protection: https://vercel.com/docs/deployments/skew-protection
- */
- skewProtection?: boolean;
-}
-
-interface VercelISRConfig {
- /**
- * A secret random string that you create.
- * Its presence in the `__prerender_bypass` cookie will result in fresh responses being served, bypassing the cache. See Vercel’s documentation on [Draft Mode](https://vercel.com/docs/build-output-api/v3/features#draft-mode) for more information.
- * Its presence in the `x-prerender-revalidate` header will result in a fresh response which will then be cached for all future requests to be used. See Vercel’s documentation on [On-Demand Incremental Static Regeneration (ISR)](https://vercel.com/docs/build-output-api/v3/features#on-demand-incremental-static-regeneration-isr) for more information.
- *
- * @default `undefined`
- */
- bypassToken?: string;
-
- /**
- * Expiration time (in seconds) before the pages will be re-generated.
- *
- * Setting to `false` means that the page will stay cached as long as the current deployment is in production.
- *
- * @default `false`
- */
- expiration?: number | false;
-
- /**
- * Paths that will always be served by a serverless function instead of an ISR function.
- *
- * @default `[]`
- */
- exclude?: string[];
-}
-
-export default function vercelServerless({
- webAnalytics,
- speedInsights,
- includeFiles: _includeFiles = [],
- excludeFiles: _excludeFiles = [],
- imageService,
- imagesConfig,
- devImageService = 'sharp',
- functionPerRoute = false,
- edgeMiddleware = false,
- maxDuration,
- isr = false,
- skewProtection = false,
-}: VercelServerlessConfig = {}): AstroIntegration {
- if (maxDuration) {
- if (typeof maxDuration !== 'number') {
- // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
- throw new TypeError(`maxDuration must be a number`, { cause: maxDuration });
- }
- if (maxDuration <= 0) {
- // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
- throw new TypeError(`maxDuration must be a positive number`, { cause: maxDuration });
- }
- }
-
- let _config: AstroConfig;
- let _buildTempFolder: URL;
- let _serverEntry: string;
- let _entryPoints: Map<RouteData, URL>;
- let _middlewareEntryPoint: URL | undefined;
- // Extra files to be merged with `includeFiles` during build
- const extraFilesToInclude: URL[] = [];
- // Secret used to verify that the caller is the astro-generated edge middleware and not a third-party
- const middlewareSecret = crypto.randomUUID();
-
- return {
- name: PACKAGE_NAME,
- hooks: {
- 'astro:config:setup': async ({ command, config, updateConfig, injectScript, logger }) => {
- if (maxDuration && maxDuration > 900) {
- logger.warn(
- `maxDuration is set to ${maxDuration} seconds, which is longer than the maximum allowed duration of 900 seconds.`
- );
- logger.warn(
- // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
- `Please make sure that your plan allows for this duration. See https://vercel.com/docs/functions/serverless-functions/runtimes#maxduration for more information.`
- );
- }
-
- if (webAnalytics?.enabled) {
- injectScript(
- 'head-inline',
- await getInjectableWebAnalyticsContent({
- mode: command === 'dev' ? 'development' : 'production',
- })
- );
- }
- if (command === 'build' && speedInsights?.enabled) {
- injectScript('page', 'import "@astrojs/vercel/speed-insights"');
- }
-
- const vercelConfigPath = new URL('vercel.json', config.root);
- if (existsSync(vercelConfigPath)) {
- try {
- const vercelConfig = JSON.parse(readFileSync(vercelConfigPath, 'utf-8'));
- 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`
- );
- updateConfig({
- trailingSlash: 'ignore',
- });
- }
- } catch (_err) {
- logger.warn(`Your "vercel.json" config is not a valid json file.`);
- }
- }
-
- updateConfig({
- outDir: new URL('./.vercel/output/', config.root),
- build: {
- client: new URL('./.vercel/output/static/', config.root),
- server: new URL('./.vercel/output/_functions/', config.root),
- redirects: false,
- },
- vite: {
- ...getSpeedInsightsViteConfig(speedInsights?.enabled),
- ssr: {
- external: [
- '@vercel/nft',
- ...((await shouldExternalizeAstroEnvSetup()) ? ['astro/env/setup'] : []),
- ],
- },
- },
- ...getAstroImageConfig(
- imageService,
- imagesConfig,
- command,
- devImageService,
- config.image
- ),
- });
- },
- '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 })
- );
-
- _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(
- Array.from(entryPoints).filter(([routeData]) => !routeData.prerender)
- );
- _middlewareEntryPoint = middlewareEntryPoint;
- },
- 'astro:build:done': async ({ routes, logger }) => {
- // Merge any includes from `vite.assetsInclude
- if (_config.vite.assetsInclude) {
- const mergeGlobbedIncludes = (globPattern: unknown) => {
- if (typeof globPattern === 'string') {
- const entries = glob.sync(globPattern).map((p) => pathToFileURL(p));
- extraFilesToInclude.push(...entries);
- } else if (Array.isArray(globPattern)) {
- for (const pattern of globPattern) {
- mergeGlobbedIncludes(pattern);
- }
- }
- };
-
- mergeGlobbedIncludes(_config.vite.assetsInclude);
- }
-
- const routeDefinitions: Array<{
- src: string;
- dest: string;
- middlewarePath?: string;
- }> = [];
-
- const includeFiles = _includeFiles
- .map((file) => new URL(file, _config.root))
- .concat(extraFilesToInclude);
- const excludeFiles = _excludeFiles.map((file) => new URL(file, _config.root));
-
- const builder = new VercelBuilder(_config, excludeFiles, includeFiles, logger, maxDuration);
-
- // Multiple entrypoint support
- if (_entryPoints.size) {
- const getRouteFuncName = (route: RouteData) => route.component.replace('src/pages/', '');
-
- const getFallbackFuncName = (entryFile: URL) =>
- basename(entryFile.toString())
- .replace('entry.', '')
- .replace(/\.mjs$/, '');
-
- for (const [route, entryFile] of _entryPoints) {
- const func = route.component.startsWith('src/pages/')
- ? getRouteFuncName(route)
- : getFallbackFuncName(entryFile);
-
- await builder.buildServerlessFolder(entryFile, func, _config.root);
-
- routeDefinitions.push({
- src: route.pattern.source,
- dest: func,
- });
- }
- } 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 dest = _middlewareEntryPoint ? MIDDLEWARE_PATH : NODE_PATH;
- for (const route of isrConfig.exclude) {
- // 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) {
- const src = route.pattern.source;
- const dest = src.startsWith('^\\/_image') ? NODE_PATH : ISR_PATH;
- if (!route.prerender) 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) {
- if (!route.prerender) routeDefinitions.push({ src: route.pattern.source, dest });
- }
- }
- }
- if (_middlewareEntryPoint) {
- await builder.buildMiddlewareFolder(
- _middlewareEntryPoint,
- MIDDLEWARE_PATH,
- middlewareSecret
- );
- }
- const fourOhFourRoute = routes.find((route) => route.pathname === '/404');
- // Output configuration
- // https://vercel.com/docs/build-output-api/v3#build-output-configuration
- // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
- await writeJson(new URL(`./config.json`, _config.outDir), {
- version: 3,
- routes: [
- ...getRedirects(routes, _config),
- {
- src: `^/${_config.build.assets}/(.*)$`,
- headers: { 'cache-control': 'public, max-age=31536000, immutable' },
- continue: true,
- },
- { handle: 'filesystem' },
- ...routeDefinitions,
- ...(fourOhFourRoute
- ? [
- {
- 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),
- }
- : {}),
- });
-
- // Remove temporary folder
- await removeDir(_buildTempFolder);
- },
- },
- };
-}
-
-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 = {};
-
- constructor(
- readonly config: AstroConfig,
- readonly excludeFiles: URL[],
- readonly includeFiles: URL[],
- 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;
- // .vercel/output/functions/<name>.func/
- const functionFolder = new URL(`./functions/${functionName}.func/`, config.outDir);
- const packageJson = new URL(`./functions/${functionName}.func/package.json`, config.outDir);
- const vcConfig = new URL(`./functions/${functionName}.func/.vc-config.json`, config.outDir);
-
- // Copy necessary files (e.g. node_modules/)
- const { handler } = await copyDependenciesToFunction(
- {
- entry,
- outDir: functionFolder,
- includeFiles,
- excludeFiles,
- logger,
- root,
- },
- NTF_CACHE
- );
-
- // Enable ESM
- // https://aws.amazon.com/blogs/compute/using-node-js-es-modules-and-top-level-await-in-aws-lambda/
- await writeJson(packageJson, { type: 'module' });
-
- // Serverless function config
- // https://vercel.com/docs/build-output-api/v3#vercel-primitives/serverless-functions/configuration
- await writeJson(vcConfig, {
- runtime,
- handler: handler.replaceAll('\\', '/'),
- launcherType: 'Nodejs',
- maxDuration,
- supportsResponseStreaming: true,
- });
- }
-
- async buildISRFolder(entry: URL, functionName: string, isr: VercelISRConfig, root: URL) {
- await this.buildServerlessFolder(entry, functionName, root);
- const prerenderConfig = new URL(
- `./functions/${functionName}.prerender-config.json`,
- this.config.outDir
- );
- // https://vercel.com/docs/build-output-api/v3/primitives#prerender-configuration-file
- await writeJson(prerenderConfig, {
- expiration: isr.expiration ?? false,
- bypassToken: isr.bypassToken,
- allowQuery: [ASTRO_PATH_PARAM],
- passQuery: true,
- });
- }
-
- async buildMiddlewareFolder(entry: URL, functionName: string, middlewareSecret: string) {
- const functionFolder = new URL(`./functions/${functionName}.func/`, this.config.outDir);
-
- await generateEdgeMiddleware(
- entry,
- this.config.root,
- new URL(VERCEL_EDGE_MIDDLEWARE_FILE, this.config.srcDir),
- new URL('./middleware.mjs', functionFolder),
- middlewareSecret,
- this.logger
- );
-
- // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
- await writeJson(new URL(`./.vc-config.json`, functionFolder), {
- runtime: 'edge',
- entrypoint: 'middleware.mjs',
- });
- }
-}
-
-function getRuntime(process: NodeJS.Process, logger: AstroIntegrationLogger): Runtime {
- const version = process.version.slice(1); // 'v18.19.0' --> '18.19.0'
- const major = version.split('.')[0]; // '18.19.0' --> '18'
- const support = SUPPORTED_NODE_VERSIONS[major];
- if (support === undefined) {
- logger.warn(
- // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
- // biome-ignore lint/style/useTemplate: <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`
- );
- return 'nodejs18.x';
- }
- if (support.status === 'default') {
- return `nodejs${major}.x`;
- }
- if (support.status === 'retiring') {
- if (support.warnDate && new Date() >= support.warnDate) {
- logger.warn(
- `Your project is being built for Node.js ${major} as the runtime, which is retiring by ${support.removal}.`
- );
- }
- return `nodejs${major}.x`;
- }
- if (support.status === 'beta') {
- logger.warn(
- `Your project is being built for Node.js ${major} as the runtime, which is currently in beta for Vercel Serverless Functions.`
- );
- return `nodejs${major}.x`;
- }
- if (support.status === 'deprecated') {
- const removeDate = new Intl.DateTimeFormat(undefined, { dateStyle: 'long' }).format(
- support.removal
- );
- logger.warn(
- // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
- // biome-ignore lint/style/useTemplate: <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`
- );
- return `nodejs${major}.x`;
- }
- return 'nodejs18.x';
+import type { AstroIntegration } from 'astro';
+import type { VercelServerlessConfig } from '../index.js';
+import vercelIntegration from '../index.js';
+
+export default function serverless(config: VercelServerlessConfig): AstroIntegration {
+ console.warn(
+ 'The "@astrojs/vercel/serverless" import is deprecated and will be removed in a future release. Please import from "@astrojs/vercel" instead.'
+ );
+ return vercelIntegration(config);
}
diff --git a/packages/integrations/vercel/src/serverless/entrypoint.ts b/packages/integrations/vercel/src/serverless/entrypoint.ts
index 31f78bb8a..f1f1f256c 100644
--- a/packages/integrations/vercel/src/serverless/entrypoint.ts
+++ b/packages/integrations/vercel/src/serverless/entrypoint.ts
@@ -1,21 +1,17 @@
+// 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,
ASTRO_PATH_HEADER,
ASTRO_PATH_PARAM,
-} from './adapter.js';
+} from '../index.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..6663731b7 100644
--- a/packages/integrations/vercel/src/serverless/middleware.ts
+++ b/packages/integrations/vercel/src/serverless/middleware.ts
@@ -7,7 +7,7 @@ import {
ASTRO_MIDDLEWARE_SECRET_HEADER,
ASTRO_PATH_HEADER,
NODE_PATH,
-} from './adapter.js';
+} from '../index.js';
/**
* It generates the Vercel Edge Middleware file.
@@ -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/speed-insights.ts b/packages/integrations/vercel/src/speed-insights.ts
deleted file mode 100644
index 84aadfc7b..000000000
--- a/packages/integrations/vercel/src/speed-insights.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-import type { Metric } from 'web-vitals';
-import { onCLS, onFCP, onFID, onLCP, onTTFB } from 'web-vitals';
-
-const SPEED_INSIGHTS_INTAKE = 'https://vitals.vercel-analytics.com/v1/vitals';
-
-type Options = { path: string; analyticsId: string };
-
-const getConnectionSpeed = () => {
- return 'connection' in navigator &&
- // biome-ignore lint/complexity/useLiteralKeys: <explanation>
- navigator['connection'] &&
- // biome-ignore lint/complexity/useLiteralKeys: <explanation>
- 'effectiveType' in (navigator['connection'] as unknown as { effectiveType: string })
- ? // biome-ignore lint/complexity/useLiteralKeys: <explanation>
- (navigator['connection'] as unknown as { effectiveType: string })['effectiveType']
- : '';
-};
-
-const sendToSpeedInsights = (metric: Metric, options: Options) => {
- const body = {
- dsn: options.analyticsId,
- id: metric.id,
- page: options.path,
- href: location.href,
- event_name: metric.name,
- value: metric.value.toString(),
- speed: getConnectionSpeed(),
- };
- const blob = new Blob([new URLSearchParams(body).toString()], {
- type: 'application/x-www-form-urlencoded',
- });
- if (navigator.sendBeacon) {
- navigator.sendBeacon(SPEED_INSIGHTS_INTAKE, blob);
- } else
- fetch(SPEED_INSIGHTS_INTAKE, {
- body: blob,
- method: 'POST',
- credentials: 'omit',
- keepalive: true,
- });
-};
-
-function collectWebVitals() {
- const analyticsId = (import.meta as any).env.PUBLIC_VERCEL_ANALYTICS_ID;
-
- if (!analyticsId) {
- console.error('[Speed Insights] VERCEL_ANALYTICS_ID not found');
- return;
- }
-
- const options: Options = { path: window.location.pathname, analyticsId };
-
- try {
- onFID((metric) => sendToSpeedInsights(metric, options));
- onTTFB((metric) => sendToSpeedInsights(metric, options));
- onLCP((metric) => sendToSpeedInsights(metric, options));
- onCLS((metric) => sendToSpeedInsights(metric, options));
- onFCP((metric) => sendToSpeedInsights(metric, options));
- } catch (err) {
- console.error('[Speed Insights]', err);
- }
-}
-
-const mode = (import.meta as any).env.MODE as 'development' | 'production';
-
-if (mode === 'production') {
- collectWebVitals();
-}
diff --git a/packages/integrations/vercel/src/static/adapter.ts b/packages/integrations/vercel/src/static/adapter.ts
index 4a5bdf966..348994902 100644
--- a/packages/integrations/vercel/src/static/adapter.ts
+++ b/packages/integrations/vercel/src/static/adapter.ts
@@ -1,159 +1,10 @@
-import type { AstroAdapter, AstroConfig, AstroIntegration } from 'astro';
-
-import { emptyDir, writeJson } from '@astrojs/internal-helpers/fs';
-import {
- type DevImageService,
- type VercelImageConfig,
- getAstroImageConfig,
- getDefaultImageConfig,
-} from '../image/shared.js';
-import { isServerLikeOutput } from '../lib/prerender.js';
-import { getRedirects } from '../lib/redirects.js';
-import {
- type VercelSpeedInsightsConfig,
- getSpeedInsightsViteConfig,
-} from '../lib/speed-insights.js';
-import {
- type VercelWebAnalyticsConfig,
- getInjectableWebAnalyticsContent,
-} from '../lib/web-analytics.js';
-
-const PACKAGE_NAME = '@astrojs/vercel/static';
-
-function getAdapter(): AstroAdapter {
- return {
- name: PACKAGE_NAME,
- supportedAstroFeatures: {
- assets: {
- supportKind: 'stable',
- isSquooshCompatible: true,
- isSharpCompatible: true,
- },
- staticOutput: 'stable',
- serverOutput: 'unsupported',
- hybridOutput: 'unsupported',
- envGetSecret: 'unsupported',
- },
- adapterFeatures: {
- edgeMiddleware: false,
- functionPerRoute: false,
- },
- };
-}
-
-export interface VercelStaticConfig {
- webAnalytics?: VercelWebAnalyticsConfig;
- /**
- * @deprecated This option lets you configure the legacy speed insights API which is now deprecated by Vercel.
- *
- * See [Vercel Speed Insights Quickstart](https://vercel.com/docs/speed-insights/quickstart) for instructions on how to use the library instead.
- *
- * https://vercel.com/docs/speed-insights/quickstart
- */
- speedInsights?: VercelSpeedInsightsConfig;
- imageService?: boolean;
- imagesConfig?: VercelImageConfig;
- devImageService?: DevImageService;
-}
-
-export default function vercelStatic({
- webAnalytics,
- speedInsights,
- imageService,
- imagesConfig,
- devImageService = 'sharp',
-}: VercelStaticConfig = {}): AstroIntegration {
- let _config: AstroConfig;
-
- return {
- name: '@astrojs/vercel',
- hooks: {
- 'astro:config:setup': async ({ command, config, injectScript, updateConfig }) => {
- if (webAnalytics?.enabled) {
- injectScript(
- 'head-inline',
- await getInjectableWebAnalyticsContent({
- mode: command === 'dev' ? 'development' : 'production',
- })
- );
- }
- if (command === 'build' && speedInsights?.enabled) {
- injectScript('page', 'import "@astrojs/vercel/speed-insights"');
- }
- const outDir = new URL('./.vercel/output/static/', config.root);
- updateConfig({
- outDir,
- build: {
- format: 'directory',
- redirects: false,
- },
- vite: {
- ...getSpeedInsightsViteConfig(speedInsights?.enabled),
- },
- ...getAstroImageConfig(
- imageService,
- imagesConfig,
- command,
- devImageService,
- config.image
- ),
- });
- },
- '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.
- // This is because, when building to static, outDir = .vercel/output/static/,
- // so .vercel/output itself won't get cleaned.
- await emptyDir(new URL('./.vercel/output/', _config.root));
- },
- 'astro:build:done': async ({ routes }) => {
- // Output configuration
- // https://vercel.com/docs/build-output-api/v3#build-output-configuration
- await writeJson(new URL('./.vercel/output/config.json', _config.root), {
- version: 3,
- routes: [
- ...getRedirects(routes, _config),
- {
- src: `^/${_config.build.assets}/(.*)$`,
- headers: { 'cache-control': 'public, max-age=31536000, immutable' },
- continue: true,
- },
- { handle: 'filesystem' },
- ...(routes.find((route) => route.pathname === '/404')
- ? [
- {
- // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
- src: `/.*`,
- // biome-ignore lint/style/noUnusedTemplateLiteral: <explanation>
- dest: `/404.html`,
- status: 404,
- },
- ]
- : []),
- ],
- ...(imageService || imagesConfig
- ? {
- images: imagesConfig
- ? {
- ...imagesConfig,
- domains: [...imagesConfig.domains, ..._config.image.domains],
- remotePatterns: [
- ...(imagesConfig.remotePatterns ?? []),
- ..._config.image.remotePatterns,
- ],
- }
- : getDefaultImageConfig(_config.image),
- }
- : {}),
- });
- },
- },
- };
+import type { AstroIntegration } from 'astro';
+import type { VercelServerlessConfig } from '../index.js';
+import vercelIntegration from '../index.js';
+
+export default function staticAdapter(config: VercelServerlessConfig): AstroIntegration {
+ console.warn(
+ 'The "@astrojs/vercel/static" import is deprecated and will be removed in a future release. Please import from "@astrojs/vercel" instead.'
+ );
+ return vercelIntegration(config);
}
diff --git a/packages/integrations/vercel/test/fixtures/basic/astro.config.mjs b/packages/integrations/vercel/test/fixtures/basic/astro.config.mjs
index 942139f3d..2fba43b6e 100644
--- a/packages/integrations/vercel/test/fixtures/basic/astro.config.mjs
+++ b/packages/integrations/vercel/test/fixtures/basic/astro.config.mjs
@@ -2,7 +2,5 @@ import vercel from '@astrojs/vercel/serverless';
import { defineConfig } from 'astro/config';
export default defineConfig({
- adapter: vercel({
- functionPerRoute: true
- })
+ adapter: vercel({})
});
diff --git a/packages/integrations/vercel/test/fixtures/basic/package.json b/packages/integrations/vercel/test/fixtures/basic/package.json
index b6a96bb66..859459d0c 100644
--- a/packages/integrations/vercel/test/fixtures/basic/package.json
+++ b/packages/integrations/vercel/test/fixtures/basic/package.json
@@ -4,6 +4,6 @@
"private": true,
"dependencies": {
"@astrojs/vercel": "workspace:*",
- "astro": "^4.16.8"
+ "astro": "^5.0.0-alpha.8"
}
}
diff --git a/packages/integrations/vercel/test/fixtures/functionPerRoute/astro.config.mjs b/packages/integrations/vercel/test/fixtures/functionPerRoute/astro.config.mjs
deleted file mode 100644
index eb2c7699e..000000000
--- a/packages/integrations/vercel/test/fixtures/functionPerRoute/astro.config.mjs
+++ /dev/null
@@ -1,9 +0,0 @@
-import vercel from '@astrojs/vercel/serverless';
-import { defineConfig } from 'astro/config';
-
-export default defineConfig({
- adapter: vercel({
- functionPerRoute: true
- }),
- output: "server"
-});
diff --git a/packages/integrations/vercel/test/fixtures/functionPerRoute/package.json b/packages/integrations/vercel/test/fixtures/functionPerRoute/package.json
deleted file mode 100644
index 158427780..000000000
--- a/packages/integrations/vercel/test/fixtures/functionPerRoute/package.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "name": "@test/astro-vercel-function-per-route",
- "version": "0.0.0",
- "private": true,
- "dependencies": {
- "@astrojs/vercel": "workspace:*",
- "astro": "^4.16.8"
- }
-}
diff --git a/packages/integrations/vercel/test/fixtures/functionPerRoute/src/pages/one.astro b/packages/integrations/vercel/test/fixtures/functionPerRoute/src/pages/one.astro
deleted file mode 100644
index 0c7fb90a7..000000000
--- a/packages/integrations/vercel/test/fixtures/functionPerRoute/src/pages/one.astro
+++ /dev/null
@@ -1,8 +0,0 @@
-<html>
- <head>
- <title>One</title>
- </head>
- <body>
- <h1>One</h1>
- </body>
-</html>
diff --git a/packages/integrations/vercel/test/fixtures/functionPerRoute/src/pages/prerender.astro b/packages/integrations/vercel/test/fixtures/functionPerRoute/src/pages/prerender.astro
deleted file mode 100644
index c61b83a97..000000000
--- a/packages/integrations/vercel/test/fixtures/functionPerRoute/src/pages/prerender.astro
+++ /dev/null
@@ -1,12 +0,0 @@
----
-export const prerender = true;
----
-
-<html>
- <head>
- <title>Prerendered Page</title>
- </head>
- <body>
- <h1>Prerendered Page</h1>
- </body>
-</html>
diff --git a/packages/integrations/vercel/test/fixtures/functionPerRoute/src/pages/two.astro b/packages/integrations/vercel/test/fixtures/functionPerRoute/src/pages/two.astro
deleted file mode 100644
index e7ba9910e..000000000
--- a/packages/integrations/vercel/test/fixtures/functionPerRoute/src/pages/two.astro
+++ /dev/null
@@ -1,8 +0,0 @@
-<html>
- <head>
- <title>Two</title>
- </head>
- <body>
- <h1>Two</h1>
- </body>
-</html>
diff --git a/packages/integrations/vercel/test/fixtures/image/package.json b/packages/integrations/vercel/test/fixtures/image/package.json
index 0f7fdbe14..28b7bccd2 100644
--- a/packages/integrations/vercel/test/fixtures/image/package.json
+++ b/packages/integrations/vercel/test/fixtures/image/package.json
@@ -7,6 +7,6 @@
},
"dependencies": {
"@astrojs/vercel": "workspace:*",
- "astro": "^4.16.8"
+ "astro": "^5.0.0-alpha.8"
}
}
diff --git a/packages/integrations/vercel/test/fixtures/isr/package.json b/packages/integrations/vercel/test/fixtures/isr/package.json
index 9bddeeab9..7d47893c8 100644
--- a/packages/integrations/vercel/test/fixtures/isr/package.json
+++ b/packages/integrations/vercel/test/fixtures/isr/package.json
@@ -4,6 +4,6 @@
"private": true,
"dependencies": {
"@astrojs/vercel": "workspace:*",
- "astro": "^4.16.8"
+ "astro": "^5.0.0-alpha.8"
}
}
diff --git a/packages/integrations/vercel/test/fixtures/max-duration/package.json b/packages/integrations/vercel/test/fixtures/max-duration/package.json
index 55b27c01c..ea3d5523b 100644
--- a/packages/integrations/vercel/test/fixtures/max-duration/package.json
+++ b/packages/integrations/vercel/test/fixtures/max-duration/package.json
@@ -4,6 +4,6 @@
"private": true,
"dependencies": {
"@astrojs/vercel": "workspace:*",
- "astro": "^4.16.8"
+ "astro": "^5.0.0-alpha.8"
}
}
diff --git a/packages/integrations/vercel/test/fixtures/middleware-with-edge-file/package.json b/packages/integrations/vercel/test/fixtures/middleware-with-edge-file/package.json
index d5a18b199..d40a35aac 100644
--- a/packages/integrations/vercel/test/fixtures/middleware-with-edge-file/package.json
+++ b/packages/integrations/vercel/test/fixtures/middleware-with-edge-file/package.json
@@ -4,6 +4,6 @@
"private": true,
"dependencies": {
"@astrojs/vercel": "workspace:*",
- "astro": "^4.16.8"
+ "astro": "^5.0.0-alpha.8"
}
}
diff --git a/packages/integrations/vercel/test/fixtures/middleware-without-edge-file/package.json b/packages/integrations/vercel/test/fixtures/middleware-without-edge-file/package.json
index 60ca05cbf..24e41eb3d 100644
--- a/packages/integrations/vercel/test/fixtures/middleware-without-edge-file/package.json
+++ b/packages/integrations/vercel/test/fixtures/middleware-without-edge-file/package.json
@@ -4,6 +4,6 @@
"private": true,
"dependencies": {
"@astrojs/vercel": "workspace:*",
- "astro": "^4.16.8"
+ "astro": "^5.0.0-alpha.8"
}
}
diff --git a/packages/integrations/vercel/test/fixtures/no-output/package.json b/packages/integrations/vercel/test/fixtures/no-output/package.json
index 1dd0579d5..ee2bc9cd8 100644
--- a/packages/integrations/vercel/test/fixtures/no-output/package.json
+++ b/packages/integrations/vercel/test/fixtures/no-output/package.json
@@ -4,6 +4,6 @@
"private": true,
"dependencies": {
"@astrojs/vercel": "workspace:*",
- "astro": "^4.16.8"
+ "astro": "^5.0.0-alpha.8"
}
}
diff --git a/packages/integrations/vercel/test/fixtures/prerendered-error-pages/package.json b/packages/integrations/vercel/test/fixtures/prerendered-error-pages/package.json
index e1ab6fe58..e503f9b42 100644
--- a/packages/integrations/vercel/test/fixtures/prerendered-error-pages/package.json
+++ b/packages/integrations/vercel/test/fixtures/prerendered-error-pages/package.json
@@ -4,6 +4,6 @@
"private": true,
"dependencies": {
"@astrojs/vercel": "workspace:*",
- "astro": "^4.16.8"
+ "astro": "^5.0.0-alpha.8"
}
}
diff --git a/packages/integrations/vercel/test/fixtures/redirects-serverless/astro.config.mjs b/packages/integrations/vercel/test/fixtures/redirects-serverless/astro.config.mjs
index 1071b7e67..04e8ffa9d 100644
--- a/packages/integrations/vercel/test/fixtures/redirects-serverless/astro.config.mjs
+++ b/packages/integrations/vercel/test/fixtures/redirects-serverless/astro.config.mjs
@@ -2,6 +2,6 @@ import vercel from '@astrojs/vercel/serverless';
import { defineConfig } from 'astro/config';
export default defineConfig({
- output: 'hybrid',
+ output: 'static',
adapter: vercel(),
});
diff --git a/packages/integrations/vercel/test/fixtures/redirects-serverless/package.json b/packages/integrations/vercel/test/fixtures/redirects-serverless/package.json
index f8e392f4c..64c719061 100644
--- a/packages/integrations/vercel/test/fixtures/redirects-serverless/package.json
+++ b/packages/integrations/vercel/test/fixtures/redirects-serverless/package.json
@@ -4,6 +4,6 @@
"private": true,
"dependencies": {
"@astrojs/vercel": "workspace:*",
- "astro": "^4.16.8"
+ "astro": "^5.0.0-alpha.8"
}
}
diff --git a/packages/integrations/vercel/test/fixtures/redirects/package.json b/packages/integrations/vercel/test/fixtures/redirects/package.json
index 0d4da60fa..0f544f40c 100644
--- a/packages/integrations/vercel/test/fixtures/redirects/package.json
+++ b/packages/integrations/vercel/test/fixtures/redirects/package.json
@@ -4,6 +4,6 @@
"private": true,
"dependencies": {
"@astrojs/vercel": "workspace:*",
- "astro": "^4.16.8"
+ "astro": "^5.0.0-alpha.8"
}
}
diff --git a/packages/integrations/vercel/test/fixtures/server-islands/astro.config.mjs b/packages/integrations/vercel/test/fixtures/server-islands/astro.config.mjs
index 534197429..94f52a3d6 100644
--- a/packages/integrations/vercel/test/fixtures/server-islands/astro.config.mjs
+++ b/packages/integrations/vercel/test/fixtures/server-islands/astro.config.mjs
@@ -3,8 +3,5 @@ import { defineConfig } from 'astro/config';
export default defineConfig({
output: "server",
- adapter: vercel(),
- experimental: {
- serverIslands: true,
- }
+ adapter: vercel()
});
diff --git a/packages/integrations/vercel/test/fixtures/server-islands/package.json b/packages/integrations/vercel/test/fixtures/server-islands/package.json
index 425fe8a86..7828ee335 100644
--- a/packages/integrations/vercel/test/fixtures/server-islands/package.json
+++ b/packages/integrations/vercel/test/fixtures/server-islands/package.json
@@ -4,6 +4,6 @@
"private": true,
"dependencies": {
"@astrojs/vercel": "workspace:*",
- "astro": "^4.16.8"
+ "astro": "^5.0.0-alpha.8"
}
}
diff --git a/packages/integrations/vercel/test/fixtures/serverless-prerender/package.json b/packages/integrations/vercel/test/fixtures/serverless-prerender/package.json
index 6d472b842..fbfdcb86e 100644
--- a/packages/integrations/vercel/test/fixtures/serverless-prerender/package.json
+++ b/packages/integrations/vercel/test/fixtures/serverless-prerender/package.json
@@ -4,6 +4,6 @@
"private": true,
"dependencies": {
"@astrojs/vercel": "workspace:*",
- "astro": "^4.16.8"
+ "astro": "^5.0.0-alpha.8"
}
}
diff --git a/packages/integrations/vercel/test/fixtures/serverless-prerender/src/pages/index.astro b/packages/integrations/vercel/test/fixtures/serverless-prerender/src/pages/index.astro
index b6b833e53..2a976957b 100644
--- a/packages/integrations/vercel/test/fixtures/serverless-prerender/src/pages/index.astro
+++ b/packages/integrations/vercel/test/fixtures/serverless-prerender/src/pages/index.astro
@@ -1,5 +1,5 @@
---
-export const prerender = import.meta.env.PRERENDER;
+export const prerender = true;
---
<html>
diff --git a/packages/integrations/vercel/test/fixtures/serverless-with-dynamic-routes/astro.config.mjs b/packages/integrations/vercel/test/fixtures/serverless-with-dynamic-routes/astro.config.mjs
index 3132bfc53..4d8f76288 100644
--- a/packages/integrations/vercel/test/fixtures/serverless-with-dynamic-routes/astro.config.mjs
+++ b/packages/integrations/vercel/test/fixtures/serverless-with-dynamic-routes/astro.config.mjs
@@ -5,7 +5,6 @@ export default defineConfig({
adapter: vercel({
// Pass some value to make sure it doesn't error out
includeFiles: ['included.js'],
- functionPerRoute: true,
}),
output: 'server'
});
diff --git a/packages/integrations/vercel/test/fixtures/serverless-with-dynamic-routes/package.json b/packages/integrations/vercel/test/fixtures/serverless-with-dynamic-routes/package.json
index eb5584508..22988666c 100644
--- a/packages/integrations/vercel/test/fixtures/serverless-with-dynamic-routes/package.json
+++ b/packages/integrations/vercel/test/fixtures/serverless-with-dynamic-routes/package.json
@@ -4,6 +4,6 @@
"private": true,
"dependencies": {
"@astrojs/vercel": "workspace:*",
- "astro": "^4.16.8"
+ "astro": "^5.0.0-beta.5"
}
}
diff --git a/packages/integrations/vercel/test/fixtures/serverless-with-dynamic-routes/src/pages/index.astro b/packages/integrations/vercel/test/fixtures/serverless-with-dynamic-routes/src/pages/index.astro
index b6b833e53..2a976957b 100644
--- a/packages/integrations/vercel/test/fixtures/serverless-with-dynamic-routes/src/pages/index.astro
+++ b/packages/integrations/vercel/test/fixtures/serverless-with-dynamic-routes/src/pages/index.astro
@@ -1,5 +1,5 @@
---
-export const prerender = import.meta.env.PRERENDER;
+export const prerender = true;
---
<html>
diff --git a/packages/integrations/vercel/test/fixtures/static-assets/package.json b/packages/integrations/vercel/test/fixtures/static-assets/package.json
index 1984ca851..693155026 100644
--- a/packages/integrations/vercel/test/fixtures/static-assets/package.json
+++ b/packages/integrations/vercel/test/fixtures/static-assets/package.json
@@ -4,6 +4,6 @@
"private": true,
"dependencies": {
"@astrojs/vercel": "workspace:*",
- "astro": "^4.16.8"
+ "astro": "^5.0.0-alpha.8"
}
}
diff --git a/packages/integrations/vercel/test/fixtures/static/package.json b/packages/integrations/vercel/test/fixtures/static/package.json
index a8e3bb197..440627b64 100644
--- a/packages/integrations/vercel/test/fixtures/static/package.json
+++ b/packages/integrations/vercel/test/fixtures/static/package.json
@@ -4,6 +4,6 @@
"private": true,
"dependencies": {
"@astrojs/vercel": "workspace:*",
- "astro": "^4.16.8"
+ "astro": "^5.0.0-alpha.8"
}
}
diff --git a/packages/integrations/vercel/test/fixtures/streaming/package.json b/packages/integrations/vercel/test/fixtures/streaming/package.json
index 6f13191da..13bbdd7cb 100644
--- a/packages/integrations/vercel/test/fixtures/streaming/package.json
+++ b/packages/integrations/vercel/test/fixtures/streaming/package.json
@@ -4,6 +4,6 @@
"private": true,
"dependencies": {
"@astrojs/vercel": "workspace:*",
- "astro": "^4.16.8"
+ "astro": "^5.0.0-alpha.8"
}
}
diff --git a/packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-server/astro.config.mjs b/packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-server/astro.config.mjs
deleted file mode 100644
index 794734f78..000000000
--- a/packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-server/astro.config.mjs
+++ /dev/null
@@ -1,10 +0,0 @@
-import vercel from '@astrojs/vercel/serverless';
-import { defineConfig } from 'astro/config';
-
-export default defineConfig({
- adapter: vercel({
- speedInsights: {
- enabled: true
- }
- })
-});
diff --git a/packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-server/package.json b/packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-server/package.json
deleted file mode 100644
index 72201744c..000000000
--- a/packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-server/package.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "name": "@test/astro-vercel-with-speed-insights-enabled-output-as-server",
- "version": "0.0.0",
- "private": true,
- "dependencies": {
- "@astrojs/vercel": "workspace:*",
- "astro": "^4.16.8"
- }
-}
diff --git a/packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-server/src/pages/one.astro b/packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-server/src/pages/one.astro
deleted file mode 100644
index 0c7fb90a7..000000000
--- a/packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-server/src/pages/one.astro
+++ /dev/null
@@ -1,8 +0,0 @@
-<html>
- <head>
- <title>One</title>
- </head>
- <body>
- <h1>One</h1>
- </body>
-</html>
diff --git a/packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-server/src/pages/two.astro b/packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-server/src/pages/two.astro
deleted file mode 100644
index e7ba9910e..000000000
--- a/packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-server/src/pages/two.astro
+++ /dev/null
@@ -1,8 +0,0 @@
-<html>
- <head>
- <title>Two</title>
- </head>
- <body>
- <h1>Two</h1>
- </body>
-</html>
diff --git a/packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-static/astro.config.mjs b/packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-static/astro.config.mjs
deleted file mode 100644
index 589aa3662..000000000
--- a/packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-static/astro.config.mjs
+++ /dev/null
@@ -1,10 +0,0 @@
-import vercel from '@astrojs/vercel/static';
-import { defineConfig } from 'astro/config';
-
-export default defineConfig({
- adapter: vercel({
- speedInsights: {
- enabled: true
- }
- })
-});
diff --git a/packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-static/package.json b/packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-static/package.json
deleted file mode 100644
index 703a52301..000000000
--- a/packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-static/package.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "name": "@test/astro-vercel-with-speed-insights-enabled-output-as-static",
- "version": "0.0.0",
- "private": true,
- "dependencies": {
- "@astrojs/vercel": "workspace:*",
- "astro": "^4.16.8"
- }
-}
diff --git a/packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-static/src/pages/one.astro b/packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-static/src/pages/one.astro
deleted file mode 100644
index 0c7fb90a7..000000000
--- a/packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-static/src/pages/one.astro
+++ /dev/null
@@ -1,8 +0,0 @@
-<html>
- <head>
- <title>One</title>
- </head>
- <body>
- <h1>One</h1>
- </body>
-</html>
diff --git a/packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-static/src/pages/two.astro b/packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-static/src/pages/two.astro
deleted file mode 100644
index e7ba9910e..000000000
--- a/packages/integrations/vercel/test/fixtures/with-speed-insights-enabled/output-as-static/src/pages/two.astro
+++ /dev/null
@@ -1,8 +0,0 @@
-<html>
- <head>
- <title>Two</title>
- </head>
- <body>
- <h1>Two</h1>
- </body>
-</html>
diff --git a/packages/integrations/vercel/test/fixtures/with-web-analytics-enabled/output-as-static/package.json b/packages/integrations/vercel/test/fixtures/with-web-analytics-enabled/output-as-static/package.json
index 4ef0f48ee..61d3de1f0 100644
--- a/packages/integrations/vercel/test/fixtures/with-web-analytics-enabled/output-as-static/package.json
+++ b/packages/integrations/vercel/test/fixtures/with-web-analytics-enabled/output-as-static/package.json
@@ -4,6 +4,6 @@
"private": true,
"dependencies": {
"@astrojs/vercel": "workspace:*",
- "astro": "^4.16.8"
+ "astro": "^5.0.0-alpha.8"
}
}
diff --git a/packages/integrations/vercel/test/hosted/hosted-astro-project/package.json b/packages/integrations/vercel/test/hosted/hosted-astro-project/package.json
index 183ff95e9..914b2f66d 100644
--- a/packages/integrations/vercel/test/hosted/hosted-astro-project/package.json
+++ b/packages/integrations/vercel/test/hosted/hosted-astro-project/package.json
@@ -7,6 +7,6 @@
},
"dependencies": {
"@astrojs/vercel": "workspace:*",
- "astro": "^4.16.8"
+ "astro": "^5.0.0-alpha.8"
}
}
diff --git a/packages/integrations/vercel/test/isr.test.js b/packages/integrations/vercel/test/isr.test.js
index 668fcffe3..e3af25d66 100644
--- a/packages/integrations/vercel/test/isr.test.js
+++ b/packages/integrations/vercel/test/isr.test.js
@@ -42,10 +42,6 @@ describe('ISR', () => {
dest: '_render',
},
{
- src: '^\\/_image$',
- dest: '_render',
- },
- {
src: '^\\/excluded\\/([^/]+?)\\/?$',
dest: '/_isr?x_astro_path=$0',
},
@@ -61,6 +57,10 @@ describe('ISR', () => {
src: '^\\/two\\/?$',
dest: '/_isr?x_astro_path=$0',
},
+ {
+ src: '^\\/_image\\/?$',
+ dest: '_render',
+ },
]);
});
});
diff --git a/packages/integrations/vercel/test/no-output.test.js b/packages/integrations/vercel/test/no-output.test.js
deleted file mode 100644
index 34709804d..000000000
--- a/packages/integrations/vercel/test/no-output.test.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import assert from 'node:assert/strict';
-import { before, describe, it } from 'node:test';
-import { loadFixture } from './test-utils.js';
-
-describe('Missing output config', () => {
- /** @type {import('./test-utils').Fixture} */
- let fixture;
-
- before(async () => {
- fixture = await loadFixture({
- root: './fixtures/no-output/',
- });
- });
-
- it('throws during the build', async () => {
- let error = undefined;
- try {
- await fixture.build();
- } catch (err) {
- error = err;
- }
- assert.notEqual(error, undefined);
- assert.match(error.message, /output: "server"/);
- });
-});
diff --git a/packages/integrations/vercel/test/prerendered-error-pages.test.js b/packages/integrations/vercel/test/prerendered-error-pages.test.js
index 5812c3364..8e8559da0 100644
--- a/packages/integrations/vercel/test/prerendered-error-pages.test.js
+++ b/packages/integrations/vercel/test/prerendered-error-pages.test.js
@@ -15,10 +15,13 @@ describe('prerendered error pages routing', () => {
it('falls back to 404.html', async () => {
const deploymentConfig = JSON.parse(await fixture.readFile('../.vercel/output/config.json'));
- assert.deepEqual(deploymentConfig.routes.at(-1), {
- src: '/.*',
- dest: '/404.html',
- status: 404,
- });
+ assert.deepEqual(
+ deploymentConfig.routes.find((r) => r.status === 404),
+ {
+ src: '/.*',
+ dest: '/404.html',
+ status: 404,
+ }
+ );
});
});
diff --git a/packages/integrations/vercel/test/serverless-prerender.test.js b/packages/integrations/vercel/test/serverless-prerender.test.js
index 03ec8c225..140e422bd 100644
--- a/packages/integrations/vercel/test/serverless-prerender.test.js
+++ b/packages/integrations/vercel/test/serverless-prerender.test.js
@@ -44,7 +44,7 @@ describe('Serverless hybrid rendering', () => {
process.env.PRERENDER = true;
fixture = await loadFixture({
root: './fixtures/serverless-prerender/',
- output: 'hybrid',
+ output: 'static',
});
await fixture.build();
});
diff --git a/packages/integrations/vercel/test/serverless-with-dynamic-routes.test.js b/packages/integrations/vercel/test/serverless-with-dynamic-routes.test.js
index 0967f864b..b402ad0bb 100644
--- a/packages/integrations/vercel/test/serverless-with-dynamic-routes.test.js
+++ b/packages/integrations/vercel/test/serverless-with-dynamic-routes.test.js
@@ -10,18 +10,13 @@ describe('Serverless with dynamic routes', () => {
process.env.PRERENDER = true;
fixture = await loadFixture({
root: './fixtures/serverless-with-dynamic-routes/',
- output: 'hybrid',
+ output: 'server',
});
await fixture.build();
});
it('build successful', async () => {
assert.ok(await fixture.readFile('../.vercel/output/static/index.html'));
- assert.ok(
- await fixture.readFile('../.vercel/output/functions/[id]/index.astro.func/.vc-config.json')
- );
- assert.ok(
- await fixture.readFile('../.vercel/output/functions/api/[id].js.func/.vc-config.json')
- );
+ assert.ok(await fixture.readFile('../.vercel/output/functions/_render.func/.vc-config.json'));
});
});
diff --git a/packages/integrations/vercel/test/speed-insights.test.js b/packages/integrations/vercel/test/speed-insights.test.js
deleted file mode 100644
index 28ca84cd2..000000000
--- a/packages/integrations/vercel/test/speed-insights.test.js
+++ /dev/null
@@ -1,47 +0,0 @@
-import assert from 'node:assert/strict';
-import { before, describe, it } from 'node:test';
-import { loadFixture } from './test-utils.js';
-
-describe('Vercel Speed Insights', () => {
- describe('output: server', () => {
- /** @type {import('./test-utils.js').Fixture} */
- let fixture;
-
- before(async () => {
- fixture = await loadFixture({
- root: './fixtures/with-speed-insights-enabled/output-as-server/',
- output: 'server',
- });
- await fixture.build();
- });
-
- it('ensures that Vercel Speed Insights is present in the bundle', async () => {
- const [page] = await fixture.readdir('../.vercel/output/static/_astro');
-
- const bundle = await fixture.readFile(`../.vercel/output/static/_astro/${page}`);
-
- assert.match(bundle, /VERCEL_ANALYTICS_ID/);
- });
- });
-
- describe('output: static', () => {
- /** @type {import('./test-utils.js').Fixture} */
- let fixture;
-
- before(async () => {
- fixture = await loadFixture({
- root: './fixtures/with-speed-insights-enabled/output-as-static/',
- output: 'static',
- });
- await fixture.build();
- });
-
- it('ensures that Vercel Speed Insights is present in the bundle', async () => {
- const [page] = await fixture.readdir('../.vercel/output/static/_astro');
-
- const bundle = await fixture.readFile(`../.vercel/output/static/_astro/${page}`);
-
- assert.match(bundle, /VERCEL_ANALYTICS_ID/);
- });
- });
-});
diff --git a/packages/integrations/vercel/test/split.test.js b/packages/integrations/vercel/test/split.test.js
deleted file mode 100644
index e64a4dc60..000000000
--- a/packages/integrations/vercel/test/split.test.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import assert from 'node:assert/strict';
-import { before, describe, it } from 'node:test';
-import { loadFixture } from './test-utils.js';
-
-describe('build: split', () => {
- /** @type {import('./test-utils').Fixture} */
- let fixture;
-
- before(async () => {
- fixture = await loadFixture({
- root: './fixtures/functionPerRoute/',
- output: 'server',
- });
- await fixture.build();
- });
-
- it('creates separate functions for non-prerendered pages', async () => {
- const files = await fixture.readdir('../.vercel/output/functions/');
- assert.equal(files.length, 3);
- assert.equal(files.includes('prerender.astro.func'), false);
- });
-
- it('creates the route definitions in the config.json', async () => {
- const json = await fixture.readFile('../.vercel/output/config.json');
- const config = JSON.parse(json);
- assert.equal(config.routes.length, 5);
- assert.equal(
- config.routes.some((route) => route.dest === 'prerender.astro'),
- false
- );
- });
-});