@sveltejs/adapter-vercel 1.0.0-next.47 → 1.0.0-next.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,6 +6,8 @@ If you're using [adapter-auto](../adapter-auto), you don't need to install this
6
6
 
7
7
  ## Usage
8
8
 
9
+ > 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/svelte/[YOUR_PROJECT]/settings/environment-variables` and adding `ENABLE_VC_BUILD` with the value `1`.
10
+
9
11
  Add `"@sveltejs/adapter-vercel": "next"` to the `devDependencies` in your `package.json` and run `npm install`.
10
12
 
11
13
  Then in your `svelte.config.js`:
@@ -15,18 +17,25 @@ import vercel from '@sveltejs/adapter-vercel';
15
17
 
16
18
  export default {
17
19
  kit: {
18
- ...
19
- adapter: vercel(options)
20
+ // default options are shown
21
+ adapter: vercel({
22
+ // if true, will deploy the app using edge functions
23
+ // (https://vercel.com/docs/concepts/functions/edge-functions)
24
+ // rather than serverless functions
25
+ edge: false,
26
+
27
+ // an array of dependencies that esbuild should treat
28
+ // as external when bundling functions
29
+ external: [],
30
+
31
+ // if true, will split your app into multiple functions
32
+ // instead of creating a single one for the entire app
33
+ split: false
34
+ })
20
35
  }
21
36
  };
22
37
  ```
23
38
 
