@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 +44 -1
- package/bin/cli.cjs +29 -305
- package/bin/cli.mjs +27 -288
- package/dist/_virtual/_rolldown/runtime.cjs +23 -0
- package/dist/cli/args.cjs +67 -0
- package/dist/cli/args.mjs +65 -0
- package/dist/cli/output.cjs +51 -0
- package/dist/cli/output.mjs +46 -0
- package/dist/cli/reporter.cjs +127 -0
- package/dist/cli/reporter.mjs +127 -0
- package/dist/config.cjs +54 -0
- package/dist/config.mjs +51 -0
- package/dist/constants.cjs +16 -0
- package/dist/constants.mjs +12 -0
- package/dist/execute.cjs +192 -0
- package/dist/execute.mjs +190 -0
- package/dist/index.cjs +51 -707
- package/dist/index.mjs +51 -710
- package/dist/plan.cjs +260 -0
- package/dist/plan.mjs +253 -0
- package/dist/reporter.cjs +33 -0
- package/dist/reporter.mjs +27 -0
- package/dist/runtime.cjs +68 -0
- package/dist/runtime.mjs +67 -0
- package/package.json +16 -11
- package/types/release.d.ts +2 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
//#region src/cli/reporter.ts
|
|
2
|
+
function createReporter({ output, git, showTags = false, verbosity = "summary" }) {
|
|
3
|
+
if (verbosity === "detailed") return new DetailedReporter({
|
|
4
|
+
output,
|
|
5
|
+
git,
|
|
6
|
+
showTags
|
|
7
|
+
});
|
|
8
|
+
return new SummaryReporter({
|
|
9
|
+
output,
|
|
10
|
+
git,
|
|
11
|
+
showTags
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
var SummaryReporter = class {
|
|
15
|
+
output;
|
|
16
|
+
git;
|
|
17
|
+
showTags;
|
|
18
|
+
scope = null;
|
|
19
|
+
position = /* @__PURE__ */ new Map();
|
|
20
|
+
constructor({ output, git, showTags }) {
|
|
21
|
+
this.output = output;
|
|
22
|
+
this.git = git;
|
|
23
|
+
this.showTags = showTags;
|
|
24
|
+
}
|
|
25
|
+
async onStart(context) {
|
|
26
|
+
this.output.info(context.dry ? "Starting dry release" : "Starting release");
|
|
27
|
+
}
|
|
28
|
+
async onScope(scope, context) {
|
|
29
|
+
this.scope = scope;
|
|
30
|
+
this.position = new Map(scope.slices.map((slice, index) => [slice.id, index + 1]));
|
|
31
|
+
}
|
|
32
|
+
async onSliceStart(slice) {
|
|
33
|
+
this.output.info("Running slice %s", [this.describeProgress(slice)]);
|
|
34
|
+
}
|
|
35
|
+
async onSuccess(result) {
|
|
36
|
+
if (!result.changed) {
|
|
37
|
+
this.output.success("No changes since last release");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const changed = result.slices.filter((slice) => slice.changed);
|
|
41
|
+
const primary = changed[0];
|
|
42
|
+
const packages = collectPackages(changed);
|
|
43
|
+
this.output.success("Release slices: %s", [String(changed.length)]);
|
|
44
|
+
this.output.success("Updated packages: %s", [String(packages.length)]);
|
|
45
|
+
if (primary) this.output.success("Next version: %s", [primary.nextVersion]);
|
|
46
|
+
if (result.dry) {
|
|
47
|
+
this.output.info("No committing or tagging since this was a dry run");
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
this.output.success("Committed %s staged files", [String(result.files.length)]);
|
|
51
|
+
if (this.showTags) {
|
|
52
|
+
const tags = collectTags(changed);
|
|
53
|
+
if (tags.length) this.output.success("Tags: %s", [tags.join(", ")]);
|
|
54
|
+
}
|
|
55
|
+
this.output.info("Run `%s` to publish", [`git push --follow-tags origin ${await this.resolveBranch()}`]);
|
|
56
|
+
}
|
|
57
|
+
async onError(error) {
|
|
58
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
59
|
+
this.output.error(message);
|
|
60
|
+
}
|
|
61
|
+
describeProgress(slice) {
|
|
62
|
+
const position = this.position.get(slice.id);
|
|
63
|
+
const total = this.scope?.slices.length;
|
|
64
|
+
const label = describeSlice(slice);
|
|
65
|
+
return position && total ? `${position}/${total}: ${label}` : label;
|
|
66
|
+
}
|
|
67
|
+
async resolveBranch() {
|
|
68
|
+
try {
|
|
69
|
+
return await this.git.revParse("HEAD", { abbrevRef: true });
|
|
70
|
+
} catch {
|
|
71
|
+
return "%branch%";
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
var DetailedReporter = class extends SummaryReporter {
|
|
76
|
+
async onScope(scope, context) {
|
|
77
|
+
await super.onScope(scope, context);
|
|
78
|
+
this.output.info("%s scope: %s packages, %s affected, %s slices", [
|
|
79
|
+
scope.mode,
|
|
80
|
+
String(scope.packages.length),
|
|
81
|
+
String(scope.affected.length),
|
|
82
|
+
String(scope.slices.length)
|
|
83
|
+
]);
|
|
84
|
+
}
|
|
85
|
+
async onSliceSuccess(slice) {
|
|
86
|
+
if (!slice.changed) {
|
|
87
|
+
this.output.warn("Completed slice %s without version changes (%s)", [describeSlice(slice), slice.currentVersion]);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
this.output.success("Completed slice %s: %s -> %s (%s)", [
|
|
91
|
+
describeSlice(slice),
|
|
92
|
+
slice.currentVersion,
|
|
93
|
+
slice.nextVersion,
|
|
94
|
+
slice.releaseType
|
|
95
|
+
]);
|
|
96
|
+
if (this.showTags && slice.tag) this.output.info("Tag: %s", [slice.tag]);
|
|
97
|
+
}
|
|
98
|
+
async onSuccess(result) {
|
|
99
|
+
await super.onSuccess(result);
|
|
100
|
+
if (!result.changed) return;
|
|
101
|
+
const packages = collectPackages(result.slices.filter((slice) => slice.changed));
|
|
102
|
+
this.output.info("Updated packages: %s", [describePackages(packages)]);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
function describeSlice(slice) {
|
|
106
|
+
if (slice.partition) return `${slice.partition} [${describePackages(slice.packages)}]`;
|
|
107
|
+
return describePackages(slice.packages);
|
|
108
|
+
}
|
|
109
|
+
function describePackages(packages) {
|
|
110
|
+
return packages.map((pkg) => pkg.name ?? pkg.path).join(", ");
|
|
111
|
+
}
|
|
112
|
+
function collectTags(slices) {
|
|
113
|
+
return slices.map((slice) => slice.tag).filter((tag) => !!tag);
|
|
114
|
+
}
|
|
115
|
+
function collectPackages(slices) {
|
|
116
|
+
const packages = [];
|
|
117
|
+
const seen = /* @__PURE__ */ new Set();
|
|
118
|
+
for (const slice of slices) for (const pkg of slice.packages) {
|
|
119
|
+
const identity = pkg.name ?? pkg.path;
|
|
120
|
+
if (seen.has(identity)) continue;
|
|
121
|
+
seen.add(identity);
|
|
122
|
+
packages.push(pkg);
|
|
123
|
+
}
|
|
124
|
+
return packages;
|
|
125
|
+
}
|
|
126
|
+
//#endregion
|
|
127
|
+
export { createReporter };
|
package/dist/config.cjs
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require("./_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_constants = require("./constants.cjs");
|
|
3
|
+
let node_fs = require("node:fs");
|
|
4
|
+
let node_path = require("node:path");
|
|
5
|
+
let node_url = require("node:url");
|
|
6
|
+
//#region src/config.ts
|
|
7
|
+
/**
|
|
8
|
+
* Resolves repository release configuration from `package.json`, `release.config.*`,
|
|
9
|
+
* and inline overrides using the public precedence rules.
|
|
10
|
+
*/
|
|
11
|
+
async function resolveConfig(cwd, inline) {
|
|
12
|
+
const root = cwd ?? process.cwd();
|
|
13
|
+
const fromPackage = loadPackageJsonConfig(root);
|
|
14
|
+
const fromFile = await loadFileConfig(root);
|
|
15
|
+
return {
|
|
16
|
+
...fromPackage,
|
|
17
|
+
...fromFile,
|
|
18
|
+
...inline ?? {}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/** Converts repository configuration to executor-ready release options. */
|
|
22
|
+
function toScopeOptions(config) {
|
|
23
|
+
const options = { ...config };
|
|
24
|
+
delete options.changelogFile;
|
|
25
|
+
return options;
|
|
26
|
+
}
|
|
27
|
+
/** Extracts inline overrides from high-level run options. */
|
|
28
|
+
function toInlineConfig(options) {
|
|
29
|
+
const config = { ...options };
|
|
30
|
+
delete config.cwd;
|
|
31
|
+
delete config.dry;
|
|
32
|
+
delete config.reporter;
|
|
33
|
+
return config;
|
|
34
|
+
}
|
|
35
|
+
function loadPackageJsonConfig(cwd) {
|
|
36
|
+
const file = (0, node_path.join)(cwd, "package.json");
|
|
37
|
+
if (!(0, node_fs.existsSync)(file)) return {};
|
|
38
|
+
const content = JSON.parse((0, node_fs.readFileSync)(file, "utf-8"));
|
|
39
|
+
return isRecord(content.release) ? content.release : {};
|
|
40
|
+
}
|
|
41
|
+
async function loadFileConfig(cwd) {
|
|
42
|
+
const configFile = require_constants.RELEASE_CONFIG_FILENAMES.map((file) => (0, node_path.join)(cwd, file)).find((file) => (0, node_fs.existsSync)(file));
|
|
43
|
+
if (!configFile) return {};
|
|
44
|
+
const module = await import((0, node_url.pathToFileURL)(configFile).href);
|
|
45
|
+
const config = module.default ?? module;
|
|
46
|
+
return isRecord(config) ? config : {};
|
|
47
|
+
}
|
|
48
|
+
function isRecord(value) {
|
|
49
|
+
return !!value && typeof value === "object";
|
|
50
|
+
}
|
|
51
|
+
//#endregion
|
|
52
|
+
exports.resolveConfig = resolveConfig;
|
|
53
|
+
exports.toInlineConfig = toInlineConfig;
|
|
54
|
+
exports.toScopeOptions = toScopeOptions;
|
package/dist/config.mjs
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { RELEASE_CONFIG_FILENAMES } from "./constants.mjs";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
//#region src/config.ts
|
|
6
|
+
/**
|
|
7
|
+
* Resolves repository release configuration from `package.json`, `release.config.*`,
|
|
8
|
+
* and inline overrides using the public precedence rules.
|
|
9
|
+
*/
|
|
10
|
+
async function resolveConfig(cwd, inline) {
|
|
11
|
+
const root = cwd ?? process.cwd();
|
|
12
|
+
const fromPackage = loadPackageJsonConfig(root);
|
|
13
|
+
const fromFile = await loadFileConfig(root);
|
|
14
|
+
return {
|
|
15
|
+
...fromPackage,
|
|
16
|
+
...fromFile,
|
|
17
|
+
...inline ?? {}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** Converts repository configuration to executor-ready release options. */
|
|
21
|
+
function toScopeOptions(config) {
|
|
22
|
+
const options = { ...config };
|
|
23
|
+
delete options.changelogFile;
|
|
24
|
+
return options;
|
|
25
|
+
}
|
|
26
|
+
/** Extracts inline overrides from high-level run options. */
|
|
27
|
+
function toInlineConfig(options) {
|
|
28
|
+
const config = { ...options };
|
|
29
|
+
delete config.cwd;
|
|
30
|
+
delete config.dry;
|
|
31
|
+
delete config.reporter;
|
|
32
|
+
return config;
|
|
33
|
+
}
|
|
34
|
+
function loadPackageJsonConfig(cwd) {
|
|
35
|
+
const file = join(cwd, "package.json");
|
|
36
|
+
if (!existsSync(file)) return {};
|
|
37
|
+
const content = JSON.parse(readFileSync(file, "utf-8"));
|
|
38
|
+
return isRecord(content.release) ? content.release : {};
|
|
39
|
+
}
|
|
40
|
+
async function loadFileConfig(cwd) {
|
|
41
|
+
const configFile = RELEASE_CONFIG_FILENAMES.map((file) => join(cwd, file)).find((file) => existsSync(file));
|
|
42
|
+
if (!configFile) return {};
|
|
43
|
+
const module = await import(pathToFileURL(configFile).href);
|
|
44
|
+
const config = module.default ?? module;
|
|
45
|
+
return isRecord(config) ? config : {};
|
|
46
|
+
}
|
|
47
|
+
function isRecord(value) {
|
|
48
|
+
return !!value && typeof value === "object";
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
export { resolveConfig, toInlineConfig, toScopeOptions };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/constants.ts
|
|
2
|
+
var DEFAULT_CHANGELOG_FILE = "CHANGELOG.md";
|
|
3
|
+
var DEFAULT_RELEASE_PREFIX = "chore(release): ";
|
|
4
|
+
var DEFAULT_RELEASE_MODE = "sync";
|
|
5
|
+
var DEFAULT_DEPENDENCY_POLICY_INTERNAL = "preserve";
|
|
6
|
+
var RELEASE_CONFIG_FILENAMES = [
|
|
7
|
+
"release.config.ts",
|
|
8
|
+
"release.config.mjs",
|
|
9
|
+
"release.config.js"
|
|
10
|
+
];
|
|
11
|
+
//#endregion
|
|
12
|
+
exports.DEFAULT_CHANGELOG_FILE = DEFAULT_CHANGELOG_FILE;
|
|
13
|
+
exports.DEFAULT_DEPENDENCY_POLICY_INTERNAL = DEFAULT_DEPENDENCY_POLICY_INTERNAL;
|
|
14
|
+
exports.DEFAULT_RELEASE_MODE = DEFAULT_RELEASE_MODE;
|
|
15
|
+
exports.DEFAULT_RELEASE_PREFIX = DEFAULT_RELEASE_PREFIX;
|
|
16
|
+
exports.RELEASE_CONFIG_FILENAMES = RELEASE_CONFIG_FILENAMES;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/constants.ts
|
|
2
|
+
var DEFAULT_CHANGELOG_FILE = "CHANGELOG.md";
|
|
3
|
+
var DEFAULT_RELEASE_PREFIX = "chore(release): ";
|
|
4
|
+
var DEFAULT_RELEASE_MODE = "sync";
|
|
5
|
+
var DEFAULT_DEPENDENCY_POLICY_INTERNAL = "preserve";
|
|
6
|
+
var RELEASE_CONFIG_FILENAMES = [
|
|
7
|
+
"release.config.ts",
|
|
8
|
+
"release.config.mjs",
|
|
9
|
+
"release.config.js"
|
|
10
|
+
];
|
|
11
|
+
//#endregion
|
|
12
|
+
export { DEFAULT_CHANGELOG_FILE, DEFAULT_DEPENDENCY_POLICY_INTERNAL, DEFAULT_RELEASE_MODE, DEFAULT_RELEASE_PREFIX, RELEASE_CONFIG_FILENAMES };
|
package/dist/execute.cjs
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
const require_runtime = require("./_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_reporter = require("./reporter.cjs");
|
|
3
|
+
const require_constants = require("./constants.cjs");
|
|
4
|
+
const require_plan = require("./plan.cjs");
|
|
5
|
+
let node_path = require("node:path");
|
|
6
|
+
let _modulify_conventional_changelog = require("@modulify/conventional-changelog");
|
|
7
|
+
let _modulify_pkg = require("@modulify/pkg");
|
|
8
|
+
let _modulify_conventional_bump = require("@modulify/conventional-bump");
|
|
9
|
+
let semver = require("semver");
|
|
10
|
+
semver = require_runtime.__toESM(semver, 1);
|
|
11
|
+
//#region src/execute.ts
|
|
12
|
+
async function runScope(runtime, scope, options, reporting) {
|
|
13
|
+
const slices = [];
|
|
14
|
+
for (const slice of scope.slices) {
|
|
15
|
+
if (reporting) await require_reporter.reportSliceStart(reporting.reporter, require_plan.toSlice(slice, runtime.cwd), reporting.context);
|
|
16
|
+
const result = await executeSlice(runtime, slice, options);
|
|
17
|
+
if (reporting) await require_reporter.reportSliceSuccess(reporting.reporter, result, reporting.context);
|
|
18
|
+
slices.push(result);
|
|
19
|
+
}
|
|
20
|
+
const files = require_plan.uniqueStrings(slices.flatMap((slice) => slice.files));
|
|
21
|
+
const changed = slices.some((slice) => slice.changed);
|
|
22
|
+
return {
|
|
23
|
+
mode: scope.mode,
|
|
24
|
+
changed,
|
|
25
|
+
dry: runtime.dry,
|
|
26
|
+
packages: scope.packages.map((pkg) => require_plan.toScopePackage(pkg, runtime.cwd)),
|
|
27
|
+
affected: scope.affected.map((pkg) => require_plan.toScopePackage(pkg, runtime.cwd)),
|
|
28
|
+
slices,
|
|
29
|
+
files
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
async function executeSlice(runtime, slice, options) {
|
|
33
|
+
const normalizedSlice = {
|
|
34
|
+
...slice,
|
|
35
|
+
packages: require_plan.uniquePackages(slice.packages)
|
|
36
|
+
};
|
|
37
|
+
const currentVersion = resolveSliceCurrentVersion(normalizedSlice, runtime.cwd);
|
|
38
|
+
const releaseMeta = await collectSliceMeta(runtime, normalizedSlice);
|
|
39
|
+
const release = (0, _modulify_conventional_bump.resolveNextVersion)(currentVersion, {
|
|
40
|
+
recommendation: releaseMeta.recommendation,
|
|
41
|
+
type: options.releaseAs,
|
|
42
|
+
prerelease: options.prerelease,
|
|
43
|
+
preMajor: options.releaseAs ? false : options.preMajor ?? isPreMajorVersion(currentVersion)
|
|
44
|
+
});
|
|
45
|
+
const releaseType = String(release.type);
|
|
46
|
+
const nextVersion = release.version;
|
|
47
|
+
const base = require_plan.toSlice(normalizedSlice, runtime.cwd);
|
|
48
|
+
if (nextVersion === currentVersion) return {
|
|
49
|
+
...base,
|
|
50
|
+
currentVersion,
|
|
51
|
+
nextVersion,
|
|
52
|
+
releaseType,
|
|
53
|
+
changed: false,
|
|
54
|
+
dry: runtime.dry,
|
|
55
|
+
files: []
|
|
56
|
+
};
|
|
57
|
+
const files = await applySlice(runtime, normalizedSlice, nextVersion, options, (0, _modulify_conventional_changelog.renderChangelog)(nextVersion, {
|
|
58
|
+
notes: releaseMeta.notes,
|
|
59
|
+
url: await runtime.history.url()
|
|
60
|
+
}));
|
|
61
|
+
const context = createTagContext(normalizedSlice, nextVersion, releaseType, runtime.cwd);
|
|
62
|
+
const tag = options.tagName ? options.tagName(context) : createDefaultTagName(normalizedSlice, nextVersion, runtime.cwd);
|
|
63
|
+
if (runtime.dry) return {
|
|
64
|
+
...base,
|
|
65
|
+
currentVersion,
|
|
66
|
+
nextVersion,
|
|
67
|
+
releaseType,
|
|
68
|
+
changed: true,
|
|
69
|
+
dry: true,
|
|
70
|
+
tag,
|
|
71
|
+
files
|
|
72
|
+
};
|
|
73
|
+
const messageContext = {
|
|
74
|
+
...context,
|
|
75
|
+
tag
|
|
76
|
+
};
|
|
77
|
+
const commitMessage = options.commitMessage ? options.commitMessage(messageContext) : require_constants.DEFAULT_RELEASE_PREFIX + tag;
|
|
78
|
+
const tagMessage = options.tagMessage ? options.tagMessage(messageContext) : require_constants.DEFAULT_RELEASE_PREFIX + tag;
|
|
79
|
+
await runtime.git.add(files);
|
|
80
|
+
await runtime.git.commit({
|
|
81
|
+
files,
|
|
82
|
+
message: commitMessage
|
|
83
|
+
});
|
|
84
|
+
await runtime.git.tag({
|
|
85
|
+
name: tag,
|
|
86
|
+
message: tagMessage
|
|
87
|
+
});
|
|
88
|
+
return {
|
|
89
|
+
...base,
|
|
90
|
+
currentVersion,
|
|
91
|
+
nextVersion,
|
|
92
|
+
releaseType,
|
|
93
|
+
changed: true,
|
|
94
|
+
dry: false,
|
|
95
|
+
files,
|
|
96
|
+
tag,
|
|
97
|
+
commitMessage,
|
|
98
|
+
tagMessage
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
async function applySlice(runtime, slice, nextVersion, options, changes) {
|
|
102
|
+
const bumpedPackageNames = slice.packages.reduce((all, pkg) => {
|
|
103
|
+
const name = pkg.manifest.name ?? pkg.name;
|
|
104
|
+
if (name) all.add(name);
|
|
105
|
+
return all;
|
|
106
|
+
}, /* @__PURE__ */ new Set());
|
|
107
|
+
const dependencyPolicy = options.dependencyPolicy ?? "preserve";
|
|
108
|
+
const files = [];
|
|
109
|
+
for (const pkg of slice.packages) {
|
|
110
|
+
const diff = { version: nextVersion };
|
|
111
|
+
const manifest = pkg.manifest;
|
|
112
|
+
if (hasDependencies(manifest.peerDependencies)) diff.peerDependencies = actualizeDependencies(manifest.peerDependencies, nextVersion, bumpedPackageNames, dependencyPolicy);
|
|
113
|
+
if (hasDependencies(manifest.dependencies)) diff.dependencies = actualizeDependencies(manifest.dependencies, nextVersion, bumpedPackageNames, dependencyPolicy);
|
|
114
|
+
if (hasDependencies(manifest.optionalDependencies)) diff.optionalDependencies = actualizeDependencies(manifest.optionalDependencies, nextVersion, bumpedPackageNames, dependencyPolicy);
|
|
115
|
+
if (hasDependencies(manifest.devDependencies)) diff.devDependencies = actualizeDependencies(manifest.devDependencies, nextVersion, bumpedPackageNames, dependencyPolicy);
|
|
116
|
+
files.push((0, node_path.relative)(runtime.cwd, (0, _modulify_pkg.update)(pkg.path, diff, runtime.dry)));
|
|
117
|
+
}
|
|
118
|
+
const installArgs = resolveInstallArgs(runtime, options.install);
|
|
119
|
+
if (!runtime.dry && installArgs) await runtime.sh.exec(runtime.packageManager.command, ["install", ...installArgs]);
|
|
120
|
+
if (!runtime.dry) await runtime.writeChangelog(changes);
|
|
121
|
+
files.push((0, node_path.relative)(runtime.cwd, (0, node_path.join)(runtime.cwd, runtime.packageManager.lockfile)));
|
|
122
|
+
files.push((0, node_path.relative)(runtime.cwd, (0, node_path.join)(runtime.cwd, runtime.changelogFile)));
|
|
123
|
+
return require_plan.uniqueStrings(files);
|
|
124
|
+
}
|
|
125
|
+
async function collectSliceMeta(runtime, slice) {
|
|
126
|
+
const result = await runtime.history.traverse({
|
|
127
|
+
...slice.range,
|
|
128
|
+
traversers: [(0, _modulify_conventional_bump.createRecommendationAnalyzer)({ strict: true }), (0, _modulify_conventional_changelog.createChangelogCapacitor)()]
|
|
129
|
+
});
|
|
130
|
+
return {
|
|
131
|
+
recommendation: result.results.get("recommendation"),
|
|
132
|
+
notes: result.results.get("changelog")
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function resolveInstallArgs(runtime, install) {
|
|
136
|
+
if (install === false) return null;
|
|
137
|
+
if (Array.isArray(install)) return install;
|
|
138
|
+
if (runtime.packageManager.command === "yarn") return ["--no-immutable"];
|
|
139
|
+
return [];
|
|
140
|
+
}
|
|
141
|
+
function createTagContext(slice, version, releaseType, cwd) {
|
|
142
|
+
return {
|
|
143
|
+
id: slice.id,
|
|
144
|
+
kind: slice.kind,
|
|
145
|
+
mode: slice.mode,
|
|
146
|
+
partition: slice.partition,
|
|
147
|
+
packages: slice.packages.map((pkg) => require_plan.toScopePackage(pkg, cwd)),
|
|
148
|
+
version,
|
|
149
|
+
releaseType
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function resolveSliceCurrentVersion(slice, cwd) {
|
|
153
|
+
const versions = require_plan.uniqueStrings(slice.packages.map((pkg) => pkg.manifest.version ?? "0.0.0"));
|
|
154
|
+
if (slice.mode === "sync" && versions.length > 1) {
|
|
155
|
+
const packageList = slice.packages.map((pkg) => require_plan.packageIdentity(pkg, cwd)).join(", ");
|
|
156
|
+
throw new Error(`Sync release slice "${slice.id}" requires aligned package versions: ${packageList}`);
|
|
157
|
+
}
|
|
158
|
+
return versions[0];
|
|
159
|
+
}
|
|
160
|
+
function isPreMajorVersion(version) {
|
|
161
|
+
return semver.default.valid(version) ? semver.default.lt(version, "1.0.0") : false;
|
|
162
|
+
}
|
|
163
|
+
function createDefaultTagName(slice, version, cwd) {
|
|
164
|
+
if (slice.kind === "sync") return `v${version}`;
|
|
165
|
+
if (slice.partition) return `${slice.partition}@${version}`;
|
|
166
|
+
return `${require_plan.packageIdentity(slice.packages[0], cwd)}@${version}`;
|
|
167
|
+
}
|
|
168
|
+
function hasDependencies(dependencies) {
|
|
169
|
+
return !!dependencies && Object.keys(dependencies).length > 0;
|
|
170
|
+
}
|
|
171
|
+
function actualizeDependencies(dependencies, nextVersion, bumpedPackages, internalPolicy) {
|
|
172
|
+
return Object.keys(dependencies).reduce((all, name) => ({
|
|
173
|
+
...all,
|
|
174
|
+
[name]: bumpedPackages.has(name) ? updateDependencyRange(dependencies[name], nextVersion, internalPolicy) : dependencies[name]
|
|
175
|
+
}), {});
|
|
176
|
+
}
|
|
177
|
+
function updateDependencyRange(current, nextVersion, policy) {
|
|
178
|
+
if (policy === "caret") return "^" + nextVersion;
|
|
179
|
+
if (policy === "exact") return nextVersion;
|
|
180
|
+
if (current.startsWith("workspace:")) {
|
|
181
|
+
const value = current.slice(10);
|
|
182
|
+
if (value === "*") return "workspace:*";
|
|
183
|
+
if (value.startsWith("^")) return `workspace:^${nextVersion}`;
|
|
184
|
+
if (value.startsWith("~")) return `workspace:~${nextVersion}`;
|
|
185
|
+
return `workspace:${nextVersion}`;
|
|
186
|
+
}
|
|
187
|
+
if (current.startsWith("^")) return "^" + nextVersion;
|
|
188
|
+
if (current.startsWith("~")) return "~" + nextVersion;
|
|
189
|
+
return nextVersion;
|
|
190
|
+
}
|
|
191
|
+
//#endregion
|
|
192
|
+
exports.runScope = runScope;
|
package/dist/execute.mjs
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { reportSliceStart, reportSliceSuccess } from "./reporter.mjs";
|
|
2
|
+
import { DEFAULT_RELEASE_PREFIX } from "./constants.mjs";
|
|
3
|
+
import { packageIdentity, toScopePackage, toSlice, uniquePackages, uniqueStrings } from "./plan.mjs";
|
|
4
|
+
import { join, relative } from "node:path";
|
|
5
|
+
import { createChangelogCapacitor, renderChangelog } from "@modulify/conventional-changelog";
|
|
6
|
+
import { update } from "@modulify/pkg";
|
|
7
|
+
import { createRecommendationAnalyzer, resolveNextVersion } from "@modulify/conventional-bump";
|
|
8
|
+
import semver from "semver";
|
|
9
|
+
//#region src/execute.ts
|
|
10
|
+
async function runScope(runtime, scope, options, reporting) {
|
|
11
|
+
const slices = [];
|
|
12
|
+
for (const slice of scope.slices) {
|
|
13
|
+
if (reporting) await reportSliceStart(reporting.reporter, toSlice(slice, runtime.cwd), reporting.context);
|
|
14
|
+
const result = await executeSlice(runtime, slice, options);
|
|
15
|
+
if (reporting) await reportSliceSuccess(reporting.reporter, result, reporting.context);
|
|
16
|
+
slices.push(result);
|
|
17
|
+
}
|
|
18
|
+
const files = uniqueStrings(slices.flatMap((slice) => slice.files));
|
|
19
|
+
const changed = slices.some((slice) => slice.changed);
|
|
20
|
+
return {
|
|
21
|
+
mode: scope.mode,
|
|
22
|
+
changed,
|
|
23
|
+
dry: runtime.dry,
|
|
24
|
+
packages: scope.packages.map((pkg) => toScopePackage(pkg, runtime.cwd)),
|
|
25
|
+
affected: scope.affected.map((pkg) => toScopePackage(pkg, runtime.cwd)),
|
|
26
|
+
slices,
|
|
27
|
+
files
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
async function executeSlice(runtime, slice, options) {
|
|
31
|
+
const normalizedSlice = {
|
|
32
|
+
...slice,
|
|
33
|
+
packages: uniquePackages(slice.packages)
|
|
34
|
+
};
|
|
35
|
+
const currentVersion = resolveSliceCurrentVersion(normalizedSlice, runtime.cwd);
|
|
36
|
+
const releaseMeta = await collectSliceMeta(runtime, normalizedSlice);
|
|
37
|
+
const release = resolveNextVersion(currentVersion, {
|
|
38
|
+
recommendation: releaseMeta.recommendation,
|
|
39
|
+
type: options.releaseAs,
|
|
40
|
+
prerelease: options.prerelease,
|
|
41
|
+
preMajor: options.releaseAs ? false : options.preMajor ?? isPreMajorVersion(currentVersion)
|
|
42
|
+
});
|
|
43
|
+
const releaseType = String(release.type);
|
|
44
|
+
const nextVersion = release.version;
|
|
45
|
+
const base = toSlice(normalizedSlice, runtime.cwd);
|
|
46
|
+
if (nextVersion === currentVersion) return {
|
|
47
|
+
...base,
|
|
48
|
+
currentVersion,
|
|
49
|
+
nextVersion,
|
|
50
|
+
releaseType,
|
|
51
|
+
changed: false,
|
|
52
|
+
dry: runtime.dry,
|
|
53
|
+
files: []
|
|
54
|
+
};
|
|
55
|
+
const files = await applySlice(runtime, normalizedSlice, nextVersion, options, renderChangelog(nextVersion, {
|
|
56
|
+
notes: releaseMeta.notes,
|
|
57
|
+
url: await runtime.history.url()
|
|
58
|
+
}));
|
|
59
|
+
const context = createTagContext(normalizedSlice, nextVersion, releaseType, runtime.cwd);
|
|
60
|
+
const tag = options.tagName ? options.tagName(context) : createDefaultTagName(normalizedSlice, nextVersion, runtime.cwd);
|
|
61
|
+
if (runtime.dry) return {
|
|
62
|
+
...base,
|
|
63
|
+
currentVersion,
|
|
64
|
+
nextVersion,
|
|
65
|
+
releaseType,
|
|
66
|
+
changed: true,
|
|
67
|
+
dry: true,
|
|
68
|
+
tag,
|
|
69
|
+
files
|
|
70
|
+
};
|
|
71
|
+
const messageContext = {
|
|
72
|
+
...context,
|
|
73
|
+
tag
|
|
74
|
+
};
|
|
75
|
+
const commitMessage = options.commitMessage ? options.commitMessage(messageContext) : DEFAULT_RELEASE_PREFIX + tag;
|
|
76
|
+
const tagMessage = options.tagMessage ? options.tagMessage(messageContext) : DEFAULT_RELEASE_PREFIX + tag;
|
|
77
|
+
await runtime.git.add(files);
|
|
78
|
+
await runtime.git.commit({
|
|
79
|
+
files,
|
|
80
|
+
message: commitMessage
|
|
81
|
+
});
|
|
82
|
+
await runtime.git.tag({
|
|
83
|
+
name: tag,
|
|
84
|
+
message: tagMessage
|
|
85
|
+
});
|
|
86
|
+
return {
|
|
87
|
+
...base,
|
|
88
|
+
currentVersion,
|
|
89
|
+
nextVersion,
|
|
90
|
+
releaseType,
|
|
91
|
+
changed: true,
|
|
92
|
+
dry: false,
|
|
93
|
+
files,
|
|
94
|
+
tag,
|
|
95
|
+
commitMessage,
|
|
96
|
+
tagMessage
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
async function applySlice(runtime, slice, nextVersion, options, changes) {
|
|
100
|
+
const bumpedPackageNames = slice.packages.reduce((all, pkg) => {
|
|
101
|
+
const name = pkg.manifest.name ?? pkg.name;
|
|
102
|
+
if (name) all.add(name);
|
|
103
|
+
return all;
|
|
104
|
+
}, /* @__PURE__ */ new Set());
|
|
105
|
+
const dependencyPolicy = options.dependencyPolicy ?? "preserve";
|
|
106
|
+
const files = [];
|
|
107
|
+
for (const pkg of slice.packages) {
|
|
108
|
+
const diff = { version: nextVersion };
|
|
109
|
+
const manifest = pkg.manifest;
|
|
110
|
+
if (hasDependencies(manifest.peerDependencies)) diff.peerDependencies = actualizeDependencies(manifest.peerDependencies, nextVersion, bumpedPackageNames, dependencyPolicy);
|
|
111
|
+
if (hasDependencies(manifest.dependencies)) diff.dependencies = actualizeDependencies(manifest.dependencies, nextVersion, bumpedPackageNames, dependencyPolicy);
|
|
112
|
+
if (hasDependencies(manifest.optionalDependencies)) diff.optionalDependencies = actualizeDependencies(manifest.optionalDependencies, nextVersion, bumpedPackageNames, dependencyPolicy);
|
|
113
|
+
if (hasDependencies(manifest.devDependencies)) diff.devDependencies = actualizeDependencies(manifest.devDependencies, nextVersion, bumpedPackageNames, dependencyPolicy);
|
|
114
|
+
files.push(relative(runtime.cwd, update(pkg.path, diff, runtime.dry)));
|
|
115
|
+
}
|
|
116
|
+
const installArgs = resolveInstallArgs(runtime, options.install);
|
|
117
|
+
if (!runtime.dry && installArgs) await runtime.sh.exec(runtime.packageManager.command, ["install", ...installArgs]);
|
|
118
|
+
if (!runtime.dry) await runtime.writeChangelog(changes);
|
|
119
|
+
files.push(relative(runtime.cwd, join(runtime.cwd, runtime.packageManager.lockfile)));
|
|
120
|
+
files.push(relative(runtime.cwd, join(runtime.cwd, runtime.changelogFile)));
|
|
121
|
+
return uniqueStrings(files);
|
|
122
|
+
}
|
|
123
|
+
async function collectSliceMeta(runtime, slice) {
|
|
124
|
+
const result = await runtime.history.traverse({
|
|
125
|
+
...slice.range,
|
|
126
|
+
traversers: [createRecommendationAnalyzer({ strict: true }), createChangelogCapacitor()]
|
|
127
|
+
});
|
|
128
|
+
return {
|
|
129
|
+
recommendation: result.results.get("recommendation"),
|
|
130
|
+
notes: result.results.get("changelog")
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function resolveInstallArgs(runtime, install) {
|
|
134
|
+
if (install === false) return null;
|
|
135
|
+
if (Array.isArray(install)) return install;
|
|
136
|
+
if (runtime.packageManager.command === "yarn") return ["--no-immutable"];
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
function createTagContext(slice, version, releaseType, cwd) {
|
|
140
|
+
return {
|
|
141
|
+
id: slice.id,
|
|
142
|
+
kind: slice.kind,
|
|
143
|
+
mode: slice.mode,
|
|
144
|
+
partition: slice.partition,
|
|
145
|
+
packages: slice.packages.map((pkg) => toScopePackage(pkg, cwd)),
|
|
146
|
+
version,
|
|
147
|
+
releaseType
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function resolveSliceCurrentVersion(slice, cwd) {
|
|
151
|
+
const versions = uniqueStrings(slice.packages.map((pkg) => pkg.manifest.version ?? "0.0.0"));
|
|
152
|
+
if (slice.mode === "sync" && versions.length > 1) {
|
|
153
|
+
const packageList = slice.packages.map((pkg) => packageIdentity(pkg, cwd)).join(", ");
|
|
154
|
+
throw new Error(`Sync release slice "${slice.id}" requires aligned package versions: ${packageList}`);
|
|
155
|
+
}
|
|
156
|
+
return versions[0];
|
|
157
|
+
}
|
|
158
|
+
function isPreMajorVersion(version) {
|
|
159
|
+
return semver.valid(version) ? semver.lt(version, "1.0.0") : false;
|
|
160
|
+
}
|
|
161
|
+
function createDefaultTagName(slice, version, cwd) {
|
|
162
|
+
if (slice.kind === "sync") return `v${version}`;
|
|
163
|
+
if (slice.partition) return `${slice.partition}@${version}`;
|
|
164
|
+
return `${packageIdentity(slice.packages[0], cwd)}@${version}`;
|
|
165
|
+
}
|
|
166
|
+
function hasDependencies(dependencies) {
|
|
167
|
+
return !!dependencies && Object.keys(dependencies).length > 0;
|
|
168
|
+
}
|
|
169
|
+
function actualizeDependencies(dependencies, nextVersion, bumpedPackages, internalPolicy) {
|
|
170
|
+
return Object.keys(dependencies).reduce((all, name) => ({
|
|
171
|
+
...all,
|
|
172
|
+
[name]: bumpedPackages.has(name) ? updateDependencyRange(dependencies[name], nextVersion, internalPolicy) : dependencies[name]
|
|
173
|
+
}), {});
|
|
174
|
+
}
|
|
175
|
+
function updateDependencyRange(current, nextVersion, policy) {
|
|
176
|
+
if (policy === "caret") return "^" + nextVersion;
|
|
177
|
+
if (policy === "exact") return nextVersion;
|
|
178
|
+
if (current.startsWith("workspace:")) {
|
|
179
|
+
const value = current.slice(10);
|
|
180
|
+
if (value === "*") return "workspace:*";
|
|
181
|
+
if (value.startsWith("^")) return `workspace:^${nextVersion}`;
|
|
182
|
+
if (value.startsWith("~")) return `workspace:~${nextVersion}`;
|
|
183
|
+
return `workspace:${nextVersion}`;
|
|
184
|
+
}
|
|
185
|
+
if (current.startsWith("^")) return "^" + nextVersion;
|
|
186
|
+
if (current.startsWith("~")) return "~" + nextVersion;
|
|
187
|
+
return nextVersion;
|
|
188
|
+
}
|
|
189
|
+
//#endregion
|
|
190
|
+
export { runScope };
|