diff options
Diffstat (limited to 'packages/integrations/vercel/src')
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; |