24
- ## Options
25
-
26
- You can pass an `options` argument, if necessary, with the following:
27
-
28
- - `external` — an array of dependencies that [esbuild](https://esbuild.github.io/api/#external) should treat as external
29
-
30
39
  ## Changelog
31
40
 
32
41
  [The Changelog for this package is available on GitHub](https://github.com/sveltejs/kit/blob/master/packages/adapter-vercel/CHANGELOG.md).
package/files/edge.js ADDED
@@ -0,0 +1,15 @@
1
+ import { Server } from 'SERVER';
2
+ import { manifest } from 'MANIFEST';
3
+
4
+ const server = new Server(manifest);
5
+
6
+ /**
7
+ * @param {Request} request
8
+ */
9
+ export default (request) => {
10
+ return server.respond(request, {
11
+ getClientAddress() {
12
+ return request.headers.get('x-forwarded-for');
13
+ }
14
+ });
15
+ };
@@ -1,8 +1,10 @@
1
- import './shims';
1
+ import { installFetch } from '@sveltejs/kit/install-fetch';
2
2
  import { getRequest, setResponse } from '@sveltejs/kit/node';
3
3
  import { Server } from 'SERVER';
4
4
  import { manifest } from 'MANIFEST';
5
5
 
6
+ installFetch();
7
+
6
8
  const server = new Server(manifest);
7
9
 
8
10
  /**
package/index.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import { Adapter } from '@sveltejs/kit';
2
2
 
3
3
  type Options = {
4
+ edge?: boolean;
4
5
  external?: string[];
6
+ split?: boolean;
5
7
  };
6
8
 
7
9
  declare function plugin(options?: Options): Adapter;
package/index.js CHANGED
@@ -1,10 +1,8 @@
1
- import { writeFileSync } from 'fs';
2
- import { posix } from 'path';
1
+ import { mkdirSync, writeFileSync } from 'fs';
2
+ import { dirname, posix } from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
  import esbuild from 'esbuild';
5
5
 
6
- const dir = '.vercel_build_output';
7
-
8
6
  // rules for clean URLs and trailing slash handling,
9
7
  // generated with @vercel/routing-utils
10
8
  const redirects = {
@@ -83,99 +81,308 @@ const redirects = {
83
81
  };
84
82
 
85
83
  /** @type {import('.')} **/
86
- export default function ({ external = [] } = {}) {
84
+ export default function ({ external = [], edge, split } = {}) {
87
85
  return {
88
86
  name: '@sveltejs/adapter-vercel',
89
87
 
90
88
  async adapt(builder) {
91
- const tmp = builder.getBuildDirectory('vercel-tmp');
89
+ if (process.env.ENABLE_VC_BUILD) {
90
+ await v3(builder, external, edge, split);
91
+ } else {
92
+ if (edge || split) {
93
+ throw new Error('edge and split options can only be used with ENABLE_VC_BUILD');
94
+ }
92
95
 
93
- builder.rimraf(dir);
94
- builder.rimraf(tmp);
96
+ await v1(builder, external);
97
+ }
98
+ }
99
+ };
100
+ }
95
101
 
96
- const files = fileURLToPath(new URL('./files', import.meta.url).href);
102
+ /**
103
+ * @param {import('@sveltejs/kit').Builder} builder
104
+ * @param {string[]} external
105
+ */
106
+ async function v1(builder, external) {
107
+ const dir = '.vercel_build_output';
97
108
 
98
- const dirs = {
99
- static: `${dir}/static`,
100
- lambda: `${dir}/functions/node/render`
101
- };
109
+ const tmp = builder.getBuildDirectory('vercel-tmp');
102
110
 
103
- builder.log.minor('Generating serverless function...');
111
+ builder.rimraf(dir);
112
+ builder.rimraf(tmp);
104
113
 
105
- const relativePath = posix.relative(tmp, builder.getServerDirectory());
114
+ const files = fileURLToPath(new URL('./files', import.meta.url).href);
106
115
 
107
- builder.copy(files, tmp, {
108
- replace: {
109
- SERVER: `${relativePath}/index.js`,
110
- MANIFEST: './manifest.js'
111
- }
112
- });
113
-
114
- writeFileSync(
115
- `${tmp}/manifest.js`,
116
- `export const manifest = ${builder.generateManifest({
117
- relativePath
118
- })};\n`
119
- );
120
-
121
- await esbuild.build({
122
- entryPoints: [`${tmp}/entry.js`],
123
- outfile: `${dirs.lambda}/index.js`,
124
- target: 'node14',
125
- bundle: true,
126
- platform: 'node',
127
- external
128
- });
129
-
130
- writeFileSync(`${dirs.lambda}/package.json`, JSON.stringify({ type: 'commonjs' }));
131
-
132
- builder.log.minor('Copying assets...');
133
-
134
- builder.writeStatic(dirs.static);
135
- builder.writeClient(dirs.static);
136
- builder.writePrerendered(dirs.static);
137
-
138
- builder.log.minor('Writing routes...');
139
-
140
- builder.mkdirp(`${dir}/config`);
141
-
142
- const prerendered_pages = Array.from(builder.prerendered.pages, ([src, page]) => ({
143
- src,
144
- dest: page.file
145
- }));
146
-
147
- const prerendered_redirects = Array.from(
148
- builder.prerendered.redirects,
149
- ([src, redirect]) => ({
150
- src,
151
- headers: {
152
- Location: redirect.location
153
- },
154
- status: redirect.status
155
- })
156
- );
157
-
158
- writeFileSync(
159
- `${dir}/config/routes.json`,
160
- JSON.stringify([
161
- ...redirects[builder.config.kit.trailingSlash],
162
- ...prerendered_pages,
163
- ...prerendered_redirects,
164
- {
165
- src: `/${builder.config.kit.appDir}/.+`,
166
- headers: {
167
- 'cache-control': 'public, immutable, max-age=31536000'
168
- }
169
- },
170
- {
171
- handle: 'filesystem'
172
- },
173
- {
174
- src: '/.*',
175
- dest: '.vercel/functions/render'
176
- }
177
- ])
178
- );
116
+ const dirs = {
117
+ static: `${dir}/static`,
118
+ lambda: `${dir}/functions/node/render`
119
+ };
120
+
121
+ builder.log.minor('Generating serverless function...');
122
+
123
+ const relativePath = posix.relative(tmp, builder.getServerDirectory());
124
+
125
+ builder.copy(`${files}/serverless.js`, `${tmp}/serverless.js`, {
126
+ replace: {
127
+ SERVER: `${relativePath}/index.js`,
128
+ MANIFEST: './manifest.js'
179
129
  }
130
+ });
131
+
132
+ writeFileSync(
133
+ `${tmp}/manifest.js`,
134
+ `export const manifest = ${builder.generateManifest({
135
+ relativePath
136
+ })};\n`
137
+ );
138
+
139
+ await esbuild.build({
140
+ entryPoints: [`${tmp}/serverless.js`],
141
+ outfile: `${dirs.lambda}/index.js`,
142
+ target: 'node14',
143
+ bundle: true,
144
+ platform: 'node',
145
+ external
146
+ });
147
+
148
+ writeFileSync(`${dirs.lambda}/package.json`, JSON.stringify({ type: 'commonjs' }));
149
+
150
+ builder.log.minor('Copying assets...');
151
+
152
+ builder.writeStatic(dirs.static);
153
+ builder.writeClient(dirs.static);
154
+ builder.writePrerendered(dirs.static);
155
+
156
+ builder.log.minor('Writing routes...');
157
+
158
+ builder.mkdirp(`${dir}/config`);
159
+
160
+ const prerendered_pages = Array.from(builder.prerendered.pages, ([src, page]) => ({
161
+ src,
162
+ dest: page.file
163
+ }));
164
+
165
+ const prerendered_redirects = Array.from(builder.prerendered.redirects, ([src, redirect]) => ({
166
+ src,
167
+ headers: {
168
+ Location: redirect.location
169
+ },
170
+ status: redirect.status
171
+ }));
172
+
173
+ writeFileSync(
174
+ `${dir}/config/routes.json`,
175
+ JSON.stringify([
176
+ ...redirects[builder.config.kit.trailingSlash],
177
+ ...prerendered_pages,
178
+ ...prerendered_redirects,
179
+ {
180
+ src: `/${builder.config.kit.appDir}/.+`,
181
+ headers: {
182
+ 'cache-control': 'public, immutable, max-age=31536000'
183
+ }
184
+ },
185
+ {
186
+ handle: 'filesystem'
187
+ },
188
+ {
189
+ src: '/.*',
190
+ dest: '.vercel/functions/render'
191
+ }
192
+ ])
193
+ );
194
+ }
195
+
196
+ /**
197
+ * @param {import('@sveltejs/kit').Builder} builder
198
+ * @param {string[]} external
199
+ * @param {boolean} edge
200
+ * @param {boolean} split
201
+ */
202
+ async function v3(builder, external, edge, split) {
203
+ const dir = '.vercel/output';
204
+
205
+ const tmp = builder.getBuildDirectory('vercel-tmp');
206
+
207
+ builder.rimraf(dir);
208
+ builder.rimraf(tmp);
209
+
210
+ const files = fileURLToPath(new URL('./files', import.meta.url).href);
211
+
212
+ const dirs = {
213
+ static: `${dir}/static`,
214
+ functions: `${dir}/functions`
180
215
  };
216
+
217
+ const prerendered_redirects = Array.from(builder.prerendered.redirects, ([src, redirect]) => ({
218
+ src,
219
+ headers: {
220
+ Location: redirect.location
221
+ },
222
+ status: redirect.status
223
+ }));
224
+
225
+ /** @type {any[]} */
226
+ const routes = [
227
+ ...redirects[builder.config.kit.trailingSlash],
228
+ ...prerendered_redirects,
229
+ {
230
+ src: `/${builder.config.kit.appDir}/.+`,
231
+ headers: {
232
+ 'cache-control': 'public, immutable, max-age=31536000'
233
+ }
234
+ },
235
+ {
236
+ handle: 'filesystem'
237
+ }
238
+ ];
239
+
240
+ builder.log.minor('Generating serverless function...');
241
+
242
+ /**
243
+ * @param {string} name
244
+ * @param {string} pattern
245
+ * @param {(options: { relativePath: string }) => string} generate_manifest
246
+ */
247
+ async function generate_serverless_function(name, pattern, generate_manifest) {
248
+ const tmp = builder.getBuildDirectory(`vercel-tmp/${name}`);
249
+ const relativePath = posix.relative(tmp, builder.getServerDirectory());
250
+
251
+ builder.copy(`${files}/serverless.js`, `${tmp}/serverless.js`, {
252
+ replace: {
253
+ SERVER: `${relativePath}/index.js`,
254
+ MANIFEST: './manifest.js'
255
+ }
256
+ });
257
+
258
+ write(
259
+ `${tmp}/manifest.js`,
260
+ `export const manifest = ${generate_manifest({ relativePath })};\n`
261
+ );
262
+
263
+ await esbuild.build({
264
+ entryPoints: [`${tmp}/serverless.js`],
265
+ outfile: `${dirs.functions}/${name}.func/index.js`,
266
+ target: 'node14',
267
+ bundle: true,
268
+ platform: 'node',
269
+ format: 'cjs',
270
+ external
271
+ });
272
+
273
+ write(
274
+ `${dirs.functions}/${name}.func/.vc-config.json`,
275
+ JSON.stringify({
276
+ runtime: 'nodejs14.x',
277
+ handler: 'index.js',
278
+ launcherType: 'Nodejs'
279
+ })
280
+ );
281
+
282
+ write(`${dirs.functions}/${name}.func/package.json`, JSON.stringify({ type: 'commonjs' }));
283
+
284
+ routes.push({ src: pattern, dest: `/${name}` });
285
+ }
286
+
287
+ /**
288
+ * @param {string} name
289
+ * @param {string} pattern
290
+ * @param {(options: { relativePath: string }) => string} generate_manifest
291
+ */
292
+ async function generate_edge_function(name, pattern, generate_manifest) {
293
+ const tmp = builder.getBuildDirectory(`vercel-tmp/${name}`);
294
+ const relativePath = posix.relative(tmp, builder.getServerDirectory());
295
+
296
+ builder.copy(`${files}/edge.js`, `${tmp}/edge.js`, {
297
+ replace: {
298
+ SERVER: `${relativePath}/index.js`,
299
+ MANIFEST: './manifest.js'
300
+ }
301
+ });
302
+
303
+ write(
304
+ `${tmp}/manifest.js`,
305
+ `export const manifest = ${generate_manifest({ relativePath })};\n`
306
+ );
307
+
308
+ await esbuild.build({
309
+ entryPoints: [`${tmp}/edge.js`],
310
+ outfile: `${dirs.functions}/${name}.func/index.js`,
311
+ target: 'node14',
312
+ bundle: true,
313
+ platform: 'node',
314
+ format: 'esm',
315
+ external
316
+ });
317
+
318
+ write(
319
+ `${dirs.functions}/${name}.func/.vc-config.json`,
320
+ JSON.stringify({
321
+ runtime: 'edge',
322
+ entrypoint: 'index.js'
323
+ // TODO expose envVarsInUse
324
+ })
325
+ );
326
+
327
+ routes.push({ src: pattern, middlewarePath: name });
328
+ }
329
+
330
+ const generate_function = edge ? generate_edge_function : generate_serverless_function;
331
+
332
+ if (split) {
333
+ await builder.createEntries((route) => {
334
+ return {
335
+ id: route.pattern.toString(), // TODO is `id` necessary?
336
+ filter: (other) => route.pattern.toString() === other.pattern.toString(),
337
+ complete: async (entry) => {
338
+ const src = `${route.pattern
339
+ .toString()
340
+ .slice(1, -2) // remove leading / and trailing $/
341
+ .replace(/\\\//g, '/')}(?:/__data.json)?$`; // TODO adding /__data.json is a temporary workaround — those endpoints should be treated as distinct routes
342
+
343
+ await generate_function(route.id, src, entry.generateManifest);
344
+ }
345
+ };
346
+ });
347
+ } else {
348
+ await generate_function('render', '/.*', builder.generateManifest);
349
+ }
350
+
351
+ builder.log.minor('Copying assets...');
352
+
353
+ builder.writeStatic(dirs.static);
354
+ builder.writeClient(dirs.static);
355
+ builder.writePrerendered(dirs.static);
356
+
357
+ builder.log.minor('Writing routes...');
358
+
359
+ /** @type {Record<string, { path: string }>} */
360
+ const overrides = {};
361
+ builder.prerendered.pages.forEach((page, src) => {
362
+ overrides[page.file] = { path: src.slice(1) };
363
+ });
364
+
365
+ write(
366
+ `${dir}/config.json`,
367
+ JSON.stringify({
368
+ version: 3,
369
+ target: 'production',
370
+ routes,
371
+ overrides
372
+ })
373
+ );
374
+ }
375
+
376
+ /**
377
+ * @param {string} file
378
+ * @param {string} data
379
+ */
380
+ function write(file, data) {
381
+ try {
382
+ mkdirSync(dirname(file), { recursive: true });
383
+ } catch {
384
+ // do nothing
385
+ }
386
+
387
+ writeFileSync(file, data);
181
388
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/adapter-vercel",
3
- "version": "1.0.0-next.47",
3
+ "version": "1.0.0-next.48",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/sveltejs/kit",
@@ -25,7 +25,7 @@
25
25
  "esbuild": "^0.14.21"
26
26
  },
27
27
  "devDependencies": {
28
- "@sveltejs/kit": "1.0.0-next.297",
28
+ "@sveltejs/kit": "1.0.0-next.317",
29
29
  "typescript": "^4.6.2"
30
30
  },
31
31
  "scripts": {
package/files/shims.js DELETED
@@ -1,2 +0,0 @@
1
- import { installFetch } from '@sveltejs/kit/install-fetch';
2
- installFetch();