diff --git a/.changeset/wise-geckos-applaud.md b/.changeset/wise-geckos-applaud.md
new file mode 100644
index 000000000..eae1e3e82
--- /dev/null
+++ b/.changeset/wise-geckos-applaud.md
@@ -0,0 +1,5 @@
+---
+'@astrojs/vercel': minor
+---
+
+Add support for using the Vercel Image Optimization API through `astro:assets`
diff --git a/packages/integrations/vercel/README.md b/packages/integrations/vercel/README.md
index b20131f67..0af5632b5 100644
--- a/packages/integrations/vercel/README.md
+++ b/packages/integrations/vercel/README.md
@@ -14,7 +14,7 @@ Learn how to deploy your Astro site in our [Vercel deployment guide](https://doc
## Why Astro Vercel
-If you're using Astro as a static site builder — its behavior out of the box — you don't need an adapter.
+If you're using Astro as a static site builder — its behavior out of the box — you don't need an adapter.
If you wish to [use server-side rendering (SSR)](https://docs.astro.build/en/guides/server-side-rendering/), Astro requires an adapter that matches your deployment runtime.
@@ -108,6 +108,63 @@ export default defineConfig({
});
```
+### imageConfig
+
+**Type:** `VercelImageConfig`
+**Available for:** Edge, Serverless, Static
+**Added in:** `@astrojs/vercel@3.3.0`
+
+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.
+
+```js
+// astro.config.mjs
+import { defineConfig } from 'astro/config';
+import vercel from '@astrojs/vercel/static';
+
+export default defineConfig({
+ output: 'server',
+ adapter: vercel({
+ imageConfig: {
+ sizes: [320, 640, 1280]
+ }
+ })
+});
+```
+
+### imageService
+
+**Type:** `boolean`
+**Available for:** Edge, Serverless, Static
+**Added in:** `@astrojs/vercel@3.3.0`
+
+When enabled, an [Image Service](https://docs.astro.build/en/reference/image-service-reference/) powered by the Vercel Image Optimization API will be automatically configured and used in production. In development, a built-in Squoosh-based service will be used instead.
+
+```js
+// astro.config.mjs
+import { defineConfig } from 'astro/config';
+import vercel from '@astrojs/vercel/static';
+
+export default defineConfig({
+ output: 'server',
+ adapter: vercel({
+ imageService: true
+ })
+});
+```
+
+```astro
+---
+import { Image } from "astro:assets";
+import astroLogo from "../assets/logo.png";
+---
+
+
+
+
+
+
+```
+
### includeFiles
**Type:** `string[]`
diff --git a/packages/integrations/vercel/package.json b/packages/integrations/vercel/package.json
index cee787e41..621ae1da5 100644
--- a/packages/integrations/vercel/package.json
+++ b/packages/integrations/vercel/package.json
@@ -23,6 +23,8 @@
"./serverless/entrypoint": "./dist/serverless/entrypoint.js",
"./static": "./dist/static/adapter.js",
"./analytics": "./dist/analytics.js",
+ "./build-image-service": "./dist/image/build-service.js",
+ "./dev-image-service": "./dist/image/dev-service.js",
"./package.json": "./package.json"
},
"typesVersions": {
@@ -60,6 +62,7 @@
"astro": "workspace:*",
"astro-scripts": "workspace:*",
"chai": "^4.3.6",
- "mocha": "^9.2.2"
+ "mocha": "^9.2.2",
+ "cheerio": "^1.0.0-rc.11"
}
}
diff --git a/packages/integrations/vercel/src/edge/adapter.ts b/packages/integrations/vercel/src/edge/adapter.ts
index a2e937987..3570f5b61 100644
--- a/packages/integrations/vercel/src/edge/adapter.ts
+++ b/packages/integrations/vercel/src/edge/adapter.ts
@@ -4,6 +4,12 @@ import esbuild from 'esbuild';
import { relative as relativePath } from 'node:path';
import { fileURLToPath } from 'node:url';
+import {
+ defaultImageConfig,
+ getImageConfig,
+ throwIfAssetsNotEnabled,
+ type VercelImageConfig,
+} from '../image/shared.js';
import {
copyFilesToFunction,
getFilesFromFolder,
@@ -26,11 +32,15 @@ function getAdapter(): AstroAdapter {
export interface VercelEdgeConfig {
includeFiles?: string[];
analytics?: boolean;
+ imageService?: boolean;
+ imagesConfig?: VercelImageConfig;
}
export default function vercelEdge({
includeFiles = [],
analytics,
+ imageService,
+ imagesConfig,
}: VercelEdgeConfig = {}): AstroIntegration {
let _config: AstroConfig;
let buildTempFolder: URL;
@@ -52,9 +62,11 @@ export default function vercelEdge({
client: new URL('./static/', outDir),
server: new URL('./dist/', config.root),
},
+ ...getImageConfig(imageService, imagesConfig, command),
});
},
'astro:config:done': ({ setAdapter, config }) => {
+ throwIfAssetsNotEnabled(config, imageService);
setAdapter(getAdapter());
_config = config;
buildTempFolder = config.build.server;
@@ -64,7 +76,7 @@ export default function vercelEdge({
if (config.output === 'static') {
throw new Error(`
[@astrojs/vercel] \`output: "server"\` is required to use the edge adapter.
-
+
`);
}
},
@@ -135,6 +147,9 @@ export default function vercelEdge({
{ handle: 'filesystem' },
{ src: '/.*', dest: 'render' },
],
+ ...(imageService || imagesConfig
+ ? { images: imagesConfig ? imagesConfig : defaultImageConfig }
+ : {}),
});
},
},
diff --git a/packages/integrations/vercel/src/image/build-service.ts b/packages/integrations/vercel/src/image/build-service.ts
new file mode 100644
index 000000000..23cd664a2
--- /dev/null
+++ b/packages/integrations/vercel/src/image/build-service.ts
@@ -0,0 +1,60 @@
+import type { ExternalImageService } from 'astro';
+import { isESMImportedImage, sharedValidateOptions } from './shared';
+
+const service: ExternalImageService = {
+ validateOptions: (options, serviceOptions) =>
+ sharedValidateOptions(options, serviceOptions, 'production'),
+ 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;
+ }
+
+ let targetWidth = props.width;
+ let targetHeight = props.height;
+ if (isESMImportedImage(props.src)) {
+ const aspectRatio = props.src.width / props.src.height;
+ if (targetHeight && !targetWidth) {
+ // If we have a height but no width, use height to calculate the width
+ targetWidth = Math.round(targetHeight * aspectRatio);
+ } else if (targetWidth && !targetHeight) {
+ // If we have a width but no height, use width to calculate the height
+ targetHeight = Math.round(targetWidth / aspectRatio);
+ } else if (!targetWidth && !targetHeight) {
+ // If we have neither width or height, use the original image's dimensions
+ targetWidth = props.src.width;
+ targetHeight = props.src.height;
+ }
+ }
+
+ const { src, width, height, format, quality, ...attributes } = props;
+
+ return {
+ ...attributes,
+ width: targetWidth,
+ height: targetHeight,
+ loading: attributes.loading ?? 'lazy',
+ decoding: attributes.decoding ?? 'async',
+ };
+ },
+ getURL(options, serviceOptions) {
+ const fileSrc =
+ typeof options.src === 'string' ? options.src : removeLeadingForwardSlash(options.src.src);
+
+ const searchParams = new URLSearchParams();
+ searchParams.append('url', fileSrc);
+
+ options.width && searchParams.append('w', options.width.toString());
+ options.quality && searchParams.append('q', options.quality.toString());
+
+ return '/_vercel/image?' + searchParams;
+ },
+};
+
+function removeLeadingForwardSlash(path: string) {
+ return path.startsWith('/') ? path.substring(1) : path;
+}
+
+export default service;
diff --git a/packages/integrations/vercel/src/image/dev-service.ts b/packages/integrations/vercel/src/image/dev-service.ts
new file mode 100644
index 000000000..04df9932a
--- /dev/null
+++ b/packages/integrations/vercel/src/image/dev-service.ts
@@ -0,0 +1,57 @@
+import type { LocalImageService } from 'astro';
+// @ts-expect-error
+import squooshService from 'astro/assets/services/squoosh';
+import { sharedValidateOptions } from './shared';
+
+const service: LocalImageService = {
+ validateOptions: (options, serviceOptions) =>
+ sharedValidateOptions(options, serviceOptions, 'development'),
+ 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(props, serviceOptions);
+ },
+ getURL(options) {
+ const fileSrc = typeof options.src === 'string' ? options.src : options.src.src;
+
+ const searchParams = new URLSearchParams();
+ searchParams.append('href', fileSrc);
+
+ options.width && searchParams.append('w', options.width.toString());
+ options.quality && searchParams.append('q', options.quality.toString());
+
+ return '/_image?' + searchParams;
+ },
+ parseURL(url) {
+ const params = url.searchParams;
+
+ if (!params.has('href')) {
+ return undefined;
+ }
+
+ const transform = {
+ src: params.get('href')!,
+ width: params.has('w') ? parseInt(params.get('w')!) : undefined,
+ quality: params.get('q'),
+ };
+
+ return transform;
+ },
+ 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 = '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/image/shared.ts b/packages/integrations/vercel/src/image/shared.ts
new file mode 100644
index 000000000..0b6db2037
--- /dev/null
+++ b/packages/integrations/vercel/src/image/shared.ts
@@ -0,0 +1,151 @@
+import type { AstroConfig, ImageMetadata, ImageQualityPreset, ImageTransform } from 'astro';
+
+export const defaultImageConfig: VercelImageConfig = {
+ sizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
+ domains: [],
+};
+
+export function isESMImportedImage(src: ImageMetadata | string): src is ImageMetadata {
+ return typeof src === 'object';
+}
+// https://vercel.com/docs/build-output-api/v3/configuration#images
+type ImageFormat = 'image/avif' | 'image/webp';
+
+type RemotePattern = {
+ protocol?: 'http' | 'https';
+ hostname: string;
+ port?: string;
+ pathname?: string;
+};
+
+export type VercelImageConfig = {
+ /**
+ * Supported image widths.
+ */
+ sizes: number[];
+ /**
+ * Allowed external domains that can use Image Optimization. Leave empty for only allowing the deployment domain to use Image Optimization.
+ */
+ domains: string[];
+ /**
+ * Allowed external patterns that can use Image Optimization. Similar to `domains` but provides more control with RegExp.
+ */
+ remotePatterns?: RemotePattern[];
+ /**
+ * Cache duration (in seconds) for the optimized images.
+ */
+ minimumCacheTTL?: number;
+ /**
+ * Supported output image formats
+ */
+ formats?: ImageFormat[];
+ /**
+ * Allow SVG input image URLs. This is disabled by default for security purposes.
+ */
+ dangerouslyAllowSVG?: boolean;
+ /**
+ * Change the [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) of the optimized images.
+ */
+ contentSecurityPolicy?: string;
+};
+
+export const qualityTable: Record = {
+ low: 25,
+ mid: 50,
+ high: 80,
+ max: 100,
+};
+
+// TODO: Remove once Astro 3.0 is out and `experimental.assets` is no longer needed
+export function throwIfAssetsNotEnabled(config: AstroConfig, imageService: boolean | undefined) {
+ if (!config.experimental.assets && imageService) {
+ throw new Error(
+ `Using the Vercel Image Optimization-powered image service requires \`experimental.assets\` to be enabled. See https://docs.astro.build/en/guides/assets/ for more information.`
+ );
+ }
+}
+
+export function getImageConfig(
+ images: boolean | undefined,
+ imagesConfig: VercelImageConfig | undefined,
+ command: string
+) {
+ if (images) {
+ return {
+ image: {
+ service: {
+ entrypoint:
+ command === 'dev'
+ ? '@astrojs/vercel/dev-image-service'
+ : '@astrojs/vercel/build-image-service',
+ config: imagesConfig ? imagesConfig : defaultImageConfig,
+ },
+ },
+ };
+ }
+
+ return {};
+}
+
+export function sharedValidateOptions(
+ options: ImageTransform,
+ serviceOptions: Record,
+ mode: 'development' | 'production'
+) {
+ const vercelImageOptions = serviceOptions as VercelImageConfig;
+
+ if (
+ mode === 'development' &&
+ (!vercelImageOptions.sizes || vercelImageOptions.sizes.length === 0)
+ ) {
+ throw new Error('Vercel Image Optimization requires at least one size to be configured.');
+ }
+
+ const configuredWidths = vercelImageOptions.sizes.sort((a, b) => a - b);
+
+ // The logic for finding the perfect width is a bit confusing, here it goes:
+ // For images where no width has been specified:
+ // - For local, imported images, fallback to nearest width we can find in our configured
+ // - For remote images, that's an error, width is always required.
+ // For images where a width has been specified:
+ // - If the width that the user asked for isn't in `sizes`, then fallback to the nearest one, but save the width
+ // the user asked for so we can put it on the `img` tag later.
+ // - Otherwise, just use as-is.
+ // The end goal is:
+ // - The size on the page is always the one the user asked for or the base image's size
+ // - The actual size of the image file is always one of `sizes`, either the one the user asked for or the nearest to it
+ if (!options.width) {
+ const src = options.src;
+ if (isESMImportedImage(src)) {
+ const nearestWidth = configuredWidths.reduce((prev, curr) => {
+ return Math.abs(curr - src.width) < Math.abs(prev - src.width) ? curr : prev;
+ });
+
+ // Use the image's base width to inform the `width` and `height` on the `img` tag
+ options.inputtedWidth = src.width;
+ options.width = nearestWidth;
+ } else {
+ throw new Error(`Missing \`width\` parameter for remote image ${options.src}`);
+ }
+ } else {
+ if (!configuredWidths.includes(options.width)) {
+ const nearestWidth = configuredWidths.reduce((prev, curr) => {
+ return Math.abs(curr - options.width!) < Math.abs(prev - options.width!) ? curr : prev;
+ });
+
+ // Save the width the user asked for to inform the `width` and `height` on the `img` tag
+ options.inputtedWidth = options.width;
+ options.width = nearestWidth;
+ }
+ }
+
+ if (options.quality && typeof options.quality === 'string') {
+ options.quality = options.quality in qualityTable ? qualityTable[options.quality] : undefined;
+ }
+
+ if (!options.quality) {
+ options.quality = 100;
+ }
+
+ return options;
+}
diff --git a/packages/integrations/vercel/src/serverless/adapter.ts b/packages/integrations/vercel/src/serverless/adapter.ts
index 24b9c735b..47d164519 100644
--- a/packages/integrations/vercel/src/serverless/adapter.ts
+++ b/packages/integrations/vercel/src/serverless/adapter.ts
@@ -2,6 +2,12 @@ import type { AstroAdapter, AstroConfig, AstroIntegration } from 'astro';
import glob from 'fast-glob';
import { pathToFileURL } from 'url';
+import {
+ defaultImageConfig,
+ getImageConfig,
+ throwIfAssetsNotEnabled,
+ type VercelImageConfig,
+} from '../image/shared.js';
import { getVercelOutput, removeDir, writeJson } from '../lib/fs.js';
import { copyDependenciesToFunction } from '../lib/nft.js';
import { getRedirects } from '../lib/redirects.js';
@@ -20,12 +26,16 @@ export interface VercelServerlessConfig {
includeFiles?: string[];
excludeFiles?: string[];
analytics?: boolean;
+ imageService?: boolean;
+ imagesConfig?: VercelImageConfig;
}
export default function vercelServerless({
includeFiles,
excludeFiles,
analytics,
+ imageService,
+ imagesConfig,
}: VercelServerlessConfig = {}): AstroIntegration {
let _config: AstroConfig;
let buildTempFolder: URL;
@@ -47,9 +57,11 @@ export default function vercelServerless({
client: new URL('./static/', outDir),
server: new URL('./dist/', config.root),
},
+ ...getImageConfig(imageService, imagesConfig, command),
});
},
'astro:config:done': ({ setAdapter, config }) => {
+ throwIfAssetsNotEnabled(config, imageService);
setAdapter(getAdapter());
_config = config;
buildTempFolder = config.build.server;
@@ -59,7 +71,7 @@ export default function vercelServerless({
if (config.output === 'static') {
throw new Error(`
[@astrojs/vercel] \`output: "server"\` is required to use the serverless adapter.
-
+
`);
}
},
@@ -115,6 +127,9 @@ export default function vercelServerless({
{ handle: 'filesystem' },
{ src: '/.*', dest: 'render' },
],
+ ...(imageService || imagesConfig
+ ? { images: imagesConfig ? imagesConfig : defaultImageConfig }
+ : {}),
});
},
},
diff --git a/packages/integrations/vercel/src/static/adapter.ts b/packages/integrations/vercel/src/static/adapter.ts
index 5455edbc0..2aa489133 100644
--- a/packages/integrations/vercel/src/static/adapter.ts
+++ b/packages/integrations/vercel/src/static/adapter.ts
@@ -1,5 +1,11 @@
import type { AstroAdapter, AstroConfig, AstroIntegration } from 'astro';
+import {
+ defaultImageConfig,
+ getImageConfig,
+ throwIfAssetsNotEnabled,
+ type VercelImageConfig,
+} from '../image/shared.js';
import { emptyDir, getVercelOutput, writeJson } from '../lib/fs.js';
import { getRedirects } from '../lib/redirects.js';
@@ -11,15 +17,21 @@ function getAdapter(): AstroAdapter {
export interface VercelStaticConfig {
analytics?: boolean;
+ imageService?: boolean;
+ imagesConfig?: VercelImageConfig;
}
-export default function vercelStatic({ analytics }: VercelStaticConfig = {}): AstroIntegration {
+export default function vercelStatic({
+ analytics,
+ imageService,
+ imagesConfig,
+}: VercelStaticConfig = {}): AstroIntegration {
let _config: AstroConfig;
return {
name: '@astrojs/vercel',
hooks: {
- 'astro:config:setup': ({ command, config, updateConfig, injectScript }) => {
+ 'astro:config:setup': ({ command, config, injectScript, updateConfig }) => {
if (command === 'build' && analytics) {
injectScript('page', 'import "@astrojs/vercel/analytics"');
}
@@ -29,9 +41,11 @@ export default function vercelStatic({ analytics }: VercelStaticConfig = {}): As
build: {
format: 'directory',
},
+ ...getImageConfig(imageService, imagesConfig, command),
});
},
'astro:config:done': ({ setAdapter, config }) => {
+ throwIfAssetsNotEnabled(config, imageService);
setAdapter(getAdapter());
_config = config;
@@ -51,6 +65,9 @@ export default function vercelStatic({ analytics }: VercelStaticConfig = {}): As
await writeJson(new URL(`./config.json`, getVercelOutput(_config.root)), {
version: 3,
routes: [...getRedirects(routes, _config), { handle: 'filesystem' }],
+ ...(imageService || imagesConfig
+ ? { images: imagesConfig ? imagesConfig : defaultImageConfig }
+ : {}),
});
},
},
diff --git a/packages/integrations/vercel/test/fixtures/image/astro.config.mjs b/packages/integrations/vercel/test/fixtures/image/astro.config.mjs
new file mode 100644
index 000000000..a38be5065
--- /dev/null
+++ b/packages/integrations/vercel/test/fixtures/image/astro.config.mjs
@@ -0,0 +1,9 @@
+import vercel from '@astrojs/vercel/static';
+import { defineConfig } from 'astro/config';
+
+export default defineConfig({
+ adapter: vercel({imageService: true}),
+ experimental: {
+ assets: true
+ }
+});
diff --git a/packages/integrations/vercel/test/fixtures/image/package.json b/packages/integrations/vercel/test/fixtures/image/package.json
new file mode 100644
index 000000000..ea9d554f5
--- /dev/null
+++ b/packages/integrations/vercel/test/fixtures/image/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@test/astro-vercel-image",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "@astrojs/vercel": "workspace:*",
+ "astro": "workspace:*"
+ }
+}
diff --git a/packages/integrations/vercel/test/fixtures/image/src/assets/astro.jpeg b/packages/integrations/vercel/test/fixtures/image/src/assets/astro.jpeg
new file mode 100644
index 000000000..1443ee4b4
Binary files /dev/null and b/packages/integrations/vercel/test/fixtures/image/src/assets/astro.jpeg differ
diff --git a/packages/integrations/vercel/test/fixtures/image/src/pages/index.astro b/packages/integrations/vercel/test/fixtures/image/src/pages/index.astro
new file mode 100644
index 000000000..0a154874f
--- /dev/null
+++ b/packages/integrations/vercel/test/fixtures/image/src/pages/index.astro
@@ -0,0 +1,6 @@
+---
+import { Image } from "astro:assets";
+import astro from "../assets/astro.jpeg";
+---
+
+
diff --git a/packages/integrations/vercel/test/image.test.js b/packages/integrations/vercel/test/image.test.js
new file mode 100644
index 000000000..834b6d69b
--- /dev/null
+++ b/packages/integrations/vercel/test/image.test.js
@@ -0,0 +1,60 @@
+import { expect } from 'chai';
+import * as cheerio from 'cheerio';
+import { loadFixture } from './test-utils.js';
+
+describe('Image', () => {
+ /** @type {import('../../../astro/test/test-utils.js').Fixture} */
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({
+ root: './fixtures/image/',
+ });
+ await fixture.build();
+ });
+
+ it('build successful', async () => {
+ expect(await fixture.readFile('../.vercel/output/static/index.html')).to.be.ok;
+ });
+
+ it('has link to vercel in build with proper attributes', async () => {
+ const html = await fixture.readFile('../.vercel/output/static/index.html');
+ const $ = cheerio.load(html);
+ const img = $('img');
+
+ expect(img.attr('src').startsWith('/_vercel/image?url=_astr')).to.be.true;
+ expect(img.attr('loading')).to.equal('lazy');
+ expect(img.attr('width')).to.equal('225');
+ });
+
+ it('has proper vercel config', async () => {
+ const vercelConfig = JSON.parse(await fixture.readFile('../.vercel/output/config.json'));
+
+ expect(vercelConfig.images).to.deep.equal({
+ sizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
+ domains: [],
+ });
+ });
+
+ describe('dev', () => {
+ let devServer;
+
+ before(async () => {
+ devServer = await fixture.startDevServer();
+ });
+
+ after(async () => {
+ await devServer.stop();
+ });
+
+ it('has link to local image in dev with proper attributes', async () => {
+ const html = await fixture.fetch('/').then((res) => res.text());
+ const $ = cheerio.load(html);
+ const img = $('img');
+
+ expect(img.attr('src').startsWith('/_image?href=')).to.be.true;
+ expect(img.attr('loading')).to.equal('lazy');
+ expect(img.attr('width')).to.equal('225');
+ });
+ });
+});
diff --git a/packages/integrations/vercel/test/serverless-prerender.test.js b/packages/integrations/vercel/test/serverless-prerender.test.js
index 4cada43a7..491c6d0bd 100644
--- a/packages/integrations/vercel/test/serverless-prerender.test.js
+++ b/packages/integrations/vercel/test/serverless-prerender.test.js
@@ -1,5 +1,5 @@
-import { loadFixture } from './test-utils.js';
import { expect } from 'chai';
+import { loadFixture } from './test-utils.js';
describe('Serverless prerender', () => {
/** @type {import('./test-utils').Fixture} */
@@ -13,6 +13,6 @@ describe('Serverless prerender', () => {
it('build successful', async () => {
await fixture.build();
- expect(fixture.readFile('/static/index.html')).to.be.ok;
+ expect(await fixture.readFile('../.vercel/output/static/index.html')).to.be.ok;
});
});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 225ce6c09..bf43c2847 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -4639,10 +4639,22 @@ importers:
chai:
specifier: ^4.3.6
version: 4.3.6
+ cheerio:
+ specifier: ^1.0.0-rc.11
+ version: 1.0.0-rc.11
mocha:
specifier: ^9.2.2
version: 9.2.2
+ packages/integrations/vercel/test/fixtures/image:
+ dependencies:
+ '@astrojs/vercel':
+ specifier: workspace:*
+ version: link:../../..
+ astro:
+ specifier: workspace:*
+ version: link:../../../../../astro
+
packages/integrations/vercel/test/fixtures/no-output:
dependencies:
'@astrojs/vercel':