vite-plugin-vercel 0.3.6 → 2.0.0

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
@@ -1,28 +1,27 @@
1
1
  # vite-plugin-vercel
2
2
 
3
- Vercel adapter for [`vite`](https://vitejs.dev/).
3
+ Vercel adapter for [Vite](https://vitejs.dev/).
4
4
 
5
- Its purpose is to help you bundle your application in `.vercel` folder as supported by
6
- [Vercel API v3](https://vercel.com/docs/build-output-api/v3).
5
+ Bundle your Vite application as supported by [Vercel Output API (v3)](https://vercel.com/docs/build-output-api/v3).
7
6
 
8
7
  ## Features
9
8
 
10
- - [x] [SSG/Static files support](https://vercel.com/docs/build-output-api/v3/primitives#static-files)
9
+ - [x] [SSG/Static files](https://vercel.com/docs/build-output-api/v3/primitives#static-files)
11
10
  - see [`prerender` config](/packages/vercel/src/types.ts#L37)
12
- - [x] [SSR/Serverless functions support](https://vercel.com/docs/build-output-api/v3/primitives#serverless-functions)
11
+ - [x] [SSR/Serverless functions](https://vercel.com/docs/build-output-api/v3/primitives#serverless-functions)
13
12
  - `.[jt]s` files under the `<root>/api` folder of your project are automatically bundled as Serverless functions under `.vercel/output/functions/api/*.func`
14
13
  - see [`additionalEndpoints` config](/packages/vercel/src/types.ts#L62)
15
- - [x] [ISR/Prerender functions support](https://vercel.com/docs/build-output-api/v3/primitives#prerender-functions)
14
+ - [x] [ISR/Prerender functions](https://vercel.com/docs/build-output-api/v3/primitives#prerender-functions)
16
15
  - see [`isr` config](/packages/vercel/src/types.ts#L89). Also see implementation of [vike](/packages/vike-integration/vike.ts) for example
17
- - [x] [Edge functions support](https://vercel.com/docs/build-output-api/v3/primitives#edge-functions)
18
- - [ ] [Images optimization support](https://vercel.com/docs/build-output-api/v3/configuration#images)
19
- - [ ] [Preview mode support](https://vercel.com/docs/build-output-api/v3/features#preview-mode)
20
- - [x] [Advanced config override](/packages/vercel/src/types.ts#L19)
21
- - [ ] Complete config override
16
+ - [x] [Edge functions](https://vercel.com/docs/build-output-api/v3/primitives#edge-functions)
17
+ - [ ] [Edge middleware](https://vercel.com/docs/functions/edge-middleware/middleware-api)
18
+ - [ ] [Images optimization](https://vercel.com/docs/build-output-api/v3/configuration#images)
19
+ - [ ] [Preview mode](https://vercel.com/docs/build-output-api/v3/features#preview-mode)
20
+ - [x] [Advanced config](/packages/vercel/src/types.ts#L19)
22
21
 
23
22
  ## Simple usage
24
23
 
25
- Then, install this package as a dev dependency and add it to your Vite config:
24
+ Install this package as a dev dependency and add it to your Vite config:
26
25
 
27
26
  ```ts
28
27
  // vite.config.ts
@@ -32,11 +31,47 @@ import vercel from 'vite-plugin-vercel';
32
31
  export default defineConfig({
33
32
  plugins: [vercel()],
34
33
  vercel: {
35
- // optional configuration options, see below for details
34
+ // optional configuration options, see "Advanced usage" below for details
36
35
  },
37
36
  });
38
37
  ```
39
38
 
39
+ > [!NOTE]
40
+ > Files under `/api` or `/_api` directory will automatically be added under `/api/*` route
41
+ > Prefer using `/_api` directory, as `@vercel/build` is currently force building `/api` files,
42
+ > with no way to disable it, thus avoiding double compilation and unexpected behaviour.
43
+
44
+ ### Configure endpoints
45
+
46
+ Endpoints under `/api`, `/_api` or added through `additionalEndpoints` can be configured
47
+ by exporting values from the endpoint file:
48
+
49
+ ```ts
50
+ // file: _api/endpoint.ts
51
+
52
+ // Should run on edge runtime
53
+ export const edge = true;
54
+
55
+ // Always add those header to this endpoint
56
+ export const headers = {
57
+ 'Some-Header': 'some value',
58
+ };
59
+
60
+ // Enable Incremental Static Regeneration for this endpoint
61
+ export const isr = {
62
+ expiration: 30,
63
+ };
64
+
65
+ export default async function handler() {
66
+ return new Response('Edge Function: OK', {
67
+ status: 200,
68
+ });
69
+ }
70
+ ```
71
+
72
+ > [!NOTE]
73
+ > Please create an issue if you need other per-endpoints configurations
74
+
40
75
  ## Usage with vike
41
76
 
42
77
  [vike](https://vike.dev/) is supported through [@vite-plugin-vercel/vike](/packages/vike-integration/README.md) plugin.
@@ -126,7 +161,7 @@ export default defineConfig({
126
161
  */
127
162
  trailingSlash: true,
128
163
  /**
129
- * By default, all `api/*` endpoints are compiled under `.vercel/output/functions/api/*.func`.
164
+ * By default, all `api/*` and `_api/*` endpoints are compiled under `.vercel/output/functions/api/*.func`.
130
165
  * If others serverless functions need to be compiled under `.vercel/output/functions`, they should be added here.
131
166
  * For instance, a framework can leverage this to have a generic ssr endpoint
132
167
  * without requiring the user to write any code.
package/dist/index.cjs CHANGED
@@ -158,7 +158,7 @@ function reorderEnforce(arr) {
158
158
  ...arr.filter((r) => r.enforce === "post")
159
159
  ];
160
160
  }
161
- function getConfig(resolvedConfig, rewrites, overrides) {
161
+ function getConfig(resolvedConfig, rewrites, overrides, headers) {
162
162
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
163
163
  const _rewrites = [
164
164
  // User provided config always comes first
@@ -169,23 +169,49 @@ function getConfig(resolvedConfig, rewrites, overrides) {
169
169
  cleanUrls: ((_b = resolvedConfig.vercel) == null ? void 0 : _b.cleanUrls) ?? true,
170
170
  trailingSlash: (_c = resolvedConfig.vercel) == null ? void 0 : _c.trailingSlash,
171
171
  rewrites: reorderEnforce(_rewrites),
172
- redirects: ((_d = resolvedConfig.vercel) == null ? void 0 : _d.redirects) ? reorderEnforce((_e = resolvedConfig.vercel) == null ? void 0 : _e.redirects) : void 0
172
+ redirects: ((_d = resolvedConfig.vercel) == null ? void 0 : _d.redirects) ? reorderEnforce((_e = resolvedConfig.vercel) == null ? void 0 : _e.redirects) : void 0,
173
+ headers
173
174
  });
174
175
  if (error) {
175
176
  throw error;
176
177
  }
177
- if (((_g = (_f = resolvedConfig.vercel) == null ? void 0 : _f.config) == null ? void 0 : _g.routes) && resolvedConfig.vercel.config.routes.length > 0) {
178
+ if (((_g = (_f = resolvedConfig.vercel) == null ? void 0 : _f.config) == null ? void 0 : _g.routes) && resolvedConfig.vercel.config.routes.length > 0 && !resolvedConfig.vercel.config.routes.every(
179
+ (r) => "continue" in r && r.continue
180
+ )) {
178
181
  console.warn(
179
- "It is discouraged to use `vercel.config.routes` to override routes. Prefer using `vercel.rewrites` and `vercel.redirects`."
182
+ 'Did you forget to add `"continue": true` to your routes? See https://vercel.com/docs/build-output-api/v3/configuration#source-route\nIf not, it is discouraged to use `vercel.config.routes` to override routes. Prefer using `vercel.rewrites` and `vercel.redirects`.'
180
183
  );
181
184
  }
185
+ let userRoutes = [];
186
+ let buildRoutes = [];
187
+ if ((_i = (_h = resolvedConfig.vercel) == null ? void 0 : _h.config) == null ? void 0 : _i.routes) {
188
+ const norm = (0, import_routing_utils.normalizeRoutes)(resolvedConfig.vercel.config.routes);
189
+ if (norm.error) {
190
+ throw norm.error;
191
+ }
192
+ userRoutes = norm.routes ?? [];
193
+ }
194
+ if (routes) {
195
+ const norm = (0, import_routing_utils.normalizeRoutes)(routes);
196
+ if (norm.error) {
197
+ throw norm.error;
198
+ }
199
+ buildRoutes = norm.routes ?? [];
200
+ }
201
+ const cleanRoutes = (0, import_routing_utils.mergeRoutes)({
202
+ userRoutes,
203
+ builds: [
204
+ {
205
+ use: "@vercel/node",
206
+ entrypoint: "index.js",
207
+ routes: buildRoutes
208
+ }
209
+ ]
210
+ });
182
211
  return vercelOutputConfigSchema.parse({
183
212
  version: 3,
184
- ...(_h = resolvedConfig.vercel) == null ? void 0 : _h.config,
185
- routes: [
186
- ...routes ?? [],
187
- ...((_j = (_i = resolvedConfig.vercel) == null ? void 0 : _i.config) == null ? void 0 : _j.routes) ?? []
188
- ],
213
+ ...(_j = resolvedConfig.vercel) == null ? void 0 : _j.config,
214
+ routes: cleanRoutes,
189
215
  overrides: {
190
216
  ...(_l = (_k = resolvedConfig.vercel) == null ? void 0 : _k.config) == null ? void 0 : _l.overrides,
191
217
  ...overrides
@@ -195,11 +221,11 @@ function getConfig(resolvedConfig, rewrites, overrides) {
195
221
  function getConfigDestination(resolvedConfig) {
196
222
  return import_path2.default.join(getOutput(resolvedConfig), "config.json");
197
223
  }
198
- async function writeConfig(resolvedConfig, rewrites, overrides) {
224
+ async function writeConfig(resolvedConfig, rewrites, overrides, headers) {
199
225
  await import_promises.default.writeFile(
200
226
  getConfigDestination(resolvedConfig),
201
227
  JSON.stringify(
202
- getConfig(resolvedConfig, rewrites, overrides),
228
+ getConfig(resolvedConfig, rewrites, overrides, headers),
203
229
  void 0,
204
230
  2
205
231
  ),
@@ -252,6 +278,21 @@ var vercelOutputVcConfigSchema = import_zod2.z.union([
252
278
 
253
279
  // src/build.ts
254
280
  var import_promises2 = __toESM(require("fs/promises"), 1);
281
+ var import_eval = __toESM(require("eval"), 1);
282
+
283
+ // src/schemas/exports.ts
284
+ var import_zod3 = require("zod");
285
+ var vercelEndpointExports = import_zod3.z.object({
286
+ edge: import_zod3.z.boolean().optional(),
287
+ headers: import_zod3.z.record(import_zod3.z.string()).optional(),
288
+ isr: import_zod3.z.object({
289
+ expiration: import_zod3.z.number().or(import_zod3.z.literal(false))
290
+ }).optional()
291
+ });
292
+
293
+ // src/build.ts
294
+ var import_magicast = require("magicast");
295
+ var import_build_utils = require("@vercel/build-utils");
255
296
  function getAdditionalEndpoints(resolvedConfig) {
256
297
  var _a;
257
298
  return (((_a = resolvedConfig.vercel) == null ? void 0 : _a.additionalEndpoints) ?? []).map((e) => ({
@@ -364,7 +405,7 @@ async function buildFn(resolvedConfig, entry, buildOptions) {
364
405
  }
365
406
  const ctx = { found: false, index: "" };
366
407
  options.plugins.push(vercelOgPlugin(ctx));
367
- await (0, import_esbuild.build)(options);
408
+ const output = await (0, import_esbuild.build)(options);
368
409
  if (ctx.found && ctx.index) {
369
410
  const dir = (0, import_path3.dirname)(ctx.index);
370
411
  const externalFiles = await (0, import_fast_glob.default)(`${dir}/*.{ttf,wasm}`);
@@ -380,6 +421,7 @@ async function buildFn(resolvedConfig, entry, buildOptions) {
380
421
  }
381
422
  }
382
423
  await writeVcConfig(resolvedConfig, entry.destination, Boolean(entry.edge));
424
+ return output;
383
425
  }
384
426
  async function writeVcConfig(resolvedConfig, destination, edge) {
385
427
  var _a;
@@ -388,6 +430,7 @@ async function writeVcConfig(resolvedConfig, destination, edge) {
388
430
  destination,
389
431
  ".vc-config.json"
390
432
  );
433
+ const nodeVersion = await (0, import_build_utils.getNodeVersion)(getOutput(resolvedConfig));
391
434
  await import_promises2.default.writeFile(
392
435
  vcConfig,
393
436
  JSON.stringify(
@@ -396,7 +439,7 @@ async function writeVcConfig(resolvedConfig, destination, edge) {
396
439
  runtime: "edge",
397
440
  entrypoint: "index.js"
398
441
  } : {
399
- runtime: "nodejs16.x",
442
+ runtime: nodeVersion.runtime,
400
443
  handler: "index.js",
401
444
  maxDuration: (_a = resolvedConfig.vercel) == null ? void 0 : _a.defaultMaxDuration,
402
445
  launcherType: "Nodejs",
@@ -415,28 +458,102 @@ function getSourceAndDestination(destination) {
415
458
  }
416
459
  return import_path3.default.posix.resolve("/", destination, ":match*");
417
460
  }
461
+ async function removeDefaultExport(filepath) {
462
+ const mod = await (0, import_magicast.loadFile)(filepath);
463
+ try {
464
+ delete mod.exports.default;
465
+ } catch (_) {
466
+ }
467
+ return (0, import_magicast.generateCode)(mod).code;
468
+ }
469
+ async function extractExports(filepath) {
470
+ var _a;
471
+ const contents = await removeDefaultExport(filepath);
472
+ const buildOptions = {
473
+ ...standardBuildOptions,
474
+ minify: false,
475
+ write: false,
476
+ legalComments: "none"
477
+ };
478
+ buildOptions.stdin = {
479
+ sourcefile: filepath,
480
+ contents,
481
+ loader: filepath.endsWith(".ts") ? "ts" : filepath.endsWith(".tsx") ? "tsx" : filepath.endsWith(".js") ? "js" : filepath.endsWith(".jsx") ? "jsx" : "default",
482
+ resolveDir: (0, import_path3.dirname)(filepath)
483
+ };
484
+ try {
485
+ const output = await (0, import_esbuild.build)(buildOptions);
486
+ const bundle = new TextDecoder().decode((_a = output.outputFiles[0]) == null ? void 0 : _a.contents);
487
+ return vercelEndpointExports.parse((0, import_eval.default)(bundle, filepath, {}, true));
488
+ } catch (e) {
489
+ console.warn(`Warning: failed to read exports of '${filepath}'`, e);
490
+ }
491
+ }
418
492
  async function buildEndpoints(resolvedConfig) {
419
493
  const entries = getEntries(resolvedConfig);
420
494
  for (const entry of entries) {
495
+ if (typeof entry.source === "string") {
496
+ const exports = await extractExports(entry.source);
497
+ if (exports) {
498
+ if (entry.headers || exports.headers) {
499
+ entry.headers = {
500
+ ...exports.headers,
501
+ ...entry.headers
502
+ };
503
+ }
504
+ if (entry.edge !== void 0 && exports.edge !== void 0) {
505
+ throw new Error(
506
+ `edge configuration should be defined either in the endpoint itself or through Vite config, not both ('${entry.source}')`
507
+ );
508
+ }
509
+ if (exports.edge !== void 0) {
510
+ entry.edge = exports.edge;
511
+ }
512
+ if (entry.isr !== void 0 && exports.isr !== void 0) {
513
+ throw new Error(
514
+ `isr configuration should be defined either in the endpoint itself or through Vite config, not both ('${entry.source}')`
515
+ );
516
+ }
517
+ if (exports.isr) {
518
+ entry.isr = exports.isr;
519
+ }
520
+ }
521
+ }
421
522
  await buildFn(resolvedConfig, entry);
422
523
  }
423
- return entries.filter((e) => e.addRoute !== false).map((e) => e.destination.replace(/\.func$/, "")).map((destination) => ({
424
- source: getSourceAndDestination(destination),
425
- destination: getSourceAndDestination(destination)
426
- }));
524
+ const isrEntries = entries.filter((e) => e.isr).map(
525
+ (e) => [
526
+ e.destination.replace(/\.func$/, ""),
527
+ { expiration: e.isr.expiration }
528
+ ]
529
+ );
530
+ return {
531
+ rewrites: entries.filter((e) => e.addRoute !== false).map((e) => e.destination.replace(/\.func$/, "")).map((destination) => ({
532
+ source: getSourceAndDestination(destination),
533
+ destination: getSourceAndDestination(destination)
534
+ })),
535
+ isr: Object.fromEntries(isrEntries),
536
+ headers: entries.filter((e) => e.headers).map((e) => ({
537
+ source: "/" + e.destination.replace(/\.func$/, ""),
538
+ headers: Object.entries(e.headers).map(([key, value]) => ({
539
+ key,
540
+ value
541
+ }))
542
+ }))
543
+ };
427
544
  }
428
545
 
429
546
  // src/prerender.ts
430
547
  var import_path4 = __toESM(require("path"), 1);
431
548
 
432
549
  // src/schemas/config/prerender-config.ts
433
- var import_zod3 = require("zod");
434
- var vercelOutputPrerenderConfigSchema = import_zod3.z.object({
435
- expiration: import_zod3.z.union([import_zod3.z.number().int().positive(), import_zod3.z.literal(false)]),
436
- group: import_zod3.z.number().int().optional(),
437
- bypassToken: import_zod3.z.string().optional(),
438
- fallback: import_zod3.z.string().optional(),
439
- allowQuery: import_zod3.z.array(import_zod3.z.string()).optional()
550
+ var import_zod4 = require("zod");
551
+ var vercelOutputPrerenderConfigSchema = import_zod4.z.object({
552
+ expiration: import_zod4.z.union([import_zod4.z.number().int().positive(), import_zod4.z.literal(false)]),
553
+ group: import_zod4.z.number().int().optional(),
554
+ bypassToken: import_zod4.z.string().optional(),
555
+ fallback: import_zod4.z.string().optional(),
556
+ allowQuery: import_zod4.z.array(import_zod4.z.string()).optional()
440
557
  }).strict();
441
558
 
442
559
  // src/prerender.ts
@@ -490,8 +607,12 @@ function getPrerenderSymlinkInfo(resolvedConfig, destination, target) {
490
607
  )
491
608
  };
492
609
  }
493
- async function buildPrerenderConfigs(resolvedConfig) {
494
- const isr = await getIsrConfig(resolvedConfig);
610
+ async function buildPrerenderConfigs(resolvedConfig, extractedIsr) {
611
+ const isr = Object.assign(
612
+ {},
613
+ extractedIsr,
614
+ await getIsrConfig(resolvedConfig)
615
+ );
495
616
  const entries = Object.entries(isr);
496
617
  const rewrites = [];
497
618
  for (const [destination, { symlink, route, ...isr2 }] of entries) {
@@ -556,12 +677,17 @@ function vercelPlugin() {
556
677
  }
557
678
  const overrides = await execPrerender(resolvedConfig);
558
679
  const userOverrides = await computeStaticHtmlOverrides(resolvedConfig);
559
- const rewrites = await buildEndpoints(resolvedConfig);
560
- rewrites.push(...await buildPrerenderConfigs(resolvedConfig));
561
- await writeConfig(resolvedConfig, rewrites, {
562
- ...userOverrides,
563
- ...overrides
564
- });
680
+ const { rewrites, isr, headers } = await buildEndpoints(resolvedConfig);
681
+ rewrites.push(...await buildPrerenderConfigs(resolvedConfig, isr));
682
+ await writeConfig(
683
+ resolvedConfig,
684
+ rewrites,
685
+ {
686
+ ...userOverrides,
687
+ ...overrides
688
+ },
689
+ headers
690
+ );
565
691
  }
566
692
  };
567
693
  }
package/dist/index.d.cts CHANGED
@@ -639,6 +639,14 @@ interface ViteVercelApiEntry {
639
639
  * Set to `true` to mark this function as an Edge Function
640
640
  */
641
641
  edge?: boolean;
642
+ /**
643
+ * Additional headers
644
+ */
645
+ headers?: Record<string, string>;
646
+ /**
647
+ * ISR config
648
+ */
649
+ isr?: VercelOutputIsr;
642
650
  }
643
651
 
644
652
  declare function allPlugins(options?: {
package/dist/index.d.ts CHANGED
@@ -639,6 +639,14 @@ interface ViteVercelApiEntry {
639
639
  * Set to `true` to mark this function as an Edge Function
640
640
  */
641
641
  edge?: boolean;
642
+ /**
643
+ * Additional headers
644
+ */
645
+ headers?: Record<string, string>;
646
+ /**
647
+ * ISR config
648
+ */
649
+ isr?: VercelOutputIsr;
642
650
  }
643
651
 
644
652
  declare function allPlugins(options?: {
package/dist/index.js CHANGED
@@ -116,7 +116,11 @@ var vercelOutputConfigSchema = z.object({
116
116
 
117
117
  // src/config.ts
118
118
  import fs from "fs/promises";
119
- import { getTransformedRoutes } from "@vercel/routing-utils";
119
+ import {
120
+ getTransformedRoutes,
121
+ mergeRoutes,
122
+ normalizeRoutes
123
+ } from "@vercel/routing-utils";
120
124
  function reorderEnforce(arr) {
121
125
  return [
122
126
  ...arr.filter((r) => r.enforce === "pre"),
@@ -124,7 +128,7 @@ function reorderEnforce(arr) {
124
128
  ...arr.filter((r) => r.enforce === "post")
125
129
  ];
126
130
  }
127
- function getConfig(resolvedConfig, rewrites, overrides) {
131
+ function getConfig(resolvedConfig, rewrites, overrides, headers) {
128
132
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
129
133
  const _rewrites = [
130
134
  // User provided config always comes first
@@ -135,23 +139,49 @@ function getConfig(resolvedConfig, rewrites, overrides) {
135
139
  cleanUrls: ((_b = resolvedConfig.vercel) == null ? void 0 : _b.cleanUrls) ?? true,
136
140
  trailingSlash: (_c = resolvedConfig.vercel) == null ? void 0 : _c.trailingSlash,
137
141
  rewrites: reorderEnforce(_rewrites),
138
- redirects: ((_d = resolvedConfig.vercel) == null ? void 0 : _d.redirects) ? reorderEnforce((_e = resolvedConfig.vercel) == null ? void 0 : _e.redirects) : void 0
142
+ redirects: ((_d = resolvedConfig.vercel) == null ? void 0 : _d.redirects) ? reorderEnforce((_e = resolvedConfig.vercel) == null ? void 0 : _e.redirects) : void 0,
143
+ headers
139
144
  });
140
145
  if (error) {
141
146
  throw error;
142
147
  }
143
- if (((_g = (_f = resolvedConfig.vercel) == null ? void 0 : _f.config) == null ? void 0 : _g.routes) && resolvedConfig.vercel.config.routes.length > 0) {
148
+ if (((_g = (_f = resolvedConfig.vercel) == null ? void 0 : _f.config) == null ? void 0 : _g.routes) && resolvedConfig.vercel.config.routes.length > 0 && !resolvedConfig.vercel.config.routes.every(
149
+ (r) => "continue" in r && r.continue
150
+ )) {
144
151
  console.warn(
145
- "It is discouraged to use `vercel.config.routes` to override routes. Prefer using `vercel.rewrites` and `vercel.redirects`."
152
+ 'Did you forget to add `"continue": true` to your routes? See https://vercel.com/docs/build-output-api/v3/configuration#source-route\nIf not, it is discouraged to use `vercel.config.routes` to override routes. Prefer using `vercel.rewrites` and `vercel.redirects`.'
146
153
  );
147
154
  }
155
+ let userRoutes = [];
156
+ let buildRoutes = [];
157
+ if ((_i = (_h = resolvedConfig.vercel) == null ? void 0 : _h.config) == null ? void 0 : _i.routes) {
158
+ const norm = normalizeRoutes(resolvedConfig.vercel.config.routes);
159
+ if (norm.error) {
160
+ throw norm.error;
161
+ }
162
+ userRoutes = norm.routes ?? [];
163
+ }
164
+ if (routes) {
165
+ const norm = normalizeRoutes(routes);
166
+ if (norm.error) {
167
+ throw norm.error;
168
+ }
169
+ buildRoutes = norm.routes ?? [];
170
+ }
171
+ const cleanRoutes = mergeRoutes({
172
+ userRoutes,
173
+ builds: [
174
+ {
175
+ use: "@vercel/node",
176
+ entrypoint: "index.js",
177
+ routes: buildRoutes
178
+ }
179
+ ]
180
+ });
148
181
  return vercelOutputConfigSchema.parse({
149
182
  version: 3,
150
- ...(_h = resolvedConfig.vercel) == null ? void 0 : _h.config,
151
- routes: [
152
- ...routes ?? [],
153
- ...((_j = (_i = resolvedConfig.vercel) == null ? void 0 : _i.config) == null ? void 0 : _j.routes) ?? []
154
- ],
183
+ ...(_j = resolvedConfig.vercel) == null ? void 0 : _j.config,
184
+ routes: cleanRoutes,
155
185
  overrides: {
156
186
  ...(_l = (_k = resolvedConfig.vercel) == null ? void 0 : _k.config) == null ? void 0 : _l.overrides,
157
187
  ...overrides
@@ -161,11 +191,11 @@ function getConfig(resolvedConfig, rewrites, overrides) {
161
191
  function getConfigDestination(resolvedConfig) {
162
192
  return path2.join(getOutput(resolvedConfig), "config.json");
163
193
  }
164
- async function writeConfig(resolvedConfig, rewrites, overrides) {
194
+ async function writeConfig(resolvedConfig, rewrites, overrides, headers) {
165
195
  await fs.writeFile(
166
196
  getConfigDestination(resolvedConfig),
167
197
  JSON.stringify(
168
- getConfig(resolvedConfig, rewrites, overrides),
198
+ getConfig(resolvedConfig, rewrites, overrides, headers),
169
199
  void 0,
170
200
  2
171
201
  ),
@@ -218,6 +248,21 @@ var vercelOutputVcConfigSchema = z2.union([
218
248
 
219
249
  // src/build.ts
220
250
  import fs2, { copyFile } from "fs/promises";
251
+ import _eval from "eval";
252
+
253
+ // src/schemas/exports.ts
254
+ import { z as z3 } from "zod";
255
+ var vercelEndpointExports = z3.object({
256
+ edge: z3.boolean().optional(),
257
+ headers: z3.record(z3.string()).optional(),
258
+ isr: z3.object({
259
+ expiration: z3.number().or(z3.literal(false))
260
+ }).optional()
261
+ });
262
+
263
+ // src/build.ts
264
+ import { generateCode, loadFile } from "magicast";
265
+ import { getNodeVersion } from "@vercel/build-utils";
221
266
  function getAdditionalEndpoints(resolvedConfig) {
222
267
  var _a;
223
268
  return (((_a = resolvedConfig.vercel) == null ? void 0 : _a.additionalEndpoints) ?? []).map((e) => ({
@@ -330,7 +375,7 @@ async function buildFn(resolvedConfig, entry, buildOptions) {
330
375
  }
331
376
  const ctx = { found: false, index: "" };
332
377
  options.plugins.push(vercelOgPlugin(ctx));
333
- await build(options);
378
+ const output = await build(options);
334
379
  if (ctx.found && ctx.index) {
335
380
  const dir = dirname(ctx.index);
336
381
  const externalFiles = await glob(`${dir}/*.{ttf,wasm}`);
@@ -346,6 +391,7 @@ async function buildFn(resolvedConfig, entry, buildOptions) {
346
391
  }
347
392
  }
348
393
  await writeVcConfig(resolvedConfig, entry.destination, Boolean(entry.edge));
394
+ return output;
349
395
  }
350
396
  async function writeVcConfig(resolvedConfig, destination, edge) {
351
397
  var _a;
@@ -354,6 +400,7 @@ async function writeVcConfig(resolvedConfig, destination, edge) {
354
400
  destination,
355
401
  ".vc-config.json"
356
402
  );
403
+ const nodeVersion = await getNodeVersion(getOutput(resolvedConfig));
357
404
  await fs2.writeFile(
358
405
  vcConfig,
359
406
  JSON.stringify(
@@ -362,7 +409,7 @@ async function writeVcConfig(resolvedConfig, destination, edge) {
362
409
  runtime: "edge",
363
410
  entrypoint: "index.js"
364
411
  } : {
365
- runtime: "nodejs16.x",
412
+ runtime: nodeVersion.runtime,
366
413
  handler: "index.js",
367
414
  maxDuration: (_a = resolvedConfig.vercel) == null ? void 0 : _a.defaultMaxDuration,
368
415
  launcherType: "Nodejs",
@@ -381,28 +428,102 @@ function getSourceAndDestination(destination) {
381
428
  }
382
429
  return path3.posix.resolve("/", destination, ":match*");
383
430
  }
431
+ async function removeDefaultExport(filepath) {
432
+ const mod = await loadFile(filepath);
433
+ try {
434
+ delete mod.exports.default;
435
+ } catch (_) {
436
+ }
437
+ return generateCode(mod).code;
438
+ }
439
+ async function extractExports(filepath) {
440
+ var _a;
441
+ const contents = await removeDefaultExport(filepath);
442
+ const buildOptions = {
443
+ ...standardBuildOptions,
444
+ minify: false,
445
+ write: false,
446
+ legalComments: "none"
447
+ };
448
+ buildOptions.stdin = {
449
+ sourcefile: filepath,
450
+ contents,
451
+ loader: filepath.endsWith(".ts") ? "ts" : filepath.endsWith(".tsx") ? "tsx" : filepath.endsWith(".js") ? "js" : filepath.endsWith(".jsx") ? "jsx" : "default",
452
+ resolveDir: dirname(filepath)
453
+ };
454
+ try {
455
+ const output = await build(buildOptions);
456
+ const bundle = new TextDecoder().decode((_a = output.outputFiles[0]) == null ? void 0 : _a.contents);
457
+ return vercelEndpointExports.parse(_eval(bundle, filepath, {}, true));
458
+ } catch (e) {
459
+ console.warn(`Warning: failed to read exports of '${filepath}'`, e);
460
+ }
461
+ }
384
462
  async function buildEndpoints(resolvedConfig) {
385
463
  const entries = getEntries(resolvedConfig);
386
464
  for (const entry of entries) {
465
+ if (typeof entry.source === "string") {
466
+ const exports = await extractExports(entry.source);
467
+ if (exports) {
468
+ if (entry.headers || exports.headers) {
469
+ entry.headers = {
470
+ ...exports.headers,
471
+ ...entry.headers
472
+ };
473
+ }
474
+ if (entry.edge !== void 0 && exports.edge !== void 0) {
475
+ throw new Error(
476
+ `edge configuration should be defined either in the endpoint itself or through Vite config, not both ('${entry.source}')`
477
+ );
478
+ }
479
+ if (exports.edge !== void 0) {
480
+ entry.edge = exports.edge;
481
+ }
482
+ if (entry.isr !== void 0 && exports.isr !== void 0) {
483
+ throw new Error(
484
+ `isr configuration should be defined either in the endpoint itself or through Vite config, not both ('${entry.source}')`
485
+ );
486
+ }
487
+ if (exports.isr) {
488
+ entry.isr = exports.isr;
489
+ }
490
+ }
491
+ }
387
492
  await buildFn(resolvedConfig, entry);
388
493
  }
389
- return entries.filter((e) => e.addRoute !== false).map((e) => e.destination.replace(/\.func$/, "")).map((destination) => ({
390
- source: getSourceAndDestination(destination),
391
- destination: getSourceAndDestination(destination)
392
- }));
494
+ const isrEntries = entries.filter((e) => e.isr).map(
495
+ (e) => [
496
+ e.destination.replace(/\.func$/, ""),
497
+ { expiration: e.isr.expiration }
498
+ ]
499
+ );
500
+ return {
501
+ rewrites: entries.filter((e) => e.addRoute !== false).map((e) => e.destination.replace(/\.func$/, "")).map((destination) => ({
502
+ source: getSourceAndDestination(destination),
503
+ destination: getSourceAndDestination(destination)
504
+ })),
505
+ isr: Object.fromEntries(isrEntries),
506
+ headers: entries.filter((e) => e.headers).map((e) => ({
507
+ source: "/" + e.destination.replace(/\.func$/, ""),
508
+ headers: Object.entries(e.headers).map(([key, value]) => ({
509
+ key,
510
+ value
511
+ }))
512
+ }))
513
+ };
393
514
  }
394
515
 
395
516
  // src/prerender.ts
396
517
  import path4 from "path";
397
518
 
398
519
  // src/schemas/config/prerender-config.ts
399
- import { z as z3 } from "zod";
400
- var vercelOutputPrerenderConfigSchema = z3.object({
401
- expiration: z3.union([z3.number().int().positive(), z3.literal(false)]),
402
- group: z3.number().int().optional(),
403
- bypassToken: z3.string().optional(),
404
- fallback: z3.string().optional(),
405
- allowQuery: z3.array(z3.string()).optional()
520
+ import { z as z4 } from "zod";
521
+ var vercelOutputPrerenderConfigSchema = z4.object({
522
+ expiration: z4.union([z4.number().int().positive(), z4.literal(false)]),
523
+ group: z4.number().int().optional(),
524
+ bypassToken: z4.string().optional(),
525
+ fallback: z4.string().optional(),
526
+ allowQuery: z4.array(z4.string()).optional()
406
527
  }).strict();
407
528
 
408
529
  // src/prerender.ts
@@ -456,8 +577,12 @@ function getPrerenderSymlinkInfo(resolvedConfig, destination, target) {
456
577
  )
457
578
  };
458
579
  }
459
- async function buildPrerenderConfigs(resolvedConfig) {
460
- const isr = await getIsrConfig(resolvedConfig);
580
+ async function buildPrerenderConfigs(resolvedConfig, extractedIsr) {
581
+ const isr = Object.assign(
582
+ {},
583
+ extractedIsr,
584
+ await getIsrConfig(resolvedConfig)
585
+ );
461
586
  const entries = Object.entries(isr);
462
587
  const rewrites = [];
463
588
  for (const [destination, { symlink, route, ...isr2 }] of entries) {
@@ -522,12 +647,17 @@ function vercelPlugin() {
522
647
  }
523
648
  const overrides = await execPrerender(resolvedConfig);
524
649
  const userOverrides = await computeStaticHtmlOverrides(resolvedConfig);
525
- const rewrites = await buildEndpoints(resolvedConfig);
526
- rewrites.push(...await buildPrerenderConfigs(resolvedConfig));
527
- await writeConfig(resolvedConfig, rewrites, {
528
- ...userOverrides,
529
- ...overrides
530
- });
650
+ const { rewrites, isr, headers } = await buildEndpoints(resolvedConfig);
651
+ rewrites.push(...await buildPrerenderConfigs(resolvedConfig, isr));
652
+ await writeConfig(
653
+ resolvedConfig,
654
+ rewrites,
655
+ {
656
+ ...userOverrides,
657
+ ...overrides
658
+ },
659
+ headers
660
+ );
531
661
  }
532
662
  };
533
663
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-vercel",
3
- "version": "0.3.6",
3
+ "version": "2.0.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -20,9 +20,9 @@
20
20
  "repository": "https://github.com/magne4000/vite-plugin-vercel",
21
21
  "license": "MIT",
22
22
  "peerDependencies": {
23
- "vite": "^4.2.0",
24
23
  "vike": "*",
25
- "@vite-plugin-vercel/vike": "0.4.1"
24
+ "vite": "^4.2.0",
25
+ "@vite-plugin-vercel/vike": "2.0.0"
26
26
  },
27
27
  "peerDependenciesMeta": {
28
28
  "@vite-plugin-vercel/vike": {
@@ -33,22 +33,25 @@
33
33
  }
34
34
  },
35
35
  "devDependencies": {
36
- "@types/node": "^16.18.48",
37
- "@typescript-eslint/eslint-plugin": "^6.6.0",
38
- "@typescript-eslint/parser": "^6.6.0",
39
- "eslint": "^8.48.0",
36
+ "@types/node": "^16.18.58",
37
+ "@typescript-eslint/eslint-plugin": "^6.7.5",
38
+ "@typescript-eslint/parser": "^6.7.5",
39
+ "eslint": "^8.51.0",
40
40
  "tsup": "^7.2.0",
41
41
  "typescript": "^5.2.2",
42
- "vite": "^4.4.9",
43
- "vike": "^0.4.142",
44
- "@vite-plugin-vercel/vike": "0.4.1"
42
+ "vike": "^0.4.143",
43
+ "vite": "^4.4.11",
44
+ "@vite-plugin-vercel/vike": "2.0.0"
45
45
  },
46
46
  "dependencies": {
47
47
  "@brillout/libassert": "^0.5.8",
48
+ "@vercel/build-utils": "^7.2.2",
48
49
  "@vercel/routing-utils": "^3.0.0",
49
- "esbuild": "^0.19.2",
50
+ "esbuild": "^0.19.4",
51
+ "eval": "^0.1.8",
50
52
  "fast-glob": "^3.3.1",
51
- "zod": "^3.22.2"
53
+ "magicast": "^0.3.0",
54
+ "zod": "^3.22.4"
52
55
  },
53
56
  "scripts": {
54
57
  "build": "tsup",