flecks/build/publish.js

151 lines
4.9 KiB
JavaScript
Raw Normal View History

2024-02-09 01:11:27 -06:00
const {Buffer} = require('buffer');
2024-02-08 02:58:40 -06:00
const {exec} = require('child_process');
2024-02-09 01:11:27 -06:00
const {createHash} = require('crypto');
const {createReadStream} = require('fs');
const {cp, mkdir, writeFile} = require('fs/promises');
2024-02-08 01:23:57 -06:00
const {join} = require('path');
2024-02-09 01:11:27 -06:00
const {
processCode,
spawnWith,
} = require('@flecks/core/src/server');
const Arborist = require('@npmcli/arborist');
2024-02-08 01:23:57 -06:00
const {glob} = require('glob');
2024-02-09 01:11:27 -06:00
const concurrent = require('./concurrent');
2024-02-08 01:23:57 -06:00
const {
FLECKS_CORE_ROOT = process.cwd(),
} = process.env;
2024-02-09 01:11:27 -06:00
const args = ['npm', 'publish', ...process.argv.slice(2)];
const bumpedVersions = {};
const creators = ['create-app', 'create-fleck'];
const localVersions = {};
const packCache = join(FLECKS_CORE_ROOT, 'node_modules', '.cache', '@flecks', 'publish');
2024-02-08 01:23:57 -06:00
const {workspaces} = require(join(FLECKS_CORE_ROOT, 'package.json'));
2024-02-09 01:11:27 -06:00
const run = (cmd) => (
new Promise((resolve) => {
exec(cmd, (error, stdout) => {
if (error) {
resolve(undefined)
return;
}
resolve(stdout.trim());
});
})
);
// Get integrity sums for creator dependencies.
const packPkg = async (pkg) => {
await processCode(spawnWith(
['npm', 'pack', '--pack-destination', packCache],
{cwd: join(FLECKS_CORE_ROOT, 'packages', pkg, 'dist', 'fleck'), stdio: 'ignore'},
));
};
const bumpDependencies = (dependencies) => (
Object.fromEntries(
Object.entries(dependencies)
.map(([pkg, version]) => ([pkg, localVersions[pkg] || version])),
)
);
const integrityForPkg = async (pkg) => {
const pack = join(packCache, `flecks-${pkg.split('/')[1]}-${localVersions[pkg]}.tgz`);
const buffers = [];
// eslint-disable-next-line no-restricted-syntax
for await (const data of createReadStream(pack).pipe(createHash('sha512'))) {
buffers.push(data);
}
return `sha512-${Buffer.concat(buffers).toString('base64')}`;
};
const shrinkwrap = async (path) => {
const arb = new Arborist({
path,
registry: await run('npm config get registry'),
2024-02-08 01:23:57 -06:00
});
2024-02-09 01:11:27 -06:00
const {meta} = await arb.buildIdealTree({saveType: 'prod'});
const shrinkwrap = await meta.commit();
shrinkwrap.packages = Object.fromEntries(
await Promise.all(
Object.entries(shrinkwrap.packages)
.map(async ([pkg, config]) => {
if (pkg.match(/node_modules\/@flecks\/[^/]+$/)) {
if (config.dependencies) {
config.dependencies = bumpDependencies(config.dependencies);
}
if (config.devDependencies) {
config.devDependencies = bumpDependencies(config.devDependencies);
}
const subpkg = pkg.split('/').slice(1).join('/');
config.version = localVersions[subpkg];
config.integrity = await integrityForPkg(subpkg);
}
return [pkg, config];
}),
),
);
return shrinkwrap;
};
const shrinkwrapsAndPublish = async (creator) => {
const dist = join(FLECKS_CORE_ROOT, 'packages', creator, 'dist', 'fleck');
const fakePackage = join(packCache, creator);
await mkdir(fakePackage, {recursive: true});
// Get a shrinkwrap from template package.json and insert it as a package-lock.json.
await cp(join(dist, 'template', 'package.json.noconflict'), join(fakePackage, 'package.json'));
await writeFile(
join(dist, 'template', 'package-lock.json.noconflict'),
JSON.stringify(await shrinkwrap(fakePackage), null, 2),
);
// Get a shrinkwrap from the built creator and insert it as shrinkwrap.
await writeFile(
join(dist, 'npm-shrinkwrap.json'),
JSON.stringify(await shrinkwrap(dist), null, 2),
);
// Publish.
await processCode(spawnWith(args, {cwd: dist}));
};
(async () => {
await concurrent(
(await Promise.all(workspaces.map((path) => glob(join(FLECKS_CORE_ROOT, path))))).flat(),
async (cwd) => {
const {name, version} = require(join(cwd, 'package.json'));
const [localVersion, remoteVersion] = await Promise.all([
version,
run(`npm view ${name} version`),
]);
localVersions[name] = version;
if (localVersion === remoteVersion) {
return undefined;
}
bumpedVersions[name] = version;
// Skip creators for now.
if (creators.some((creator) => name.endsWith(creator))) {
return undefined;
}
return processCode(spawnWith(args, {cwd: join(cwd, 'dist', 'fleck')}));
},
);
// No creators? Bail.
2024-02-09 06:32:14 -06:00
if (!bumpedVersions['@flecks/create-app'] && !bumpedVersions['@flecks/create-fleck']) {
2024-02-09 01:11:27 -06:00
return;
}
// Pack dependencies.
await mkdir(packCache, {recursive: true});
const dependencies = ['build', 'core', 'fleck', 'server'];
await Promise.all(dependencies.map(packPkg));
if (bumpedVersions['@flecks/create-fleck']) {
await shrinkwrapsAndPublish('create-fleck');
}
if (bumpedVersions['@flecks/create-app']) {
// Needs packed create-fleck for package lock.
await packPkg('create-fleck');
await shrinkwrapsAndPublish('create-app');
}
2024-02-08 01:23:57 -06:00
})();