vite-plugin-vercel 10.0.0-beta.5 → 10.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 +94 -51
- package/dist/index.cjs +829 -0
- package/dist/index.d.cts +694 -0
- package/dist/index.d.ts +692 -3
- package/dist/index.js +800 -10
- package/index.d.ts +10 -0
- package/package.json +36 -16
- package/dist/api.d.ts +0 -1
- package/dist/api.js +0 -2
- package/dist/types.d.ts +0 -1
- package/dist/types.js +0 -2
- package/dist/universal-middleware-dev.d.ts +0 -3
- package/dist/universal-middleware-dev.js +0 -7
- package/dist/universal-middleware-prod.d.ts +0 -3
- package/dist/universal-middleware-prod.js +0 -7
- package/dist/utils.d.ts +0 -3
- package/dist/utils.js +0 -2
package/dist/index.js
CHANGED
|
@@ -1,12 +1,802 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
};
|
|
8
|
-
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import fs5 from "fs/promises";
|
|
3
|
+
import path6 from "path";
|
|
4
|
+
|
|
5
|
+
// src/build.ts
|
|
6
|
+
import fs, { copyFile } from "fs/promises";
|
|
7
|
+
import { builtinModules } from "module";
|
|
8
|
+
import path2, { basename } from "path";
|
|
9
|
+
import { findRoot } from "@manypkg/find-root";
|
|
10
|
+
import { getNodeVersion } from "@vercel/build-utils";
|
|
11
|
+
import { nodeFileTrace } from "@vercel/nft";
|
|
12
|
+
import { build } from "esbuild";
|
|
13
|
+
import glob from "fast-glob";
|
|
14
|
+
import { generateCode, loadFile } from "magicast";
|
|
15
|
+
|
|
16
|
+
// src/assert.ts
|
|
17
|
+
import { newError } from "@brillout/libassert";
|
|
18
|
+
var libName = "vite-plugin-vercel";
|
|
19
|
+
function assert(condition, errorMessage) {
|
|
20
|
+
if (condition) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const err = newError(`[${libName}][Wrong Usage] ${errorMessage}`, 2);
|
|
24
|
+
throw err;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// src/schemas/config/vc-config.ts
|
|
28
|
+
import { z } from "zod";
|
|
29
|
+
var vercelOutputEdgeVcConfigSchema = z.object({
|
|
30
|
+
runtime: z.literal("edge"),
|
|
31
|
+
entrypoint: z.string(),
|
|
32
|
+
envVarsInUse: z.array(z.string()).optional()
|
|
33
|
+
}).strict();
|
|
34
|
+
var vercelOutputServerlessVcConfigSchema = z.object({
|
|
35
|
+
runtime: z.string(),
|
|
36
|
+
handler: z.string(),
|
|
37
|
+
memory: z.number().int().min(128).max(3008).optional(),
|
|
38
|
+
maxDuration: z.number().int().positive().optional(),
|
|
39
|
+
environment: z.record(z.string()).optional(),
|
|
40
|
+
regions: z.array(z.string()).optional(),
|
|
41
|
+
supportsWrapper: z.boolean().optional(),
|
|
42
|
+
supportsResponseStreaming: z.boolean().optional()
|
|
43
|
+
}).strict();
|
|
44
|
+
var vercelOutputServerlessNodeVcConfigSchema = vercelOutputServerlessVcConfigSchema.extend({
|
|
45
|
+
launcherType: z.literal("Nodejs"),
|
|
46
|
+
shouldAddHelpers: z.boolean().optional(),
|
|
47
|
+
shouldAddSourcemapSupport: z.boolean().optional(),
|
|
48
|
+
awsLambdaHandler: z.string().optional()
|
|
49
|
+
}).strict();
|
|
50
|
+
var vercelOutputVcConfigSchema = z.union([
|
|
51
|
+
vercelOutputEdgeVcConfigSchema,
|
|
52
|
+
vercelOutputServerlessVcConfigSchema,
|
|
53
|
+
vercelOutputServerlessNodeVcConfigSchema
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
// src/schemas/exports.ts
|
|
57
|
+
import { z as z2 } from "zod";
|
|
58
|
+
var vercelEndpointExports = z2.object({
|
|
59
|
+
edge: z2.boolean().optional(),
|
|
60
|
+
headers: z2.record(z2.string()).optional(),
|
|
61
|
+
streaming: z2.boolean().optional(),
|
|
62
|
+
isr: z2.object({
|
|
63
|
+
expiration: z2.number().or(z2.literal(false))
|
|
64
|
+
}).optional()
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// src/utils.ts
|
|
68
|
+
import { normalizePath } from "vite";
|
|
69
|
+
import path from "path";
|
|
70
|
+
function getRoot(config) {
|
|
71
|
+
return normalizePath(config.root || process.cwd());
|
|
72
|
+
}
|
|
73
|
+
function getOutput(config, suffix) {
|
|
74
|
+
return path.join(
|
|
75
|
+
config.vercel?.outDir ? "" : getRoot(config),
|
|
76
|
+
config.vercel?.outDir ?? ".vercel/output",
|
|
77
|
+
suffix ?? ""
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
function getPublic(config) {
|
|
81
|
+
return path.join(getRoot(config), config.publicDir || "public");
|
|
82
|
+
}
|
|
83
|
+
function pathRelativeTo(filePath, config, rel) {
|
|
84
|
+
const root = getRoot(config);
|
|
85
|
+
return normalizePath(path.relative(normalizePath(path.join(root, rel)), filePath));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/build.ts
|
|
89
|
+
async function getAdditionalEndpoints(resolvedConfig) {
|
|
90
|
+
const userEndpoints = [];
|
|
91
|
+
if (Array.isArray(resolvedConfig.vercel?.additionalEndpoints)) {
|
|
92
|
+
for (const endpoint of resolvedConfig.vercel.additionalEndpoints) {
|
|
93
|
+
if (typeof endpoint === "function") {
|
|
94
|
+
const res = await endpoint();
|
|
95
|
+
if (Array.isArray(res)) {
|
|
96
|
+
userEndpoints.push(...res);
|
|
97
|
+
} else {
|
|
98
|
+
userEndpoints.push(res);
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
userEndpoints.push(endpoint);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return userEndpoints.map((e) => ({
|
|
106
|
+
...e,
|
|
107
|
+
route: e.route ?? true,
|
|
108
|
+
// path.resolve removes the trailing slash if any
|
|
109
|
+
destination: `${path2.posix.resolve("/", e.destination)}.func`
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
async function getEntries(resolvedConfig) {
|
|
113
|
+
const apiEntries = glob.sync(`${getRoot(resolvedConfig)}/api/**/*.*([a-zA-Z0-9])`).filter((filepath) => !path2.basename(filepath).startsWith("_"));
|
|
114
|
+
if (apiEntries.length > 0) {
|
|
115
|
+
console.warn(
|
|
116
|
+
"@vercel/build is currently force building /api files itself, with no way to disable it. In order to avoid double compilation, you should temporarily rename /api to /_api while using this plugin. /_api functions are compiled under .vercel/output/functions/api/*.func as if they were in /api."
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
const otherApiEntries = glob.sync(`${getRoot(resolvedConfig)}/_api/**/*.*([a-zA-Z0-9])`).filter((filepath) => !path2.basename(filepath).startsWith("_"));
|
|
120
|
+
return [...apiEntries, ...otherApiEntries].reduce(
|
|
121
|
+
(entryPoints, filePath) => {
|
|
122
|
+
const outFilePath = pathRelativeTo(filePath, resolvedConfig, filePath.includes("/_api/") ? "_api" : "api");
|
|
123
|
+
const parsed = path2.posix.parse(outFilePath);
|
|
124
|
+
entryPoints.push({
|
|
125
|
+
source: filePath,
|
|
126
|
+
destination: `api/${path2.posix.join(parsed.dir, parsed.name)}.func`,
|
|
127
|
+
route: true
|
|
128
|
+
});
|
|
129
|
+
return entryPoints;
|
|
130
|
+
},
|
|
131
|
+
await getAdditionalEndpoints(resolvedConfig)
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
var edgeWasmPlugin = {
|
|
135
|
+
name: "edge-wasm-vercel",
|
|
136
|
+
setup(build2) {
|
|
137
|
+
build2.onResolve({ filter: /\.wasm/ }, (args) => {
|
|
138
|
+
return {
|
|
139
|
+
path: args.path.replace(/\.wasm\?module$/i, ".wasm"),
|
|
140
|
+
external: true
|
|
141
|
+
};
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
var vercelOgPlugin = (ctx) => {
|
|
146
|
+
return {
|
|
147
|
+
name: "vercel-og",
|
|
148
|
+
setup(build2) {
|
|
149
|
+
build2.onResolve({ filter: /@vercel\/og/ }, () => {
|
|
150
|
+
ctx.found = true;
|
|
151
|
+
return void 0;
|
|
152
|
+
});
|
|
153
|
+
build2.onLoad({ filter: /@vercel\/og/ }, (args) => {
|
|
154
|
+
ctx.index = args.path;
|
|
155
|
+
return void 0;
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
var standardBuildOptions = {
|
|
161
|
+
bundle: true,
|
|
162
|
+
target: "es2022",
|
|
163
|
+
format: "esm",
|
|
164
|
+
platform: "node",
|
|
165
|
+
logLevel: "info",
|
|
166
|
+
logOverride: {
|
|
167
|
+
"ignored-bare-import": "verbose",
|
|
168
|
+
"require-resolve-not-external": "verbose"
|
|
169
|
+
},
|
|
170
|
+
minify: false,
|
|
171
|
+
plugins: [],
|
|
172
|
+
define: {
|
|
173
|
+
"process.env.NODE_ENV": '"production"',
|
|
174
|
+
"import.meta.env.NODE_ENV": '"production"'
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
async function buildFn(resolvedConfig, entry, buildOptions) {
|
|
178
|
+
assert(
|
|
179
|
+
entry.destination.length > 0,
|
|
180
|
+
`Endpoint ${typeof entry.source === "string" ? entry.source : "-"} does not have build destination`
|
|
181
|
+
);
|
|
182
|
+
const options = Object.assign({}, standardBuildOptions);
|
|
183
|
+
if (buildOptions) {
|
|
184
|
+
Object.assign(options, buildOptions);
|
|
185
|
+
}
|
|
186
|
+
const filename = entry.edge || options.format === "cjs" ? "index.js" : "index.mjs";
|
|
187
|
+
const outfile = path2.join(getOutput(resolvedConfig, "functions"), entry.destination, filename);
|
|
188
|
+
Object.assign(options, { outfile });
|
|
189
|
+
if (!options.stdin) {
|
|
190
|
+
if (typeof entry.source === "string") {
|
|
191
|
+
options.entryPoints = [entry.source];
|
|
192
|
+
} else {
|
|
193
|
+
assert(typeof entry.source === "object", "`{ source }` must be a string or an object");
|
|
194
|
+
assert(typeof entry.source.contents === "string", "`{ contents }` must be a string");
|
|
195
|
+
options.stdin = entry.source;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (entry.edge) {
|
|
199
|
+
options.platform = void 0;
|
|
200
|
+
options.external = [...builtinModules, ...builtinModules.map((m) => `node:${m}`)];
|
|
201
|
+
options.conditions = ["edge-light", "worker", "browser", "module", "import", "require"];
|
|
202
|
+
options.plugins?.push(edgeWasmPlugin);
|
|
203
|
+
options.format = "esm";
|
|
204
|
+
} else if (options.format === "esm") {
|
|
205
|
+
options.banner = {
|
|
206
|
+
js: `import { createRequire as VPV_createRequire } from "node:module";
|
|
207
|
+
import { fileURLToPath as VPV_fileURLToPath } from "node:url";
|
|
208
|
+
import { dirname as VPV_dirname } from "node:path";
|
|
209
|
+
const require = VPV_createRequire(import.meta.url);
|
|
210
|
+
const __filename = VPV_fileURLToPath(import.meta.url);
|
|
211
|
+
const __dirname = VPV_dirname(__filename);
|
|
212
|
+
`
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
const ctx = { found: false, index: "" };
|
|
216
|
+
options.plugins?.push(vercelOgPlugin(ctx));
|
|
217
|
+
const output = await build(options);
|
|
218
|
+
if (typeof entry.source === "string") {
|
|
219
|
+
let base = resolvedConfig.root;
|
|
220
|
+
try {
|
|
221
|
+
const dir = await findRoot(resolvedConfig.root);
|
|
222
|
+
base = dir.rootDir;
|
|
223
|
+
} catch (e) {
|
|
224
|
+
}
|
|
225
|
+
const { fileList, reasons } = await nodeFileTrace([entry.source], {
|
|
226
|
+
base,
|
|
227
|
+
processCwd: resolvedConfig.root,
|
|
228
|
+
mixedModules: true,
|
|
229
|
+
ignore: [
|
|
230
|
+
"**/node_modules/react{,-dom,-dom-server-turbopack}/**/*.development.js",
|
|
231
|
+
"**/*.d.ts",
|
|
232
|
+
"**/*.map",
|
|
233
|
+
"**/node_modules/webpack5/**/*"
|
|
234
|
+
],
|
|
235
|
+
async readFile(filepath) {
|
|
236
|
+
if (filepath.endsWith(".ts") || filepath.endsWith(".tsx")) {
|
|
237
|
+
const result = await build({
|
|
238
|
+
...standardBuildOptions,
|
|
239
|
+
entryPoints: [entry.source],
|
|
240
|
+
bundle: false,
|
|
241
|
+
write: false
|
|
242
|
+
});
|
|
243
|
+
return result.outputFiles[0].text;
|
|
244
|
+
}
|
|
245
|
+
return fs.readFile(filepath, "utf-8");
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
for (const file of fileList) {
|
|
249
|
+
if (reasons.has(file) && reasons.get(file)?.type.includes("asset") && !file.endsWith(".js") && !file.endsWith(".cjs") && !file.endsWith(".mjs") && !file.endsWith("package.json")) {
|
|
250
|
+
await copyFile(
|
|
251
|
+
path2.join(base, file),
|
|
252
|
+
path2.join(getOutput(resolvedConfig, "functions"), entry.destination, basename(file))
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
await writeVcConfig(resolvedConfig, entry.destination, filename, {
|
|
258
|
+
edge: Boolean(entry.edge),
|
|
259
|
+
streaming: entry.streaming
|
|
260
|
+
});
|
|
261
|
+
return output;
|
|
262
|
+
}
|
|
263
|
+
async function writeVcConfig(resolvedConfig, destination, filename, options) {
|
|
264
|
+
const vcConfig = path2.join(getOutput(resolvedConfig, "functions"), destination, ".vc-config.json");
|
|
265
|
+
const nodeVersion = await getNodeVersion(getOutput(resolvedConfig));
|
|
266
|
+
await fs.writeFile(
|
|
267
|
+
vcConfig,
|
|
268
|
+
JSON.stringify(
|
|
269
|
+
vercelOutputVcConfigSchema.parse(
|
|
270
|
+
options.edge ? {
|
|
271
|
+
runtime: "edge",
|
|
272
|
+
entrypoint: filename
|
|
273
|
+
} : {
|
|
274
|
+
runtime: nodeVersion.runtime,
|
|
275
|
+
handler: filename,
|
|
276
|
+
maxDuration: resolvedConfig.vercel?.defaultMaxDuration,
|
|
277
|
+
launcherType: "Nodejs",
|
|
278
|
+
shouldAddHelpers: true,
|
|
279
|
+
supportsResponseStreaming: options.streaming ?? resolvedConfig.vercel?.defaultSupportsResponseStreaming
|
|
280
|
+
}
|
|
281
|
+
),
|
|
282
|
+
void 0,
|
|
283
|
+
2
|
|
284
|
+
),
|
|
285
|
+
"utf-8"
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
function getSourceAndDestination(destination) {
|
|
289
|
+
if (destination.startsWith("api/")) {
|
|
290
|
+
return path2.posix.resolve("/", destination);
|
|
291
|
+
}
|
|
292
|
+
return path2.posix.resolve("/", destination, ":match*");
|
|
293
|
+
}
|
|
294
|
+
var RE_BRACKETS = /^\[([^/]+)\]$/gm;
|
|
295
|
+
function replaceBrackets(source) {
|
|
296
|
+
return source.split("/").map((segment) => segment.replace(RE_BRACKETS, ":$1")).join("/");
|
|
297
|
+
}
|
|
298
|
+
function isPrimitive(test) {
|
|
299
|
+
return test !== Object(test);
|
|
300
|
+
}
|
|
301
|
+
function _eval(code) {
|
|
302
|
+
const func = new Function(`{ return function(){ return ${code} } };`);
|
|
303
|
+
return func.call(null).call(null);
|
|
304
|
+
}
|
|
305
|
+
function evalExport(exp) {
|
|
306
|
+
if (!exp) return;
|
|
307
|
+
const code = isPrimitive(exp) ? exp : generateCode(exp).code;
|
|
308
|
+
return _eval(code);
|
|
309
|
+
}
|
|
310
|
+
async function extractExports(filepath) {
|
|
311
|
+
try {
|
|
312
|
+
const mod = await loadFile(filepath);
|
|
313
|
+
const subject = {
|
|
314
|
+
edge: evalExport(mod.exports.edge),
|
|
315
|
+
headers: evalExport(mod.exports.headers),
|
|
316
|
+
streaming: evalExport(mod.exports.streaming),
|
|
317
|
+
isr: evalExport(mod.exports.isr)
|
|
318
|
+
};
|
|
319
|
+
return vercelEndpointExports.parse(subject);
|
|
320
|
+
} catch (e) {
|
|
321
|
+
console.warn(`Warning: failed to read exports of '${filepath}'`, e);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
async function extractHeaders(resolvedConfig) {
|
|
325
|
+
let headers = [];
|
|
326
|
+
if (typeof resolvedConfig.vercel?.headers === "function") {
|
|
327
|
+
headers = await resolvedConfig.vercel.headers();
|
|
328
|
+
} else if (Array.isArray(resolvedConfig.vercel?.headers)) {
|
|
329
|
+
headers = resolvedConfig.vercel.headers;
|
|
330
|
+
}
|
|
331
|
+
return headers;
|
|
332
|
+
}
|
|
333
|
+
async function buildEndpoints(resolvedConfig) {
|
|
334
|
+
const entries = await getEntries(resolvedConfig);
|
|
335
|
+
const headers = await extractHeaders(resolvedConfig);
|
|
336
|
+
for (const entry of entries) {
|
|
337
|
+
if (typeof entry.source === "string") {
|
|
338
|
+
const exports = await extractExports(entry.source);
|
|
339
|
+
if (exports) {
|
|
340
|
+
if (entry.headers || exports.headers) {
|
|
341
|
+
entry.headers = {
|
|
342
|
+
...exports.headers,
|
|
343
|
+
...entry.headers
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
if (entry.edge !== void 0 && exports.edge !== void 0) {
|
|
347
|
+
throw new Error(
|
|
348
|
+
`edge configuration should be defined either in the endpoint itself or through Vite config, not both ('${entry.source}')`
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
if (exports.edge !== void 0) {
|
|
352
|
+
entry.edge = exports.edge;
|
|
353
|
+
}
|
|
354
|
+
if (entry.isr !== void 0 && exports.isr !== void 0) {
|
|
355
|
+
throw new Error(
|
|
356
|
+
`isr configuration should be defined either in the endpoint itself or through Vite config, not both ('${entry.source}')`
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
if ((entry.isr !== void 0 || exports.isr !== void 0) && (entry.edge !== void 0 || exports.edge !== void 0)) {
|
|
360
|
+
throw new Error(`isr cannot be enabled for edge functions ('${entry.source}')`);
|
|
361
|
+
}
|
|
362
|
+
if (exports.isr) {
|
|
363
|
+
entry.isr = exports.isr;
|
|
364
|
+
}
|
|
365
|
+
if (typeof exports.streaming === "boolean") {
|
|
366
|
+
entry.streaming = exports.streaming;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
await buildFn(resolvedConfig, entry, entry.buildOptions);
|
|
371
|
+
}
|
|
372
|
+
const isrEntries = entries.filter((e) => e.isr).map((e) => [e.destination.replace(/\.func$/, ""), { expiration: e.isr?.expiration }]);
|
|
373
|
+
return {
|
|
374
|
+
rewrites: entries.filter((e) => {
|
|
375
|
+
if (e.addRoute === void 0 && e.route !== void 0) {
|
|
376
|
+
return e.route !== false;
|
|
377
|
+
}
|
|
378
|
+
if (e.addRoute !== void 0 && e.route === void 0) {
|
|
379
|
+
return e.addRoute !== false;
|
|
380
|
+
}
|
|
381
|
+
if (e.addRoute !== void 0 && e.route !== void 0) {
|
|
382
|
+
throw new Error("Cannot use both `route` and `addRoute` in `additionalEndpoints`");
|
|
383
|
+
}
|
|
384
|
+
return true;
|
|
385
|
+
}).map((e) => {
|
|
386
|
+
const destination = e.destination.replace(/\.func$/, "");
|
|
387
|
+
if (typeof e.route === "string") {
|
|
388
|
+
return {
|
|
389
|
+
source: `(${e.route})`,
|
|
390
|
+
destination: `${destination}/?__original_path=$1`
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
return {
|
|
394
|
+
source: replaceBrackets(getSourceAndDestination(destination)),
|
|
395
|
+
destination: getSourceAndDestination(destination)
|
|
396
|
+
};
|
|
397
|
+
}),
|
|
398
|
+
isr: Object.fromEntries(isrEntries),
|
|
399
|
+
headers: [
|
|
400
|
+
...entries.filter((e) => e.headers).map((e) => ({
|
|
401
|
+
source: `/${e.destination.replace(/\.func$/, "")}`,
|
|
402
|
+
headers: Object.entries(e.headers ?? {}).map(([key, value]) => ({
|
|
403
|
+
key,
|
|
404
|
+
value
|
|
405
|
+
}))
|
|
406
|
+
})),
|
|
407
|
+
...headers
|
|
408
|
+
]
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// src/config.ts
|
|
413
|
+
import path3 from "path";
|
|
414
|
+
|
|
415
|
+
// src/schemas/config/config.ts
|
|
416
|
+
import { z as z3 } from "zod";
|
|
417
|
+
var HasOrMissing = z3.array(
|
|
418
|
+
z3.union([
|
|
419
|
+
z3.object({
|
|
420
|
+
type: z3.literal("host"),
|
|
421
|
+
value: z3.string()
|
|
422
|
+
}).strict(),
|
|
423
|
+
z3.object({
|
|
424
|
+
type: z3.literal("header"),
|
|
425
|
+
key: z3.string(),
|
|
426
|
+
value: z3.string().optional()
|
|
427
|
+
}).strict(),
|
|
428
|
+
z3.object({
|
|
429
|
+
type: z3.literal("cookie"),
|
|
430
|
+
key: z3.string(),
|
|
431
|
+
value: z3.string().optional()
|
|
432
|
+
}).strict(),
|
|
433
|
+
z3.object({
|
|
434
|
+
type: z3.literal("query"),
|
|
435
|
+
key: z3.string(),
|
|
436
|
+
value: z3.string().optional()
|
|
437
|
+
}).strict()
|
|
438
|
+
])
|
|
439
|
+
).optional();
|
|
440
|
+
var vercelOutputConfigSchema = z3.object({
|
|
441
|
+
version: z3.literal(3),
|
|
442
|
+
routes: z3.array(
|
|
443
|
+
z3.union([
|
|
444
|
+
z3.object({
|
|
445
|
+
src: z3.string(),
|
|
446
|
+
dest: z3.string().optional(),
|
|
447
|
+
headers: z3.record(z3.string()).optional(),
|
|
448
|
+
methods: z3.array(z3.string()).optional(),
|
|
449
|
+
status: z3.number().int().positive().optional(),
|
|
450
|
+
continue: z3.boolean().optional(),
|
|
451
|
+
check: z3.boolean().optional(),
|
|
452
|
+
missing: HasOrMissing,
|
|
453
|
+
has: HasOrMissing,
|
|
454
|
+
locale: z3.object({
|
|
455
|
+
redirect: z3.record(z3.string()).optional(),
|
|
456
|
+
cookie: z3.string().optional()
|
|
457
|
+
}).strict().optional(),
|
|
458
|
+
middlewarePath: z3.string().optional()
|
|
459
|
+
}).strict(),
|
|
460
|
+
z3.object({
|
|
461
|
+
handle: z3.union([
|
|
462
|
+
z3.literal("rewrite"),
|
|
463
|
+
z3.literal("filesystem"),
|
|
464
|
+
z3.literal("resource"),
|
|
465
|
+
z3.literal("miss"),
|
|
466
|
+
z3.literal("hit"),
|
|
467
|
+
z3.literal("error")
|
|
468
|
+
]),
|
|
469
|
+
src: z3.string().optional(),
|
|
470
|
+
dest: z3.string().optional(),
|
|
471
|
+
status: z3.number().optional()
|
|
472
|
+
}).strict()
|
|
473
|
+
])
|
|
474
|
+
).optional(),
|
|
475
|
+
images: z3.object({
|
|
476
|
+
sizes: z3.tuple([z3.number().int().positive(), z3.number().int().positive()]),
|
|
477
|
+
domains: z3.array(z3.string()).nonempty().optional(),
|
|
478
|
+
minimumCacheTTL: z3.number().int().positive().optional(),
|
|
479
|
+
formats: z3.union([z3.literal("image/avif"), z3.literal("image/webp")]).array().nonempty().optional(),
|
|
480
|
+
dangerouslyAllowSVG: z3.boolean().optional(),
|
|
481
|
+
contentSecurityPolicy: z3.string().optional()
|
|
482
|
+
}).strict().optional(),
|
|
483
|
+
wildcard: z3.array(
|
|
484
|
+
z3.object({
|
|
485
|
+
domain: z3.string(),
|
|
486
|
+
value: z3.string()
|
|
487
|
+
}).strict()
|
|
488
|
+
).optional(),
|
|
489
|
+
overrides: z3.record(
|
|
490
|
+
z3.object({
|
|
491
|
+
path: z3.string().optional(),
|
|
492
|
+
contentType: z3.string().optional()
|
|
493
|
+
}).strict()
|
|
494
|
+
).optional(),
|
|
495
|
+
cache: z3.array(z3.string()).optional()
|
|
496
|
+
}).strict();
|
|
497
|
+
|
|
498
|
+
// src/config.ts
|
|
499
|
+
import fs2 from "fs/promises";
|
|
500
|
+
import {
|
|
501
|
+
getTransformedRoutes,
|
|
502
|
+
mergeRoutes,
|
|
503
|
+
normalizeRoutes
|
|
504
|
+
} from "@vercel/routing-utils";
|
|
505
|
+
function reorderEnforce(arr) {
|
|
506
|
+
return [
|
|
507
|
+
...arr.filter((r) => r.enforce === "pre"),
|
|
508
|
+
...arr.filter((r) => !r.enforce),
|
|
509
|
+
...arr.filter((r) => r.enforce === "post")
|
|
510
|
+
];
|
|
511
|
+
}
|
|
512
|
+
function getConfig(resolvedConfig, rewrites, overrides, headers) {
|
|
513
|
+
const _rewrites = [
|
|
514
|
+
// User provided config always comes first
|
|
515
|
+
...resolvedConfig.vercel?.rewrites ?? [],
|
|
516
|
+
...rewrites ?? []
|
|
517
|
+
];
|
|
518
|
+
const _enforcedRewrites = reorderEnforce(_rewrites).map((r) => {
|
|
519
|
+
r.source = r.source.replaceAll(/:\[(\.\.\.)(.*)\]/g, ":$2*");
|
|
520
|
+
r.source = r.source.replaceAll(/:(\.\.\.)(.*)/g, ":$2+");
|
|
521
|
+
return r;
|
|
522
|
+
});
|
|
523
|
+
const { routes, error } = getTransformedRoutes({
|
|
524
|
+
cleanUrls: resolvedConfig.vercel?.cleanUrls ?? true,
|
|
525
|
+
trailingSlash: resolvedConfig.vercel?.trailingSlash,
|
|
526
|
+
rewrites: _enforcedRewrites,
|
|
527
|
+
redirects: resolvedConfig.vercel?.redirects ? reorderEnforce(resolvedConfig.vercel?.redirects) : void 0,
|
|
528
|
+
headers
|
|
529
|
+
});
|
|
530
|
+
if (error) {
|
|
531
|
+
throw error;
|
|
532
|
+
}
|
|
533
|
+
if (resolvedConfig.vercel?.config?.routes && resolvedConfig.vercel.config.routes.length > 0 && !resolvedConfig.vercel.config.routes.every((r) => "continue" in r && r.continue)) {
|
|
534
|
+
console.warn(
|
|
535
|
+
'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`.'
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
let userRoutes = [];
|
|
539
|
+
let buildRoutes = [];
|
|
540
|
+
if (resolvedConfig.vercel?.config?.routes) {
|
|
541
|
+
const norm = normalizeRoutes(resolvedConfig.vercel.config.routes);
|
|
542
|
+
if (norm.error) {
|
|
543
|
+
throw norm.error;
|
|
544
|
+
}
|
|
545
|
+
userRoutes = norm.routes ?? [];
|
|
546
|
+
}
|
|
547
|
+
if (routes) {
|
|
548
|
+
const norm = normalizeRoutes(routes);
|
|
549
|
+
if (norm.error) {
|
|
550
|
+
throw norm.error;
|
|
551
|
+
}
|
|
552
|
+
buildRoutes = norm.routes ?? [];
|
|
553
|
+
}
|
|
554
|
+
const cleanRoutes = mergeRoutes({
|
|
555
|
+
userRoutes,
|
|
556
|
+
builds: [
|
|
557
|
+
{
|
|
558
|
+
use: "@vercel/node",
|
|
559
|
+
entrypoint: "index.js",
|
|
560
|
+
routes: buildRoutes
|
|
561
|
+
}
|
|
562
|
+
]
|
|
563
|
+
});
|
|
564
|
+
return vercelOutputConfigSchema.parse({
|
|
565
|
+
version: 3,
|
|
566
|
+
...resolvedConfig.vercel?.config,
|
|
567
|
+
routes: cleanRoutes,
|
|
568
|
+
overrides: {
|
|
569
|
+
...resolvedConfig.vercel?.config?.overrides,
|
|
570
|
+
...overrides
|
|
571
|
+
}
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
function getConfigDestination(resolvedConfig) {
|
|
575
|
+
return path3.join(getOutput(resolvedConfig), "config.json");
|
|
576
|
+
}
|
|
577
|
+
async function writeConfig(resolvedConfig, rewrites, overrides, headers) {
|
|
578
|
+
await fs2.writeFile(
|
|
579
|
+
getConfigDestination(resolvedConfig),
|
|
580
|
+
JSON.stringify(getConfig(resolvedConfig, rewrites, overrides, headers), void 0, 2),
|
|
581
|
+
"utf-8"
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// src/helpers.ts
|
|
586
|
+
import fs3 from "fs/promises";
|
|
587
|
+
import path4 from "path";
|
|
588
|
+
async function copyDir(src, dest) {
|
|
589
|
+
await fs3.mkdir(dest, { recursive: true });
|
|
590
|
+
const entries = await fs3.readdir(src, { withFileTypes: true });
|
|
591
|
+
for (const entry of entries) {
|
|
592
|
+
const srcPath = path4.join(src, entry.name);
|
|
593
|
+
const destPath = path4.join(dest, entry.name);
|
|
594
|
+
entry.isDirectory() ? await copyDir(srcPath, destPath) : await fs3.copyFile(srcPath, destPath);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// src/prerender.ts
|
|
599
|
+
import fs4 from "fs/promises";
|
|
600
|
+
import path5 from "path";
|
|
601
|
+
|
|
602
|
+
// src/schemas/config/prerender-config.ts
|
|
603
|
+
import { z as z4 } from "zod";
|
|
604
|
+
var vercelOutputPrerenderConfigSchema = z4.object({
|
|
605
|
+
expiration: z4.union([z4.number().int().positive(), z4.literal(false)]),
|
|
606
|
+
group: z4.number().int().optional(),
|
|
607
|
+
bypassToken: z4.string().optional(),
|
|
608
|
+
fallback: z4.string().optional(),
|
|
609
|
+
allowQuery: z4.array(z4.string()).optional()
|
|
610
|
+
}).strict();
|
|
611
|
+
|
|
612
|
+
// src/prerender.ts
|
|
613
|
+
function execPrerender(resolvedConfig) {
|
|
614
|
+
const prerender = resolvedConfig.vercel?.prerender;
|
|
615
|
+
if (prerender === false) {
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
return prerender?.(resolvedConfig);
|
|
619
|
+
}
|
|
620
|
+
var group = 1;
|
|
621
|
+
async function writePrerenderConfig(resolvedConfig, destination, isr) {
|
|
622
|
+
const parsed = path5.parse(destination);
|
|
623
|
+
const outfile = path5.join(getOutput(resolvedConfig, "functions"), parsed.dir, `${parsed.name}.prerender-config.json`);
|
|
624
|
+
await fs4.mkdir(path5.join(getOutput(resolvedConfig, "functions"), parsed.dir), { recursive: true });
|
|
625
|
+
await fs4.writeFile(
|
|
626
|
+
outfile,
|
|
627
|
+
JSON.stringify(
|
|
628
|
+
vercelOutputPrerenderConfigSchema.parse({
|
|
629
|
+
group: group++,
|
|
630
|
+
...isr
|
|
631
|
+
}),
|
|
632
|
+
void 0,
|
|
633
|
+
2
|
|
634
|
+
),
|
|
635
|
+
"utf-8"
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
function getPrerenderSymlinkInfo(resolvedConfig, destination, target) {
|
|
639
|
+
const parsed = path5.parse(destination);
|
|
640
|
+
const targetParsed = path5.parse(target);
|
|
641
|
+
return {
|
|
642
|
+
target: path5.join(getOutput(resolvedConfig, "functions"), targetParsed.dir, `${targetParsed.name}.func`),
|
|
643
|
+
link: path5.join(getOutput(resolvedConfig, "functions"), parsed.dir, `${parsed.name}.func`)
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
async function buildPrerenderConfigs(resolvedConfig, extractedIsr) {
|
|
647
|
+
const isr = Object.assign({}, extractedIsr, await getIsrConfig(resolvedConfig));
|
|
648
|
+
const entries = Object.entries(isr);
|
|
649
|
+
const rewrites = [];
|
|
650
|
+
for (const [destination, { symlink, route, ...isr2 }] of entries) {
|
|
651
|
+
await writePrerenderConfig(resolvedConfig, destination, isr2);
|
|
652
|
+
if (symlink) {
|
|
653
|
+
const info = getPrerenderSymlinkInfo(resolvedConfig, destination, symlink);
|
|
654
|
+
await copyDir(info.target, info.link);
|
|
655
|
+
}
|
|
656
|
+
if (route) {
|
|
657
|
+
rewrites.push({
|
|
658
|
+
source: `(${route})`,
|
|
659
|
+
destination: `${destination}/?__original_path=$1`
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
return rewrites;
|
|
664
|
+
}
|
|
665
|
+
async function getIsrConfig(resolvedConfig) {
|
|
666
|
+
const isr = resolvedConfig.vercel?.isr ?? {};
|
|
667
|
+
if (typeof isr === "function") {
|
|
668
|
+
return await isr();
|
|
669
|
+
}
|
|
670
|
+
return isr;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// src/index.ts
|
|
674
|
+
function vercelPluginCleanup() {
|
|
675
|
+
let resolvedConfig;
|
|
676
|
+
return {
|
|
677
|
+
apply: "build",
|
|
678
|
+
name: "vite-plugin-vercel:cleanup",
|
|
679
|
+
enforce: "pre",
|
|
680
|
+
configResolved(config) {
|
|
681
|
+
resolvedConfig = config;
|
|
682
|
+
},
|
|
683
|
+
writeBundle: {
|
|
684
|
+
order: "pre",
|
|
685
|
+
sequential: true,
|
|
686
|
+
async handler() {
|
|
687
|
+
if (!resolvedConfig.build?.ssr) {
|
|
688
|
+
await cleanOutputDirectory(resolvedConfig);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
function vercelPlugin() {
|
|
695
|
+
let resolvedConfig;
|
|
696
|
+
let vikeFound = false;
|
|
697
|
+
return {
|
|
698
|
+
apply: "build",
|
|
699
|
+
name: "vite-plugin-vercel",
|
|
700
|
+
enforce: "post",
|
|
701
|
+
configResolved(config) {
|
|
702
|
+
resolvedConfig = config;
|
|
703
|
+
vikeFound = resolvedConfig.plugins.some((p) => p.name.match("^vite-plugin-ssr:|^vike:"));
|
|
704
|
+
if (typeof resolvedConfig.vercel?.distContainsOnlyStatic === "undefined") {
|
|
705
|
+
resolvedConfig.vercel ??= {};
|
|
706
|
+
resolvedConfig.vercel.distContainsOnlyStatic = !vikeFound;
|
|
707
|
+
}
|
|
708
|
+
},
|
|
709
|
+
writeBundle: {
|
|
710
|
+
order: "post",
|
|
711
|
+
sequential: true,
|
|
712
|
+
async handler() {
|
|
713
|
+
if (!resolvedConfig.build?.ssr) {
|
|
714
|
+
if (vikeFound) {
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
const overrides = await execPrerender(resolvedConfig);
|
|
719
|
+
const userOverrides = await computeStaticHtmlOverrides(resolvedConfig);
|
|
720
|
+
const { rewrites, isr, headers } = await buildEndpoints(resolvedConfig);
|
|
721
|
+
rewrites.push(...await buildPrerenderConfigs(resolvedConfig, isr));
|
|
722
|
+
await writeConfig(
|
|
723
|
+
resolvedConfig,
|
|
724
|
+
rewrites,
|
|
725
|
+
{
|
|
726
|
+
...userOverrides,
|
|
727
|
+
...overrides
|
|
728
|
+
},
|
|
729
|
+
headers
|
|
730
|
+
);
|
|
731
|
+
await copyDistToStatic(resolvedConfig);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
async function cleanOutputDirectory(resolvedConfig) {
|
|
737
|
+
await fs5.rm(getOutput(resolvedConfig), {
|
|
738
|
+
recursive: true,
|
|
739
|
+
force: true
|
|
740
|
+
});
|
|
741
|
+
await fs5.mkdir(getOutput(resolvedConfig), { recursive: true });
|
|
742
|
+
}
|
|
743
|
+
async function copyDistToStatic(resolvedConfig) {
|
|
744
|
+
if (resolvedConfig.vercel?.distContainsOnlyStatic) {
|
|
745
|
+
await copyDir(resolvedConfig.build.outDir, getOutput(resolvedConfig, "static"));
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
async function computeStaticHtmlOverrides(resolvedConfig) {
|
|
749
|
+
const staticAbsolutePath = getOutput(resolvedConfig, "static");
|
|
750
|
+
const files = await getStaticHtmlFiles(staticAbsolutePath);
|
|
751
|
+
const publicDir = getPublic(resolvedConfig);
|
|
752
|
+
const publicFiles = await getStaticHtmlFiles(publicDir);
|
|
753
|
+
files.push(...publicFiles.map((f) => f.replace(publicDir, staticAbsolutePath)));
|
|
754
|
+
return files.reduce(
|
|
755
|
+
(acc, curr) => {
|
|
756
|
+
const relPath = path6.relative(staticAbsolutePath, curr);
|
|
757
|
+
const parsed = path6.parse(relPath);
|
|
758
|
+
const pathJoined = path6.join(parsed.dir, parsed.name);
|
|
759
|
+
acc[relPath] = {
|
|
760
|
+
path: pathJoined
|
|
761
|
+
};
|
|
762
|
+
return acc;
|
|
763
|
+
},
|
|
764
|
+
{}
|
|
765
|
+
);
|
|
766
|
+
}
|
|
767
|
+
async function getStaticHtmlFiles(src) {
|
|
768
|
+
try {
|
|
769
|
+
await fs5.stat(src);
|
|
770
|
+
} catch (e) {
|
|
771
|
+
return [];
|
|
772
|
+
}
|
|
773
|
+
const entries = await fs5.readdir(src, { withFileTypes: true });
|
|
774
|
+
const htmlFiles = [];
|
|
775
|
+
for (const entry of entries) {
|
|
776
|
+
const srcPath = path6.join(src, entry.name);
|
|
777
|
+
entry.isDirectory() ? htmlFiles.push(...await getStaticHtmlFiles(srcPath)) : srcPath.endsWith(".html") ? htmlFiles.push(srcPath) : void 0;
|
|
778
|
+
}
|
|
779
|
+
return htmlFiles;
|
|
780
|
+
}
|
|
781
|
+
async function tryImportVpvv(options) {
|
|
782
|
+
try {
|
|
783
|
+
await import("vike/plugin");
|
|
784
|
+
const vpvv = await import("@vite-plugin-vercel/vike");
|
|
785
|
+
return vpvv.default(options);
|
|
786
|
+
} catch (e) {
|
|
787
|
+
try {
|
|
788
|
+
await import("vite-plugin-ssr/plugin");
|
|
789
|
+
const vpvv = await import("@vite-plugin-vercel/vike");
|
|
790
|
+
return vpvv.default(options);
|
|
791
|
+
} catch (e2) {
|
|
792
|
+
return null;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
function allPlugins(options = {}) {
|
|
797
|
+
const { smart, ...rest } = options;
|
|
798
|
+
return [vercelPluginCleanup(), vercelPlugin(), smart !== false ? tryImportVpvv(rest) : null];
|
|
799
|
+
}
|
|
9
800
|
export {
|
|
10
|
-
|
|
11
|
-
vercel
|
|
801
|
+
allPlugins as default
|
|
12
802
|
};
|