Ghost/ghost/core/monobundle.js
Daniel Lockyer 00d2cc9f44 Improved speed of monobundle script
- right now, it loops through all packages serially, which isn't
  effectively using multi-core machines
- by using `concurrently`, we can rely on it to use all the cores it
  can, so this should dramatically speed up the bundling step
2024-07-22 15:45:46 +02:00

178 lines
5.1 KiB
JavaScript
Executable File

#!/usr/bin/env node
/* eslint-disable no-console */
const fs = require('fs');
const path = require('path');
const concurrently = require('concurrently');
const detectIndent = require('detect-indent');
const detectNewline = require('detect-newline');
const findRoot = require('find-root');
const {flattenDeep} = require('lodash');
const glob = require('glob');
const DETECT_TRAILING_WHITESPACE = /\s+$/;
const jsonFiles = new Map();
class JSONFile {
/**
* @param {string} filePath
* @returns {JSONFile}
*/
static for(filePath) {
if (jsonFiles.has(filePath)) {
return jsonFiles.get(filePath);
}
let jsonFile = new this(filePath);
jsonFiles.set(filePath, jsonFile);
return jsonFile;
}
/**
* @param {string} filename
*/
constructor(filename) {
this.filename = filename;
this.reload();
}
reload() {
const contents = fs.readFileSync(this.filename, {encoding: 'utf8'});
this.pkg = JSON.parse(contents);
this.lineEndings = detectNewline(contents);
this.indent = detectIndent(contents).amount;
let trailingWhitespace = DETECT_TRAILING_WHITESPACE.exec(contents);
this.trailingWhitespace = trailingWhitespace ? trailingWhitespace : '';
}
write() {
let contents = JSON.stringify(this.pkg, null, this.indent).replace(/\n/g, this.lineEndings);
fs.writeFileSync(this.filename, contents + this.trailingWhitespace, {encoding: 'utf8'});
}
}
/**
* @param {object} packageJson
*/
function getPackages(packageJson) {
if (!('workspaces' in packageJson)) {
return null;
}
const {workspaces} = packageJson;
if (Array.isArray(workspaces)) {
return workspaces;
}
return workspaces.packages || null;
}
/**
* @param {string} from
* @returns {string[]}
*/
function getWorkspaces(from) {
const root = findRoot(from, (dir) => {
const pkg = path.join(dir, 'package.json');
return fs.existsSync(pkg) && getPackages(require(pkg)) !== null;
});
const packages = getPackages(require(path.join(root, 'package.json')));
return flattenDeep(packages.map(name => glob.sync(path.join(root, `${name}/`))));
}
(async () => {
const cwd = process.cwd();
const nearestPkgJson = findRoot(cwd);
console.log('nearestPkgJson', nearestPkgJson);
const pkgInfo = JSONFile.for(path.join(nearestPkgJson, 'package.json'));
if (pkgInfo.pkg.name !== 'ghost') {
console.log('This script must be run from the `ghost` npm package directory');
process.exit(1);
}
const bundlePath = './components';
if (!fs.existsSync(bundlePath)){
fs.mkdirSync(bundlePath);
}
const workspaces = getWorkspaces(cwd)
.filter(w => !w.startsWith(cwd) && fs.existsSync(path.join(w, 'package.json')))
.filter(w => !w.includes('apps/'))
.filter(w => !w.includes('ghost/admin'));
console.log('workspaces', workspaces);
console.log('\n-------------------------\n');
const packagesToPack = [];
for (const w of workspaces) {
const workspacePkgInfo = JSONFile.for(path.join(w, 'package.json'));
if (!workspacePkgInfo.pkg.private) {
continue;
}
console.log(`packaging ${w}`);
workspacePkgInfo.pkg.version = pkgInfo.pkg.version;
workspacePkgInfo.write();
const slugifiedName = workspacePkgInfo.pkg.name.replace(/@/g, '').replace(/\//g, '-');
const packedFilename = `file:` + path.join(bundlePath, `${slugifiedName}-${workspacePkgInfo.pkg.version}.tgz`);
if (pkgInfo.pkg.dependencies[workspacePkgInfo.pkg.name]) {
console.log(`- dependencies override for ${workspacePkgInfo.pkg.name} to ${packedFilename}`);
pkgInfo.pkg.dependencies[workspacePkgInfo.pkg.name] = packedFilename;
}
if (pkgInfo.pkg.devDependencies[workspacePkgInfo.pkg.name]) {
console.log(`- devDependencies override for ${workspacePkgInfo.pkg.name} to ${packedFilename}`);
pkgInfo.pkg.devDependencies[workspacePkgInfo.pkg.name] = packedFilename;
}
if (pkgInfo.pkg.optionalDependencies[workspacePkgInfo.pkg.name]) {
console.log(`- optionalDependencies override for ${workspacePkgInfo.pkg.name} to ${packedFilename}`);
pkgInfo.pkg.optionalDependencies[workspacePkgInfo.pkg.name] = packedFilename;
}
console.log(`- resolution override for ${workspacePkgInfo.pkg.name} to ${packedFilename}\n`);
pkgInfo.pkg.resolutions[workspacePkgInfo.pkg.name] = packedFilename;
packagesToPack.push(w);
}
pkgInfo.write();
const {result} = concurrently(packagesToPack.map(w => ({
name: w,
cwd: w,
command: 'npm pack --pack-destination ../core/components'
})));
try {
await result;
} catch (e) {
console.error(e);
throw e;
}
const filesToCopy = [
'README.md',
'LICENSE',
'PRIVACY.md',
'yarn.lock'
];
for (const file of filesToCopy) {
console.log(`copying ../../${file} to ${file}`);
fs.copyFileSync(path.join('../../', file), file);
}
})();