vite-plugin-vercel 11.0.0-beta.8 → 11.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,14 +1,8 @@
1
1
  # vite-plugin-vercel
2
2
 
3
- > [!NOTE]
4
- > You are on the [Vite Environment API](https://vite.dev/guide/api-environment.html#environment-configuration) beta branch (v10). Check out [v9 branch](https://github.com/magne4000/vite-plugin-vercel/tree/v9) for current stable version.
5
-
6
3
  Vercel adapter for [Vite](https://vitejs.dev/).
7
4
 
8
- Bundle your Vite application as supported by [Vercel Output API (v3)](https://vercel.com/docs/build-output-api/v3).
9
-
10
- > [!NOTE]
11
- > This plugin is mostly a re-export of [`@photonjs/vercel`](https://github.com/photon-js/photon/tree/main/packages/adapter-vercel)
5
+ Bundle your Vite application as supported by [Vercel Output API](https://vercel.com/docs/build-output-api).
12
6
 
13
7
  ## Install
14
8
 
@@ -68,7 +62,7 @@ export default defineConfig({
68
62
  ```
69
63
 
70
64
  > [!NOTE]
71
- > `@vercel/build` currently forces the building of files in the _/api_ folder, with no way to disable this behavior.
65
+ > `@vercel/build` forces the building of files in the _/api_ folder, with no way to disable this behavior.
72
66
  > It's recommended to place your files in a different folder.
73
67
 
74
68
  ### Configure endpoints
@@ -114,7 +108,7 @@ import vercel from 'vite-plugin-vercel';
114
108
 
115
109
  export default defineConfig({
116
110
  plugins: [vercel({
117
- // All the followings optional
111
+ // All the followings are optional
118
112
 
119
113
  /**
120
114
  * How long Functions should be allowed to run for every request, in seconds.
@@ -165,7 +159,7 @@ export default defineConfig({
165
159
  trailingSlash: true,
166
160
  /**
167
161
  * Use `getVercelEntries` for mapping your filesystem routes to entries.
168
- * If you are interfacing this plugin with a framework, entries can also be added through the Photon API
162
+ * If you are interfacing this plugin with a framework, entries can also be added through the {@link https://github.com/photon-js/universal-deploy | universal-deploy} API
169
163
  */
170
164
  entries: {
171
165
  root: {
@@ -209,7 +203,7 @@ Related documentation: https://vercel.com/docs/edge-network/headers/request-head
209
203
 
210
204
  ## Migrations
211
205
 
212
- - [Migration from v9 to v10](https://github.com/magne4000/vite-plugin-vercel/blob/main/MIGRATION.md)
206
+ - [Migration from v9 to v11](https://github.com/magne4000/vite-plugin-vercel/blob/main/MIGRATION.md)
213
207
 
214
208
  ## Demo
215
209
 
package/dist/api.js CHANGED
@@ -1,3 +1,65 @@
1
- import { n as getVercelAPI, t as createAPI } from "./api-DR2y7JVQ.js";
1
+ import { t as assert } from "./assert-vtiLgQIb.js";
2
2
 
3
+ //#region src/api.ts
4
+ function createAPI(outfiles, pluginConfig) {
5
+ return {
6
+ getOutFiles() {
7
+ return outfiles;
8
+ },
9
+ get config() {
10
+ pluginConfig.config ??= {};
11
+ return pluginConfig.config;
12
+ },
13
+ get defaultMaxDuration() {
14
+ return pluginConfig.defaultMaxDuration;
15
+ },
16
+ set defaultMaxDuration(value) {
17
+ pluginConfig.defaultMaxDuration = value;
18
+ },
19
+ get expiration() {
20
+ return pluginConfig.expiration;
21
+ },
22
+ set expiration(value) {
23
+ pluginConfig.expiration = value;
24
+ },
25
+ get rewrites() {
26
+ pluginConfig.rewrites ??= [];
27
+ return pluginConfig.rewrites;
28
+ },
29
+ get headers() {
30
+ pluginConfig.headers ??= [];
31
+ return pluginConfig.headers;
32
+ },
33
+ get redirects() {
34
+ pluginConfig.redirects ??= [];
35
+ return pluginConfig.redirects;
36
+ },
37
+ get cleanUrls() {
38
+ return pluginConfig.cleanUrls;
39
+ },
40
+ set cleanUrls(value) {
41
+ pluginConfig.cleanUrls = value;
42
+ },
43
+ get trailingSlash() {
44
+ return pluginConfig.trailingSlash;
45
+ },
46
+ set trailingSlash(value) {
47
+ pluginConfig.trailingSlash = value;
48
+ },
49
+ get defaultSupportsResponseStreaming() {
50
+ return pluginConfig.defaultSupportsResponseStreaming;
51
+ },
52
+ set defaultSupportsResponseStreaming(value) {
53
+ pluginConfig.defaultSupportsResponseStreaming = value;
54
+ }
55
+ };
56
+ }
57
+ function getVercelAPI(pluginContextOrServer) {
58
+ const vpv = ("environment" in pluginContextOrServer ? pluginContextOrServer.environment.config : pluginContextOrServer.config).plugins.find((p) => p.name === "vite-plugin-vercel:api");
59
+ assert(vpv, "Could not find vite-plugin-vercel:api plugin");
60
+ assert(vpv.api, "Missing `api`. Make sure vite-plugin-vercel is up-to-date");
61
+ return vpv.api("environment" in pluginContextOrServer ? pluginContextOrServer : void 0);
62
+ }
63
+
64
+ //#endregion
3
65
  export { createAPI, getVercelAPI };
@@ -0,0 +1,8 @@
1
+ //#region src/utils/assert.ts
2
+ function assert(condition, errorMessage) {
3
+ if (condition) return;
4
+ throw new Error(`[vite-plugin-vercel] ${errorMessage}`);
5
+ }
6
+
7
+ //#endregion
8
+ export { assert as t };
package/dist/index.d.ts CHANGED
@@ -1,18 +1,31 @@
1
1
  import { EntryMeta } from "@universal-deploy/store";
2
2
 
3
3
  //#region src/index.d.ts
4
-
4
+ interface GetVercelEntriesOptions {
5
+ /**
6
+ * Map entries to URL patterns
7
+ * @example
8
+ * // When dir: "endpoints/myapi" and destination: "api"
9
+ * // endpoints/myapi/page.ts -> /api/page
10
+ * // endpoints/myapi/name/[name].ts -> /api/name/*
11
+ */
12
+ destination?: string;
13
+ /**
14
+ * If true, parse entries exports for per-file settings.
15
+ * Entries can export `edge`, `isr`, `headers` and `streaming`. See README for more details
16
+ *
17
+ * @default true
18
+ */
19
+ tryParseExports?: boolean;
20
+ }
5
21
  /**
6
22
  * Scans the filesystem for entry points.
7
- * @experimental
23
+ * @beta
8
24
  */
9
25
  declare function getVercelEntries(dir: string, {
10
26
  destination,
11
27
  tryParseExports
12
- }: {
13
- destination?: string | undefined;
14
- tryParseExports?: boolean | undefined;
15
- }): Promise<EntryMeta[]>;
28
+ }: GetVercelEntriesOptions): Promise<EntryMeta[]>;
16
29
  declare function extractExports(filepath: string): Promise<{
17
30
  edge?: boolean | undefined;
18
31
  headers?: Record<string, string> | undefined;
@@ -22,4 +35,4 @@ declare function extractExports(filepath: string): Promise<{
22
35
  } | undefined;
23
36
  } | null>;
24
37
  //#endregion
25
- export { extractExports, getVercelEntries };
38
+ export { GetVercelEntriesOptions, extractExports, getVercelEntries };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as pathRelativeTo } from "./path-B4ThGm96.js";
1
+ import { t as pathRelativeTo } from "./path-DoLvGKnz.js";
2
2
  import path from "node:path";
3
3
  import { vercelEndpointExports } from "@vite-plugin-vercel/schemas";
4
4
  import { fromNextFs } from "convert-route/next-fs";
@@ -10,7 +10,7 @@ import { normalizePath } from "vite";
10
10
  //#region src/index.ts
11
11
  /**
12
12
  * Scans the filesystem for entry points.
13
- * @experimental
13
+ * @beta
14
14
  */
15
15
  async function getVercelEntries(dir, { destination = dir, tryParseExports = true }) {
16
16
  const normalizedDir = normalizePath(dir);
@@ -31,7 +31,7 @@ async function getVercelEntries(dir, { destination = dir, tryParseExports = true
31
31
  if (xports?.isr) entry.vercel.isr = xports.isr;
32
32
  if (xports?.headers) entry.vercel.headers = xports.headers;
33
33
  if (xports?.streaming) entry.vercel.streaming = xports.streaming;
34
- entry.pattern = entryToRou3(key);
34
+ entry.route = entryToRou3(key);
35
35
  if (key.includes("[...")) entry.vercel.enforce = "post";
36
36
  entryPoints.push(entry);
37
37
  }
package/dist/vite.js CHANGED
@@ -1,16 +1,18 @@
1
- import { t as pathRelativeTo$1 } from "./path-B4ThGm96.js";
2
- import { n as getVercelAPI, r as assert, t as createAPI } from "./api-DR2y7JVQ.js";
1
+ import { t as pathRelativeTo$1 } from "./path-DoLvGKnz.js";
2
+ import { t as assert } from "./assert-vtiLgQIb.js";
3
+ import { createAPI, getVercelAPI } from "./api.js";
3
4
  import { builtinModules } from "node:module";
4
5
  import path from "node:path";
5
6
  import { vercelOutputConfigSchema, vercelOutputPrerenderConfigSchema, vercelOutputVcConfigSchema } from "@vite-plugin-vercel/schemas";
6
7
  import { fromRou3 } from "convert-route/rou3";
7
8
  import { BuildEnvironment, createRunnableDevEnvironment, mergeConfig, normalizePath } from "vite";
8
- import { catchAll, devServer } from "@universal-deploy/store/vite";
9
- import { store } from "@universal-deploy/store";
9
+ import { catchAll, devServer } from "@universal-deploy/vite";
10
+ import { addEntry, getAllEntries } from "@universal-deploy/store";
10
11
  import fs, { existsSync, readFileSync, writeFileSync } from "node:fs";
11
12
  import { copyFile, cp, rmdir, unlink } from "node:fs/promises";
12
13
  import { findRoot } from "@manypkg/find-root";
13
14
  import { nodeFileTrace, resolve } from "@vercel/nft";
15
+ import { transform } from "oxc-transform";
14
16
  import { build } from "rolldown";
15
17
  import { cpus } from "node:os";
16
18
  import { externals } from "nf3/plugin";
@@ -36,20 +38,65 @@ function getBuildEnvNames(pluginConfig) {
36
38
  */
37
39
  function dedupeRoutes() {
38
40
  const entriesToKeep = [];
39
- const entriesGroupedByModuleId = groupBy(store.entries, (e) => e.id);
41
+ const entriesGroupedByModuleId = groupBy(getAllEntries(), (e) => e.id);
40
42
  for (const entries of entriesGroupedByModuleId.values()) {
41
43
  let groupedEntry;
42
44
  for (const entry of entries) if (entry.vercel && Object.keys(entry.vercel).length > 0) {
43
- if (!Array.isArray(entry.pattern)) entry.pattern = [entry.pattern];
45
+ if (!Array.isArray(entry.route)) entry.route = [entry.route];
44
46
  entriesToKeep.push(entry);
45
47
  } else if (!groupedEntry) {
46
48
  groupedEntry = structuredClone(entry);
47
- if (!Array.isArray(groupedEntry.pattern)) groupedEntry.pattern = [groupedEntry.pattern];
49
+ if (!Array.isArray(groupedEntry.route)) groupedEntry.route = [groupedEntry.route];
48
50
  entriesToKeep.push(groupedEntry);
49
- } else groupedEntry.pattern.push(...[entry.pattern].flat());
51
+ } else groupedEntry.route.push(...[entry.route].flat());
50
52
  }
51
53
  return entriesToKeep;
52
54
  }
55
+ /**
56
+ * Sorts routes by specificity.
57
+ *
58
+ * Specificity rules:
59
+ * 1. Segment type priority at each position: Static > Dynamic/Param > Catch-all.
60
+ * 2. Total route length: Longer routes are generally more specific.
61
+ * 3. Prefix match exception: A shorter static route is more specific than a longer route
62
+ * that only adds a catch-all segment to the same prefix.
63
+ * 4. Alphabetical order: Final tie-breaker for static segments at the same position.
64
+ *
65
+ * @param routes - Array of routes to sort.
66
+ * @returns A new array of routes sorted from most specific to least specific.
67
+ */
68
+ function sortRoutes(routes) {
69
+ return [...routes].sort((a, b) => {
70
+ const lenA = a.pathname.length;
71
+ const lenB = b.pathname.length;
72
+ const commonLen = Math.min(lenA, lenB);
73
+ for (let i = 0; i < commonLen; i++) {
74
+ const segA = a.pathname[i];
75
+ const segB = b.pathname[i];
76
+ if (segA.value && !segB.value) return -1;
77
+ if (!segA.value && segB.value) return 1;
78
+ if (segA.value && segB.value) {
79
+ if (segA.value !== segB.value) {
80
+ if (lenA !== lenB) return lenB - lenA;
81
+ return segA.value.localeCompare(segB.value);
82
+ }
83
+ }
84
+ if (!segA.optional && segB.optional) return -1;
85
+ if (segA.optional && !segB.optional) return 1;
86
+ if (segA.catchAll && segB.catchAll) {
87
+ if (!segA.catchAll.greedy && segB.catchAll.greedy) return -1;
88
+ if (segA.catchAll.greedy && !segB.catchAll.greedy) return 1;
89
+ }
90
+ if (segA.catchAll && !segB.catchAll) return 1;
91
+ if (!segA.catchAll && segB.catchAll) return -1;
92
+ }
93
+ if (lenA !== lenB) {
94
+ if (lenA > lenB) return a.pathname[lenB].catchAll ? 1 : -1;
95
+ return b.pathname[lenA].catchAll ? -1 : 1;
96
+ }
97
+ return 0;
98
+ });
99
+ }
53
100
  function groupBy(list, fn, selector) {
54
101
  return Array.from(list).reduce((acc, curr) => {
55
102
  const key = fn(curr);
@@ -68,13 +115,36 @@ function removeExtension$1(subject) {
68
115
 
69
116
  //#endregion
70
117
  //#region src/utils/destination.ts
118
+ function parseViteId(id) {
119
+ const [path, query] = id.split("?");
120
+ return {
121
+ path: decodeURIComponent(path),
122
+ query
123
+ };
124
+ }
125
+ function extractBestPath({ path, query }) {
126
+ if (!query) return path;
127
+ return query.split("&").map(decodeURIComponent).find((p) => p.includes("/")) || path;
128
+ }
129
+ function shortenPath(path, maxSegments = 2) {
130
+ return removeExtension$1(path.split(/[\\/]/).filter(Boolean).slice(-maxSegments).join("_"));
131
+ }
132
+ function mapPath(path, maxSegments = 2) {
133
+ const nodeModuleMatch = path.match(/node_modules[\\/](.+?)([\\/].*)?$/);
134
+ if (nodeModuleMatch) return `${nodeModuleMatch[1].split(/[\\/]/)[0]}_${removeExtension$1(nodeModuleMatch[2]?.split(/[\\/]/).pop() || "index")}`;
135
+ return shortenPath(path, maxSegments);
136
+ }
137
+ function hashString(str, length = 6) {
138
+ let hash = 5381;
139
+ for (let i = 0; i < str.length; i++) hash = hash * 33 ^ str.charCodeAt(i);
140
+ hash = hash >>> 0;
141
+ return hash.toString(36).slice(0, length);
142
+ }
143
+ function uniqueViteName(id, entry, maxSegments = 2) {
144
+ return removeExtension$1(`${mapPath(extractBestPath(parseViteId(id)), maxSegments)}_${hashString(JSON.stringify(entry))}`.replace(/[^a-zA-Z0-9\-_[\]/]/g, "-"));
145
+ }
71
146
  function entryDestinationDefault(root, entry) {
72
- let rel = pathRelativeTo$1(entry.id, root);
73
- if (rel.includes("node_modules")) {
74
- const split = rel.split("node_modules");
75
- rel = split[split.length - 1];
76
- }
77
- return `${removeExtension$1(rel).replace(/[^a-zA-Z0-9\-_[\]/]/g, "-")}`;
147
+ return uniqueViteName(pathRelativeTo$1(entry.id, root), entry);
78
148
  }
79
149
  function entryDestination(root, entry, postfix) {
80
150
  return `${path.posix.join("functions/", entryDestinationDefault(root, entry))}${postfix}`;
@@ -93,10 +163,10 @@ function apiPlugin(pluginConfig) {
93
163
  applyToEnvironment({ name }) {
94
164
  return name === envNames.edge || name === envNames.node;
95
165
  },
96
- writeBundle(_opts, bundle$2) {
166
+ writeBundle(_opts, bundle) {
97
167
  const root = this.environment.config.root ?? process.cwd();
98
168
  const entryMapByDestination = new Map(dedupeRoutes().map((e) => [entryDestination(root, e, ".func/index"), e]));
99
- for (const [key, value] of Object.entries(bundle$2)) if (value.type === "chunk" && entryMapByDestination.has(removeExtension$1(key))) outfiles.push({
169
+ for (const [key, value] of Object.entries(bundle)) if (value.type === "chunk" && entryMapByDestination.has(removeExtension$1(key))) outfiles.push({
100
170
  type: "chunk",
101
171
  root: this.environment.config.root,
102
172
  outdir: this.environment.config.build.outDir,
@@ -177,8 +247,8 @@ function basicBundlePlugin(pluginConfig) {
177
247
  name: "vite-plugin-vercel:bundle",
178
248
  enforce: "post",
179
249
  apply: "build",
180
- generateBundle(_opts, bundle$2) {
181
- for (const b of Object.values(bundle$2)) {
250
+ generateBundle(_opts, bundle) {
251
+ for (const b of Object.values(bundle)) {
182
252
  const outFile = joinAbsolute(this.environment, this.environment.config.build.outDir, b.fileName);
183
253
  if (b.type === "asset") {
184
254
  const originalFileNames = b.originalFileNames.map((relativePath) => path.resolve(this.environment.config.root, relativePath));
@@ -199,9 +269,9 @@ function basicBundlePlugin(pluginConfig) {
199
269
  if (!isVercelLastBuildStep(this.environment, pluginConfig)) return;
200
270
  this.environment.logger.info("Creating Vercel bundles...");
201
271
  const outfiles = getVercelAPI(this).getOutFiles();
202
- const filesToKeep = [];
203
- for (const outfile of outfiles) if (outfile.type === "chunk") filesToKeep.push(...await bundle$1(this, bundledAssets, outfile));
204
- await cleanup$1(filesToKeep, bundledChunks);
272
+ const filesToKeepP = [];
273
+ for (const outfile of outfiles) if (outfile.type === "chunk") filesToKeepP.push(bundle$1(this, bundledAssets, outfile));
274
+ await cleanup$1((await Promise.all(filesToKeepP)).flat(), bundledChunks);
205
275
  }
206
276
  },
207
277
  sharedDuringBuild: true
@@ -222,8 +292,8 @@ async function bundle$1(pluginContext, bundledAssets, outfile) {
222
292
  const buildOptions = {};
223
293
  buildOptions.output = {
224
294
  format: "esm",
225
- legalComments: "none",
226
- inlineDynamicImports: true
295
+ comments: { legal: false },
296
+ codeSplitting: false
227
297
  };
228
298
  buildOptions.checks = { pluginTimings: false };
229
299
  buildOptions.input = [source];
@@ -243,13 +313,6 @@ async function bundle$1(pluginContext, bundledAssets, outfile) {
243
313
  } else {
244
314
  buildOptions.platform = "node";
245
315
  buildOptions.output.file = destination.replace(/\.js$/, ".mjs");
246
- buildOptions.output.banner = `import { createRequire as topLevelCreateRequire } from 'node:module';
247
- import { dirname as topLevelDirname } from 'node:path';
248
- import { fileURLToPath as topLevelFileURLToPath } from 'node:url';
249
- var require = topLevelCreateRequire(import.meta.url);
250
- var __filename = topLevelFileURLToPath(import.meta.url);
251
- var __dirname = topLevelDirname(__filename);
252
- `;
253
316
  }
254
317
  try {
255
318
  await build(buildOptions);
@@ -275,23 +338,9 @@ var __dirname = topLevelDirname(__filename);
275
338
  "**/node_modules/webpack5/**/*"
276
339
  ],
277
340
  async readFile(filepath) {
278
- if (filepath.endsWith(".ts") || filepath.endsWith(".tsx")) return (await build({
279
- output: {
280
- format: "esm",
281
- minify: false
282
- },
283
- external: /.*/,
284
- platform: "node",
285
- logLevel: "info",
286
- plugins: [],
287
- transform: { define: {
288
- "process.env.NODE_ENV": "\"production\"",
289
- "import.meta.env.NODE_ENV": "\"production\""
290
- } },
291
- input: [entryFilePath],
292
- write: false
293
- })).output[0].code;
294
- return readFileSync(filepath, "utf-8");
341
+ const code = readFileSync(filepath, "utf-8");
342
+ if (filepath.endsWith(".ts") || filepath.endsWith(".tsx")) return (await transform(filepath, code, { target: "es2022" })).code;
343
+ return code;
295
344
  }
296
345
  });
297
346
  for (const file of fileList) if (reasons.has(file) && reasons.get(file)?.type.includes("asset") && !file.endsWith(".js") && !file.endsWith(".cjs") && !file.endsWith(".mjs") && !file.endsWith("package.json")) {
@@ -343,6 +392,7 @@ async function cleanup$1(filesToKeep, bundledChunks) {
343
392
  //#endregion
344
393
  //#region src/plugins/bundle/nf3.ts
345
394
  function nf3BundlePlugin(pluginConfig) {
395
+ console.warn("[vite-plugin-vercel] bundleStrategy 'nf3' is highly experimental. Use at your own risk; support is not provided for related issues.");
346
396
  const envNames = getBuildEnvNames(pluginConfig);
347
397
  const externalsPlugin = externals({});
348
398
  delete externalsPlugin.buildEnd;
@@ -361,15 +411,15 @@ function nf3BundlePlugin(pluginConfig) {
361
411
  async writeBundle(_, output) {
362
412
  const isEdge = this.environment.name === envNames.edge;
363
413
  const config = this.environment.config;
364
- const outDir$1 = normalizePath(path.isAbsolute(config.build.outDir) ? config.build.outDir : path.join(config.root, config.build.outDir));
414
+ const outDir = normalizePath(path.isAbsolute(config.build.outDir) ? config.build.outDir : path.join(config.root, config.build.outDir));
365
415
  const entries = Object.entries(output).filter((e) => "isEntry" in e[1] && e[1].isEntry).map((e) => ({
366
416
  name: e[1].name,
367
417
  fileName: e[1].fileName,
368
- outPath: path.join(outDir$1, e[1].fileName)
418
+ outPath: path.join(outDir, e[1].fileName)
369
419
  }));
370
420
  if (entries.length === 0) return;
371
421
  const outPaths = entries.map((entry) => entry.outPath);
372
- const input = Object.fromEntries(outPaths.map((e) => [removeExtension(pathRelativeTo(e, outDir$1)), e]));
422
+ const input = Object.fromEntries(outPaths.map((e) => [removeExtension(pathRelativeTo(e, outDir)), e]));
373
423
  const limit = pLimit(Math.max(1, Math.ceil(cpus().length / 2)));
374
424
  const nonVitePlugins = this.environment.config.plugins.filter((p) => {
375
425
  return !p.name.startsWith("vite:") && p.name !== "alias" && p.name !== "commonjs" && p.name !== "nitro:externals";
@@ -378,12 +428,12 @@ function nf3BundlePlugin(pluginConfig) {
378
428
  return rest;
379
429
  });
380
430
  const localOutput = (await Promise.all(Object.values(input).map((entryPath) => limit(async () => {
381
- const outDir$2 = path.dirname(entryPath);
431
+ const outDir = path.dirname(entryPath);
382
432
  return { output: (await bundle({
383
433
  plugins: nonVitePlugins,
384
434
  isEdge,
385
435
  input: { index: entryPath },
386
- outDir: outDir$2,
436
+ outDir,
387
437
  externals: {
388
438
  conditions: this.environment.config.resolve.conditions,
389
439
  rootDir: this.environment.config.root,
@@ -396,7 +446,7 @@ function nf3BundlePlugin(pluginConfig) {
396
446
  }
397
447
  })).output.map((o) => ({
398
448
  ...o,
399
- fileName: path.join(outDir$2, o.fileName)
449
+ fileName: path.join(outDir, o.fileName)
400
450
  })) };
401
451
  })))).flatMap((r) => r.output);
402
452
  buildOutput = buildOutput ? [...buildOutput, ...localOutput] : localOutput;
@@ -447,23 +497,20 @@ function removeExtension(subject) {
447
497
 
448
498
  //#endregion
449
499
  //#region src/plugins/clean-outdir.ts
500
+ const outDir$1 = path.posix.join(process.cwd(), ".vercel/output");
450
501
  function vercelCleanupPlugin(pluginConfig) {
451
502
  let alreadyRun = false;
452
- const envNames = getBuildEnvNames(pluginConfig);
453
503
  return {
454
504
  apply: "build",
455
505
  name: "vite-plugin-vercel:cleanup",
456
506
  enforce: "pre",
457
- applyToEnvironment(env) {
458
- return env.name === envNames.client;
459
- },
460
507
  buildStart: {
461
508
  order: "pre",
462
509
  sequential: true,
463
510
  handler() {
464
511
  if (alreadyRun) return;
465
512
  alreadyRun = true;
466
- cleanOutputDirectory(pluginConfig?.outDir && path.isAbsolute(pluginConfig.outDir) ? pluginConfig.outDir : path.join(this.environment.config.root, pluginConfig?.outDir ?? ".vercel/output"));
513
+ cleanOutputDirectory(pluginConfig.outDir ?? outDir$1);
467
514
  }
468
515
  },
469
516
  sharedDuringBuild: true
@@ -493,9 +540,32 @@ function getVcConfig(pluginConfig, filename, options) {
493
540
  });
494
541
  }
495
542
 
543
+ //#endregion
544
+ //#region src/utils/request.ts
545
+ function getOriginalRequest(request) {
546
+ const xOriginalPath = request.headers.get("x-original-path");
547
+ let newUrl = null;
548
+ let newRequest = request;
549
+ if (typeof xOriginalPath === "string") newUrl = new URL(xOriginalPath, request.url).toString();
550
+ if (newUrl && request.url !== newUrl) newRequest = new Request(newUrl, {
551
+ method: request.method,
552
+ headers: request.headers,
553
+ body: request.body,
554
+ mode: request.mode,
555
+ credentials: request.credentials,
556
+ cache: request.cache,
557
+ redirect: request.redirect,
558
+ referrer: request.referrer,
559
+ integrity: request.integrity
560
+ });
561
+ return newRequest;
562
+ }
563
+
496
564
  //#endregion
497
565
  //#region src/plugins/loader.ts
498
- const re_DUMMY = /* @__PURE__ */ new RegExp(`__DUMMY__$`);
566
+ const re_DUMMY = new RegExp(`__DUMMY__$`);
567
+ const re_edge = /[?&]vercel_edge\b/;
568
+ const re_node = /[?&]vercel_node\b/;
499
569
  function loaderPlugin(pluginConfig) {
500
570
  const envNames = getBuildEnvNames(pluginConfig);
501
571
  let root;
@@ -520,19 +590,57 @@ function loaderPlugin(pluginConfig) {
520
590
  name: "vite-plugin-vercel:load-edge",
521
591
  apply: "build",
522
592
  resolveId: {
523
- filter: { id: [/\?edge$/] },
593
+ filter: { id: re_edge },
524
594
  async handler(id, importer, opts) {
525
- const resolved = await this.resolve(id.replace(/\?edge$/, ""), importer, opts);
595
+ const resolved = await this.resolve(id.replace(re_edge, ""), importer, opts);
526
596
  if (!resolved) return null;
527
- return `${resolved.id}?edge`;
597
+ return `${resolved.id}?vercel_edge`;
528
598
  }
529
599
  },
530
600
  load: {
531
- filter: { id: [/\?edge$/] },
601
+ filter: { id: re_edge },
532
602
  async handler(id) {
533
- const mod = id.replace(/\?edge$/, "");
603
+ const mod = id.replace(re_edge, "");
534
604
  return `import mod from ${JSON.stringify(mod)};
535
- const def = mod.fetch;
605
+
606
+ ${getOriginalRequest.toString()}
607
+
608
+ const fn = (r) => {
609
+ return mod.fetch(getOriginalRequest(r));
610
+ };
611
+
612
+ export default fn`;
613
+ }
614
+ }
615
+ },
616
+ {
617
+ name: "vite-plugin-vercel:load-node",
618
+ apply: "build",
619
+ resolveId: {
620
+ filter: { id: re_node },
621
+ async handler(id, importer, opts) {
622
+ const resolved = await this.resolve(id.replace(re_node, ""), importer, opts);
623
+ if (!resolved) return null;
624
+ return `${resolved.id}?vercel_node`;
625
+ }
626
+ },
627
+ load: {
628
+ filter: { id: re_node },
629
+ async handler(id) {
630
+ const mod = id.replace(re_node, "");
631
+ return `import mod from ${JSON.stringify(mod)};
632
+
633
+ ${getOriginalRequest.toString()}
634
+
635
+ if (mod?.fetch) {
636
+ const ori = mod.fetch;
637
+ mod.fetch = (r) => {
638
+ return ori(getOriginalRequest(r));
639
+ }
640
+ }
641
+
642
+ const def = mod?.server?.nodeHandler ?? mod;
643
+
536
644
  export default def;`;
537
645
  }
538
646
  }
@@ -556,8 +664,11 @@ export default def;`;
556
664
  if (name === envNames.node || isEdge) {
557
665
  const entries = dedupeRoutes().filter((e) => (e.vercel?.edge ?? false) === isEdge);
558
666
  return { build: { rollupOptions: {
559
- input: Object.fromEntries(entries.map((e) => [entryDestination(root ?? process.cwd(), e, ".func/index"), isEdge ? `${e.id}?edge` : e.id])),
560
- output: { hoistTransitiveImports: false }
667
+ input: Object.fromEntries(entries.map((e) => [entryDestination(root ?? process.cwd(), e, ".func/index"), isEdge ? `${e.id}?vercel_edge` : `${e.id}?vercel_node`])),
668
+ output: {
669
+ hoistTransitiveImports: false,
670
+ entryFileNames: "[name].js"
671
+ }
561
672
  } } };
562
673
  }
563
674
  }
@@ -582,8 +693,8 @@ export default def;`;
582
693
  source: JSON.stringify(vercelOutputPrerenderConfigSchema.parse(entry.vercel.isr), void 0, 2)
583
694
  });
584
695
  pluginConfig.rewrites ??= [];
585
- for (const pattern of [entry.pattern].flat()) {
586
- const source = toPathToRegexpV6(fromRou3(pattern));
696
+ for (const ir of sortRoutes([entry.route].flat().map((p) => fromRou3(p)))) {
697
+ const source = toPathToRegexpV6(ir);
587
698
  pluginConfig.rewrites.push({
588
699
  enforce: entry.vercel?.enforce,
589
700
  source,
@@ -645,7 +756,13 @@ function getConfig(pluginConfig) {
645
756
  return r;
646
757
  })),
647
758
  redirects: pluginConfig.redirects ? reorderEnforce(pluginConfig.redirects) : void 0,
648
- headers: pluginConfig.headers
759
+ headers: [{
760
+ source: "/(.*)",
761
+ headers: [{
762
+ key: "x-original-path",
763
+ value: "/$1"
764
+ }]
765
+ }, ...pluginConfig.headers ?? []]
649
766
  });
650
767
  if (error) throw error;
651
768
  if (pluginConfig.config?.routes && pluginConfig.config.routes.length > 0 && !pluginConfig.config.routes.every((r) => "continue" in r && r.continue)) console.warn("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 `routes` config to override routes. Prefer using `rewrites` and `redirects`.");
@@ -708,7 +825,9 @@ function setupEnvs(pluginConfig) {
708
825
  handler() {
709
826
  if (!injected) {
710
827
  injected = true;
711
- if (pluginConfig.entries) store.entries.push(...pluginConfig.entries);
828
+ if (pluginConfig.entries) pluginConfig.entries.forEach((entry) => {
829
+ addEntry(entry);
830
+ });
712
831
  }
713
832
  const outDirOverride = pluginConfig.outDir ? { build: { outDir: pluginConfig.outDir } } : {};
714
833
  const environments = {};
@@ -716,7 +835,8 @@ function setupEnvs(pluginConfig) {
716
835
  build: {
717
836
  outDir: path.join(pluginConfig.outDir ?? outDir, "static"),
718
837
  copyPublicDir: true,
719
- rollupOptions: { input: getDummyInput() }
838
+ rollupOptions: { input: getDummyInput() },
839
+ emptyOutDir: false
720
840
  },
721
841
  consumer: "client"
722
842
  };
@@ -746,8 +866,9 @@ function setupEnvs(pluginConfig) {
746
866
  target: "es2022",
747
867
  rollupOptions: {
748
868
  input: {},
749
- treeshake: { preset: "smallest" }
750
- }
869
+ treeshake: true
870
+ },
871
+ rolldownOptions: {}
751
872
  },
752
873
  optimizeDeps: {
753
874
  ...config.optimizeDeps,
@@ -763,8 +884,8 @@ function setupEnvs(pluginConfig) {
763
884
  },
764
885
  generateBundle: {
765
886
  order: "post",
766
- async handler(_opts, bundle$2) {
767
- cleanupDummy(bundle$2);
887
+ async handler(_opts, bundle) {
888
+ cleanupDummy(bundle);
768
889
  }
769
890
  },
770
891
  sharedDuringBuild: true
@@ -780,8 +901,8 @@ function setupEnvs(pluginConfig) {
780
901
  },
781
902
  generateBundle: {
782
903
  order: "post",
783
- async handler(_opts, bundle$2) {
784
- cleanupDummy(bundle$2);
904
+ async handler(_opts, bundle) {
905
+ cleanupDummy(bundle);
785
906
  this.emitFile({
786
907
  type: "asset",
787
908
  fileName: "config.json",
@@ -796,8 +917,8 @@ function setupEnvs(pluginConfig) {
796
917
  applyToEnvironment(env) {
797
918
  return env.name === envNames.client;
798
919
  },
799
- generateBundle: { async handler(_opts, bundle$2) {
800
- cleanupDummy(bundle$2);
920
+ generateBundle: { async handler(_opts, bundle) {
921
+ cleanupDummy(bundle);
801
922
  const topLevelConfig = this.environment.getTopLevelConfig();
802
923
  const clientEnv = topLevelConfig.environments.client;
803
924
  if (clientEnv) try {
@@ -845,9 +966,9 @@ function createVercelEnvironmentOptions(overrides) {
845
966
  function getDummyInput() {
846
967
  return { [DUMMY]: `${virtualEntry}:${DUMMY}` };
847
968
  }
848
- function cleanupDummy(bundle$2) {
849
- const dummy = Object.keys(bundle$2).find((key) => key.includes("_DUMMY_"));
850
- if (dummy) delete bundle$2[dummy];
969
+ function cleanupDummy(bundle) {
970
+ const dummy = Object.keys(bundle).find((key) => key.includes("_DUMMY_"));
971
+ if (dummy) delete bundle[dummy];
851
972
  }
852
973
 
853
974
  //#endregion
@@ -864,7 +985,6 @@ function vercel(pluginConfig = {}) {
864
985
  devServer()
865
986
  ];
866
987
  }
867
- var plugins_default = vercel;
868
988
 
869
989
  //#endregion
870
- export { plugins_default as default, vercel };
990
+ export { vercel as default, vercel };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-vercel",
3
- "version": "11.0.0-beta.8",
3
+ "version": "11.0.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -26,29 +26,31 @@
26
26
  "@types/node": "^22.19.3",
27
27
  "@universal-middleware/express": "^0.4.22",
28
28
  "@vercel/node": "^5.5.15",
29
- "tsdown": "^0.18.4",
29
+ "tsdown": "^0.20.3",
30
30
  "typescript": "^5.9.3",
31
31
  "vite": "^7.2.4",
32
32
  "vitest": "^4.0.16"
33
33
  },
34
34
  "dependencies": {
35
35
  "@manypkg/find-root": "^3.1.0",
36
- "@universal-deploy/store": "^0.0.2",
36
+ "@universal-deploy/store": "^0.2.0",
37
+ "@universal-deploy/vite": "^0.1.0",
37
38
  "@universal-middleware/core": "^0.4.13",
38
39
  "@universal-middleware/vercel": "^0.4.29",
39
40
  "@vercel/build-utils": "^13.2.3",
40
- "@vercel/nft": "^1.1.1",
41
+ "@vercel/nft": "^1.2.0",
41
42
  "@vercel/routing-utils": "^5.3.1",
42
- "@vite-plugin-vercel/schemas": "latest",
43
43
  "convert-route": "^1.0.0",
44
44
  "fast-glob": "^3.3.3",
45
45
  "magicast": "^0.5.1",
46
- "nf3": "^0.3.1",
46
+ "nf3": "^0.3.4",
47
+ "oxc-transform": "^0.108.0",
47
48
  "p-limit": "^7.2.0",
48
49
  "path-to-regexp": "^8.3.0",
49
- "rolldown": "^1.0.0-beta.58",
50
+ "rolldown": "^1.0.0-rc.4",
50
51
  "strip-ansi": "^7.1.2",
51
- "vite-plugin-wasm": "^3.5.0"
52
+ "vite-plugin-wasm": "^3.5.0",
53
+ "@vite-plugin-vercel/schemas": "^1.0.0"
52
54
  },
53
55
  "scripts": {
54
56
  "dev": "tsdown --watch",
@@ -1,70 +0,0 @@
1
- //#region src/utils/assert.ts
2
- function assert(condition, errorMessage) {
3
- if (condition) return;
4
- throw new Error(`[vite-plugin-vercel] ${errorMessage}`);
5
- }
6
-
7
- //#endregion
8
- //#region src/api.ts
9
- function createAPI(outfiles, pluginConfig) {
10
- return {
11
- getOutFiles() {
12
- return outfiles;
13
- },
14
- get config() {
15
- pluginConfig.config ??= {};
16
- return pluginConfig.config;
17
- },
18
- get defaultMaxDuration() {
19
- return pluginConfig.defaultMaxDuration;
20
- },
21
- set defaultMaxDuration(value) {
22
- pluginConfig.defaultMaxDuration = value;
23
- },
24
- get expiration() {
25
- return pluginConfig.expiration;
26
- },
27
- set expiration(value) {
28
- pluginConfig.expiration = value;
29
- },
30
- get rewrites() {
31
- pluginConfig.rewrites ??= [];
32
- return pluginConfig.rewrites;
33
- },
34
- get headers() {
35
- pluginConfig.headers ??= [];
36
- return pluginConfig.headers;
37
- },
38
- get redirects() {
39
- pluginConfig.redirects ??= [];
40
- return pluginConfig.redirects;
41
- },
42
- get cleanUrls() {
43
- return pluginConfig.cleanUrls;
44
- },
45
- set cleanUrls(value) {
46
- pluginConfig.cleanUrls = value;
47
- },
48
- get trailingSlash() {
49
- return pluginConfig.trailingSlash;
50
- },
51
- set trailingSlash(value) {
52
- pluginConfig.trailingSlash = value;
53
- },
54
- get defaultSupportsResponseStreaming() {
55
- return pluginConfig.defaultSupportsResponseStreaming;
56
- },
57
- set defaultSupportsResponseStreaming(value) {
58
- pluginConfig.defaultSupportsResponseStreaming = value;
59
- }
60
- };
61
- }
62
- function getVercelAPI(pluginContextOrServer) {
63
- const vpv = ("environment" in pluginContextOrServer ? pluginContextOrServer.environment.config : pluginContextOrServer.config).plugins.find((p) => p.name === "vite-plugin-vercel:api");
64
- assert(vpv, "Could not find vite-plugin-vercel:api plugin");
65
- assert(vpv.api, "Missing `api`. Make sure vite-plugin-vercel is up-to-date");
66
- return vpv.api("environment" in pluginContextOrServer ? pluginContextOrServer : void 0);
67
- }
68
-
69
- //#endregion
70
- export { getVercelAPI as n, assert as r, createAPI as t };
File without changes