185 lines
4.2 KiB
JavaScript
185 lines
4.2 KiB
JavaScript
const { spawnSync } = require('child_process');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const repoRoot = path.resolve(__dirname, '..');
|
|
const prismaSchemaPath = path.join(repoRoot, 'prisma', 'schema.prisma');
|
|
const generatedClientDir = path.join(repoRoot, 'node_modules', '.prisma', 'client');
|
|
const generatedSchemaPath = path.join(generatedClientDir, 'schema.prisma');
|
|
|
|
function readFileIfExists(filePath) {
|
|
try {
|
|
return fs.readFileSync(filePath, 'utf8');
|
|
} catch (error) {
|
|
if (error && error.code === 'ENOENT') {
|
|
return null;
|
|
}
|
|
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
function normalizeSchema(schema) {
|
|
return schema.replace(/\s+/g, '');
|
|
}
|
|
|
|
function schemasMatch() {
|
|
const sourceSchema = readFileIfExists(prismaSchemaPath);
|
|
const generatedSchema = readFileIfExists(generatedSchemaPath);
|
|
|
|
return Boolean(
|
|
sourceSchema &&
|
|
generatedSchema &&
|
|
normalizeSchema(sourceSchema) === normalizeSchema(generatedSchema)
|
|
);
|
|
}
|
|
|
|
function run(command, args, options = {}) {
|
|
const shouldUseShell =
|
|
process.platform === 'win32' && command.toLowerCase().endsWith('.cmd');
|
|
|
|
const result = spawnSync(command, args, {
|
|
cwd: repoRoot,
|
|
encoding: 'utf8',
|
|
stdio: 'pipe',
|
|
shell: shouldUseShell,
|
|
env: {
|
|
...process.env,
|
|
...options.env,
|
|
},
|
|
});
|
|
|
|
if (result.stdout) {
|
|
process.stdout.write(result.stdout);
|
|
}
|
|
|
|
if (result.stderr) {
|
|
process.stderr.write(result.stderr);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
function isWSL() {
|
|
return (
|
|
process.platform === 'linux' &&
|
|
fs.existsSync('/proc/version') &&
|
|
fs.readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft')
|
|
);
|
|
}
|
|
|
|
function isWindowsPrismaRenameLock(output) {
|
|
const text = [output.stdout, output.stderr]
|
|
.filter(Boolean)
|
|
.join('\n');
|
|
|
|
return (
|
|
process.platform === 'win32' &&
|
|
text.includes('EPERM: operation not permitted, rename') &&
|
|
text.includes('query_engine-windows.dll.node')
|
|
);
|
|
}
|
|
|
|
function isPrismaCopyfileEio(output) {
|
|
const text = [output.stdout, output.stderr]
|
|
.filter(Boolean)
|
|
.join('\n');
|
|
|
|
return (
|
|
text.includes('EIO: i/o error, copyfile') &&
|
|
(text.includes('libquery_engine-') || text.includes('query_engine-'))
|
|
);
|
|
}
|
|
|
|
function cleanupPrismaTempFiles() {
|
|
if (!fs.existsSync(generatedClientDir)) {
|
|
return;
|
|
}
|
|
|
|
for (const entry of fs.readdirSync(generatedClientDir)) {
|
|
if (!entry.includes('.tmp')) {
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
fs.rmSync(path.join(generatedClientDir, entry), { force: true });
|
|
} catch (error) {
|
|
console.warn(`Failed to remove stale Prisma temp file ${entry}:`, error);
|
|
}
|
|
}
|
|
}
|
|
|
|
function runPrismaGenerate() {
|
|
const prismaBin =
|
|
process.platform === 'win32'
|
|
? path.join(repoRoot, 'node_modules', '.bin', 'prisma.cmd')
|
|
: path.join(repoRoot, 'node_modules', '.bin', 'prisma');
|
|
|
|
if (isWSL()) {
|
|
cleanupPrismaTempFiles();
|
|
}
|
|
|
|
let result = run(prismaBin, ['generate']);
|
|
|
|
if (result.error) {
|
|
throw result.error;
|
|
}
|
|
|
|
if ((result.status ?? 1) === 0) {
|
|
return 0;
|
|
}
|
|
|
|
const retryablePrismaFsError =
|
|
isWindowsPrismaRenameLock(result) || isPrismaCopyfileEio(result);
|
|
|
|
if (retryablePrismaFsError) {
|
|
cleanupPrismaTempFiles();
|
|
result = run(prismaBin, ['generate']);
|
|
|
|
if (result.error) {
|
|
throw result.error;
|
|
}
|
|
|
|
if ((result.status ?? 1) === 0) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (!retryablePrismaFsError || !schemasMatch()) {
|
|
return result.status ?? 1;
|
|
}
|
|
|
|
console.warn(
|
|
'\nPrisma generate hit a filesystem copy/rename issue, but the generated client already matches prisma/schema.prisma. Continuing with the existing client.\n'
|
|
);
|
|
|
|
return 0;
|
|
}
|
|
|
|
function runNextBuild() {
|
|
const nextBin =
|
|
process.platform === 'win32'
|
|
? path.join(repoRoot, 'node_modules', '.bin', 'next.cmd')
|
|
: path.join(repoRoot, 'node_modules', '.bin', 'next');
|
|
|
|
const memoryLimit = isWSL() ? '8192' : '4096';
|
|
|
|
return run(nextBin, ['build'], {
|
|
env: {
|
|
NODE_OPTIONS: `--max-old-space-size=${memoryLimit}`,
|
|
SKIP_ENV_VALIDATION: 'true',
|
|
},
|
|
});
|
|
}
|
|
|
|
const prismaExitCode = runPrismaGenerate();
|
|
if (prismaExitCode !== 0) {
|
|
process.exit(prismaExitCode);
|
|
}
|
|
|
|
const nextResult = runNextBuild();
|
|
if (nextResult.error) {
|
|
throw nextResult.error;
|
|
}
|
|
|
|
process.exit(nextResult.status ?? 1); |