From 0ae59f09962d494ae746668bdc49c52c3296b302 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 13 Jul 2022 17:48:05 -0400 Subject: [PATCH] remove ENABLE_VC_BUILD checks, use v3 build output API for all vercel apps --- .changeset/warm-knives-swim.md | 6 + packages/adapter-static/platforms.js | 24 +- packages/adapter-vercel/README.md | 2 - packages/adapter-vercel/index.js | 417 ++++++++++----------------- 4 files changed, 159 insertions(+), 290 deletions(-) create mode 100644 .changeset/warm-knives-swim.md diff --git a/.changeset/warm-knives-swim.md b/.changeset/warm-knives-swim.md new file mode 100644 index 000000000000..e0e2b4699e2f --- /dev/null +++ b/.changeset/warm-knives-swim.md @@ -0,0 +1,6 @@ +--- +'@sveltejs/adapter-static': patch +'@sveltejs/adapter-vercel': patch +--- + +Remove ENABLE_VC_BUILD check, use v3 build output API for all apps deployed to Vercel diff --git a/packages/adapter-static/platforms.js b/packages/adapter-static/platforms.js index dc55bcd0684c..d92967d09a3c 100644 --- a/packages/adapter-static/platforms.js +++ b/packages/adapter-static/platforms.js @@ -63,10 +63,8 @@ function vercel_routes(builder) { /** @type {Platform[]} */ export const platforms = [ { - // Build Output API - // TODO remove the ENABLE_VC_BUILD check when no longer required name: 'Vercel', - test: () => !!process.env.VERCEL && !!process.env.ENABLE_VC_BUILD, + test: () => !!process.env.VERCEL, defaults: (config) => ({ pages: `.vercel/output/static/${config.kit.appDir}/prerendered`, assets: '.vercel/output/static' @@ -80,25 +78,5 @@ export const platforms = [ }) ); } - }, - { - // Legacy filesystem API - // TODO remove once Build Output API leaves beta - name: 'Vercel', - test: () => !!process.env.VERCEL, - defaults: (config) => ({ - pages: `.vercel_build_output/static/${config.kit.appDir}/prerendered`, - assets: '.vercel_build_output/static' - }), - done: (builder) => { - if (!fs.existsSync('.vercel_build_output/config')) { - fs.mkdirSync('.vercel_build_output/config'); - } - - fs.writeFileSync( - '.vercel_build_output/config/routes.json', - JSON.stringify(vercel_routes(builder)) - ); - } } ]; diff --git a/packages/adapter-vercel/README.md b/packages/adapter-vercel/README.md index 5f721fdb3c0c..03a2db9192b9 100644 --- a/packages/adapter-vercel/README.md +++ b/packages/adapter-vercel/README.md @@ -6,8 +6,6 @@ If you're using [adapter-auto](../adapter-auto), you don't need to install this ## Usage -> The `edge` and `split` options depend on the Vercel Build Output API which is currently in beta. For now, you must opt in by visiting `https://vercel.com/[YOUR_USERNAME]/[YOUR_PROJECT]/settings/environment-variables` and adding `ENABLE_VC_BUILD` with the value `1`. - Add `"@sveltejs/adapter-vercel": "next"` to the `devDependencies` in your `package.json` and run `npm install`. Then in your `svelte.config.js`: diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index c893881eff77..6cb29e970a2c 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -81,296 +81,183 @@ const redirects = { ] }; -const files = fileURLToPath(new URL('./files', import.meta.url).href); - /** @type {import('.').default} **/ export default function ({ external = [], edge, split } = {}) { return { name: '@sveltejs/adapter-vercel', async adapt(builder) { - if (process.env.ENABLE_VC_BUILD) { - await v3(builder, external, edge, split); - } else { - if (edge || split) { - throw new Error('`edge` and `split` options can only be used with ENABLE_VC_BUILD'); - } - - await v1(builder, external); - } - } - }; -} - -/** - * @param {import('@sveltejs/kit').Builder} builder - * @param {string[]} external - */ -async function v1(builder, external) { - const node_version = get_node_version(); - - const dir = '.vercel_build_output'; - - const tmp = builder.getBuildDirectory('vercel-tmp'); - - builder.rimraf(dir); - builder.rimraf(tmp); - - const dirs = { - static: `${dir}/static`, - lambda: `${dir}/functions/node/render` - }; + const node_version = get_node_version(); - builder.log.minor('Generating serverless function...'); + const dir = '.vercel/output'; - const relativePath = path.posix.relative(tmp, builder.getServerDirectory()); + const tmp = builder.getBuildDirectory('vercel-tmp'); - builder.copy(`${files}/serverless.js`, `${tmp}/serverless.js`, { - replace: { - SERVER: `${relativePath}/index.js`, - MANIFEST: './manifest.js' - } - }); + builder.rimraf(dir); + builder.rimraf(tmp); - fs.writeFileSync( - `${tmp}/manifest.js`, - `export const manifest = ${builder.generateManifest({ - relativePath - })};\n` - ); + const files = fileURLToPath(new URL('./files', import.meta.url).href); - await esbuild.build({ - entryPoints: [`${tmp}/serverless.js`], - outfile: `${dirs.lambda}/index.js`, - target: `node${node_version.full}`, - bundle: true, - platform: 'node', - external, - format: 'cjs', - sourcemap: 'linked' - }); - - fs.writeFileSync(`${dirs.lambda}/package.json`, JSON.stringify({ type: 'commonjs' })); - - builder.log.minor('Copying assets...'); - - builder.writeStatic(dirs.static); - builder.writeClient(dirs.static); - builder.writePrerendered(dirs.static); - - builder.log.minor('Writing routes...'); - - builder.mkdirp(`${dir}/config`); - - const prerendered_pages = Array.from(builder.prerendered.pages, ([src, page]) => ({ - src, - dest: page.file - })); + const dirs = { + static: `${dir}/static`, + functions: `${dir}/functions` + }; - const prerendered_redirects = Array.from(builder.prerendered.redirects, ([src, redirect]) => ({ - src, - headers: { - Location: redirect.location - }, - status: redirect.status - })); - - fs.writeFileSync( - `${dir}/config/routes.json`, - JSON.stringify([ - ...redirects[builder.config.kit.trailingSlash], - ...prerendered_pages, - ...prerendered_redirects, - { - src: `/${builder.config.kit.appDir}/immutable/.+`, - headers: { - 'cache-control': 'public, immutable, max-age=31536000' + const prerendered_redirects = Array.from( + builder.prerendered.redirects, + ([src, redirect]) => ({ + src, + headers: { + Location: redirect.location + }, + status: redirect.status + }) + ); + + /** @type {any[]} */ + const routes = [ + ...redirects[builder.config.kit.trailingSlash], + ...prerendered_redirects, + { + src: `/${builder.config.kit.appDir}/.+`, + headers: { + 'cache-control': 'public, immutable, max-age=31536000' + } + }, + { + handle: 'filesystem' } - }, - { - handle: 'filesystem' - }, - { - src: '/.*', - dest: '.vercel/functions/render' - } - ]) - ); -} - -/** - * @param {import('@sveltejs/kit').Builder} builder - * @param {string[]} external - * @param {boolean} edge - * @param {boolean} split - */ -async function v3(builder, external, edge, split) { - const node_version = get_node_version(); - - const dir = '.vercel/output'; - - const tmp = builder.getBuildDirectory('vercel-tmp'); - - builder.rimraf(dir); - builder.rimraf(tmp); - - const files = fileURLToPath(new URL('./files', import.meta.url).href); - - const dirs = { - static: `${dir}/static`, - functions: `${dir}/functions` - }; - - const prerendered_redirects = Array.from(builder.prerendered.redirects, ([src, redirect]) => ({ - src, - headers: { - Location: redirect.location - }, - status: redirect.status - })); - - /** @type {any[]} */ - const routes = [ - ...redirects[builder.config.kit.trailingSlash], - ...prerendered_redirects, - { - src: `/${builder.config.kit.appDir}/.+`, - headers: { - 'cache-control': 'public, immutable, max-age=31536000' - } - }, - { - handle: 'filesystem' - } - ]; - - builder.log.minor('Generating serverless function...'); - - /** - * @param {string} name - * @param {string} pattern - * @param {(options: { relativePath: string }) => string} generate_manifest - */ - async function generate_serverless_function(name, pattern, generate_manifest) { - const relativePath = path.posix.relative(tmp, builder.getServerDirectory()); - - builder.copy(`${files}/serverless.js`, `${tmp}/index.js`, { - replace: { - SERVER: `${relativePath}/index.js`, - MANIFEST: './manifest.js' - } - }); - - write( - `${tmp}/manifest.js`, - `export const manifest = ${generate_manifest({ relativePath })};\n` - ); + ]; + + builder.log.minor('Generating serverless function...'); + + /** + * @param {string} name + * @param {string} pattern + * @param {(options: { relativePath: string }) => string} generate_manifest + */ + async function generate_serverless_function(name, pattern, generate_manifest) { + const relativePath = path.posix.relative(tmp, builder.getServerDirectory()); + + builder.copy(`${files}/serverless.js`, `${tmp}/index.js`, { + replace: { + SERVER: `${relativePath}/index.js`, + MANIFEST: './manifest.js' + } + }); - await create_function_bundle( - `${tmp}/index.js`, - `${dirs.functions}/${name}.func`, - `nodejs${node_version.major}.x` - ); + write( + `${tmp}/manifest.js`, + `export const manifest = ${generate_manifest({ relativePath })};\n` + ); - routes.push({ src: pattern, dest: `/${name}` }); - } + await create_function_bundle( + `${tmp}/index.js`, + `${dirs.functions}/${name}.func`, + `nodejs${node_version.major}.x` + ); - /** - * @param {string} name - * @param {string} pattern - * @param {(options: { relativePath: string }) => string} generate_manifest - */ - async function generate_edge_function(name, pattern, generate_manifest) { - const tmp = builder.getBuildDirectory(`vercel-tmp/${name}`); - const relativePath = path.posix.relative(tmp, builder.getServerDirectory()); - - builder.copy(`${files}/edge.js`, `${tmp}/edge.js`, { - replace: { - SERVER: `${relativePath}/index.js`, - MANIFEST: './manifest.js' + routes.push({ src: pattern, dest: `/${name}` }); } - }); - write( - `${tmp}/manifest.js`, - `export const manifest = ${generate_manifest({ relativePath })};\n` - ); - - await esbuild.build({ - entryPoints: [`${tmp}/edge.js`], - outfile: `${dirs.functions}/${name}.func/index.js`, - target: 'es2020', // TODO verify what the edge runtime supports - bundle: true, - platform: 'node', - format: 'esm', - external, - sourcemap: 'linked' - }); - - write( - `${dirs.functions}/${name}.func/.vc-config.json`, - JSON.stringify({ - runtime: 'edge', - entrypoint: 'index.js' - // TODO expose envVarsInUse - }) - ); - - routes.push({ src: pattern, dest: `/${name}` }); - } - - const generate_function = edge ? generate_edge_function : generate_serverless_function; - - if (split) { - await builder.createEntries((route) => { - return { - id: route.pattern.toString(), // TODO is `id` necessary? - filter: (other) => route.pattern.toString() === other.pattern.toString(), - complete: async (entry) => { - let sliced_pattern = route.pattern - .toString() - // remove leading / and trailing $/ - .slice(1, -2) - // replace escaped \/ with / - .replace(/\\\//g, '/'); - - // replace the root route "^/" with "^/?" - if (sliced_pattern === '^/') { - sliced_pattern = '^/?'; + /** + * @param {string} name + * @param {string} pattern + * @param {(options: { relativePath: string }) => string} generate_manifest + */ + async function generate_edge_function(name, pattern, generate_manifest) { + const tmp = builder.getBuildDirectory(`vercel-tmp/${name}`); + const relativePath = path.posix.relative(tmp, builder.getServerDirectory()); + + builder.copy(`${files}/edge.js`, `${tmp}/edge.js`, { + replace: { + SERVER: `${relativePath}/index.js`, + MANIFEST: './manifest.js' } + }); + + write( + `${tmp}/manifest.js`, + `export const manifest = ${generate_manifest({ relativePath })};\n` + ); + + await esbuild.build({ + entryPoints: [`${tmp}/edge.js`], + outfile: `${dirs.functions}/${name}.func/index.js`, + target: 'es2020', // TODO verify what the edge runtime supports + bundle: true, + platform: 'node', + format: 'esm', + external, + sourcemap: 'linked' + }); + + write( + `${dirs.functions}/${name}.func/.vc-config.json`, + JSON.stringify({ + runtime: 'edge', + entrypoint: 'index.js' + // TODO expose envVarsInUse + }) + ); + + routes.push({ src: pattern, dest: `/${name}` }); + } - const src = `${sliced_pattern}(?:/__data.json)?$`; // TODO adding /__data.json is a temporary workaround — those endpoints should be treated as distinct routes - - await generate_function(route.id || 'index', src, entry.generateManifest); - } - }; - }); - } else { - await generate_function('render', '/.*', builder.generateManifest); - } + const generate_function = edge ? generate_edge_function : generate_serverless_function; + + if (split) { + await builder.createEntries((route) => { + return { + id: route.pattern.toString(), // TODO is `id` necessary? + filter: (other) => route.pattern.toString() === other.pattern.toString(), + complete: async (entry) => { + let sliced_pattern = route.pattern + .toString() + // remove leading / and trailing $/ + .slice(1, -2) + // replace escaped \/ with / + .replace(/\\\//g, '/'); + + // replace the root route "^/" with "^/?" + if (sliced_pattern === '^/') { + sliced_pattern = '^/?'; + } + + const src = `${sliced_pattern}(?:/__data.json)?$`; // TODO adding /__data.json is a temporary workaround — those endpoints should be treated as distinct routes + + await generate_function(route.id || 'index', src, entry.generateManifest); + } + }; + }); + } else { + await generate_function('render', '/.*', builder.generateManifest); + } - builder.log.minor('Copying assets...'); + builder.log.minor('Copying assets...'); - builder.writeStatic(dirs.static); - builder.writeClient(dirs.static); - builder.writePrerendered(dirs.static); + builder.writeStatic(dirs.static); + builder.writeClient(dirs.static); + builder.writePrerendered(dirs.static); - builder.log.minor('Writing routes...'); + builder.log.minor('Writing routes...'); - /** @type {Record} */ - const overrides = {}; - builder.prerendered.pages.forEach((page, src) => { - overrides[page.file] = { path: src.slice(1) }; - }); + /** @type {Record} */ + const overrides = {}; + builder.prerendered.pages.forEach((page, src) => { + overrides[page.file] = { path: src.slice(1) }; + }); - write( - `${dir}/config.json`, - JSON.stringify({ - version: 3, - routes, - overrides - }) - ); + write( + `${dir}/config.json`, + JSON.stringify({ + version: 3, + routes, + overrides + }) + ); + } + }; } /**