@modulify/conventional-release 0.1.0 → 0.1.2

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/README.md CHANGED
@@ -1,6 +1,11 @@
1
1
  # @modulify/conventional-release
2
2
 
3
- Release orchestration package for conventional workflows.
3
+ Release-core package for conventional workflows.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/%40modulify%2Fconventional-release?label=npm)](https://www.npmjs.com/package/@modulify/conventional-release)
6
+ [![codecov](https://codecov.io/gh/modulify/conventional/branch/main/graph/badge.svg?flag=conventional-release)](https://codecov.io/gh/modulify/conventional?flags[0]=conventional-release)
7
+
8
+ [🌐 Translations](https://github.com/modulify/conventional/blob/main/packages/conventional-release/docs/INDEX.md)
4
9
 
5
10
  This workspace combines:
6
11
  - semantic version recommendation from `@modulify/conventional-bump`,
@@ -13,6 +18,22 @@ The package is library-first. It exposes:
13
18
  - `run()` to apply the release flow,
14
19
  - `conventional-release` as a config-driven CLI binary.
15
20
 
21
+ ## Scope and non-goals
22
+
23
+ This package is intentionally focused on release-core responsibilities inside the repository:
24
+
25
+ - discover the release scope
26
+ - compute versions from commit history
27
+ - update manifests and changelog files
28
+ - finalize the release with a commit and tags
29
+
30
+ It does not try to be an all-in-one delivery tool.
31
+ In particular, `npm publish`, GitHub Releases, GitLab Releases, registry credentials, and deployment-specific CI steps are out of scope for this package.
32
+
33
+ The intended layering is:
34
+ - `@modulify/conventional-release` handles planning and repository-local release finalization
35
+ - higher-level tools can add publishing, hosting, or CI-specific orchestration on top
36
+
16
37
  ## Installation
17
38
 
18
39
  ```bash
@@ -36,6 +57,9 @@ It resolves packages, filters workspaces, detects affected packages, and produce
36
57
  2. `run(options)` resolves the same scope and applies side effects.
37
58
  It updates manifests, writes the changelog, creates a commit, and creates tags.
38
59
 
60
+ That is the end of this package's responsibility boundary.
61
+ Delivery steps outside the repository, such as package publication or hosted release creation, should be implemented above this layer.
62
+
39
63
  `Scope` is the declarative view of a release.
40
64
  `Slice` is one execution unit inside that scope.
41
65
 
@@ -103,6 +127,7 @@ Useful flags:
103
127
  - `--prerelease <channel>`: use `alpha`, `beta`, or `rc`
104
128
 
105
129
  The CLI reads the same repository configuration as the library API and wires a lifecycle reporter into `run()`.
130
+ It stops after repository-local release finalization and does not publish artifacts.
106
131
 
107
132
  ## Inspect before running
108
133
 
@@ -146,6 +171,23 @@ console.log(result.slices)
146
171
 
147
172
  When `dry: true` is used, the package still resolves versions, tags, and touched files, but skips write-side effects.
148
173
 
174
+ ## Pre-1.0 releases
175
+
176
+ For packages below `1.0.0`, automatic conventional recommendations follow `@modulify/conventional-bump` pre-major semantics by default:
177
+
178
+ - recommended `major` becomes `minor`
179
+ - recommended `minor` becomes `patch`
180
+ - recommended `patch` stays `patch`
181
+
182
+ Explicit `releaseAs` overrides stay authoritative, so `releaseAs: 'major'` still produces `1.0.0`.
183
+ Set `preMajor: false` to opt out of automatic downgrades:
184
+
185
+ ```ts
186
+ await run({
187
+ preMajor: false,
188
+ })
189
+ ```
190
+
149
191
  ## Configuration sources
150
192
 
151
193
  Configuration is resolved in this order:
@@ -198,6 +240,7 @@ The most important public options are:
198
240
 
199
241
  - `mode`: release strategy, one of `sync`, `async`, or `hybrid`
200
242
  - `releaseAs`: explicit semver bump override such as `major`, `minor`, or `patch`
243
+ - `preMajor`: whether automatic recommendations should be downgraded below `1.0.0`
201
244
  - `prerelease`: prerelease channel, one of `alpha`, `beta`, or `rc`
202
245
  - `fromTag`: explicit lower bound tag for advisory commit analysis
203
246
  - `tagPrefix`: tag matcher used during advisory commit analysis
package/bin/cli.cjs CHANGED
@@ -1,308 +1,32 @@
1
- "use strict";
2
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const gitToolkit = require("@modulify/git-toolkit");
4
- const shell = require("@modulify/git-toolkit/shell");
5
- const index = require("../dist/index.cjs");
6
- const yargs = require("yargs");
7
- const helpers = require("yargs/helpers");
8
- const chalk = require("chalk");
9
- const figures = require("figures");
10
- const util = require("node:util");
11
- function _interopNamespaceDefault(e) {
12
- const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
13
- if (e) {
14
- for (const k in e) {
15
- if (k !== "default") {
16
- const d = Object.getOwnPropertyDescriptor(e, k);
17
- Object.defineProperty(n, k, d.get ? d : {
18
- enumerable: true,
19
- get: () => e[k]
20
- });
21
- }
22
- }
23
- }
24
- n.default = e;
25
- return Object.freeze(n);
26
- }
27
- const util__namespace = /* @__PURE__ */ _interopNamespaceDefault(util);
28
- const DEFAULTS = {
29
- dry: false,
30
- verbose: false,
31
- tags: false
32
- };
33
- class CliParseError extends Error {
34
- help;
35
- }
36
- async function parseArgv(argv = process.argv) {
37
- const parser = yargs(helpers.hideBin(argv)).locale("en").scriptName("conventional-release").usage("Usage: $0 [options]").option("release-as", {
38
- alias: "r",
39
- describe: "Specify the release type (major|minor|patch)",
40
- requiresArg: true,
41
- string: true
42
- }).option("prerelease", {
43
- alias: "p",
44
- describe: "Specify the prerelease type (alpha|beta|rc)",
45
- requiresArg: true,
46
- string: true
47
- }).option("dry", {
48
- type: "boolean",
49
- default: DEFAULTS.dry,
50
- describe: "See the commands that running release would run"
51
- }).option("verbose", {
52
- type: "boolean",
53
- default: DEFAULTS.verbose,
54
- describe: "Show detailed per-slice progress output"
55
- }).option("tags", {
56
- type: "boolean",
57
- default: DEFAULTS.tags,
58
- describe: "Show generated tags in the final output"
59
- }).exitProcess(false).check((options) => {
60
- if (!["alpha", "beta", "rc", void 0].includes(options.prerelease)) {
61
- throw new Error("prerelease should be one of alpha, beta, rc or undefined");
62
- }
63
- return true;
64
- }).showHelpOnFail(false).fail((message) => {
65
- throw new Error(message);
66
- }).alias("version", "v").alias("help", "h").example("$0", "Update changelog and tag release").example("$0 --dry --verbose", "Show a detailed dry-run release preview").pkgConf("release").wrap(97);
67
- let parsed;
68
- try {
69
- parsed = await parser.parseAsync();
70
- } catch (error) {
71
- const failure = new CliParseError(error.message);
72
- const help = await parser.getHelp();
73
- failure.help = [help].flat().join("\n");
74
- throw failure;
75
- }
76
- return {
77
- releaseAs: parsed.releaseAs,
78
- prerelease: parsed.prerelease,
79
- dry: parsed.dry,
80
- verbose: parsed.verbose,
81
- tags: parsed.tags
82
- };
83
- }
84
- function createReporter({
85
- output,
86
- git,
87
- showTags = false,
88
- verbosity = "summary"
89
- }) {
90
- if (verbosity === "detailed") {
91
- return new DetailedReporter({
92
- output,
93
- git,
94
- showTags
95
- });
96
- }
97
- return new SummaryReporter({
98
- output,
99
- git,
100
- showTags
101
- });
102
- }
103
- class SummaryReporter {
104
- output;
105
- git;
106
- showTags;
107
- scope = null;
108
- position = /* @__PURE__ */ new Map();
109
- constructor({
110
- output,
111
- git,
112
- showTags
113
- }) {
114
- this.output = output;
115
- this.git = git;
116
- this.showTags = showTags;
117
- }
118
- async onStart(context) {
119
- this.output.info(
120
- context.dry ? "Starting dry release" : "Starting release"
121
- );
122
- }
123
- async onScope(scope, context) {
124
- this.scope = scope;
125
- this.position = new Map(
126
- scope.slices.map((slice, index2) => [slice.id, index2 + 1])
127
- );
128
- }
129
- async onSliceStart(slice) {
130
- this.output.info("Running slice %s", [this.describeProgress(slice)]);
131
- }
132
- async onSuccess(result) {
133
- if (!result.changed) {
134
- this.output.success("No changes since last release");
135
- return;
136
- }
137
- const changed = result.slices.filter((slice) => slice.changed);
138
- const primary = changed[0];
139
- const packages = collectPackages(changed);
140
- this.output.success("Release slices: %s", [String(changed.length)]);
141
- this.output.success("Updated packages: %s", [String(packages.length)]);
142
- if (primary) {
143
- this.output.success("Next version: %s", [primary.nextVersion]);
144
- }
145
- if (result.dry) {
146
- this.output.info("No committing or tagging since this was a dry run");
147
- return;
148
- }
149
- this.output.success("Committed %s staged files", [String(result.files.length)]);
150
- if (this.showTags) {
151
- const tags = collectTags(changed);
152
- if (tags.length) {
153
- this.output.success("Tags: %s", [tags.join(", ")]);
154
- }
155
- }
156
- this.output.info("Run `%s` to publish", [
157
- `git push --follow-tags origin ${await this.resolveBranch()}`
158
- ]);
159
- }
160
- async onError(error) {
161
- const message = error instanceof Error ? error.message : String(error);
162
- this.output.error(message);
163
- }
164
- describeProgress(slice) {
165
- const position = this.position.get(slice.id);
166
- const total = this.scope?.slices.length;
167
- const label = describeSlice(slice);
168
- return position && total ? `${position}/${total}: ${label}` : label;
169
- }
170
- async resolveBranch() {
171
- try {
172
- return await this.git.revParse("HEAD", { abbrevRef: true });
173
- } catch {
174
- return "%branch%";
175
- }
176
- }
177
- }
178
- class DetailedReporter extends SummaryReporter {
179
- async onScope(scope, context) {
180
- await super.onScope(scope, context);
181
- this.output.info("%s scope: %s packages, %s affected, %s slices", [
182
- scope.mode,
183
- String(scope.packages.length),
184
- String(scope.affected.length),
185
- String(scope.slices.length)
186
- ]);
187
- }
188
- async onSliceSuccess(slice) {
189
- if (!slice.changed) {
190
- this.output.warn("Completed slice %s without version changes (%s)", [
191
- describeSlice(slice),
192
- slice.currentVersion
193
- ]);
194
- return;
195
- }
196
- this.output.success("Completed slice %s: %s -> %s (%s)", [
197
- describeSlice(slice),
198
- slice.currentVersion,
199
- slice.nextVersion,
200
- slice.releaseType
201
- ]);
202
- if (this.showTags && slice.tag) {
203
- this.output.info("Tag: %s", [slice.tag]);
204
- }
205
- }
206
- async onSuccess(result) {
207
- await super.onSuccess(result);
208
- if (!result.changed) {
209
- return;
210
- }
211
- const packages = collectPackages(result.slices.filter((slice) => slice.changed));
212
- this.output.info("Updated packages: %s", [describePackages(packages)]);
213
- }
214
- }
215
- function describeSlice(slice) {
216
- if (slice.partition) {
217
- return `${slice.partition} [${describePackages(slice.packages)}]`;
218
- }
219
- return describePackages(slice.packages);
220
- }
221
- function describePackages(packages) {
222
- return packages.map((pkg) => pkg.name ?? pkg.path).join(", ");
223
- }
224
- function collectTags(slices) {
225
- return slices.map((slice) => slice.tag).filter((tag) => !!tag);
226
- }
227
- function collectPackages(slices) {
228
- const packages = [];
229
- const seen = /* @__PURE__ */ new Set();
230
- for (const slice of slices) {
231
- for (const pkg of slice.packages) {
232
- const identity = pkg.name ?? pkg.path;
233
- if (seen.has(identity)) {
234
- continue;
235
- }
236
- seen.add(identity);
237
- packages.push(pkg);
238
- }
239
- }
240
- return packages;
241
- }
242
- class ConsoleOutput {
243
- write(message) {
244
- console.info(message);
245
- }
246
- writeError(message) {
247
- console.error(message);
248
- }
249
- }
250
- class Output {
251
- output;
252
- theme;
253
- constructor({
254
- dry,
255
- output = new ConsoleOutput(),
256
- theme = createDefaultTheme(dry)
257
- }) {
258
- this.output = output;
259
- this.theme = theme;
260
- }
261
- info(template, context = [], figure = this.theme.info) {
262
- this.output.write(format(template, context, figure));
263
- }
264
- success(template, context = [], figure = this.theme.success) {
265
- this.output.write(format(template, context, figure));
266
- }
267
- warn(template, context = [], figure = this.theme.warning) {
268
- this.output.write(format(template, context, figure));
269
- }
270
- error(template, context = [], figure = this.theme.error) {
271
- this.output.writeError(format(template, context, figure));
272
- }
273
- }
274
- function createDefaultTheme(dry) {
275
- return {
276
- success: dry ? chalk.yellow(figures.tick) : chalk.green(figures.tick),
277
- warning: chalk.yellow(figures.warning),
278
- error: chalk.red(figures.cross),
279
- info: chalk.blue(figures.info)
280
- };
281
- }
282
- function format(template, context, figure) {
283
- const bold = (arg) => chalk.bold(arg);
284
- const message = util__namespace.format(template, ...context.map(bold));
285
- return `${figure} ${message}`;
286
- }
2
+ require("../dist/_virtual/_rolldown/runtime.cjs");
3
+ const require_index = require("../dist/index.cjs");
4
+ const require_args = require("../dist/cli/args.cjs");
5
+ const require_reporter = require("../dist/cli/reporter.cjs");
6
+ const require_output = require("../dist/cli/output.cjs");
7
+ let _modulify_git_toolkit = require("@modulify/git-toolkit");
8
+ let _modulify_git_toolkit_shell = require("@modulify/git-toolkit/shell");
9
+ //#region src/cli.ts
287
10
  async function main(argv = process.argv) {
288
- const cwd = process.cwd();
289
- const options = await parseArgv(argv);
290
- const output = new Output({
291
- dry: options.dry,
292
- output: new ConsoleOutput()
293
- });
294
- const git = new gitToolkit.GitCommander({ sh: new shell.Runner(cwd) });
295
- await index.run({
296
- cwd,
297
- dry: options.dry,
298
- releaseAs: options.releaseAs,
299
- prerelease: options.prerelease,
300
- reporter: createReporter({
301
- output,
302
- git,
303
- showTags: options.tags,
304
- verbosity: options.verbose ? "detailed" : "summary"
305
- })
306
- });
307
- }
11
+ const cwd = process.cwd();
12
+ const options = await require_args.parseArgv(argv);
13
+ const output = new require_output.Output({
14
+ dry: options.dry,
15
+ output: new require_output.ConsoleOutput()
16
+ });
17
+ const git = new _modulify_git_toolkit.GitCommander({ sh: new _modulify_git_toolkit_shell.Runner(cwd) });
18
+ await require_index.run({
19
+ cwd,
20
+ dry: options.dry,
21
+ releaseAs: options.releaseAs,
22
+ prerelease: options.prerelease,
23
+ reporter: require_reporter.createReporter({
24
+ output,
25
+ git,
26
+ showTags: options.tags,
27
+ verbosity: options.verbose ? "detailed" : "summary"
28
+ })
29
+ });
30
+ }
31
+ //#endregion
308
32
  exports.main = main;