Files
makelore/scripts/prepare-publish-runtime.mjs

71 lines
2.9 KiB
JavaScript

#!/usr/bin/env node
import { cpSync, existsSync, mkdtempSync, mkdirSync, readFileSync, rmSync } from 'node:fs';
import { createRequire } from 'node:module';
import { tmpdir } from 'node:os';
import { dirname, join, resolve } from 'node:path';
import { spawnSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';
const require = createRequire(import.meta.url);
const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
const expectedVersion = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')).dependencies?.npm;
const npmPackagePath = require.resolve('npm/package.json');
const npmPackage = JSON.parse(readFileSync(npmPackagePath, 'utf8'));
const npmCli = join(dirname(npmPackagePath), 'bin', 'npm-cli.js');
const destination = join(root, 'build', 'publish-runtime');
if (npmPackage.version !== expectedVersion) {
throw new Error(`Publish npm version mismatch: expected ${expectedVersion}, got ${npmPackage.version}`);
}
if (!npmPackage.bundleDependencies?.includes('proc-log')) {
throw new Error('Publish npm package does not declare its runtime dependencies as bundled dependencies');
}
const temporaryRoot = mkdtempSync(join(tmpdir(), 'makelore-publish-runtime-'));
try {
const packResult = spawnSync(process.execPath, [
npmCli,
'pack',
dirname(npmPackagePath),
'--ignore-scripts',
'--json',
'--pack-destination',
temporaryRoot,
], { cwd: root, encoding: 'utf8', shell: false });
if (packResult.status !== 0) {
throw new Error(`Unable to pack publish npm runtime: ${packResult.stderr || packResult.stdout}`);
}
const packMetadata = JSON.parse(packResult.stdout);
if (!Array.isArray(packMetadata) || packMetadata.length !== 1) {
throw new Error('npm pack returned unexpected metadata');
}
const filename = packMetadata[0]?.filename;
if (typeof filename !== 'string'
|| !/^[^/\\]+\.tgz$/.test(filename)
|| resolve(temporaryRoot, filename) !== join(temporaryRoot, filename)) {
throw new Error(`npm pack returned an unsafe filename: ${JSON.stringify(filename)}`);
}
mkdirSync(join(temporaryRoot, 'runtime'), { recursive: true });
await import('tar').then(({ x }) => x({
file: join(temporaryRoot, filename),
cwd: join(temporaryRoot, 'runtime'),
strip: 1,
}));
const stagedRuntime = join(temporaryRoot, 'runtime');
for (const dependency of npmPackage.bundleDependencies) {
if (!existsSync(join(stagedRuntime, 'node_modules', ...dependency.split('/'), 'package.json'))) {
throw new Error(`Packed npm runtime is missing bundled dependency ${dependency}`);
}
}
rmSync(destination, { recursive: true, force: true });
mkdirSync(dirname(destination), { recursive: true });
cpSync(stagedRuntime, destination, { recursive: true });
} finally {
rmSync(temporaryRoot, { recursive: true, force: true });
}
console.log(`Prepared self-contained npm ${npmPackage.version} runtime at ${destination}`);