vercel
Version:
The command-line interface for Vercel
104 lines (89 loc) • 3.72 kB
JavaScript
/**
* This file gets copied out of the `pkg` snapshot filesystem into the `vc dev`
* builder cache directory, so it's very important that it does not rely on any
* modules from npm that would not be available in that directory (so basically,
* only Vercel Runtimes and `@vercel/build-utils`.
*/
const { FileFsRef } = require('@vercel/build-utils');
const fs = require('fs');
const path = require('path');
const os = require('os');
const crypto = require('crypto');
// Threshold for writing zipBuffer to a temp file instead of sending via IPC.
// JSON serialization of large Buffers causes OOM due to memory amplification
// (each byte becomes a separate array element in the JSON representation).
const ZIP_BUFFER_FILE_THRESHOLD = 256 * 1024 * 1024; // 256MB
process.on('unhandledRejection', err => {
// biome-ignore lint/suspicious/noConsole: intentional console usage
console.error('Exiting builder due to build error:');
// biome-ignore lint/suspicious/noConsole: intentional console usage
console.error(err);
process.exit(1);
});
process.on('message', onMessage);
function onMessage(message) {
processMessage(message).catch(err => {
Object.defineProperty(err, 'message', { enumerable: true });
Object.defineProperty(err, 'stack', { enumerable: true });
process.removeListener('message', onMessage);
process.send({ type: 'buildResult', error: err }, () => process.exit(1));
});
}
async function processMessage(message) {
const { requirePath, buildOptions } = message;
const builder = require(requirePath);
// Convert the `files` to back into `FileFsRef` instances
for (const name of Object.keys(buildOptions.files)) {
const ref = Object.assign(
Object.create(FileFsRef.prototype),
buildOptions.files[name]
);
buildOptions.files[name] = ref;
}
let result = await builder.build(buildOptions);
// `@vercel/next` sets this, but it causes "Converting circular
// structure to JSON" errors, so delete the property...
delete result.childProcesses;
// Unwrap BuildResultVX (builder.version === -1) to the actual V2 or V3 result.
// effectiveVersion reflects the actual result version for downstream checks.
let effectiveVersion = builder.version;
if (builder.version === -1) {
effectiveVersion = result.resultVersion;
result = result.result;
}
// Helper to handle zipBuffer - writes to temp file if too large for IPC
async function processLambdaOutput(output) {
const zipBuffer = await output.createZip();
// Delete files after creating zip to avoid OOM when serializing via IPC.
// The zipBuffer contains all file data, so files is no longer needed.
delete output.files;
// For large zip buffers, write to a temp file instead of sending via IPC.
// JSON serialization of large Buffers causes OOM because each byte becomes
// a separate array element (e.g., {"type":"Buffer","data":[1,2,3,...]}).
if (zipBuffer.length > ZIP_BUFFER_FILE_THRESHOLD) {
const tempDir = os.tmpdir();
const randomId = crypto.randomBytes(8).toString('hex');
const zipFilePath = path.join(
tempDir,
`vercel-dev-lambda-${randomId}.zip`
);
fs.writeFileSync(zipFilePath, zipBuffer);
output.zipBufferPath = zipFilePath;
} else {
output.zipBuffer = zipBuffer;
}
}
if (effectiveVersion === 3) {
if (result.output.type === 'Lambda') {
await processLambdaOutput(result.output);
}
} else {
for (const output of Object.values(result.output)) {
if (output.type === 'Lambda') {
await processLambdaOutput(output);
}
}
}
process.send({ type: 'buildResult', result });
}
process.send({ type: 'ready' });