From 9cc4e48e6a858d3a12e6373a5e287b32d24a1c5a Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Thu, 10 Aug 2023 13:12:13 -0400 Subject: [PATCH] Remove the Vercel Edge adapter (#8015) * Remove the Vercel Edge adapter * Update tests * Update .changeset/plenty-keys-add.md Co-authored-by: Sarah Rainsberger * Show enabling edge middleware --------- Co-authored-by: Sarah Rainsberger --- .changeset/plenty-keys-add.md | 23 +++ packages/integrations/vercel/README.md | 15 +- .../integrations/vercel/src/edge/adapter.ts | 182 ------------------ .../vercel/src/edge/entrypoint.ts | 28 --- packages/integrations/vercel/src/edge/shim.ts | 1 - .../vercel/test/static-assets.test.js | 15 -- 6 files changed, 27 insertions(+), 237 deletions(-) create mode 100644 .changeset/plenty-keys-add.md delete mode 100644 packages/integrations/vercel/src/edge/adapter.ts delete mode 100644 packages/integrations/vercel/src/edge/entrypoint.ts delete mode 100644 packages/integrations/vercel/src/edge/shim.ts diff --git a/.changeset/plenty-keys-add.md b/.changeset/plenty-keys-add.md new file mode 100644 index 0000000000..73a78ba3bd --- /dev/null +++ b/.changeset/plenty-keys-add.md @@ -0,0 +1,23 @@ +--- +'@astrojs/vercel': major +--- + +Remove the Vercel Edge adapter + + `@astrojs/vercel/serverless` now supports Edge middleware, so a separate adapter for Edge itself (deploying your entire app to the edge) is no longer necessary. Please update your Astro config to reflect this change: + + ```diff + // astro.config.mjs +import { defineConfig } from 'astro/config'; +- import vercel from '@astrojs/vercel/edge'; ++ import vercel from '@astrojs/vercel/serverless'; + +export default defineConfig({ + output: 'server', + adapter: vercel({ ++ edgeMiddleware: true + }), +}); +``` + +This adapter had several known limitations and compatibility issues that prevented many people from using it in production. To reduce maintenance costs and because we have a better story with Serveless + Edge Middleware, we are removing the Edge adapter. diff --git a/packages/integrations/vercel/README.md b/packages/integrations/vercel/README.md index fd03abba9f..3bf5e574ab 100644 --- a/packages/integrations/vercel/README.md +++ b/packages/integrations/vercel/README.md @@ -58,16 +58,12 @@ If you prefer to install the adapter manually instead, complete the following tw You can deploy to different targets: -- `edge`: SSR inside an [Edge function](https://vercel.com/docs/concepts/functions/edge-functions). - `serverless`: SSR inside a [Node.js function](https://vercel.com/docs/concepts/functions/serverless-functions). - `static`: generates a static website following Vercel's output formats, redirects, etc. -> **Note**: deploying to the Edge has [its limitations](https://vercel.com/docs/concepts/functions/edge-functions#known-limitations). An edge function can't be more than 1 MB in size and they don't support native Node.js APIs, among others. - You can change where to target by changing the import: ```js -import vercel from '@astrojs/vercel/edge'; import vercel from '@astrojs/vercel/serverless'; import vercel from '@astrojs/vercel/static'; ``` @@ -90,7 +86,7 @@ To configure this adapter, pass an object to the `vercel()` function call in `as ### analytics **Type:** `boolean`
-**Available for:** Serverless, Edge, Static
+**Available for:** Serverless, Static
**Added in:** `@astrojs/vercel@3.1.0` You can enable [Vercel Analytics](https://vercel.com/analytics) (including Web Vitals and Audiences) by setting `analytics: true`. This will inject Vercel’s tracking scripts into all your pages. @@ -111,7 +107,7 @@ export default defineConfig({ ### imagesConfig **Type:** `VercelImageConfig`
-**Available for:** Edge, Serverless, Static +**Available for:** 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. @@ -134,7 +130,7 @@ export default defineConfig({ ### imageService **Type:** `boolean`
-**Available for:** Edge, Serverless, Static +**Available for:** 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. @@ -175,7 +171,7 @@ import astroLogo from '../assets/logo.png'; ### includeFiles **Type:** `string[]`
-**Available for:** Edge, Serverless +**Available for:** Serverless Use this property to force files to be bundled with your function. This is helpful when you notice missing files. @@ -192,9 +188,6 @@ export default defineConfig({ }); ``` -> **Note** -> When building for the Edge, all the dependencies get bundled in a single file to save space. **No extra file will be bundled**. So, if you _need_ some file inside the function, you have to specify it in `includeFiles`. - ### excludeFiles **Type:** `string[]`
diff --git a/packages/integrations/vercel/src/edge/adapter.ts b/packages/integrations/vercel/src/edge/adapter.ts deleted file mode 100644 index b613f502c9..0000000000 --- a/packages/integrations/vercel/src/edge/adapter.ts +++ /dev/null @@ -1,182 +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'], - supportedAstroFeatures: { - hybridOutput: 'stable', - staticOutput: 'stable', - serverOutput: 'stable', - assets: { - supportKind: 'stable', - isSharpCompatible: false, - isSquooshCompatible: false, - }, - }, - }; -} - -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)[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 4b88bc793d..0000000000 --- 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 => { - 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 1a73feb397..0000000000 --- a/packages/integrations/vercel/src/edge/shim.ts +++ /dev/null @@ -1 +0,0 @@ -process.argv = []; diff --git a/packages/integrations/vercel/test/static-assets.test.js b/packages/integrations/vercel/test/static-assets.test.js index c22ad50191..7f360aebc7 100644 --- a/packages/integrations/vercel/test/static-assets.test.js +++ b/packages/integrations/vercel/test/static-assets.test.js @@ -66,19 +66,4 @@ describe('Static Assets', () => { checkValidCacheControl(assets); }); }); - - describe('edge adapter', async () => { - const adapter = await import('@astrojs/vercel/edge'); - - it('has cache control', async () => { - await build({ adapter }); - checkValidCacheControl(); - }); - - it('has cache control other assets', async () => { - const assets = '_foo'; - await build({ adapter, assets }); - checkValidCacheControl(assets); - }); - }); });