UNPKG

@plurid/joiner

Version:
614 lines (595 loc) 26.3 kB
'use strict'; var worker_threads = require('worker_threads'); var fs = require('fs'); var path = require('path'); var yaml = require('js-yaml'); var Deon = require('@plurid/deon'); var child_process = require('child_process'); var checkUpdates = require('npm-check-updates'); var alphaVersioning = require('alpha-versioning'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var path__default = /*#__PURE__*/_interopDefaultLegacy(path); var yaml__default = /*#__PURE__*/_interopDefaultLegacy(yaml); var Deon__default = /*#__PURE__*/_interopDefaultLegacy(Deon); var checkUpdates__default = /*#__PURE__*/_interopDefaultLegacy(checkUpdates); /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ function __awaiter(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } // #endregion external // #endregion imports // #region module const resolvePackage = (packageName, configurationData) => { const { packages, } = configurationData; const safePackageName = packageName.toLowerCase(); if (safePackageName === 'all' || safePackageName === '*') { return packages; } if (safePackageName === 'self') { // The joiner file resolves to only one package. const codePackage = packages[0]; if (!codePackage) { console.log(`\n\tPackage ${packageName} could not be resolved.\n`); return; } return [ packages[0], ]; } if (safePackageName.startsWith('%')) { // The package is a numeric value, e.g. '%0', or '%12'. const value = parseInt(safePackageName.replace('%', '')) || 0; const codePackage = packages[value]; if (!codePackage) { console.log(`\n\tPackage ${packageName} could not be resolved.\n`); return; } return [ codePackage, ]; } for (const codePackage of packages) { if (codePackage.name.toLowerCase() === safePackageName || codePackage.alias.toLowerCase() === safePackageName) { return [ codePackage, ]; } } console.log(`\n\tPackage ${packageName} could not be resolved.\n`); return; }; const locatePackages = (packages, yarnWorkspace, runFrom, packageIgnore) => __awaiter(void 0, void 0, void 0, function* () { if (!packages && !yarnWorkspace) { return []; } const packagesPaths = yield resolvePackagesPaths(packages, yarnWorkspace, runFrom); const locatedPackages = []; for (const packagePath of packagesPaths) { if (!packagePath.includes('/*')) { const packageAbsolutePath = path__default["default"].join(runFrom, packagePath); const locatedPackage = yield readPackageFile(packageAbsolutePath, packageIgnore); if (locatedPackage) { locatedPackages.push(locatedPackage); } } else { const packagesRoot = packagePath.replace('/*', ''); const packagesRootPath = path__default["default"].join(runFrom, packagesRoot); try { const rootFiles = yield fs.promises.readdir(packagesRootPath); for (const rootFile of rootFiles) { const packagePath = path__default["default"].join(packagesRootPath, rootFile); const statistics = yield fs.promises.stat(packagePath); if (statistics.isDirectory()) { const locatedPackage = yield readPackageFile(packagePath, packageIgnore); if (locatedPackage) { locatedPackages.push(locatedPackage); } } } } catch (error) { console.log(`\n\tPackages root ${packagesRootPath} could not be resolved.\n`); } } } const filteredPackagesByName = locatedPackages.filter(locatedPackage => !packageIgnore.includes(locatedPackage.name)); const filteredPackagesByPath = filteredPackagesByName.filter((filterablePackage) => { for (const packageIgnored of packageIgnore) { if (filterablePackage.path.includes(packageIgnored)) { return false; } } return true; }); return filteredPackagesByPath; }); const resolvePackagesPaths = (packages, yarnWorkspace, runFrom) => __awaiter(void 0, void 0, void 0, function* () { if (!yarnWorkspace) { return packages; } try { const packageJSONPath = path__default["default"].join(runFrom, 'package.json'); const packageRawData = yield fs.promises.readFile(packageJSONPath, 'utf-8'); const packageData = JSON.parse(packageRawData); const workspaces = packageData.workspaces || []; return workspaces; } catch (error) { console.log(`\n\tCould not read the workspaces in the package.json from:\n\t${process.cwd()}\n`); return []; } }); const readPackageFile = (packagePath, packageIgnore) => __awaiter(void 0, void 0, void 0, function* () { var _a, _b, _c, _d; try { const packageJSONPath = path__default["default"].join(packagePath, 'package.json'); const packageRawData = yield fs.promises.readFile(packageJSONPath, 'utf-8'); const packageData = JSON.parse(packageRawData); const packageName = (_a = packageData.name) !== null && _a !== void 0 ? _a : ''; const packageAlias = computePackageAlias(packageName); const packageVersion = (_b = packageData.version) !== null && _b !== void 0 ? _b : '0.0.0'; const packagePrivate = (_c = packageData.private) !== null && _c !== void 0 ? _c : false; const locatedPackage = { path: packagePath, name: packageName, alias: packageAlias, version: packageVersion, private: packagePrivate, joinerpackage: false, }; return locatedPackage; } catch (error) { try { const packageJoinerPath = path__default["default"].join(packagePath, 'joiner.package.yaml'); const packageRawData = yield fs.promises.readFile(packageJoinerPath, 'utf-8'); const packageData = yaml__default["default"].load(packageRawData); const packageName = (_d = packageData === null || packageData === void 0 ? void 0 : packageData.name) !== null && _d !== void 0 ? _d : ''; const packageAlias = computePackageAlias(packageName); const locatedPackage = { path: packagePath, name: packageName, alias: packageAlias, version: '0.0.0', private: true, joinerpackage: true, }; return locatedPackage; } catch (error) { let ignored = false; for (const packageIgnored of packageIgnore) { if (packagePath.includes(packageIgnored)) { ignored = true; } } if (!ignored) { console.log(`\n\tCould not read the package from:\n\t${packagePath}\n`); } return; } } }); /** * Extract from the package `name` the package `alias`. * * e.g. * * `@scope/one.two.three` -> `three` * * `one-two` -> `one-two` * * @param name */ const computePackageAlias = (name) => { const noScope = name.replace(/@\w+\//, ''); const split = noScope.split('.'); const lastWord = split[split.length - 1]; return lastWord; }; const resolveWatchedPackages = (packages, watchPackages) => { const allPackages = packages.map(workPackage => workPackage.name); if (!watchPackages) { return [ ...allPackages, ]; } if (typeof watchPackages === 'string') { if (watchPackages.toLowerCase() === 'all') { return [ ...allPackages, ]; } const watchPackage = packages.find(workPackage => workPackage.name === watchPackages); if (!watchPackage) { return []; } return [watchPackage.name]; } const resolvedWatchPackages = []; for (const watchPackage of watchPackages) { for (const workPackage of packages) { if (watchPackage === workPackage.name) { resolvedWatchPackages.push(watchPackage); break; } } } return resolvedWatchPackages; }; // #endregion module // #endregion external // #endregion imports // #region module const parseConfigurationFile = (configurationFile) => __awaiter(void 0, void 0, void 0, function* () { try { const configurationFilepath = configurationFile.startsWith('/') ? configurationFile : path__default["default"].join(process.cwd(), configurationFile); const configurationFileData = yield fs.promises.readFile(configurationFilepath, 'utf-8'); const extension = path__default["default"].extname(configurationFile); if (extension === Deon.DEON_FILENAME_EXTENSION) { const deon = new Deon__default["default"](); const data = yield deon.parse(configurationFileData, { filebase: path__default["default"].dirname(configurationFilepath), }); return yield handleParsedConfigurationFile(data, configurationFilepath); } const parsedData = yaml__default["default"].load(configurationFileData); return yield handleParsedConfigurationFile(parsedData, configurationFilepath); } catch (error) { console.log(`\n\tConfiguration file required.\n\n\tCheck the file '${configurationFile}' exists on the rootpath:\n\t${process.cwd()}\n`); return; } }); const handleParsedConfigurationFile = (parsedData, configurationFilepath) => __awaiter(void 0, void 0, void 0, function* () { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3; const packageName = parsedData.name || ''; const packageManager = (_b = (_a = parsedData.package) === null || _a === void 0 ? void 0 : _a.manager) !== null && _b !== void 0 ? _b : 'yarn'; const packagePublisher = (_d = (_c = parsedData.package) === null || _c === void 0 ? void 0 : _c.publisher) !== null && _d !== void 0 ? _d : 'npm'; const packageIgnore = (_f = (_e = parsedData.package) === null || _e === void 0 ? void 0 : _e.ignore) !== null && _f !== void 0 ? _f : []; const yarnWorkspace = typeof parsedData.yarnWorkspace === 'boolean' ? parsedData.yarnWorkspace : parsedData.yarnWorkspace === 'true'; const commitEngine = (_h = (_g = parsedData.commit) === null || _g === void 0 ? void 0 : _g.engine) !== null && _h !== void 0 ? _h : 'git'; const commitCombine = typeof ((_j = parsedData.commit) === null || _j === void 0 ? void 0 : _j.combine) === 'boolean' ? (_k = parsedData.commit) === null || _k === void 0 ? void 0 : _k.combine : ((_l = parsedData.commit) === null || _l === void 0 ? void 0 : _l.combine) === 'true'; const commitRoot = (_o = (_m = parsedData.commit) === null || _m === void 0 ? void 0 : _m.root) !== null && _o !== void 0 ? _o : ''; const commitFullFolder = typeof ((_p = parsedData.commit) === null || _p === void 0 ? void 0 : _p.fullFolder) === 'boolean' ? (_q = parsedData.commit) === null || _q === void 0 ? void 0 : _q.fullFolder : ((_r = parsedData.commit) === null || _r === void 0 ? void 0 : _r.fullFolder) === 'true'; const commitDivider = (_t = (_s = parsedData.commit) === null || _s === void 0 ? void 0 : _s.divider) !== null && _t !== void 0 ? _t : ' > '; const commitMessage = (_v = (_u = parsedData.commit) === null || _u === void 0 ? void 0 : _u.message) !== null && _v !== void 0 ? _v : 'setup: package'; const runFromData = (_w = parsedData.runFrom) !== null && _w !== void 0 ? _w : ''; const configurationFileDirectory = path__default["default"].dirname(configurationFilepath); const runFrom = runFromData ? path__default["default"].resolve(configurationFileDirectory, runFromData) : process.cwd(); const commands = parsedData.commands || {}; const packages = yield locatePackages(parsedData.packages, yarnWorkspace, runFrom, packageIgnore); const developmentExternalPackages = (_y = (_x = parsedData.development) === null || _x === void 0 ? void 0 : _x.externalPackages) !== null && _y !== void 0 ? _y : []; const developmentWatchPackages = resolveWatchedPackages(packages, (_z = parsedData.development) === null || _z === void 0 ? void 0 : _z.watchPackages); const developmentWatchDirectories = (_1 = (_0 = parsedData.development) === null || _0 === void 0 ? void 0 : _0.watchDirectories) !== null && _1 !== void 0 ? _1 : ['build', 'distribution', 'dist']; const developmentServerPort = (_3 = (_2 = parsedData.development) === null || _2 === void 0 ? void 0 : _2.serverPort) !== null && _3 !== void 0 ? _3 : 55000; if (packages.length === 0 && !yarnWorkspace) { console.log(`\n\tPackages required to be specified in the 'joiner' configuration file.\n`); return; } const configurationFile = { name: packageName, packages, package: { manager: packageManager, publisher: packagePublisher, ignore: packageIgnore, }, yarnWorkspace, commit: { engine: commitEngine, combine: commitCombine, root: commitRoot, fullFolder: commitFullFolder, divider: commitDivider, message: commitMessage, }, development: { externalPackages: developmentExternalPackages, watchPackages: developmentWatchPackages, watchDirectories: developmentWatchDirectories, serverPort: developmentServerPort, }, runFrom, commands, }; return configurationFile; }); // #endregion module // #endregion external // #endregion imports // #region module const updateExecution = (configPackage, configurationFile) => __awaiter(void 0, void 0, void 0, function* () { const inThePackage = `in the package '${configPackage.name}'`; console.log(`\n\tChecking for updates ${inThePackage}...`); try { const updatedPackages = yield checkUpdates__default["default"]({ packageFile: path__default["default"].join(configPackage.path, 'package.json'), silent: true, jsonUpgraded: true, upgrade: true, dep: 'prod,dev', cache: true, }); if (!updatedPackages) { console.log(`\n\tCould not find updates ${inThePackage}...`); return; } if (Object.keys(updatedPackages).length > 0) { console.log(`\n\t'${configPackage.name}' dependencies updated:\n`); for (const [key, value] of Object.entries(updatedPackages)) { console.log(`\t\t${key}: ${value}`); } console.log(`\n\tInstalling the updates ${inThePackage}...`); installCommand(configPackage, configurationFile); console.log(`\tUpdates installed ${inThePackage}.\n`); } else { console.log(`\n\tNo dependencies updates ${inThePackage}.\n`); console.log(`\n\tInstalling the dependencies ${inThePackage}...`); installCommand(configPackage, configurationFile); console.log(`\tDependencies installed ${inThePackage}.\n`); } } catch (error) { console.log(`\n\tSomething went wrong.\n`); } }); const installCommand = (configPackage, configurationFile) => { const install = `${configurationFile.package.manager} install`; child_process.execSync(install, { cwd: configPackage.path, stdio: 'ignore', }); }; // #endregion exports // #endregion external // #endregion imports // #region module const generateBatches = (packages, batch) => { const batchesCount = Math.ceil(packages.length / batch); const batches = []; for (let i = 0; i < batchesCount; i++) { const batchValues = packages.slice(i * batch, (i + 1) * batch); batches.push(batchValues); } return batches; }; // #endregion exports // #region imports // #endregion libraries // #endregion imports // #region module const runWorker = (name, data) => { return new Promise((resolve, reject) => { const worker = new worker_threads.Worker(path__default["default"].join(__dirname, `../distribution/worker-${name}.js`), { workerData: data, }); worker.on('error', reject); worker.on('exit', (code) => { if (code !== 0) { reject(new Error(`Worker stopped with exit code ${code}`)); } resolve(true); }); }); }; // #endregion exports // #endregion external // #endregion imports // #region module class Batcher { constructor(packages, batch, workerName, workerData) { this.packages = packages; this.batch = batch; this.workerName = workerName; this.workerData = workerData; } run() { return __awaiter(this, void 0, void 0, function* () { const batches = generateBatches(this.packages, this.batch); for (const batchPackages of batches) { const promises = []; for (const configPackage of batchPackages) { const resolving = runWorker(this.workerName, Object.assign({ configPackage }, this.workerData)); promises.push(resolving); } yield Promise.all(promises); } }); } } // #endregion exports // #endregion external // #endregion imports // #region module const updateCommand = (packageName, options) => __awaiter(void 0, void 0, void 0, function* () { const { batch, parallel, configuration, } = options; const configurationData = yield parseConfigurationFile(configuration); if (!configurationData) { return; } const resolvedPackage = resolvePackage(packageName, configurationData); if (!resolvedPackage) { return; } if (parallel) { const batcher = new Batcher(resolvedPackage, batch, 'update', { configurationFile: configurationData, }); yield batcher.run(); return; } for (const configPackage of resolvedPackage) { yield updateExecution(configPackage, configurationData); } }); // #endregion exports // #endregion external // #endregion imports // #region module const patchCommand = (packageName, configurationFile, versionType) => __awaiter(void 0, void 0, void 0, function* () { if (packageName === '.') { const packageData = { alias: '', joinerpackage: false, name: 'in the current directory', path: process.cwd(), private: false, version: '0.0.0-0', }; yield patchLogic(packageData, versionType); return; } const configurationData = yield parseConfigurationFile(configurationFile); if (!configurationData) { return; } const resolvedPackage = resolvePackage(packageName, configurationData); if (!resolvedPackage) { return; } for (const configPackage of resolvedPackage) { yield patchLogic(configPackage, versionType); } }); const patchLogic = (configPackage, versionType) => __awaiter(void 0, void 0, void 0, function* () { try { console.log(`\n\tPatching version for package ${configPackage.name}...`); const packageJSONPath = path__default["default"].join(configPackage.path, 'package.json'); const packageJSONRawData = yield fs.promises.readFile(packageJSONPath, 'utf-8'); const packageJSONData = JSON.parse(packageJSONRawData); const { version, } = packageJSONData; const isAlpha = alphaVersioning.isAlphaVersion(version); if (!isAlpha) { const versionTypeCommand = resolveVersionType(versionType); const patchCommand = `npm version ${versionTypeCommand} --no-git-tag-version`; child_process.execSync(patchCommand, { cwd: configPackage.path, stdio: 'ignore', }); const packageJSONPath = path__default["default"].join(configPackage.path, 'package.json'); const packageJSONRawData = yield fs.promises.readFile(packageJSONPath, 'utf-8'); const packageJSONData = JSON.parse(packageJSONRawData); const { name, version, } = packageJSONData; console.log(`\t${name} version patched to version ${version}.\n`); return; } const alphaUpdatedVersion = alphaVersioning.updateAlphaVersion(version); const updatedPackageData = Object.assign({}, packageJSONData); updatedPackageData.version = alphaUpdatedVersion; yield fs.promises.writeFile(packageJSONPath, JSON.stringify(updatedPackageData, null, 4)); } catch (error) { console.log(`\n\tSomething went wrong. Check the version field for '${configPackage.name}'.\n`); } }); const resolveVersionType = (versionType) => { const versionTypeCommand = (versionType === 'major' || versionType === 'minor' || versionType === 'patch') ? versionType : 'patch'; return versionTypeCommand; }; // #endregion exports // #endregion external // #endregion imports // #region module const commitCommand = (packageName, configurationFile, message) => __awaiter(void 0, void 0, void 0, function* () { const configurationData = yield parseConfigurationFile(configurationFile); if (!configurationData) { return; } const resolvedPackage = resolvePackage(packageName, configurationData); if (!resolvedPackage) { return; } for (const configPackage of resolvedPackage) { yield commitLogic(configPackage, configurationData, message); } }); const commitLogic = (configPackage, configurationData, message) => __awaiter(void 0, void 0, void 0, function* () { try { console.log(`\n\tCommiting the package '${configPackage.name}'...`); const addCommand = 'git add .'; child_process.execSync(addCommand, { cwd: configPackage.path, stdio: 'ignore', }); const { combine, root, fullFolder, divider, message: configMessage, } = configurationData.commit; const packageFolder = path__default["default"].relative(process.cwd(), configPackage.path); const packageFolderSplit = packageFolder.split('/'); const packageFolderName = packageFolderSplit[packageFolderSplit.length - 1]; const packageName = fullFolder ? packageFolder : packageFolderName; const commitMessage = message || configMessage; const commitCommandMessage = combine ? root + packageName + divider + commitMessage : commitMessage; const commitCommand = `git commit -m '${commitCommandMessage}'`; child_process.execSync(commitCommand, { cwd: configPackage.path, stdio: 'ignore', }); console.log(`\t'${configPackage.name}' changes commited.\n`); } catch (error) { console.log(`\n\tSomething went wrong.\n`); } }); // #endregion exports // #endregion external // #endregion imports // #region module const upcomExecution = (packageName, options) => __awaiter(void 0, void 0, void 0, function* () { yield updateCommand(packageName, options); yield patchCommand(packageName, options.configuration, 'patch'); yield commitCommand(packageName, options.configuration); }); // #endregion exports // #region imports // #endregion external // #endregion imports // #region module const main = () => { if (!worker_threads.parentPort) { return; } worker_threads.parentPort.postMessage('Upcom Worker Started'); upcomExecution(worker_threads.workerData.packageName, worker_threads.workerData.options); }; main(); // #endregion module //# sourceMappingURL=worker-upcom.js.map