vite-plugin-vercel 11.0.0-beta.2 → 11.0.0-beta.3

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/dist/types.d.ts CHANGED
@@ -12,6 +12,11 @@ type ViteVercelRedirect = Redirect & {
12
12
  };
13
13
  type PluginContext = ThisParameterType<Extract<Plugin["resolveId"], (...args: never) => any>>;
14
14
  interface ViteVercelConfig {
15
+ /**
16
+ * @experimental
17
+ * @default basic
18
+ */
19
+ bundleStrategy?: "basic" | "nf3";
15
20
  /**
16
21
  * How long Functions should be allowed to run for every request, in seconds.
17
22
  * If left empty, default value for your plan will be used.
package/dist/vite.js CHANGED
@@ -1,20 +1,22 @@
1
1
  import { t as pathRelativeTo$1 } from "./path-B4ThGm96.js";
2
- import { r as assert, t as createAPI } from "./api-DR2y7JVQ.js";
2
+ import { n as getVercelAPI, r as assert, t as createAPI } from "./api-DR2y7JVQ.js";
3
+ import { builtinModules } from "node:module";
3
4
  import path from "node:path";
4
5
  import { vercelOutputConfigSchema, vercelOutputPrerenderConfigSchema, vercelOutputVcConfigSchema } from "@vite-plugin-vercel/schemas";
5
6
  import { fromRou3 } from "convert-route/rou3";
6
7
  import { BuildEnvironment, createRunnableDevEnvironment, mergeConfig, normalizePath } from "vite";
7
8
  import { catchAll, devServer } from "@universal-deploy/store/vite";
8
9
  import { store } from "@universal-deploy/store";
9
- import fs from "node:fs";
10
+ import fs, { existsSync, readFileSync, writeFileSync } from "node:fs";
11
+ import { copyFile, cp, rmdir, unlink } from "node:fs/promises";
12
+ import { findRoot } from "@manypkg/find-root";
13
+ import { nodeFileTrace, resolve } from "@vercel/nft";
14
+ import { build } from "rolldown";
10
15
  import { cpus } from "node:os";
11
- import { resolve } from "@vercel/nft";
12
16
  import { externals } from "nf3/plugin";
13
17
  import pLimit from "p-limit";
14
- import { build } from "rolldown";
15
18
  import { getNodeVersion } from "@vercel/build-utils";
16
19
  import { toPathToRegexpV6 } from "convert-route/path-to-regexp-v6";
17
- import { cp } from "node:fs/promises";
18
20
  import { getTransformedRoutes, mergeRoutes, normalizeRoutes } from "@vercel/routing-utils";
19
21
 
20
22
  //#region src/utils/dedupeRoutes.ts
@@ -75,10 +77,10 @@ function apiPlugin(pluginConfig) {
75
77
  applyToEnvironment({ name }) {
76
78
  return name === "vercel_edge" || name === "vercel_node";
77
79
  },
78
- writeBundle(_opts, bundle$1) {
80
+ writeBundle(_opts, bundle$2) {
79
81
  const root = this.environment.config.root ?? process.cwd();
80
82
  const entryMapByDestination = new Map(dedupeRoutes().map((e) => [entryDestination(root, e, ".func/index"), e]));
81
- for (const [key, value] of Object.entries(bundle$1)) if (value.type === "chunk" && entryMapByDestination.has(removeExtension$1(key))) outfiles.push({
83
+ for (const [key, value] of Object.entries(bundle$2)) if (value.type === "chunk" && entryMapByDestination.has(removeExtension$1(key))) outfiles.push({
82
84
  type: "chunk",
83
85
  root: this.environment.config.root,
84
86
  outdir: this.environment.config.build.outDir,
@@ -96,6 +98,17 @@ function apiPlugin(pluginConfig) {
96
98
  };
97
99
  }
98
100
 
101
+ //#endregion
102
+ //#region src/utils/edge.ts
103
+ const edgeConditions = [
104
+ "edge-light",
105
+ "worker",
106
+ "browser",
107
+ "module",
108
+ "import",
109
+ "default"
110
+ ];
111
+
99
112
  //#endregion
100
113
  //#region src/utils/external.ts
101
114
  const _external = [
@@ -108,8 +121,209 @@ const _external = [
108
121
  const edgeExternal = [..._external, ..._external.map((e) => `node:${e}`)];
109
122
 
110
123
  //#endregion
111
- //#region src/plugins/bundle.ts
112
- function bundlePlugin() {
124
+ //#region src/plugins/bundle/basic.ts
125
+ const builtIns = new Set(builtinModules.flatMap((m) => [m, `node:${m}`]));
126
+ const edgeWasmPlugin = {
127
+ name: "edge-wasm-vercel",
128
+ resolveId: {
129
+ filter: { id: [/\.wasm\?module$/] },
130
+ handler(id) {
131
+ return {
132
+ id: id.replace(/\.wasm\?module$/, ".wasm"),
133
+ external: true
134
+ };
135
+ }
136
+ }
137
+ };
138
+ const dynamicNativeImportPlugin = {
139
+ name: "edge-dynamic-import-native",
140
+ resolveDynamicImport(specifier) {
141
+ if (typeof specifier === "string" && builtIns.has(specifier)) return {
142
+ id: specifier,
143
+ external: true
144
+ };
145
+ }
146
+ };
147
+ const reactEdgePlugin$1 = {
148
+ name: "react-edge-plugin",
149
+ resolveId: {
150
+ filter: { id: [/react-dom\/server/] },
151
+ async handler(_id, importer, options) {
152
+ return this.resolve("react-dom/server.edge", importer, options);
153
+ }
154
+ }
155
+ };
156
+ function basicBundlePlugin() {
157
+ const bundledAssets = /* @__PURE__ */ new Map();
158
+ const bundledChunks = [];
159
+ return [{
160
+ name: "vite-plugin-vercel:bundle",
161
+ enforce: "post",
162
+ apply: "build",
163
+ generateBundle(_opts, bundle$2) {
164
+ for (const b of Object.values(bundle$2)) {
165
+ const outFile = joinAbsolute(this.environment, this.environment.config.build.outDir, b.fileName);
166
+ if (b.type === "asset") {
167
+ const originalFileNames = b.originalFileNames.map((relativePath) => path.resolve(this.environment.config.root, relativePath));
168
+ const asset = {
169
+ env: this.environment.name,
170
+ root: this.environment.config.root,
171
+ outDir: this.environment.config.build.outDir,
172
+ outFile,
173
+ fileName: b.fileName
174
+ };
175
+ for (const originalFileName of originalFileNames) bundledAssets.set(originalFileName, asset);
176
+ } else bundledChunks.push(outFile);
177
+ }
178
+ },
179
+ closeBundle: {
180
+ order: "post",
181
+ async handler() {
182
+ if (!isVercelLastBuildStep(this.environment)) return;
183
+ this.environment.logger.info("Creating Vercel bundles...");
184
+ const outfiles = getVercelAPI(this).getOutFiles();
185
+ const filesToKeep = [];
186
+ for (const outfile of outfiles) if (outfile.type === "chunk") filesToKeep.push(...await bundle$1(this, bundledAssets, outfile));
187
+ await cleanup$1(filesToKeep, bundledChunks);
188
+ }
189
+ },
190
+ sharedDuringBuild: true
191
+ }];
192
+ }
193
+ function getAbsoluteOutFile(outfile) {
194
+ const source = joinAbsolutePosix(outfile.root, outfile.outdir, outfile.filepath);
195
+ return {
196
+ source,
197
+ destination: source.replace(outfile.outdir, outfile.outdir)
198
+ };
199
+ }
200
+ async function bundle$1(pluginContext, bundledAssets, outfile) {
201
+ const output = [];
202
+ const { source, destination } = getAbsoluteOutFile(outfile);
203
+ const isEdge = Boolean(outfile.relatedEntry.vercel?.edge);
204
+ const { environment } = pluginContext;
205
+ const buildOptions = {};
206
+ buildOptions.output = {
207
+ format: "esm",
208
+ legalComments: "none"
209
+ };
210
+ buildOptions.checks = { pluginTimings: false };
211
+ buildOptions.input = [source];
212
+ buildOptions.treeshake = true;
213
+ buildOptions.resolve ??= {};
214
+ if (isEdge) {
215
+ buildOptions.platform = "browser";
216
+ buildOptions.external = edgeExternal;
217
+ buildOptions.resolve.conditionNames = edgeConditions;
218
+ buildOptions.transform = { define: { "process.env.NODE_ENV": JSON.stringify("production") } };
219
+ buildOptions.output.file = destination.replace(/\.mjs$/, ".js");
220
+ buildOptions.plugins = [
221
+ edgeWasmPlugin,
222
+ dynamicNativeImportPlugin,
223
+ reactEdgePlugin$1
224
+ ];
225
+ } else {
226
+ buildOptions.platform = "node";
227
+ buildOptions.output.file = destination.replace(/\.js$/, ".mjs");
228
+ buildOptions.output.banner = `import { createRequire as topLevelCreateRequire } from 'node:module';
229
+ import { dirname as topLevelDirname } from 'node:path';
230
+ import { fileURLToPath as topLevelFileURLToPath } from 'node:url';
231
+ var require = topLevelCreateRequire(import.meta.url);
232
+ var __filename = topLevelFileURLToPath(import.meta.url);
233
+ var __dirname = topLevelDirname(__filename);
234
+ `;
235
+ }
236
+ try {
237
+ await build(buildOptions);
238
+ output.push(buildOptions.output.file);
239
+ } catch (e) {
240
+ throw new Error(`Error while bundling ${destination}`, { cause: e });
241
+ }
242
+ let base = environment.config.root;
243
+ try {
244
+ base = (await findRoot(environment.config.root)).rootDir;
245
+ } catch (_e) {}
246
+ const resolvedEntryP = pluginContext.resolve(outfile.relatedEntry.id);
247
+ const entryFilePath = existsSync(outfile.relatedEntry.id) ? outfile.relatedEntry.id : (await resolvedEntryP)?.id && existsSync((await resolvedEntryP).id) ? (await resolvedEntryP).id : null;
248
+ if (entryFilePath === null) return [];
249
+ const { fileList, reasons } = await nodeFileTrace([entryFilePath], {
250
+ base,
251
+ processCwd: environment.config.root,
252
+ mixedModules: true,
253
+ ignore: [
254
+ "**/node_modules/react{,-dom,-dom-server-turbopack}/**/*.development.js",
255
+ "**/*.d.ts",
256
+ "**/*.map",
257
+ "**/node_modules/webpack5/**/*"
258
+ ],
259
+ async readFile(filepath) {
260
+ if (filepath.endsWith(".ts") || filepath.endsWith(".tsx")) return (await build({
261
+ output: {
262
+ format: "esm",
263
+ minify: false
264
+ },
265
+ external: /.*/,
266
+ platform: "node",
267
+ logLevel: "info",
268
+ plugins: [],
269
+ transform: { define: {
270
+ "process.env.NODE_ENV": "\"production\"",
271
+ "import.meta.env.NODE_ENV": "\"production\""
272
+ } },
273
+ input: [entryFilePath],
274
+ write: false
275
+ })).output[0].code;
276
+ return readFileSync(filepath, "utf-8");
277
+ }
278
+ });
279
+ 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")) {
280
+ const absolutePath = path.join(base, file);
281
+ const assetRenamedByVite = bundledAssets.get(absolutePath);
282
+ const basename = path.basename(assetRenamedByVite ? assetRenamedByVite.fileName : absolutePath);
283
+ if (assetRenamedByVite) writeFileSync(destination, readFileSync(destination, "utf-8").replaceAll(`/${assetRenamedByVite.fileName}`, `./${path.basename(assetRenamedByVite.fileName)}`));
284
+ const to = path.join(path.dirname(destination), basename);
285
+ output.push(to);
286
+ await copyFile(absolutePath, to);
287
+ }
288
+ return output;
289
+ }
290
+ function joinAbsolute(env_or_p0, p1, ...p) {
291
+ if (path.isAbsolute(p1)) return path.join(p1, ...p);
292
+ return path.join(typeof env_or_p0 === "string" ? env_or_p0 : env_or_p0.config.root, p1, ...p);
293
+ }
294
+ function joinAbsolutePosix(env_or_p0, p1, ...p) {
295
+ if (path.isAbsolute(p1)) return path.posix.join(p1, ...p);
296
+ return path.posix.join(typeof env_or_p0 === "string" ? env_or_p0 : env_or_p0.config.root, p1, ...p);
297
+ }
298
+ function isVercelLastBuildStep(env) {
299
+ return (typeof env !== "string" ? env.name : env) === "vercel_node";
300
+ }
301
+ async function cleanup$1(filesToKeep, bundledChunks) {
302
+ const toKeep = new Set(filesToKeep);
303
+ const removedFiles = [];
304
+ await Promise.all(bundledChunks.map(async (file) => {
305
+ if (!toKeep.has(file)) try {
306
+ await unlink(file);
307
+ removedFiles.push(file);
308
+ } catch {}
309
+ }));
310
+ const dirsToRemove = /* @__PURE__ */ new Set();
311
+ for (const file of removedFiles) {
312
+ let dir = path.dirname(file);
313
+ while (dir && dir !== "." && dir !== "/" && !toKeep.has(dir)) {
314
+ dirsToRemove.add(dir);
315
+ dir = path.dirname(dir);
316
+ }
317
+ }
318
+ const sortedDirs = Array.from(dirsToRemove).sort((a, b) => b.length - a.length);
319
+ for (const dir of sortedDirs) try {
320
+ await rmdir(dir);
321
+ } catch {}
322
+ }
323
+
324
+ //#endregion
325
+ //#region src/plugins/bundle/nf3.ts
326
+ function nf3BundlePlugin() {
113
327
  const externalsPlugin = externals({});
114
328
  delete externalsPlugin.buildEnd;
115
329
  let buildOutput;
@@ -133,13 +347,20 @@ function bundlePlugin() {
133
347
  fileName: e[1].fileName,
134
348
  outPath: path.join(outDir$1, e[1].fileName)
135
349
  }));
