summaryrefslogtreecommitdiff
path: root/packages/integrations/vercel/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/integrations/vercel/src')
-rw-r--r--packages/integrations/vercel/src/edge/adapter.ts172
-rw-r--r--packages/integrations/vercel/src/edge/entrypoint.ts28
-rw-r--r--packages/integrations/vercel/src/edge/shim.ts1
-rw-r--r--packages/integrations/vercel/src/image/dev-service.ts5
-rw-r--r--packages/integrations/vercel/src/image/shared.ts11
-rw-r--r--packages/integrations/vercel/src/serverless/adapter.ts36
-rw-r--r--packages/integrations/vercel/src/serverless/entrypoint.ts6
-rw-r--r--packages/integrations/vercel/src/static/adapter.ts8
8 files changed, 34 insertions, 233 deletions
diff --git a/packages/integrations/vercel/src/edge/adapter.ts b/packages/integrations/vercel/src/edge/adapter.ts
deleted file mode 100644
index b83c9f2b7..000000000
--- a/packages/integrations/vercel/src/edge/adapter.ts
+++ /dev/null
@@ -1,172 +0,0 @@
-import type { AstroAdapter, AstroConfig, AstroIntegration } from 'astro';
-
-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 { exposeEnv } from '../lib/env.js';
-import {
- copyFilesToFunction,
- getFilesFromFolder,
- getVercelOutput,
- removeDir,
- writeJson,
-} from '../lib/fs.js';
-import { getRedirects } from '../lib/redirects.js';
-
-const PACKAGE_NAME = '@astrojs/vercel/edge';
-
-function getAdapter(): AstroAdapter {
- return {
- name: PACKAGE_NAME,
- serverEntrypoint: `${PACKAGE_NAME}/entrypoint`,
- exports: ['default'],
- };
-}
-
-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;
- let functionFolder: URL;
- let serverEntry: string;
-
- return {
- name: PACKAGE_NAME,
- hooks: {
- 'astro:config:setup': ({ command, config, updateConfig, injectScript }) => {
- if (command === 'build' && analytics) {
- injectScript('page', 'import "@astrojs/vercel/analytics"');
- }
- const outDir = getVercelOutput(config.root);
- const viteDefine = exposeEnv(['VERCEL_ANALYTICS_ID']);
- updateConfig({
- outDir,
- build: {
- serverEntry: 'entry.mjs',
- client: new URL('./static/', outDir),
- server: new URL('./dist/', config.root),
- },
- vite: {
- define: viteDefine,
- ssr: {
- external: ['@vercel/nft'],
- },
- },
- ...getImageConfig(imageService, imagesConfig, command),
- });
- },
- 'astro:config:done': ({ setAdapter, config }) => {
- throwIfAssetsNotEnabled(config, imageService);
- setAdapter(getAdapter());
- _config = config;
- buildTempFolder = config.build.server;
- functionFolder = new URL('./functions/render.func/', config.outDir);
- serverEntry = config.build.serverEntry;
-
- if (config.output === 'static') {
- throw new Error(`
- [@astrojs/vercel] \`output: "server"\` or \`output: "hybrid"\` is required to use the edge adapter.
-
- `);
- }
- },
- 'astro:build:setup': ({ vite, target }) => {
- if (target === 'server') {
- vite.resolve ||= {};
- vite.resolve.alias ||= {};
-
- const aliases = [{ find: 'react-dom/server', replacement: 'react-dom/server.browser' }];
-
- if (Array.isArray(vite.resolve.alias)) {
- vite.resolve.alias = [...vite.resolve.alias, ...aliases];
- } else {
- for (const alias of aliases) {
- (vite.resolve.alias as Record<string, string>)[alias.find] = alias.replacement;
- }
- }
-
- vite.ssr ||= {};
- vite.ssr.target = 'webworker';
-
- // Vercel edge runtime is a special webworker-ish environment that supports process.env,
- // but Vite would replace away `process.env` in webworkers, so we set a define here to prevent it
- vite.define = {
- 'process.env': 'process.env',
- ...vite.define,
- };
- }
- },
- 'astro:build:done': async ({ routes }) => {
- const entry = new URL(serverEntry, buildTempFolder);
- const generatedFiles = await getFilesFromFolder(buildTempFolder);
- const entryPath = fileURLToPath(entry);
-
- await esbuild.build({
- target: 'es2020',
- platform: 'browser',
- // https://runtime-keys.proposal.wintercg.org/#edge-light
- conditions: ['edge-light', 'worker', 'browser'],
- entryPoints: [entryPath],
- outfile: entryPath,
- allowOverwrite: true,
- format: 'esm',
- bundle: true,
- minify: true,
- });
-
- // Copy entry and other server files
- const commonAncestor = await copyFilesToFunction(
- [...generatedFiles, ...includeFiles.map((file) => new URL(file, _config.root))],
- functionFolder
- );
-
- // Remove temporary folder
- await removeDir(buildTempFolder);
-
- // Edge function config
- // https://vercel.com/docs/build-output-api/v3#vercel-primitives/edge-functions/configuration
- await writeJson(new URL(`./.vc-config.json`, functionFolder), {
- runtime: 'edge',
- entrypoint: relativePath(commonAncestor, entryPath),
- });
-
- // Output configuration
- // https://vercel.com/docs/build-output-api/v3#build-output-configuration
- 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' },
- { src: '/.*', dest: 'render' },
- ],
- ...(imageService || imagesConfig
- ? { images: imagesConfig ? imagesConfig : defaultImageConfig }
- : {}),
- });
- },
- },
- };
-}
diff --git a/packages/integrations/vercel/src/edge/entrypoint.ts b/packages/integrations/vercel/src/edge/entrypoint.ts
deleted file mode 100644
index 4b88bc793..000000000
--- a/packages/integrations/vercel/src/edge/entrypoint.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-// NOTE(fks): Side-effect -- shim.js must run first. This isn't guaranteed by
-// the language, but it is a Node.js behavior that we rely on here. Keep this
-// separate from the other imports so that it doesn't get organized & reordered.
-import './shim.js';
-
-// Normal Imports
-import type { SSRManifest } from 'astro';
-import { App } from 'astro/app';
-
-const clientAddressSymbol = Symbol.for('astro.clientAddress');
-
-export function createExports(manifest: SSRManifest) {
- const app = new App(manifest);
-
- const handler = async (request: Request): Promise<Response> => {
- const routeData = app.match(request);
- Reflect.set(request, clientAddressSymbol, request.headers.get('x-forwarded-for'));
- const response = await app.render(request, routeData);
- if (app.setCookieHeaders) {
- for (const setCookieHeader of app.setCookieHeaders(response)) {
- response.headers.append('Set-Cookie', setCookieHeader);
- }
- }
- return response;
- };
-
- return { default: handler };
-}
diff --git a/packages/integrations/vercel/src/edge/shim.ts b/packages/integrations/vercel/src/edge/shim.ts
deleted file mode 100644
index 1a73feb39..000000000
--- a/packages/integrations/vercel/src/edge/shim.ts
+++ /dev/null
@@ -1 +0,0 @@
-process.argv = [];
diff --git a/packages/integrations/vercel/src/image/dev-service.ts b/packages/integrations/vercel/src/image/dev-service.ts
index be6360fe3..72eb7ca0b 100644
--- a/packages/integrations/vercel/src/image/dev-service.ts
+++ b/packages/integrations/vercel/src/image/dev-service.ts
@@ -1,5 +1,4 @@
import type { LocalImageService } from 'astro';
-// @ts-expect-error
import squooshService from 'astro/assets/services/squoosh';
import { sharedValidateOptions } from './shared';
@@ -14,7 +13,9 @@ const service: LocalImageService = {
props.width = inputtedWidth;
}
- return squooshService.getHTMLAttributes(props, serviceOptions);
+ return squooshService.getHTMLAttributes
+ ? squooshService.getHTMLAttributes(props, serviceOptions)
+ : {};
},
getURL(options) {
const fileSrc = typeof options.src === 'string' ? options.src : options.src.src;
diff --git a/packages/integrations/vercel/src/image/shared.ts b/packages/integrations/vercel/src/image/shared.ts
index 473750fae..ad6b45bd0 100644
--- a/packages/integrations/vercel/src/image/shared.ts
+++ b/packages/integrations/vercel/src/image/shared.ts
@@ -1,4 +1,4 @@
-import type { AstroConfig, ImageMetadata, ImageQualityPreset, ImageTransform } from 'astro';
+import type { ImageMetadata, ImageQualityPreset, ImageTransform } from 'astro';
export const defaultImageConfig: VercelImageConfig = {
sizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
@@ -56,15 +56,6 @@ export const qualityTable: Record<ImageQualityPreset, number> = {
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,
diff --git a/packages/integrations/vercel/src/serverless/adapter.ts b/packages/integrations/vercel/src/serverless/adapter.ts
index 1a2f9d82a..d5ac9c881 100644
--- a/packages/integrations/vercel/src/serverless/adapter.ts
+++ b/packages/integrations/vercel/src/serverless/adapter.ts
@@ -3,12 +3,7 @@ import type { AstroAdapter, AstroConfig, AstroIntegration, RouteData } from 'ast
import glob from 'fast-glob';
import { basename } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
-import {
- defaultImageConfig,
- getImageConfig,
- throwIfAssetsNotEnabled,
- type VercelImageConfig,
-} from '../image/shared.js';
+import { defaultImageConfig, getImageConfig, type VercelImageConfig } from '../image/shared.js';
import { exposeEnv } from '../lib/env.js';
import { getVercelOutput, removeDir, writeJson } from '../lib/fs.js';
import { copyDependenciesToFunction } from '../lib/nft.js';
@@ -29,11 +24,31 @@ const SUPPORTED_NODE_VERSIONS: Record<
18: { status: 'current' },
};
-function getAdapter(): AstroAdapter {
+function getAdapter({
+ edgeMiddleware,
+ functionPerRoute,
+}: {
+ edgeMiddleware: boolean;
+ functionPerRoute: boolean;
+}): AstroAdapter {
return {
name: PACKAGE_NAME,
serverEntrypoint: `${PACKAGE_NAME}/entrypoint`,
exports: ['default'],
+ adapterFeatures: {
+ edgeMiddleware,
+ functionPerRoute,
+ },
+ supportedAstroFeatures: {
+ hybridOutput: 'stable',
+ staticOutput: 'stable',
+ serverOutput: 'stable',
+ assets: {
+ supportKind: 'stable',
+ isSharpCompatible: true,
+ isSquooshCompatible: true,
+ },
+ },
};
}
@@ -43,6 +58,8 @@ export interface VercelServerlessConfig {
analytics?: boolean;
imageService?: boolean;
imagesConfig?: VercelImageConfig;
+ edgeMiddleware?: boolean;
+ functionPerRoute?: boolean;
}
export default function vercelServerless({
@@ -51,6 +68,8 @@ export default function vercelServerless({
analytics,
imageService,
imagesConfig,
+ functionPerRoute = false,
+ edgeMiddleware = false,
}: VercelServerlessConfig = {}): AstroIntegration {
let _config: AstroConfig;
let buildTempFolder: URL;
@@ -111,8 +130,7 @@ export default function vercelServerless({
});
},
'astro:config:done': ({ setAdapter, config }) => {
- throwIfAssetsNotEnabled(config, imageService);
- setAdapter(getAdapter());
+ setAdapter(getAdapter({ functionPerRoute, edgeMiddleware }));
_config = config;
buildTempFolder = config.build.server;
serverEntry = config.build.serverEntry;
diff --git a/packages/integrations/vercel/src/serverless/entrypoint.ts b/packages/integrations/vercel/src/serverless/entrypoint.ts
index 9e3cb1da0..f132d71f3 100644
--- a/packages/integrations/vercel/src/serverless/entrypoint.ts
+++ b/packages/integrations/vercel/src/serverless/entrypoint.ts
@@ -1,14 +1,12 @@
-import { polyfill } from '@astrojs/webapi';
import type { SSRManifest } from 'astro';
import { App } from 'astro/app';
+import { applyPolyfills } from 'astro/app/node';
import type { IncomingMessage, ServerResponse } from 'node:http';
import { ASTRO_LOCALS_HEADER } from './adapter';
import { getRequest, setResponse } from './request-transform';
-polyfill(globalThis, {
- exclude: 'window document',
-});
+applyPolyfills();
export const createExports = (manifest: SSRManifest) => {
const app = new App(manifest);
diff --git a/packages/integrations/vercel/src/static/adapter.ts b/packages/integrations/vercel/src/static/adapter.ts
index bc83b24af..8b9845898 100644
--- a/packages/integrations/vercel/src/static/adapter.ts
+++ b/packages/integrations/vercel/src/static/adapter.ts
@@ -1,11 +1,6 @@
import type { AstroAdapter, AstroConfig, AstroIntegration } from 'astro';
-import {
- defaultImageConfig,
- getImageConfig,
- throwIfAssetsNotEnabled,
- type VercelImageConfig,
-} from '../image/shared.js';
+import { defaultImageConfig, getImageConfig, type VercelImageConfig } from '../image/shared.js';
import { exposeEnv } from '../lib/env.js';
import { emptyDir, getVercelOutput, writeJson } from '../lib/fs.js';
import { isServerLikeOutput } from '../lib/prerender.js';
@@ -52,7 +47,6 @@ export default function vercelStatic({
});
},
'astro:config:done': ({ setAdapter, config }) => {
- throwIfAssetsNotEnabled(config, imageService);
setAdapter(getAdapter());
_config = config;