Files
splatoon3.ink/app/data/TarZstdWriter.mjs
2026-08-22 09:53:22 -07:00

56 lines
1.5 KiB
JavaScript

import path from 'node:path';
import { spawn } from 'node:child_process';
import { pipeline } from 'node:stream/promises';
function processCompletion(child, name) {
let stderr = '';
child.stderr.setEncoding('utf8');
child.stderr.on('data', chunk => {
stderr += chunk;
});
return new Promise((resolve, reject) => {
child.once('error', reject);
child.once('close', (code, signal) => {
if (code === 0) {
resolve();
} else {
reject(new Error(
`${name} failed (${signal ? `signal ${signal}` : `exit ${code}`}): ${stderr.trim()}`,
));
}
});
});
}
export default class TarZstdWriter
{
async write(sourceDirectory, archivePath, files) {
let tar = spawn('tar', ['-cf', '-', '-C', sourceDirectory, '--', ...files], {
env: { ...process.env, COPYFILE_DISABLE: '1' },
stdio: ['ignore', 'pipe', 'pipe'],
});
let zstd = spawn('zstd', [
'-19',
'--long=27',
'--single-thread',
'-f',
'-o', archivePath,
], {
stdio: ['pipe', 'ignore', 'pipe'],
});
let results = await Promise.allSettled([
processCompletion(tar, 'tar'),
pipeline(tar.stdout, zstd.stdin),
processCompletion(zstd, 'zstd'),
]);
let errors = results
.filter(result => result.status === 'rejected')
.map(result => result.reason);
if (errors.length > 0) {
throw new AggregateError(errors, `Could not create ${path.basename(archivePath)}`);
}
}
}