@medyll/monorepo-pnpm-release 1.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 medyll
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,172 @@
1
+
2
+ ---
3
+
4
+ # @medyll/monorepo-pnpm-release 🤖
5
+
6
+ A lightweight, automated release manager for **pnpm workspaces**. It handles versioning, changelog generation, and publishing directly from GitHub Actions, for monorepos or standalone projects.
7
+
8
+ ## ✨ Features
9
+
10
+ * **Directory-based Detection**: Only bumps packages that have actual changes in their folder.
11
+ * **Independent Versioning**: Each package follows its own lifecycle.
12
+ * **Smart Changelogs**: Injects updates into existing `CHANGELOG.md` while preserving history.
13
+ * **Conventional Commits**: Automatically calculates `patch`, `minor`, or `major` bumps.
14
+ * **Zero-Config CI**: Automatically handles Git identity (bot) if not configured.
15
+ * **Hybrid Support**: Works perfectly for both large monorepos and single-package projects.
16
+
17
+ ---
18
+
19
+ ## 🚀 Installation & Usage
20
+
21
+ ## CLI Options
22
+
23
+ ### `--build`
24
+ Execute the `build` script in each changed package before releasing. If a package does not define a `build` script, it is skipped with a neutral info message.
25
+
26
+ ```bash
27
+ # Build changed packages only
28
+ npx @medyll/monorepo-pnpm-release --build
29
+ ```
30
+
31
+ ### `--package`
32
+ Execute the `package` script in each changed package before releasing. If a package does not define a `package` script, it is skipped with a neutral info message.
33
+
34
+ ```bash
35
+ # Package changed packages only
36
+ npx @medyll/monorepo-pnpm-release --package
37
+
38
+ # Combine both flags
39
+ npx @medyll/monorepo-pnpm-release --build --package
40
+ ```
41
+
42
+ ### `--verbose`
43
+ Enable verbose logging with detailed output during the release process. Useful for debugging or understanding command execution.
44
+
45
+ ```bash
46
+ npx @medyll/monorepo-pnpm-release --build all --verbose
47
+ ```
48
+
49
+ ### `--dry-run`
50
+ Analyze and simulate the release without making any changes.
51
+
52
+ ```bash
53
+ npx @medyll/monorepo-pnpm-release --dry-run
54
+ ```
55
+
56
+ ### Option A: One-time execution (npx)
57
+
58
+ Useful to avoid polluting your dependencies.
59
+
60
+ ```bash
61
+ npx @medyll/monorepo-pnpm-release
62
+
63
+ ```
64
+
65
+ ### Option B: Integrated dependency
66
+
67
+ Recommended to lock the tool version for the whole team.
68
+
69
+ ```bash
70
+ pnpm add -D @medyll/monorepo-pnpm-release
71
+
72
+ ```
73
+
74
+ Then add the following script to `package.json` :
75
+
76
+ ```json
77
+ "scripts": {
78
+ "release": "monorepo-pnpm-release"
79
+ }
80
+
81
+ ```
82
+
83
+ *Usage: `pnpm release*`
84
+
85
+ ---
86
+
87
+ ## 🛠 Workflow Integration
88
+
89
+ Create the file `.github/workflows/release.yml` :
90
+
91
+ ```yaml
92
+ name: Release
93
+ on:
94
+ push:
95
+ branches: [main, develop]
96
+
97
+ jobs:
98
+ release:
99
+ runs-on: ubuntu-latest
100
+ permissions:
101
+ contents: write
102
+ id-token: write
103
+ steps:
104
+ - uses: actions/checkout@v4
105
+ with:
106
+ fetch-depth: 0
107
+
108
+ - uses: pnpm/action-setup@v4
109
+ - uses: actions/setup-node@v4
110
+ with:
111
+ node-version: 20
112
+ registry-url: 'https://registry.npmjs.org'
113
+
114
+ - run: pnpm install --frozen-lockfile
115
+ - run: npx @medyll/monorepo-pnpm-release
116
+ env:
117
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
118
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
119
+
120
+ ```
121
+
122
+ ---
123
+
124
+ ## 📖 CLI Options
125
+
126
+ | Option | Alias | Description | Default |
127
+ | --- | --- | --- | --- |
128
+ | `--dry-run` | `-d` | Simulates the release without modifying Git or NPM | `false` |
129
+ | `--pre-id` | `-p` | Pre-release identifier (alpha, beta, next) | `alpha` |
130
+ | `--verbose` | `-v` | Shows detailed logs of internal steps | `false` |
131
+ | `--package` | `-k` | Runs `package` script in each changed package | `false` |
132
+ | `--build` | `-b` | Runs `build` script in each changed package | `false` |
133
+
134
+ ---
135
+
136
+ ## 📝 Commit Convention
137
+
138
+ The tool analyzes your commit messages to decide the next bump:
139
+
140
+ * `fix: ...` → **patch**
141
+ * `feat: ...` → **minor**
142
+ * `feat!: ...` or `BREAKING CHANGE:` → **major**
143
+
144
+ ---
145
+
146
+ ## 🛠 Troubleshooting
147
+
148
+ ### NPM Authentication Issues
149
+
150
+ If the CI fails at the `publish` step:
151
+
152
+ 1. **Token Type**: Use an **Automation** token (not Fine-grained without write permissions).
153
+ 2. **GitHub Secret**: Make sure the secret is named `NPM_TOKEN`.
154
+ 3. **Access**: For `@scope/` packages, ensure they are public:
155
+ ```json
156
+ "publishConfig": { "access": "public" }
157
+
158
+ ```
159
+
160
+
161
+ ### Git Identity Error
162
+
163
+ If you see `Author identity unknown`:
164
+ The tool automatically configures `github-actions[bot]`. If you use your own identity, make sure it is set before running the tool.
165
+
166
+ ---
167
+
168
+ ## 📄 License
169
+
170
+ MIT
171
+
172
+ ---
package/bin/cli.js ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import { executeRelease } from '../src/index.js';
5
+
6
+ const program = new Command();
7
+
8
+ program
9
+ .name('@medyll/monorepo-pnpm-release')
10
+ .description('Automated release tool for pnpm workspaces and single packages')
11
+ .version('1.0.0')
12
+ .option('-d, --dry-run', 'Analyze and simulate the release without any side effects', false)
13
+ .option('-p, --pre-id <id>', 'Identifier for pre-release (e.g. alpha, beta, next)', 'alpha')
14
+ .option('-v, --verbose', 'Print detailed logs', false)
15
+ .option('-b, --build', 'Execute "pnpm run build" in each changed package before release', false)
16
+ .option('-k, --package', 'Execute "pnpm run package" in each changed package before release', false)
17
+ .action(async (options) => {
18
+ try {
19
+ await executeRelease(options);
20
+ } catch (error) {
21
+ console.error(`\n❌ Execution failed: ${error.message}`);
22
+ process.exit(1);
23
+ }
24
+ });
25
+
26
+ program.parse(process.argv);
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@medyll/monorepo-pnpm-release",
3
+ "version": "1.0.17",
4
+ "description": "Minimalist monorepo release tool for pnpm workspaces",
5
+ "type": "module",
6
+ "main": "src/index.js",
7
+ "module": "src/index.js",
8
+ "exports": {
9
+ ".": "./src/index.js"
10
+ },
11
+ "bin": {
12
+ "monorepo-pnpm-release": "./bin/cli.js"
13
+ },
14
+ "files": [
15
+ "src",
16
+ "bin",
17
+ "README.md"
18
+ ],
19
+ "keywords": [
20
+ "pnpm",
21
+ "monorepo",
22
+ "release",
23
+ "versioning",
24
+ "changelog"
25
+ ],
26
+ "author": "Mydde",
27
+ "license": "MIT",
28
+ "packageConfig": {
29
+ "access": "public",
30
+ "name": "@medyll/monorepo-pnpm-release",
31
+ "directory": "."
32
+ },
33
+ "dependencies": {
34
+ "@pnpm/find-workspace-packages": "^6.0.9",
35
+ "commander": "^14.0.3",
36
+ "conventional-commits-parser": "^6.2.1",
37
+ "execa": "^9.6.1",
38
+ "semver": "^7.7.3"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^25.1.0",
42
+ "@types/semver": "^7.7.1",
43
+ "typescript": "^5.9.3"
44
+ },
45
+ "scripts": {
46
+ "start": "node bin/cli.js",
47
+ "release": "node bin/cli.js",
48
+ "test": "echo \"Error: no test specified\" && exit 1",
49
+ "check-types": "tsc --noEmit"
50
+ }
51
+ }
@@ -0,0 +1,23 @@
1
+ // author : Lebrun Meddy
2
+ import fs from 'fs/promises';
3
+ import path from 'path';
4
+
5
+ export async function updateChangelog(pkg, { verbose } = {}) {
6
+ const file = path.join(pkg.dir, 'CHANGELOG.md');
7
+ let content = '';
8
+ try {
9
+ content = await fs.readFile(file, 'utf-8');
10
+ if (verbose) console.log(`[verbose] Read existing changelog for ${pkg.name}`);
11
+ } catch {
12
+ content = '# Changelog\n';
13
+ if (verbose) console.log(`[verbose] No existing changelog for ${pkg.name}, creating new.`);
14
+ }
15
+
16
+ const newEntry = `## [${pkg.newVersion}] - ${new Date().toISOString().split('T')[0]}\n- Update dependencies and fixes.\n`;
17
+ if (verbose) console.log(`[verbose] Adding changelog entry for ${pkg.name}:`, newEntry);
18
+ // Insert after the first H1
19
+ const lines = content.split('\n');
20
+ lines.splice(1, 0, '\n' + newEntry);
21
+ await fs.writeFile(file, lines.join('\n'));
22
+ if (verbose) console.log(`[verbose] Wrote changelog for ${pkg.name}`);
23
+ }
@@ -0,0 +1,95 @@
1
+ // author : Lebrun Meddy
2
+ import findWorkspacePackages from '@pnpm/find-workspace-packages';
3
+ import { execa } from 'execa';
4
+ import fs from 'fs/promises';
5
+ import path from 'path';
6
+
7
+ /**
8
+ * Get the most recent git tag for a specific package
9
+ */
10
+ async function getLastTag(packageName) {
11
+ try {
12
+ const { stdout } = await execa('git', [
13
+ 'describe',
14
+ '--tags',
15
+ '--match', `${packageName}@*`,
16
+ '--abbrev=0'
17
+ ]);
18
+ return stdout;
19
+ } catch {
20
+ // Fallback for single packages or missing tags
21
+ return null;
22
+ }
23
+ }
24
+
25
+ /**
26
+ * Identify which packages need a release
27
+ * Works for both pnpm workspaces and single packages
28
+ */
29
+ export async function analyzeChanges({ verbose } = {}) {
30
+ let allPackages = [];
31
+ if (verbose) console.log('[verbose] Detecting workspace packages...');
32
+
33
+ try {
34
+ // Robust import handling for pnpm's internal tool
35
+ const getPkgs = typeof findWorkspacePackages === 'function'
36
+ ? findWorkspacePackages
37
+ : findWorkspacePackages.default;
38
+
39
+ allPackages = await getPkgs('.')
40
+ if (verbose) console.log('[verbose] Found packages:', allPackages.map(p => p.manifest?.name || p.dir));
41
+ // If workspace detection returns nothing, force fallback
42
+ if (!allPackages || allPackages.length === 0) {
43
+ throw new Error('No workspace found');
44
+ }
45
+ } catch (e) {
46
+ // Fallback: Check if the current directory is a standard pnpm package
47
+ const manifestPath = path.join(process.cwd(), 'package.json');
48
+ try {
49
+ const content = await fs.readFile(manifestPath, 'utf-8');
50
+ const manifest = JSON.parse(content);
51
+
52
+ // We wrap the single package in the same structure as a workspace result
53
+ allPackages = [{
54
+ dir: process.cwd(),
55
+ manifest: manifest
56
+ }];
57
+ } catch (err) {
58
+ if (verbose) console.log('[verbose] No pnpm workspace or package.json found in this directory.');
59
+ console.error("❌ No pnpm workspace or package.json found in this directory.");
60
+ return [];
61
+ }
62
+ }
63
+
64
+ const packagesToRelease = [];
65
+
66
+ for (const pkg of allPackages) {
67
+ if (verbose) console.log(`[verbose] Checking package: ${pkg.manifest.name}`);
68
+ // Skip private packages unless they are the root fallback
69
+ if (pkg.manifest.private && allPackages.length > 1) continue;
70
+
71
+ const lastTag = await getLastTag(pkg.manifest.name);
72
+ const range = lastTag ? `${lastTag}..HEAD` : 'HEAD';
73
+ if (verbose) console.log(`[verbose] Using git range: ${range}`);
74
+
75
+ // Get logs for the package directory
76
+ // pkg.dir is absolute path, works for both workspace and root
77
+ const { stdout } = await execa('git', ['log', range, '--format=%B', '--', pkg.dir]);
78
+ if (verbose) console.log(`[verbose] Git log for ${pkg.manifest.name}:`, stdout);
79
+
80
+ // Split commits by double newline to separate them properly
81
+ const commits = stdout.split('\n').filter(Boolean);
82
+
83
+ if (commits.length > 0) {
84
+ packagesToRelease.push({
85
+ name: pkg.manifest.name,
86
+ dir: pkg.dir,
87
+ currentVersion: pkg.manifest.version,
88
+ rawCommits: commits
89
+ });
90
+ if (verbose) console.log(`[verbose] Package ${pkg.manifest.name} scheduled for release.`);
91
+ }
92
+ }
93
+
94
+ return packagesToRelease;
95
+ }
package/src/git.js ADDED
@@ -0,0 +1,41 @@
1
+ // Commits & Tags multiples
2
+ import { execa } from 'execa';
3
+ // author : Lebrun Meddy
4
+
5
+ /**
6
+ * Check if git user is configured, otherwise set a default bot identity
7
+ */
8
+ async function ensureGitIdentity(verbose) {
9
+ try {
10
+ await execa('git', ['config', 'user.name']);
11
+ if (verbose) console.log('[verbose] Git user.name is set.');
12
+ } catch {
13
+ console.log("🔧 No git identity found. Setting default bot identity...");
14
+ if (verbose) console.log('[verbose] Would set git user.name and user.email to github-actions[bot]');
15
+ /* await execa('git', ['config', 'user.name', 'github-actions[bot]']);
16
+ await execa('git', ['config', 'user.email', 'github-actions[bot]@users.noreply.github.com']); */
17
+ }
18
+ }
19
+
20
+ export async function finalizeGit(releasedPackages, { verbose } = {}) {
21
+ // Check and set identity if needed
22
+ await ensureGitIdentity(verbose);
23
+ if (verbose) console.log('[verbose] Staging all changes for commit.');
24
+ await execa('git', ['add', '.']);
25
+
26
+ const commitMessage = `chore(release): publish packages\n\n${releasedPackages
27
+ .map(p => `- ${p.name}@${p.newVersion}`)
28
+ .join('\n')}`;
29
+ if (verbose) console.log('[verbose] Commit message:', commitMessage);
30
+ await execa('git', ['commit', '-m', commitMessage]);
31
+
32
+ for (const pkg of releasedPackages) {
33
+ const tagName = `${pkg.name}@${pkg.newVersion}`;
34
+ if (verbose) console.log(`[verbose] Tagging ${tagName}`);
35
+ // -f (force) prevents crashing if the tag was locally created before
36
+ await execa('git', ['tag', '-a', tagName, '-m', tagName, '-f']);
37
+ }
38
+
39
+ if (verbose) console.log('[verbose] Pushing tags and commits to origin.');
40
+ await execa('git', ['push', 'origin', '--follow-tags']);
41
+ }
package/src/index.js ADDED
@@ -0,0 +1,94 @@
1
+ // author : Lebrun Meddy
2
+ import { analyzeChanges } from "./detector.js";
3
+ import { bumpPackages } from "./versioner.js";
4
+ import { updateChangelog } from "./changelog.js";
5
+ import { finalizeGit } from "./git.js";
6
+ import { publishToRegistry } from "./publisher.js";
7
+ import { executePrePublishCommands } from "./pre-publish.js";
8
+
9
+ // Simple verbose logger
10
+ function vLog(verbose, ...args) {
11
+ if (verbose) {
12
+ console.log(`${"\x1b[90m"}[verbose]`, ...args, "\x1b[0m");
13
+ }
14
+ }
15
+
16
+ // ANSI Color codes
17
+ const colors = {
18
+ reset: "\x1b[0m",
19
+ bright: "\x1b[1m",
20
+ green: "\x1b[32m",
21
+ yellow: "\x1b[33m",
22
+ blue: "\x1b[34m",
23
+ magenta: "\x1b[35m",
24
+ cyan: "\x1b[36m",
25
+ red: "\x1b[31m",
26
+ };
27
+
28
+ export async function executeRelease(options) {
29
+ const isPre = process.env.GITHUB_REF !== "refs/heads/main";
30
+ const mode = isPre ? `PRE-RELEASE (${options.preId})` : "STABLE";
31
+ const verbose = options.verbose;
32
+
33
+ console.log(
34
+ `\n${colors.bright}${colors.blue}🚀 Starting release process in ${mode} mode...${colors.reset}\n`,
35
+ );
36
+ vLog(verbose, "Options:", options);
37
+
38
+ // 1. Detection
39
+ console.log(`${colors.cyan}🔍 Analyzing changes...${colors.reset}`);
40
+ const changes = await analyzeChanges({ verbose });
41
+
42
+ if (!changes.length) {
43
+ console.log(
44
+ `${colors.yellow}✨ Nothing to release. All packages are up to date.${colors.reset}`,
45
+ );
46
+ return;
47
+ }
48
+
49
+ console.log(
50
+ `${colors.green}Found ${changes.length} package(s) with changes.${colors.reset}`,
51
+ );
52
+
53
+ // 2. Pre-publish commands (per package)
54
+ await executePrePublishCommands(changes, options);
55
+
56
+ // 3. Bumping versions
57
+ console.log(`${colors.cyan}🆙 Bumping versions...${colors.reset}`);
58
+ const released = await bumpPackages(changes, isPre, options.preId, { verbose });
59
+
60
+ for (const pkg of released) {
61
+ console.log(
62
+ ` - ${colors.bright}${pkg.name}${colors.reset}: ${colors.yellow}${pkg.currentVersion}${colors.reset} -> ${colors.green}${pkg.newVersion}${colors.reset}`,
63
+ );
64
+
65
+ // 3. Changelogs
66
+ console.log(` - ${colors.blue}📝 Updating CHANGELOG.md...${colors.reset}`);
67
+ await updateChangelog(pkg, { verbose });
68
+
69
+ }
70
+
71
+ // 5. Execution or Dry Run
72
+ if (!options.dryRun) {
73
+ console.log(
74
+ `\n${colors.magenta}📂 Finalizing Git operations (commit & tags)...${colors.reset}`,
75
+ );
76
+ await finalizeGit(released, { verbose });
77
+
78
+ console.log(`${colors.magenta}đŸ“Ļ Publishing to registry...${colors.reset}`);
79
+ await publishToRegistry(released, isPre ? options.preId : "latest", {
80
+ verbose,
81
+ });
82
+
83
+ console.log(
84
+ `\n${colors.bright}${colors.green}🎉 Release successfully finished!${colors.reset}\n`,
85
+ );
86
+ } else {
87
+ console.log(
88
+ `\n${colors.bright}${colors.yellow}âš ī¸ DRY RUN COMPLETED${colors.reset}`,
89
+ );
90
+ console.log(
91
+ `${colors.yellow}No changes were pushed to Git or NPM.${colors.reset}\n`,
92
+ );
93
+ }
94
+ }
@@ -0,0 +1,51 @@
1
+ import { execa } from "execa";
2
+ import fs from "fs/promises";
3
+ import path from "path";
4
+
5
+ async function readPackageManifest(pkgDir) {
6
+ const manifestPath = path.join(pkgDir, "package.json");
7
+ const content = await fs.readFile(manifestPath, "utf-8");
8
+ return JSON.parse(content);
9
+ }
10
+
11
+ async function runPackageScript(pkgDir, scriptName) {
12
+ console.log(` âš™ī¸ pnpm run ${scriptName}...`);
13
+ await execa("pnpm", ["run", scriptName], { cwd: pkgDir });
14
+ }
15
+
16
+ /**
17
+ * Execute pre-publish commands (build and/or package) before release
18
+ * @param {Array} packages - Packages to process
19
+ * @param {Object} options - CLI options
20
+ * @param {boolean} options.build - Whether to run build
21
+ * @param {boolean} options.package - Whether to run package
22
+ */
23
+ export async function executePrePublishCommands(packages, options) {
24
+ const requestedScripts = [];
25
+ if (options.build) requestedScripts.push("build");
26
+ if (options.package) requestedScripts.push("package");
27
+
28
+ if (requestedScripts.length === 0) {
29
+ return;
30
+ }
31
+
32
+ for (const pkg of packages) {
33
+ const manifest = await readPackageManifest(pkg.dir);
34
+ const scripts = manifest.scripts || {};
35
+ const missingScripts = requestedScripts.filter(
36
+ (scriptName) => !scripts[scriptName],
37
+ );
38
+
39
+ if (missingScripts.length > 0) {
40
+ console.log(
41
+ ` â„šī¸ ${manifest.name}: missing script(s): ${missingScripts.join(", ")}. Skipping.`,
42
+ );
43
+ }
44
+
45
+ for (const scriptName of requestedScripts) {
46
+ if (scripts[scriptName]) {
47
+ await runPackageScript(pkg.dir, scriptName);
48
+ }
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,10 @@
1
+ // author : Lebrun Meddy
2
+
3
+ import { execa } from 'execa';
4
+
5
+ export async function publishToRegistry(released, tag, { verbose } = {}) {
6
+ for (const pkg of released) {
7
+ if (verbose) console.log(`[verbose] Publishing ${pkg.name} with tag ${tag}`);
8
+ await execa('pnpm', ['publish', '--filter', pkg.name, '--tag', tag, '--no-git-checks','--access', 'public'], { stdio: 'inherit' });
9
+ }
10
+ }
@@ -0,0 +1,23 @@
1
+ // author : Lebrun Meddy
2
+
3
+ import semver from 'semver';
4
+ import fs from 'fs/promises';
5
+ import path from 'path';
6
+
7
+ export async function bumpPackages(packages, isPre, preId, { verbose } = {}) {
8
+ return Promise.all(packages.map(async (pkg) => {
9
+ if (verbose) console.log(`[verbose] Bumping version for ${pkg.name}`);
10
+ // Logic: determine bump type (feat=minor, fix=patch, etc.)
11
+ // For this example, we assume 'patch' or 'prepatch'
12
+ const type = isPre ? 'prepatch' : 'patch';
13
+ const next = semver.inc(pkg.currentVersion, type, preId);
14
+ if (verbose) console.log(`[verbose] New version for ${pkg.name}: ${next}`);
15
+
16
+ const pJsonPath = path.join(pkg.dir, 'package.json');
17
+ const manifest = JSON.parse(await fs.readFile(pJsonPath, 'utf-8'));
18
+ manifest.version = next;
19
+ if (verbose) console.log(`[verbose] Writing new version to ${pJsonPath}`);
20
+ await fs.writeFile(pJsonPath, JSON.stringify(manifest, null, 2) + '\n');
21
+ return { ...pkg, newVersion: next };
22
+ }));
23
+ }