From 7d4a541e580e637db237b0a97d3b054f506d8907 Mon Sep 17 00:00:00 2001 From: Romain Lenzotti Date: Sat, 6 Aug 2022 12:23:57 +0200 Subject: [PATCH] feat: Add Nx support and Yarn berry --- packages/monorepo/src/MonoRepo.js | 21 +++- packages/monorepo/src/utils/cli/Lerna.js | 8 +- packages/monorepo/src/utils/cli/Nx.js | 21 ++++ packages/monorepo/src/utils/cli/Yarn.js | 3 + packages/monorepo/src/utils/cli/YarnBerry.js | 34 +++++++ packages/monorepo/src/utils/cli/index.js | 1 + .../src/utils/packages/bumpPackagesVersion.js | 51 ++++++++++ .../src/utils/packages/compilePackages.js | 95 +++++++++++-------- .../monorepo/src/utils/packages/newVersion.js | 8 +- 9 files changed, 192 insertions(+), 50 deletions(-) create mode 100644 packages/monorepo/src/utils/cli/Nx.js create mode 100644 packages/monorepo/src/utils/cli/YarnBerry.js create mode 100644 packages/monorepo/src/utils/packages/bumpPackagesVersion.js diff --git a/packages/monorepo/src/MonoRepo.js b/packages/monorepo/src/MonoRepo.js index 7c01894..3873f79 100644 --- a/packages/monorepo/src/MonoRepo.js +++ b/packages/monorepo/src/MonoRepo.js @@ -2,7 +2,7 @@ import get from "lodash/get"; import hasYarn from "has-yarn"; import logger from "fancy-log"; import {join} from "path"; -import {npm, yarn} from "./utils/cli/index.js"; +import {lerna, npm, nx, yarn} from "./utils/cli/index.js"; import {getDependencies} from "./utils/depencencies/getDependencies.js"; import {syncExamples} from "./utils/examples/syncExample.js"; import {syncDependencies} from "./utils/depencencies/syncDependencies.js"; @@ -23,6 +23,7 @@ import {publishHeroku} from "./utils/heroku/publishHeroku.js"; import {cleanTagsDocker} from "./utils/docker/cleanTagsDocker.js"; import {getWorkspaces} from "./utils/workspace/getWorkspaces.js"; import {cleanPackages} from "./utils/packages/cleanPackages.js"; +import {yarnBerry} from "./utils/cli/YarnBerry.js"; function getDefaultOptions(rootPkg) { return { @@ -260,17 +261,33 @@ export class MonoRepo { } get manager() { - return this.hasYarn ? yarn : npm; + return this.hasYarn ? (this.hasYarnBerry ? yarnBerry : yarn) : npm; + } + + get workspaceManager() { + return this.hasNx ? nx : lerna; } get hasYarn() { return hasYarn(this.rootDir); } + get hasWorkspaceManager() { + return this.hasNx || this.hasLerna; + } + + get hasYarnBerry() { + return this.hasYarn && (this.rootPkg.packageManager || "").includes("yarn@"); + } + get hasLerna() { return Boolean(this.rootPkg.dependencies.lerna || this.rootPkg.devDependencies.lerna); } + get hasNx() { + return Boolean(this.rootPkg.dependencies.nx || this.rootPkg.devDependencies.nx); + } + get hasBuild() { return Boolean(this.rootPkg.scripts.build); } diff --git a/packages/monorepo/src/utils/cli/Lerna.js b/packages/monorepo/src/utils/cli/Lerna.js index 4b01a65..d87373c 100644 --- a/packages/monorepo/src/utils/cli/Lerna.js +++ b/packages/monorepo/src/utils/cli/Lerna.js @@ -13,12 +13,12 @@ class LernaCli extends Cli { return this.sync("version", ...args); } - run(...args) { - return super.run("run", ...args); + run(cmd, ...args) { + return super.run("run", cmd, "--stream", ...args); } - install(...args) { - return super.run("install", ...args); + runMany(cmd, ...args) { + return super.run("run", cmd, "--stream", ...args); } } diff --git a/packages/monorepo/src/utils/cli/Nx.js b/packages/monorepo/src/utils/cli/Nx.js new file mode 100644 index 0000000..3df996e --- /dev/null +++ b/packages/monorepo/src/utils/cli/Nx.js @@ -0,0 +1,21 @@ +import {Cli} from "./Cli.js"; + +class NxCli extends Cli { + constructor() { + super("nx"); + } + + run(cmd, ...args) { + return super.run(cmd, ...args); + } + + runMany(cmd, ...args) { + return super.run("run-many", `--target=${cmd}`, "--all", ...args); + } + + install(...args) { + return super.run("install", ...args); + } +} + +export const nx = new NxCli(); diff --git a/packages/monorepo/src/utils/cli/Yarn.js b/packages/monorepo/src/utils/cli/Yarn.js index 7e710ff..3e1a54b 100644 --- a/packages/monorepo/src/utils/cli/Yarn.js +++ b/packages/monorepo/src/utils/cli/Yarn.js @@ -6,6 +6,9 @@ class YarnCli extends Cli { } newVersion(version) { + + + return this.version("--no-git-tag-version", "--new-version", version); } diff --git a/packages/monorepo/src/utils/cli/YarnBerry.js b/packages/monorepo/src/utils/cli/YarnBerry.js new file mode 100644 index 0000000..343809b --- /dev/null +++ b/packages/monorepo/src/utils/cli/YarnBerry.js @@ -0,0 +1,34 @@ +import {Cli} from "./Cli.js"; +import {bumpPackagesVersion} from "../packages/bumpPackagesVersion.js"; + +class YarnBerryCli extends Cli { + constructor() { + super("yarn"); + } + + newVersion(version, context) { + return bumpPackagesVersion(version, context); + } + + version(...args) { + return this.sync("version", ...args); + } + + run(...args) { + return super.run(...args); + } + + install(...args) { + return super.run(args.length ? "add" : "install", ...args); + } + + /** + * Reinstall dependencies without yarn.lock mutation + * @returns {Promise} + */ + restore() { + return super.run("install", "--immutable"); + } +} + +export const yarnBerry = new YarnBerryCli(); diff --git a/packages/monorepo/src/utils/cli/index.js b/packages/monorepo/src/utils/cli/index.js index 57a800e..6d37661 100644 --- a/packages/monorepo/src/utils/cli/index.js +++ b/packages/monorepo/src/utils/cli/index.js @@ -1,6 +1,7 @@ export * from "./Cli"; export * from "./Docker"; export * from "./Lerna"; +export * from "./Nx"; export * from "./Npm"; export * from "./Yarn"; export * from "./Git"; diff --git a/packages/monorepo/src/utils/packages/bumpPackagesVersion.js b/packages/monorepo/src/utils/packages/bumpPackagesVersion.js new file mode 100644 index 0000000..34c514f --- /dev/null +++ b/packages/monorepo/src/utils/packages/bumpPackagesVersion.js @@ -0,0 +1,51 @@ +import globby from "globby"; +import fs from "fs-extra"; + +const PACKAGE_JSON_PROPS = ["dependencies", "devDependencies", "peerDependencies"]; + +/** + * @param version {string} + * @param context {MonoRepo} + * @returns {Promise} + */ +export async function bumpPackagesVersion(version, context) { + const {workspaces, cwd} = context; + const patterns = workspaces.map((path) => `${path}/package.json`).concat("package.json"); + + const files = await globby(patterns, { + cwd, + absolute: true + }); + + const names = new Set(); + + const packages = await Promise.all( + files.map(async (file) => { + const pkg = await fs.readJson(file); + + names.add(pkg.name); + + return {file, pkg}; + }) + ); + + packages.map(({pkg, file}) => { + pkg.version = version; + + PACKAGE_JSON_PROPS.forEach((key) => bumpDependencies(pkg, key, version, names)); + + return fs.writeJson(file, pkg); + }); +} + +function bumpDependencies(pkg, key, version, names) { + pkg[key] = pkg[key] || {}; + + if (pkg[key]) { + names.forEach((name) => { + if (pkg[key][name] && !pkg[key][name].startsWith("workspace:")) { + pkg[key][name] = version; + } + }); + } +} diff --git a/packages/monorepo/src/utils/packages/compilePackages.js b/packages/monorepo/src/utils/packages/compilePackages.js index de4b7eb..c1e7ce8 100644 --- a/packages/monorepo/src/utils/packages/compilePackages.js +++ b/packages/monorepo/src/utils/packages/compilePackages.js @@ -1,18 +1,46 @@ -import {lerna} from "../cli/index.js"; import chalk from "chalk"; -import {dirname, join} from "path"; +import {dirname} from "path"; import {findPackages} from "./findPackages.js"; -/** - * - * @param context {MonoRepo} - * @returns {Promise} - */ -export async function compilePackages(context) { - const {buildCmd, logger, manager, hasLerna} = context; +async function compilePackagesWithWorkspaceManager(manager, context) { + const {buildCmd, logger} = context; + + const child = manager.runMany(buildCmd).toStream(); + + child.stdout.on("data", (data) => { + data + .toString() + .split("\n") + .filter((line) => !!line.trim()) + .map((line) => { + logger.info(line); + }); + }); + + child.stderr.on("data", (data) => { + data + .toString() + .split("\n") + .filter((line) => !!line.trim()) + .map((line) => { + logger.error(line); + }); + }); + + await child; +} + +async function compilePackagesWithPackageManager(context) { + const {buildCmd, logger, manager} = context; + + const pkgs = await findPackages(context); - if (hasLerna) { - const child = lerna.run(buildCmd, "--stream").toStream(); + for (const {path, pkg} of pkgs) { + const cwd = dirname(path); + + const child = manager.run(buildCmd).sync({ + cwd + }); child.stdout.on("data", (data) => { data @@ -20,7 +48,7 @@ export async function compilePackages(context) { .split("\n") .filter((line) => !!line.trim()) .map((line) => { - logger.info(line); + logger.info(chalk.magenta(pkg.name), line.replace(/^ > /, "")); }); }); child.stderr.on("data", (data) => { @@ -29,40 +57,25 @@ export async function compilePackages(context) { .split("\n") .filter((line) => !!line.trim()) .map((line) => { - logger.error(line); + logger.error(chalk.red(pkg.name), line.replace(/^ > /, "")); }); }); await child; - } else { - const pkgs = await findPackages(context); - - for (const {path, pkg} of pkgs) { - const cwd = dirname(path); - const child = manager.run(buildCmd).sync({ - cwd - }); + } +} - child.stdout.on("data", (data) => { - data - .toString() - .split("\n") - .filter((line) => !!line.trim()) - .map((line) => { - logger.info(chalk.magenta(pkg.name), line.replace(/^ > /, "")); - }); - }); - child.stderr.on("data", (data) => { - data - .toString() - .split("\n") - .filter((line) => !!line.trim()) - .map((line) => { - logger.error(chalk.red(pkg.name), line.replace(/^ > /, "")); - }); - }); +/** + * + * @param context {MonoRepo} + * @returns {Promise} + */ +export async function compilePackages(context) { + const {workspaceManager, manager, hasWorkspaceManager} = context; - await child; - } + if (hasWorkspaceManager) { + return compilePackagesWithWorkspaceManager(workspaceManager, context); } + + return compilePackagesWithPackageManager(manager); } diff --git a/packages/monorepo/src/utils/packages/newVersion.js b/packages/monorepo/src/utils/packages/newVersion.js index fea64f0..869d9f5 100644 --- a/packages/monorepo/src/utils/packages/newVersion.js +++ b/packages/monorepo/src/utils/packages/newVersion.js @@ -4,9 +4,11 @@ import {lerna} from "../cli/index.js"; * @param context {MonoRepo} */ export async function newVersion(context) { - if (context.hasLerna) { - lerna.newVersion(context.version); + const {hasLerna} = context + + if (hasLerna) { + lerna.newVersion(context.version, context); } - return context.manager.newVersion(context.version); + return context.manager.newVersion(context.version, context); }