136
- assert(entries.length > 0, "No entry files found in build output");
350
+ if (entries.length === 0) return;
137
351
  const outPaths = entries.map((entry) => entry.outPath);
138
352
  const input = Object.fromEntries(outPaths.map((e) => [removeExtension(pathRelativeTo(e, outDir$1)), e]));
139
353
  const limit = pLimit(Math.max(1, Math.ceil(cpus().length / 2)));
354
+ const nonVitePlugins = this.environment.config.plugins.filter((p) => {
355
+ return !p.name.startsWith("vite:") && p.name !== "alias" && p.name !== "commonjs" && p.name !== "nitro:externals";
356
+ }).map((x) => {
357
+ const { buildStart, buildEnd, writeBundle, generateBundle, ...rest } = x;
358
+ return rest;
359
+ });
140
360
  const localOutput = (await Promise.all(Object.values(input).map((entryPath) => limit(async () => {
141
361
  const outDir$2 = path.dirname(entryPath);
142
362
  return { output: (await bundle({
363
+ plugins: nonVitePlugins,
143
364
  isEdge,
144
365
  input: { index: entryPath },
145
366
  outDir: outDir$2,
@@ -169,7 +390,7 @@ function bundle(options) {
169
390
  platform: options.isEdge ? "browser" : "node",
170
391
  external: options.isEdge ? edgeExternal : [],
171
392
  write: true,
172
- plugins: [externals(options.externals)],
393
+ plugins: [...options.plugins, externals(options.externals)],
173
394
  input: options.input,
174
395
  resolve: { conditionNames: options.externals.conditions },
175
396
  output: {
@@ -435,17 +656,6 @@ function getConfig(pluginConfig) {
435
656
  //#region src/utils/const.ts
436
657
  const virtualEntry = "virtual:vite-plugin-vercel:entry";
437
658
 
438
- //#endregion
439
- //#region src/utils/edge.ts
440
- const edgeConditions = [
441
- "edge-light",
442
- "worker",
443
- "browser",
444
- "module",
445
- "import",
446
- "default"
447
- ];
448
-
449
659
  //#endregion
450
660
  //#region src/plugins/setupEnvs.ts
451
661
  const outDir = path.posix.join(process.cwd(), ".vercel/output");
@@ -467,27 +677,30 @@ function setupEnvs(pluginConfig) {
467
677
  await builder.build(builder.environments.vercel_node);
468
678
  }
469
679
  },
470
- config() {
471
- if (!injected) {
472
- injected = true;
473
- if (pluginConfig.entries) store.entries.push(...pluginConfig.entries);
680
+ config: {
681
+ order: "post",
682
+ handler() {
683
+ if (!injected) {
684
+ injected = true;
685
+ if (pluginConfig.entries) store.entries.push(...pluginConfig.entries);
686
+ }
687
+ const outDirOverride = pluginConfig.outDir ? { build: { outDir: pluginConfig.outDir } } : {};
688
+ return {
689
+ environments: {
690
+ vercel_edge: createVercelEnvironmentOptions(outDirOverride),
691
+ vercel_node: createVercelEnvironmentOptions(outDirOverride),
692
+ vercel_client: {
693
+ build: {
694
+ outDir: path.join(pluginConfig.outDir ?? outDir, "static"),
695
+ copyPublicDir: true,
696
+ rollupOptions: { input: getDummyInput() }
697
+ },
698
+ consumer: "client"
699
+ }
700
+ },
701
+ builder: {}
702
+ };
474
703
  }
475
- const outDirOverride = pluginConfig.outDir ? { build: { outDir: pluginConfig.outDir } } : {};
476
- return {
477
- environments: {
478
- vercel_edge: createVercelEnvironmentOptions(outDirOverride),
479
- vercel_node: createVercelEnvironmentOptions(outDirOverride),
480
- vercel_client: {
481
- build: {
482
- outDir: path.join(pluginConfig.outDir ?? outDir, "static"),
483
- copyPublicDir: true,
484
- rollupOptions: { input: getDummyInput() }
485
- },
486
- consumer: "client"
487
- }
488
- },
489
- builder: {}
490
- };
491
704
  },
492
705
  sharedDuringBuild: true
493
706
  },
@@ -524,8 +737,8 @@ function setupEnvs(pluginConfig) {
524
737
  },
525
738
  generateBundle: {
526
739
  order: "post",
527
- async handler(_opts, bundle$1) {
528
- cleanupDummy(bundle$1);
740
+ async handler(_opts, bundle$2) {
741
+ cleanupDummy(bundle$2);
529
742
  }
530
743
  },
531
744
  sharedDuringBuild: true
@@ -541,8 +754,8 @@ function setupEnvs(pluginConfig) {
541
754
  },
542
755
  generateBundle: {
543
756
  order: "post",
544
- async handler(_opts, bundle$1) {
545
- cleanupDummy(bundle$1);
757
+ async handler(_opts, bundle$2) {
758
+ cleanupDummy(bundle$2);
546
759
  this.emitFile({
547
760
  type: "asset",
548
761
  fileName: "config.json",
@@ -557,8 +770,8 @@ function setupEnvs(pluginConfig) {
557
770
  applyToEnvironment(env) {
558
771
  return env.name === "vercel_client";
559
772
  },
560
- generateBundle: { async handler(_opts, bundle$1) {
561
- cleanupDummy(bundle$1);
773
+ generateBundle: { async handler(_opts, bundle$2) {
774
+ cleanupDummy(bundle$2);
562
775
  const topLevelConfig = this.environment.getTopLevelConfig();
563
776
  const clientEnv = topLevelConfig.environments.client;
564
777
  if (clientEnv) try {
@@ -604,9 +817,9 @@ function createVercelEnvironmentOptions(overrides) {
604
817
  function getDummyInput() {
605
818
  return { [DUMMY]: `${virtualEntry}:${DUMMY}` };
606
819
  }
607
- function cleanupDummy(bundle$1) {
608
- const dummy = Object.keys(bundle$1).find((key) => key.includes("_DUMMY_"));
609
- if (dummy) delete bundle$1[dummy];
820
+ function cleanupDummy(bundle$2) {
821
+ const dummy = Object.keys(bundle$2).find((key) => key.includes("_DUMMY_"));
822
+ if (dummy) delete bundle$2[dummy];
610
823
  }
611
824
 
612
825
  //#endregion
@@ -618,7 +831,7 @@ function vercel(pluginConfig = {}) {
618
831
  apiPlugin(pluginConfig),
619
832
  ...setupEnvs(pluginConfig),
620
833
  ...loaderPlugin(pluginConfig),
621
- ...bundlePlugin(),
834
+ ...pluginConfig?.bundleStrategy === "nf3" ? nf3BundlePlugin() : basicBundlePlugin(),
622
835
  catchAll(),
623
836
  devServer()
624
837
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-vercel",
3
- "version": "11.0.0-beta.2",
3
+ "version": "11.0.0-beta.3",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",