@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/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;
@@ -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>;