@modulify/conventional-release 0.1.0
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 +380 -0
- package/bin/cli.cjs +308 -0
- package/bin/cli.mjs +291 -0
- package/bin/index.cjs +7 -0
- package/bin/index.js +9 -0
- package/dist/config.d.ts +12 -0
- package/dist/constants.d.ts +6 -0
- package/dist/execute.d.ts +9 -0
- package/dist/index.cjs +712 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.mjs +712 -0
- package/dist/plan.d.ts +24 -0
- package/dist/reporter.d.ts +11 -0
- package/dist/runtime.d.ts +36 -0
- package/package.json +98 -0
- package/types/domain.d.ts +124 -0
- package/types/index.d.ts +23 -0
- package/types/release.d.ts +93 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,712 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const node_fs = require("node:fs");
|
|
4
|
+
const node_path = require("node:path");
|
|
5
|
+
const node_url = require("node:url");
|
|
6
|
+
const conventionalGit = require("@modulify/conventional-git");
|
|
7
|
+
const conventionalChangelog = require("@modulify/conventional-changelog");
|
|
8
|
+
const gitToolkit = require("@modulify/git-toolkit");
|
|
9
|
+
const shell = require("@modulify/git-toolkit/shell");
|
|
10
|
+
const pkg = require("@modulify/pkg");
|
|
11
|
+
const conventionalBump = require("@modulify/conventional-bump");
|
|
12
|
+
function createRunContext({
|
|
13
|
+
cwd,
|
|
14
|
+
dry
|
|
15
|
+
}) {
|
|
16
|
+
return { cwd, dry };
|
|
17
|
+
}
|
|
18
|
+
async function reportStart(reporter, context) {
|
|
19
|
+
await reporter?.onStart?.(context);
|
|
20
|
+
}
|
|
21
|
+
async function reportScope(reporter, scope, context) {
|
|
22
|
+
await reporter?.onScope?.(scope, context);
|
|
23
|
+
}
|
|
24
|
+
async function reportSliceStart(reporter, slice, context) {
|
|
25
|
+
await reporter?.onSliceStart?.(slice, context);
|
|
26
|
+
}
|
|
27
|
+
async function reportSliceSuccess(reporter, slice, context) {
|
|
28
|
+
await reporter?.onSliceSuccess?.(slice, context);
|
|
29
|
+
}
|
|
30
|
+
async function reportSuccess(reporter, result, context) {
|
|
31
|
+
await reporter?.onSuccess?.(result, context);
|
|
32
|
+
}
|
|
33
|
+
async function reportError(reporter, error, context) {
|
|
34
|
+
await reporter?.onError?.(error, context);
|
|
35
|
+
}
|
|
36
|
+
const DEFAULT_CHANGELOG_FILE = "CHANGELOG.md";
|
|
37
|
+
const DEFAULT_RELEASE_PREFIX = "chore(release): ";
|
|
38
|
+
const DEFAULT_RELEASE_MODE = "sync";
|
|
39
|
+
const DEFAULT_DEPENDENCY_POLICY_INTERNAL = "preserve";
|
|
40
|
+
const RELEASE_CONFIG_FILENAMES = [
|
|
41
|
+
"release.config.ts",
|
|
42
|
+
"release.config.mjs",
|
|
43
|
+
"release.config.js"
|
|
44
|
+
];
|
|
45
|
+
async function resolveConfig(cwd, inline) {
|
|
46
|
+
const root = cwd ?? process.cwd();
|
|
47
|
+
const fromPackage = loadPackageJsonConfig(root);
|
|
48
|
+
const fromFile = await loadFileConfig(root);
|
|
49
|
+
return {
|
|
50
|
+
...fromPackage,
|
|
51
|
+
...fromFile,
|
|
52
|
+
...inline ?? {}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function toScopeOptions(config) {
|
|
56
|
+
const options = { ...config };
|
|
57
|
+
delete options.changelogFile;
|
|
58
|
+
return options;
|
|
59
|
+
}
|
|
60
|
+
function toInlineConfig(options) {
|
|
61
|
+
const config = { ...options };
|
|
62
|
+
delete config.cwd;
|
|
63
|
+
delete config.dry;
|
|
64
|
+
delete config.reporter;
|
|
65
|
+
return config;
|
|
66
|
+
}
|
|
67
|
+
function loadPackageJsonConfig(cwd) {
|
|
68
|
+
const file = node_path.join(cwd, "package.json");
|
|
69
|
+
if (!node_fs.existsSync(file)) {
|
|
70
|
+
return {};
|
|
71
|
+
}
|
|
72
|
+
const content = JSON.parse(node_fs.readFileSync(file, "utf-8"));
|
|
73
|
+
return isRecord(content.release) ? content.release : {};
|
|
74
|
+
}
|
|
75
|
+
async function loadFileConfig(cwd) {
|
|
76
|
+
const configFile = RELEASE_CONFIG_FILENAMES.map((file) => node_path.join(cwd, file)).find((file) => node_fs.existsSync(file));
|
|
77
|
+
if (!configFile) {
|
|
78
|
+
return {};
|
|
79
|
+
}
|
|
80
|
+
const module2 = await import(node_url.pathToFileURL(configFile).href);
|
|
81
|
+
const config = module2.default ?? module2;
|
|
82
|
+
return isRecord(config) ? config : {};
|
|
83
|
+
}
|
|
84
|
+
function isRecord(value) {
|
|
85
|
+
return !!value && typeof value === "object";
|
|
86
|
+
}
|
|
87
|
+
function createRuntime({
|
|
88
|
+
cwd = process.cwd(),
|
|
89
|
+
dry = false,
|
|
90
|
+
changelogFile = DEFAULT_CHANGELOG_FILE
|
|
91
|
+
} = {}) {
|
|
92
|
+
const sh = new shell.Runner(cwd);
|
|
93
|
+
return {
|
|
94
|
+
cwd,
|
|
95
|
+
dry,
|
|
96
|
+
changelogFile,
|
|
97
|
+
packageManager: resolvePackageManager(cwd),
|
|
98
|
+
history: new conventionalGit.Client({ cwd }),
|
|
99
|
+
writeChangelog: (changes) => conventionalChangelog.writeChangelog(changes, {
|
|
100
|
+
file: dry ? void 0 : node_path.join(cwd, changelogFile)
|
|
101
|
+
}),
|
|
102
|
+
sh,
|
|
103
|
+
git: createGit(sh)
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function createGit(sh) {
|
|
107
|
+
const git = new gitToolkit.GitCommander({ sh });
|
|
108
|
+
return {
|
|
109
|
+
add: (files) => git.add(files),
|
|
110
|
+
commit: (options) => git.commit(options),
|
|
111
|
+
tag: (options) => git.tag(options)
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function resolvePackageManager(cwd) {
|
|
115
|
+
const command = readPackageManager(cwd) ?? inferPackageManagerFromLockfile(cwd) ?? "npm";
|
|
116
|
+
return {
|
|
117
|
+
command,
|
|
118
|
+
lockfile: resolveLockfile(cwd, command)
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function readPackageManager(cwd) {
|
|
122
|
+
const file = node_path.join(cwd, "package.json");
|
|
123
|
+
if (!node_fs.existsSync(file)) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
const content = JSON.parse(node_fs.readFileSync(file, "utf-8"));
|
|
127
|
+
if (typeof content.packageManager !== "string" || content.packageManager === "") {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
const [name] = content.packageManager.split("@");
|
|
131
|
+
return isPackageManagerName(name) ? name : null;
|
|
132
|
+
}
|
|
133
|
+
function inferPackageManagerFromLockfile(cwd) {
|
|
134
|
+
if (node_fs.existsSync(node_path.join(cwd, "yarn.lock"))) return "yarn";
|
|
135
|
+
if (node_fs.existsSync(node_path.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
136
|
+
if (node_fs.existsSync(node_path.join(cwd, "package-lock.json"))) return "npm";
|
|
137
|
+
if (node_fs.existsSync(node_path.join(cwd, "bun.lock"))) return "bun";
|
|
138
|
+
if (node_fs.existsSync(node_path.join(cwd, "bun.lockb"))) return "bun";
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
function resolveLockfile(cwd, command) {
|
|
142
|
+
if (command === "bun" && node_fs.existsSync(node_path.join(cwd, "bun.lockb"))) {
|
|
143
|
+
return "bun.lockb";
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
yarn: "yarn.lock",
|
|
147
|
+
pnpm: "pnpm-lock.yaml",
|
|
148
|
+
npm: "package-lock.json",
|
|
149
|
+
bun: "bun.lock"
|
|
150
|
+
}[command];
|
|
151
|
+
}
|
|
152
|
+
function isPackageManagerName(value) {
|
|
153
|
+
return value === "yarn" || value === "pnpm" || value === "npm" || value === "bun";
|
|
154
|
+
}
|
|
155
|
+
function toScope(scope, cwd) {
|
|
156
|
+
return {
|
|
157
|
+
mode: scope.mode,
|
|
158
|
+
packages: scope.packages.map((pkg2) => toScopePackage(pkg2, cwd)),
|
|
159
|
+
affected: scope.affected.map((pkg2) => toScopePackage(pkg2, cwd)),
|
|
160
|
+
slices: scope.slices.map((slice) => toSlice(slice, cwd))
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
async function discover(runtime, options) {
|
|
164
|
+
const root = pkg.read(runtime.cwd);
|
|
165
|
+
const packages = [];
|
|
166
|
+
await pkg.walk([root], async (pkg2) => {
|
|
167
|
+
packages.push(pkg2);
|
|
168
|
+
});
|
|
169
|
+
const discovered = sortPackages(
|
|
170
|
+
uniquePackages(
|
|
171
|
+
applyWorkspaceFilters(packages, runtime.cwd, options.workspaces)
|
|
172
|
+
),
|
|
173
|
+
runtime.cwd
|
|
174
|
+
);
|
|
175
|
+
const mode = options.mode ?? DEFAULT_RELEASE_MODE;
|
|
176
|
+
const affectedPaths = mode === "hybrid" ? null : await detectAffectedPackages({
|
|
177
|
+
cwd: runtime.cwd,
|
|
178
|
+
root,
|
|
179
|
+
packages: discovered,
|
|
180
|
+
history: runtime.history,
|
|
181
|
+
range: {
|
|
182
|
+
fromTag: options.fromTag,
|
|
183
|
+
tagPrefix: options.tagPrefix
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
const affected = affectedPaths ? sortPackages(
|
|
187
|
+
uniquePackages(
|
|
188
|
+
discovered.filter((pkg2) => affectedPaths.has(pkg2.path))
|
|
189
|
+
),
|
|
190
|
+
runtime.cwd
|
|
191
|
+
) : [];
|
|
192
|
+
const slices = mode === "hybrid" ? await createHybridSlices({
|
|
193
|
+
root,
|
|
194
|
+
packages: discovered,
|
|
195
|
+
cwd: runtime.cwd,
|
|
196
|
+
history: runtime.history,
|
|
197
|
+
fromTag: options.fromTag,
|
|
198
|
+
tagPrefix: options.tagPrefix,
|
|
199
|
+
partitions: options.partitions
|
|
200
|
+
}) : createSlices({
|
|
201
|
+
mode,
|
|
202
|
+
affected,
|
|
203
|
+
cwd: runtime.cwd,
|
|
204
|
+
fromTag: options.fromTag,
|
|
205
|
+
tagPrefix: options.tagPrefix
|
|
206
|
+
});
|
|
207
|
+
const discoveredAffected = mode === "hybrid" ? sortPackages(
|
|
208
|
+
uniquePackages(
|
|
209
|
+
slices.flatMap((slice) => slice.packages)
|
|
210
|
+
),
|
|
211
|
+
runtime.cwd
|
|
212
|
+
) : affected;
|
|
213
|
+
return {
|
|
214
|
+
mode,
|
|
215
|
+
packages: discovered,
|
|
216
|
+
affected: discoveredAffected,
|
|
217
|
+
slices
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
function toScopePackage(pkg2, cwd) {
|
|
221
|
+
return {
|
|
222
|
+
name: pkg2.manifest.name ?? pkg2.name,
|
|
223
|
+
version: pkg2.manifest.version,
|
|
224
|
+
path: toPublicPath(pkg2, cwd)
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function toSlice(slice, cwd) {
|
|
228
|
+
return {
|
|
229
|
+
id: slice.id,
|
|
230
|
+
kind: slice.kind,
|
|
231
|
+
mode: slice.mode,
|
|
232
|
+
partition: slice.partition,
|
|
233
|
+
packages: slice.packages.map((pkg2) => toScopePackage(pkg2, cwd)),
|
|
234
|
+
range: slice.range
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
function uniquePackages(packages) {
|
|
238
|
+
const seen = /* @__PURE__ */ new Set();
|
|
239
|
+
return packages.filter((pkg2) => {
|
|
240
|
+
if (seen.has(pkg2.path)) {
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
seen.add(pkg2.path);
|
|
244
|
+
return true;
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
function uniqueStrings(values) {
|
|
248
|
+
return [...new Set(values)];
|
|
249
|
+
}
|
|
250
|
+
function packageIdentity(pkg2, cwd) {
|
|
251
|
+
const name = pkg2.manifest.name ?? pkg2.name;
|
|
252
|
+
if (name) {
|
|
253
|
+
return name;
|
|
254
|
+
}
|
|
255
|
+
const path = packagePath(pkg2, cwd);
|
|
256
|
+
return path === "" || path === "." ? "root" : path;
|
|
257
|
+
}
|
|
258
|
+
function createSlices({
|
|
259
|
+
mode,
|
|
260
|
+
affected,
|
|
261
|
+
cwd,
|
|
262
|
+
fromTag,
|
|
263
|
+
tagPrefix
|
|
264
|
+
}) {
|
|
265
|
+
const touched = affected;
|
|
266
|
+
if (mode === "sync") {
|
|
267
|
+
return touched.length ? [{
|
|
268
|
+
id: "sync:default",
|
|
269
|
+
kind: "sync",
|
|
270
|
+
mode: "sync",
|
|
271
|
+
packages: touched,
|
|
272
|
+
range: {
|
|
273
|
+
fromTag,
|
|
274
|
+
tagPrefix
|
|
275
|
+
}
|
|
276
|
+
}] : [];
|
|
277
|
+
}
|
|
278
|
+
return touched.map((pkg2) => ({
|
|
279
|
+
id: `async:${packageIdentity(pkg2, cwd)}`,
|
|
280
|
+
kind: "async",
|
|
281
|
+
mode: "async",
|
|
282
|
+
packages: [pkg2],
|
|
283
|
+
range: {
|
|
284
|
+
fromTag,
|
|
285
|
+
tagPrefix
|
|
286
|
+
}
|
|
287
|
+
}));
|
|
288
|
+
}
|
|
289
|
+
async function createHybridSlices({
|
|
290
|
+
root,
|
|
291
|
+
packages,
|
|
292
|
+
cwd,
|
|
293
|
+
history,
|
|
294
|
+
fromTag,
|
|
295
|
+
tagPrefix,
|
|
296
|
+
partitions
|
|
297
|
+
}) {
|
|
298
|
+
const partitionEntries = Object.entries(partitions ?? {}).sort(([a], [b]) => a.localeCompare(b));
|
|
299
|
+
const partitioned = [];
|
|
300
|
+
const usedPaths = /* @__PURE__ */ new Set();
|
|
301
|
+
for (const [partition, definition] of partitionEntries) {
|
|
302
|
+
const matched = sortPackages(
|
|
303
|
+
packages.filter((pkg2) => {
|
|
304
|
+
if (usedPaths.has(pkg2.path)) {
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
return definition.workspaces.some((selector) => matchesPackageSelector(pkg2, cwd, selector));
|
|
308
|
+
}),
|
|
309
|
+
cwd
|
|
310
|
+
);
|
|
311
|
+
for (const pkg2 of matched) {
|
|
312
|
+
usedPaths.add(pkg2.path);
|
|
313
|
+
}
|
|
314
|
+
const affectedPaths = await detectAffectedPackages({
|
|
315
|
+
cwd,
|
|
316
|
+
root,
|
|
317
|
+
packages: matched,
|
|
318
|
+
history,
|
|
319
|
+
range: resolvePartitionRange({ fromTag, tagPrefix, partition: definition })
|
|
320
|
+
});
|
|
321
|
+
const scoped = matched.filter((pkg2) => affectedPaths.has(pkg2.path));
|
|
322
|
+
if (!scoped.length) {
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
const resolvedRange = resolvePartitionRange({ fromTag, tagPrefix, partition: definition });
|
|
326
|
+
if (definition.mode === "sync") {
|
|
327
|
+
partitioned.push({
|
|
328
|
+
id: `partition:${partition}`,
|
|
329
|
+
kind: "partition",
|
|
330
|
+
mode: "sync",
|
|
331
|
+
partition,
|
|
332
|
+
packages: scoped,
|
|
333
|
+
range: resolvedRange
|
|
334
|
+
});
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
partitioned.push(...scoped.map((pkg2) => ({
|
|
338
|
+
id: `partition:${partition}:${packageIdentity(pkg2, cwd)}`,
|
|
339
|
+
kind: "partition",
|
|
340
|
+
mode: "async",
|
|
341
|
+
partition,
|
|
342
|
+
packages: [pkg2],
|
|
343
|
+
range: resolvedRange
|
|
344
|
+
})));
|
|
345
|
+
}
|
|
346
|
+
const fallbackCandidates = sortPackages(
|
|
347
|
+
packages.filter((pkg2) => !usedPaths.has(pkg2.path)),
|
|
348
|
+
cwd
|
|
349
|
+
);
|
|
350
|
+
const fallbackPaths = await detectAffectedPackages({
|
|
351
|
+
cwd,
|
|
352
|
+
root,
|
|
353
|
+
packages: fallbackCandidates,
|
|
354
|
+
history,
|
|
355
|
+
range: {
|
|
356
|
+
fromTag,
|
|
357
|
+
tagPrefix
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
const remainder = fallbackCandidates.filter((pkg2) => fallbackPaths.has(pkg2.path));
|
|
361
|
+
const fallback = remainder.map((pkg2) => ({
|
|
362
|
+
id: `hybrid:${packageIdentity(pkg2, cwd)}`,
|
|
363
|
+
kind: "async",
|
|
364
|
+
mode: "async",
|
|
365
|
+
packages: [pkg2],
|
|
366
|
+
range: {
|
|
367
|
+
fromTag,
|
|
368
|
+
tagPrefix
|
|
369
|
+
}
|
|
370
|
+
}));
|
|
371
|
+
return [...partitioned, ...fallback];
|
|
372
|
+
}
|
|
373
|
+
function resolvePartitionRange({
|
|
374
|
+
fromTag,
|
|
375
|
+
tagPrefix,
|
|
376
|
+
partition
|
|
377
|
+
}) {
|
|
378
|
+
return {
|
|
379
|
+
fromTag: partition.fromTag ?? fromTag,
|
|
380
|
+
tagPrefix: partition.tagPrefix ?? tagPrefix
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
async function detectAffectedPackages({
|
|
384
|
+
cwd,
|
|
385
|
+
root,
|
|
386
|
+
packages,
|
|
387
|
+
history,
|
|
388
|
+
range
|
|
389
|
+
}) {
|
|
390
|
+
const touched = await readCommitRangePaths(history, range);
|
|
391
|
+
if (touched === null) {
|
|
392
|
+
return new Set(packages.map((pkg2) => pkg2.path));
|
|
393
|
+
}
|
|
394
|
+
if (!touched.length) {
|
|
395
|
+
return /* @__PURE__ */ new Set();
|
|
396
|
+
}
|
|
397
|
+
const normalizedRoot = normalizePath(node_path.relative(cwd, root.path));
|
|
398
|
+
const children = packages.filter((pkg2) => pkg2.path !== root.path).map((pkg2) => ({
|
|
399
|
+
packagePath: pkg2.path,
|
|
400
|
+
relativePath: normalizePath(node_path.relative(cwd, pkg2.path))
|
|
401
|
+
})).sort((a, b) => b.relativePath.length - a.relativePath.length);
|
|
402
|
+
const affected = /* @__PURE__ */ new Set();
|
|
403
|
+
for (const path of touched.map(normalizePath)) {
|
|
404
|
+
if (!isPathInside(path, normalizedRoot)) continue;
|
|
405
|
+
const child = children.find((entry) => isPathInside(path, entry.relativePath));
|
|
406
|
+
affected.add(child?.packagePath ?? root.path);
|
|
407
|
+
}
|
|
408
|
+
return affected;
|
|
409
|
+
}
|
|
410
|
+
function applyWorkspaceFilters(packages, cwd, selector) {
|
|
411
|
+
if (!selector?.include?.length && !selector?.exclude?.length) {
|
|
412
|
+
return packages;
|
|
413
|
+
}
|
|
414
|
+
return packages.filter((pkg2) => {
|
|
415
|
+
const included = selector.include?.length ? selector.include.some((entry) => matchesPackageSelector(pkg2, cwd, entry)) : true;
|
|
416
|
+
if (!included) {
|
|
417
|
+
return false;
|
|
418
|
+
}
|
|
419
|
+
return !(selector.exclude?.some((entry) => matchesPackageSelector(pkg2, cwd, entry)) ?? false);
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
function sortPackages(packages, cwd) {
|
|
423
|
+
return [...packages].sort((a, b) => packagePath(a, cwd).localeCompare(packagePath(b, cwd)));
|
|
424
|
+
}
|
|
425
|
+
function packagePath(pkg2, cwd) {
|
|
426
|
+
return normalizePath(node_path.relative(cwd, pkg2.path));
|
|
427
|
+
}
|
|
428
|
+
function toPublicPath(pkg2, cwd) {
|
|
429
|
+
return packagePath(pkg2, cwd) || ".";
|
|
430
|
+
}
|
|
431
|
+
function matchesPackageSelector(pkg2, cwd, selector) {
|
|
432
|
+
const name = pkg2.manifest.name ?? pkg2.name ?? "";
|
|
433
|
+
const path = packagePath(pkg2, cwd);
|
|
434
|
+
return matchesGlob(name, selector) || matchesGlob(path, selector);
|
|
435
|
+
}
|
|
436
|
+
function matchesGlob(input, pattern) {
|
|
437
|
+
if (!pattern) return false;
|
|
438
|
+
if (pattern === "*") return true;
|
|
439
|
+
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "__DOUBLE_STAR__").replace(/\*/g, "[^/]*").replace(/__DOUBLE_STAR__/g, ".*");
|
|
440
|
+
return new RegExp(`^${escaped}$`).test(input);
|
|
441
|
+
}
|
|
442
|
+
async function readCommitRangePaths(history, range) {
|
|
443
|
+
try {
|
|
444
|
+
const result = await history.traverse({
|
|
445
|
+
fromTag: range.fromTag,
|
|
446
|
+
tagPrefix: range.tagPrefix,
|
|
447
|
+
changeset: true,
|
|
448
|
+
traversers: [conventionalGit.createChangesetTraverser()]
|
|
449
|
+
});
|
|
450
|
+
return result.results.get("changeset").paths;
|
|
451
|
+
} catch {
|
|
452
|
+
return null;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
function normalizePath(path) {
|
|
456
|
+
return path.replace(/\\/g, "/");
|
|
457
|
+
}
|
|
458
|
+
function isPathInside(path, root) {
|
|
459
|
+
return !root || root === "." || root === path || path.startsWith(`${root}/`);
|
|
460
|
+
}
|
|
461
|
+
async function runScope(runtime, scope, options, reporting) {
|
|
462
|
+
const slices = [];
|
|
463
|
+
for (const slice of scope.slices) {
|
|
464
|
+
if (reporting) {
|
|
465
|
+
await reportSliceStart(reporting.reporter, toSlice(slice, runtime.cwd), reporting.context);
|
|
466
|
+
}
|
|
467
|
+
const result = await executeSlice(runtime, slice, options);
|
|
468
|
+
if (reporting) {
|
|
469
|
+
await reportSliceSuccess(reporting.reporter, result, reporting.context);
|
|
470
|
+
}
|
|
471
|
+
slices.push(result);
|
|
472
|
+
}
|
|
473
|
+
const files = uniqueStrings(
|
|
474
|
+
slices.flatMap((slice) => slice.files)
|
|
475
|
+
);
|
|
476
|
+
const changed = slices.some((slice) => slice.changed);
|
|
477
|
+
return {
|
|
478
|
+
mode: scope.mode,
|
|
479
|
+
changed,
|
|
480
|
+
dry: runtime.dry,
|
|
481
|
+
packages: scope.packages.map((pkg2) => toScopePackage(pkg2, runtime.cwd)),
|
|
482
|
+
affected: scope.affected.map((pkg2) => toScopePackage(pkg2, runtime.cwd)),
|
|
483
|
+
slices,
|
|
484
|
+
files
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
async function executeSlice(runtime, slice, options) {
|
|
488
|
+
const normalizedSlice = {
|
|
489
|
+
...slice,
|
|
490
|
+
packages: uniquePackages(slice.packages)
|
|
491
|
+
};
|
|
492
|
+
const currentVersion = resolveSliceCurrentVersion(normalizedSlice, runtime.cwd);
|
|
493
|
+
const releaseMeta = await collectSliceMeta(runtime, normalizedSlice);
|
|
494
|
+
const release = conventionalBump.resolveNextVersion(currentVersion, {
|
|
495
|
+
recommendation: releaseMeta.recommendation,
|
|
496
|
+
type: options.releaseAs,
|
|
497
|
+
prerelease: options.prerelease
|
|
498
|
+
});
|
|
499
|
+
const releaseType = String(release.type);
|
|
500
|
+
const nextVersion = release.version;
|
|
501
|
+
const base = toSlice(normalizedSlice, runtime.cwd);
|
|
502
|
+
if (nextVersion === currentVersion) {
|
|
503
|
+
return {
|
|
504
|
+
...base,
|
|
505
|
+
currentVersion,
|
|
506
|
+
nextVersion,
|
|
507
|
+
releaseType,
|
|
508
|
+
changed: false,
|
|
509
|
+
dry: runtime.dry,
|
|
510
|
+
files: []
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
const changes = conventionalChangelog.renderChangelog(nextVersion, {
|
|
514
|
+
notes: releaseMeta.notes,
|
|
515
|
+
url: await runtime.history.url()
|
|
516
|
+
});
|
|
517
|
+
const files = await applySlice(runtime, normalizedSlice, nextVersion, options, changes);
|
|
518
|
+
const context = createTagContext(normalizedSlice, nextVersion, releaseType, runtime.cwd);
|
|
519
|
+
const tag = options.tagName ? options.tagName(context) : createDefaultTagName(normalizedSlice, nextVersion, runtime.cwd);
|
|
520
|
+
if (runtime.dry) {
|
|
521
|
+
return {
|
|
522
|
+
...base,
|
|
523
|
+
currentVersion,
|
|
524
|
+
nextVersion,
|
|
525
|
+
releaseType,
|
|
526
|
+
changed: true,
|
|
527
|
+
dry: true,
|
|
528
|
+
tag,
|
|
529
|
+
files
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
const messageContext = { ...context, tag };
|
|
533
|
+
const commitMessage = options.commitMessage ? options.commitMessage(messageContext) : DEFAULT_RELEASE_PREFIX + tag;
|
|
534
|
+
const tagMessage = options.tagMessage ? options.tagMessage(messageContext) : DEFAULT_RELEASE_PREFIX + tag;
|
|
535
|
+
await runtime.git.add(files);
|
|
536
|
+
await runtime.git.commit({ files, message: commitMessage });
|
|
537
|
+
await runtime.git.tag({ name: tag, message: tagMessage });
|
|
538
|
+
return {
|
|
539
|
+
...base,
|
|
540
|
+
currentVersion,
|
|
541
|
+
nextVersion,
|
|
542
|
+
releaseType,
|
|
543
|
+
changed: true,
|
|
544
|
+
dry: false,
|
|
545
|
+
files,
|
|
546
|
+
tag,
|
|
547
|
+
commitMessage,
|
|
548
|
+
tagMessage
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
async function applySlice(runtime, slice, nextVersion, options, changes) {
|
|
552
|
+
const bumpedPackageNames = slice.packages.reduce((all, pkg2) => {
|
|
553
|
+
const name = pkg2.manifest.name ?? pkg2.name;
|
|
554
|
+
if (name) {
|
|
555
|
+
all.add(name);
|
|
556
|
+
}
|
|
557
|
+
return all;
|
|
558
|
+
}, /* @__PURE__ */ new Set());
|
|
559
|
+
const dependencyPolicy = options.dependencyPolicy ?? DEFAULT_DEPENDENCY_POLICY_INTERNAL;
|
|
560
|
+
const files = [];
|
|
561
|
+
for (const pkg$1 of slice.packages) {
|
|
562
|
+
const diff = { version: nextVersion };
|
|
563
|
+
const manifest = pkg$1.manifest;
|
|
564
|
+
if (hasDependencies(manifest.peerDependencies)) {
|
|
565
|
+
diff.peerDependencies = actualizeDependencies(manifest.peerDependencies, nextVersion, bumpedPackageNames, dependencyPolicy);
|
|
566
|
+
}
|
|
567
|
+
if (hasDependencies(manifest.dependencies)) {
|
|
568
|
+
diff.dependencies = actualizeDependencies(manifest.dependencies, nextVersion, bumpedPackageNames, dependencyPolicy);
|
|
569
|
+
}
|
|
570
|
+
if (hasDependencies(manifest.optionalDependencies)) {
|
|
571
|
+
diff.optionalDependencies = actualizeDependencies(manifest.optionalDependencies, nextVersion, bumpedPackageNames, dependencyPolicy);
|
|
572
|
+
}
|
|
573
|
+
if (hasDependencies(manifest.devDependencies)) {
|
|
574
|
+
diff.devDependencies = actualizeDependencies(manifest.devDependencies, nextVersion, bumpedPackageNames, dependencyPolicy);
|
|
575
|
+
}
|
|
576
|
+
files.push(node_path.relative(runtime.cwd, pkg.update(pkg$1.path, diff, runtime.dry)));
|
|
577
|
+
}
|
|
578
|
+
const installArgs = resolveInstallArgs(runtime, options.install);
|
|
579
|
+
if (!runtime.dry && installArgs) {
|
|
580
|
+
await runtime.sh.exec(runtime.packageManager.command, ["install", ...installArgs]);
|
|
581
|
+
}
|
|
582
|
+
if (!runtime.dry) {
|
|
583
|
+
await runtime.writeChangelog(changes);
|
|
584
|
+
}
|
|
585
|
+
files.push(node_path.relative(runtime.cwd, node_path.join(runtime.cwd, runtime.packageManager.lockfile)));
|
|
586
|
+
files.push(node_path.relative(runtime.cwd, node_path.join(runtime.cwd, runtime.changelogFile)));
|
|
587
|
+
return uniqueStrings(files);
|
|
588
|
+
}
|
|
589
|
+
async function collectSliceMeta(runtime, slice) {
|
|
590
|
+
const result = await runtime.history.traverse({
|
|
591
|
+
...slice.range,
|
|
592
|
+
traversers: [conventionalBump.createRecommendationAnalyzer({
|
|
593
|
+
strict: true
|
|
594
|
+
}), conventionalChangelog.createChangelogCapacitor()]
|
|
595
|
+
});
|
|
596
|
+
return {
|
|
597
|
+
recommendation: result.results.get("recommendation"),
|
|
598
|
+
notes: result.results.get("changelog")
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
function resolveInstallArgs(runtime, install) {
|
|
602
|
+
if (install === false) {
|
|
603
|
+
return null;
|
|
604
|
+
}
|
|
605
|
+
if (Array.isArray(install)) {
|
|
606
|
+
return install;
|
|
607
|
+
}
|
|
608
|
+
if (runtime.packageManager.command === "yarn") {
|
|
609
|
+
return ["--no-immutable"];
|
|
610
|
+
}
|
|
611
|
+
return [];
|
|
612
|
+
}
|
|
613
|
+
function createTagContext(slice, version, releaseType, cwd) {
|
|
614
|
+
return {
|
|
615
|
+
id: slice.id,
|
|
616
|
+
kind: slice.kind,
|
|
617
|
+
mode: slice.mode,
|
|
618
|
+
partition: slice.partition,
|
|
619
|
+
packages: slice.packages.map((pkg2) => toScopePackage(pkg2, cwd)),
|
|
620
|
+
version,
|
|
621
|
+
releaseType
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
function resolveSliceCurrentVersion(slice, cwd) {
|
|
625
|
+
const versions = uniqueStrings(
|
|
626
|
+
slice.packages.map((pkg2) => pkg2.manifest.version ?? "0.0.0")
|
|
627
|
+
);
|
|
628
|
+
if (slice.mode === "sync" && versions.length > 1) {
|
|
629
|
+
const packageList = slice.packages.map((pkg2) => packageIdentity(pkg2, cwd)).join(", ");
|
|
630
|
+
throw new Error(`Sync release slice "${slice.id}" requires aligned package versions: ${packageList}`);
|
|
631
|
+
}
|
|
632
|
+
return versions[0];
|
|
633
|
+
}
|
|
634
|
+
function createDefaultTagName(slice, version, cwd) {
|
|
635
|
+
if (slice.kind === "sync") {
|
|
636
|
+
return `v${version}`;
|
|
637
|
+
}
|
|
638
|
+
if (slice.partition) {
|
|
639
|
+
return `${slice.partition}@${version}`;
|
|
640
|
+
}
|
|
641
|
+
const identity = packageIdentity(slice.packages[0], cwd);
|
|
642
|
+
return `${identity}@${version}`;
|
|
643
|
+
}
|
|
644
|
+
function hasDependencies(dependencies) {
|
|
645
|
+
return !!dependencies && Object.keys(dependencies).length > 0;
|
|
646
|
+
}
|
|
647
|
+
function actualizeDependencies(dependencies, nextVersion, bumpedPackages, internalPolicy) {
|
|
648
|
+
return Object.keys(dependencies).reduce((all, name) => ({
|
|
649
|
+
...all,
|
|
650
|
+
[name]: bumpedPackages.has(name) ? updateDependencyRange(dependencies[name], nextVersion, internalPolicy) : dependencies[name]
|
|
651
|
+
}), {});
|
|
652
|
+
}
|
|
653
|
+
function updateDependencyRange(current, nextVersion, policy) {
|
|
654
|
+
if (policy === "caret") return "^" + nextVersion;
|
|
655
|
+
if (policy === "exact") return nextVersion;
|
|
656
|
+
if (current.startsWith("workspace:")) {
|
|
657
|
+
const value = current.slice("workspace:".length);
|
|
658
|
+
if (value === "*") return "workspace:*";
|
|
659
|
+
if (value.startsWith("^")) return `workspace:^${nextVersion}`;
|
|
660
|
+
if (value.startsWith("~")) return `workspace:~${nextVersion}`;
|
|
661
|
+
return `workspace:${nextVersion}`;
|
|
662
|
+
}
|
|
663
|
+
if (current.startsWith("^")) return "^" + nextVersion;
|
|
664
|
+
if (current.startsWith("~")) return "~" + nextVersion;
|
|
665
|
+
return nextVersion;
|
|
666
|
+
}
|
|
667
|
+
async function run(options = {}) {
|
|
668
|
+
const inline = toInlineConfig(options);
|
|
669
|
+
const cwd = options.cwd ?? process.cwd();
|
|
670
|
+
const context = createRunContext({
|
|
671
|
+
cwd,
|
|
672
|
+
dry: options.dry ?? false
|
|
673
|
+
});
|
|
674
|
+
try {
|
|
675
|
+
await reportStart(options.reporter, context);
|
|
676
|
+
const config = await resolveConfig(cwd, inline);
|
|
677
|
+
const scopeOptions = toScopeOptions(config);
|
|
678
|
+
const runtime = createRuntime({
|
|
679
|
+
cwd,
|
|
680
|
+
dry: context.dry,
|
|
681
|
+
changelogFile: config.changelogFile ?? DEFAULT_CHANGELOG_FILE
|
|
682
|
+
});
|
|
683
|
+
const scope = await discover(runtime, scopeOptions);
|
|
684
|
+
const publicScope = toScope(scope, runtime.cwd);
|
|
685
|
+
await reportScope(options.reporter, publicScope, context);
|
|
686
|
+
const result = await runScope(runtime, scope, scopeOptions, options.reporter ? {
|
|
687
|
+
reporter: options.reporter,
|
|
688
|
+
context
|
|
689
|
+
} : void 0);
|
|
690
|
+
await reportSuccess(options.reporter, result, context);
|
|
691
|
+
return result;
|
|
692
|
+
} catch (error) {
|
|
693
|
+
await reportError(options.reporter, error, context);
|
|
694
|
+
throw error;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
async function createScope(options = {}) {
|
|
698
|
+
const inline = toInlineConfig(options);
|
|
699
|
+
const cwd = options.cwd ?? process.cwd();
|
|
700
|
+
const config = await resolveConfig(cwd, inline);
|
|
701
|
+
const scopeOptions = toScopeOptions(config);
|
|
702
|
+
const runtime = createRuntime({
|
|
703
|
+
cwd,
|
|
704
|
+
dry: options.dry ?? true,
|
|
705
|
+
changelogFile: config.changelogFile ?? DEFAULT_CHANGELOG_FILE
|
|
706
|
+
});
|
|
707
|
+
const scope = await discover(runtime, scopeOptions);
|
|
708
|
+
return toScope(scope, runtime.cwd);
|
|
709
|
+
}
|
|
710
|
+
exports.createScope = createScope;
|
|
711
|
+
exports.resolveConfig = resolveConfig;
|
|
712
|
+
exports.run = run;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Scope, Result, RunOptions } from '../types/index';
|
|
2
|
+
import { resolveConfig } from './config';
|
|
3
|
+
export type { Manifest, Mode, Options, Package, Partition, Range, Reporter, Result, RunContext, RunOptions, Scope, ScopePackage, Slice, SliceKind, SliceMode, SliceResult, TagContext, WorkspaceSelector, } from '../types/index';
|
|
4
|
+
export { resolveConfig, };
|
|
5
|
+
/** Resolves config, creates a runtime, and executes the release in one high-level call. */
|
|
6
|
+
export declare function run(options?: RunOptions): Promise<Result>;
|
|
7
|
+
/** Resolves config, creates a runtime, and returns a deterministic release scope. */
|
|
8
|
+
export declare function createScope(options?: RunOptions): Promise<Scope>;
|