Remove the Vercel Edge adapter (#8015)
* Remove the Vercel Edge adapter * Update tests * Update .changeset/plenty-keys-add.md Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca> * Show enabling edge middleware --------- Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>
This commit is contained in:
parent
34cb200216
commit
9cc4e48e6a
6 changed files with 27 additions and 237 deletions
23
.changeset/plenty-keys-add.md
Normal file
23
.changeset/plenty-keys-add.md
Normal file
|
@ -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.
|
|
@ -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`<br>
|
||||
**Available for:** Serverless, Edge, Static<br>
|
||||
**Available for:** Serverless, Static<br>
|
||||
**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`<br>
|
||||
**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`<br>
|
||||
**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[]`<br>
|
||||
**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[]`<br>
|
||||
|
|
|
@ -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<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 }
|
||||
: {}),
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
|
@ -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 };
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
process.argv = [];
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue