@anolilab/multi-semantic-release 2.0.6 → 3.0.0-alpha.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.
@@ -1,291 +0,0 @@
1
- import { createRequire } from "node:module";
2
- import { dirname } from "node:path";
3
-
4
- import { topo } from "@semrel-extra/topo";
5
- // eslint-disable-next-line you-dont-need-lodash-underscore/cast-array
6
- import { castArray, sortBy, template } from "lodash-es";
7
- import semanticRelease from "semantic-release";
8
-
9
- import createInlinePluginCreator from "./create-inline-plugin-creator.js";
10
- import getConfig from "./get-config.js";
11
- import getConfigMultiSemrel from "./get-config-multi-semrel.js";
12
- import getConfigSemantic from "./get-config-semantic.js";
13
- import getManifest from "./get-manifest.js";
14
- import logger from "./logger.js";
15
- import RescopedStream from "./rescoped-stream.js";
16
- import { check } from "./utils/blork.js";
17
- import cleanPath from "./utils/clean-path.js";
18
-
19
- /**
20
- * Load details about a package.
21
- * @param {string} path The path to load details about.
22
- * @param {object} allOptions Options that apply to all packages.
23
- * @param {MultiContext} multiContext Context object for the multirelease.
24
- * @param allOptions.cwd
25
- * @param allOptions.env
26
- * @param allOptions.globalOptions
27
- * @param allOptions.inputOptions
28
- * @param allOptions.stderr
29
- * @param allOptions.stdout
30
- * @returns {Promise<Package|void>} A package object, or void if the package was skipped.
31
- * @internal
32
- */
33
- async function getPackage(path, { cwd, env, globalOptions, inputOptions, stderr, stdout }) {
34
- // Make path absolute.
35
- // eslint-disable-next-line no-param-reassign
36
- path = cleanPath(path, cwd);
37
-
38
- const directory = dirname(path);
39
-
40
- // Get package.json file contents.
41
- const manifest = getManifest(path);
42
- const { name } = manifest;
43
-
44
- // Combine list of all dependency names.
45
- const deps = Object.keys({
46
- ...manifest.dependencies,
47
- ...manifest.devDependencies,
48
- ...manifest.peerDependencies,
49
- ...manifest.optionalDependencies,
50
- });
51
-
52
- // Load the package-specific options.
53
- const packageOptions = await getConfig(directory);
54
-
55
- // The 'final options' are the global options merged with package-specific options.
56
- // We merge this ourselves because package-specific options can override global options.
57
- const finalOptions = { ...globalOptions, ...packageOptions, ...inputOptions };
58
-
59
- // Make a fake logger so semantic-release's get-config doesn't fail.
60
- const fakeLogger = { error() {}, log() {} };
61
-
62
- // Use semantic-release's internal config with the final options (now we have the right `options.plugins` setting) to get the plugins object and the options including defaults.
63
- // We need this so we can call e.g. plugins.analyzeCommit() to be able to affect the input and output of the whole set of plugins.
64
- const { options, plugins } = await getConfigSemantic({ cwd: directory, env, stderr, stdout }, finalOptions);
65
-
66
- // Return package object.
67
- return { deps, dir: directory, fakeLogger, manifest, name, options, path, plugins };
68
- }
69
-
70
- /**
71
- * Release an individual package.
72
- * @param {Package} pkg The specific package.
73
- * @param package_
74
- * @param {Function} createInlinePlugin A function that creates an inline plugin.
75
- * @param {MultiContext} multiContext Context object for the multirelease.
76
- * @param {object} flags Argv flags.
77
- * @returns {Promise<void>} Promise that resolves when done.
78
- * @internal
79
- */
80
- async function releasePackage(package_, createInlinePlugin, multiContext, flags) {
81
- // Vars.
82
- const { dir, name, options: packageOptions } = package_;
83
- const { env, stderr, stdout } = multiContext;
84
-
85
- // Make an 'inline plugin' for this package.
86
- // The inline plugin is the only plugin we call semanticRelease() with.
87
- // The inline plugin functions then call e.g. plugins.analyzeCommits() manually and sometimes manipulate the responses.
88
- const inlinePlugin = createInlinePlugin(package_);
89
-
90
- // Set the options that we call semanticRelease() with.
91
- // This consists of:
92
- // - The global options (e.g. from the top level package.json)
93
- // - The package options (e.g. from the specific package's package.json)
94
- const options = { ...packageOptions, ...inlinePlugin };
95
-
96
- // Add the package name into tagFormat.
97
- // Thought about doing a single release for the tag (merging several packages), but it's impossible to prevent Github releasing while allowing NPM to continue.
98
- // It'd also be difficult to merge all the assets into one release without full editing/overriding the plugins.
99
- const tagFormatContext = {
100
- name,
101
- // eslint-disable-next-line no-template-curly-in-string
102
- version: "${version}",
103
- };
104
-
105
- // eslint-disable-next-line no-template-curly-in-string
106
- const tagFormatDefault = "${name}@${version}";
107
-
108
- options.tagFormat = template(flags.tagFormat || tagFormatDefault)(tagFormatContext);
109
-
110
- // These are the only two options that MSR shares with semrel
111
- // Set them manually for now, defaulting to the msr versions
112
- // This is approach can be reviewed if there's ever more crossover.
113
- // - debug is only supported in semrel as a CLI arg, always default to MSR
114
- options.debug = flags.debug;
115
- // - dryRun should use the msr version if specified, otherwise fallback to semrel
116
- options.dryRun = flags.dryRun === undefined ? options.dryRun : flags.dryRun;
117
- options.ci = flags.ci === undefined ? options.ci : flags.ci;
118
- options.branches = flags.branches ? castArray(flags.branches) : options.branches;
119
-
120
- // This options are needed for plugins that do not rely on `pluginOptions` and extract them independently.
121
- options._pkgOptions = packageOptions;
122
-
123
- // Call semanticRelease() on the directory and save result to pkg.
124
- // Don't need to log out errors as semantic-release already does that.
125
- // eslint-disable-next-line no-param-reassign
126
- package_.result = await semanticRelease(options, {
127
- cwd: dir,
128
- env,
129
- stderr: new RescopedStream(stderr, name),
130
- stdout: new RescopedStream(stdout, name),
131
- });
132
-
133
- return package_;
134
- }
135
-
136
- /**
137
- * The multi-release context.
138
- * @typedef MultiContext
139
- * @param {Package[]} packages Array of all packages in this multirelease.
140
- * @param {Package[]} releasing Array of packages that will release.
141
- * @param {string} cwd The current working directory.
142
- * @param {object} env The environment variables.
143
- * @param {Logger} logger The logger for the multirelease.
144
- * @param {Stream} stdout The output stream for this multirelease.
145
- * @param {Stream} stderr The error stream for this multirelease.
146
- */
147
-
148
- /**
149
- * Details about an individual package in a multirelease
150
- * @typedef Package
151
- * @param {string} path String path to `package.json` for the package.
152
- * @param {string} dir The working directory for the package.
153
- * @param {string} name The name of the package, e.g. `my-amazing-package`
154
- * @param {string[]} deps Array of all dependency package names for the package (merging dependencies, devDependencies, peerDependencies).
155
- * @param {Package[]} localDeps Array of local dependencies this package relies on.
156
- * @param {context|void} context The semantic-release context for this package's release (filled in once semantic-release runs).
157
- * @param {undefined|Result|false} result The result of semantic-release (object with lastRelease, nextRelease, commits, releases), false if this package was skipped (no changes or similar), or undefined if the package's release hasn't completed yet.
158
- * @param {object} _lastRelease The last release object for the package before its current release (set during anaylze-commit)
159
- * @param {object} _nextRelease The next release object (the release the package is releasing for this cycle) (set during generateNotes)
160
- */
161
-
162
- /**
163
- * Perform a multirelease.
164
- * @param {string[]} paths An array of paths to package.json files.
165
- * @param {object} inputOptions An object containing semantic-release options.
166
- * @param {object} settings An object containing: cwd, env, stdout, stderr (mainly for configuring tests).
167
- * @param settings.cwd
168
- * @param settings.env
169
- * @param {object} _flags Argv flags.
170
- * @param settings.stderr
171
- * @param settings.stdout
172
- * @returns {Promise<Package[]>} Promise that resolves to a list of package objects with `result` property describing whether it released or not.
173
- */
174
-
175
- /**
176
- *
177
- * @param paths
178
- * @param inputOptions
179
- * @param root0
180
- * @param root0.cwd
181
- * @param root0.env
182
- * @param root0.stderr
183
- * @param root0.stdout
184
- * @param _flags
185
- */
186
- async function multiSemanticRelease(
187
- paths,
188
- inputOptions = {},
189
- { cwd = process.cwd(), env: environment = process.env, stderr = process.stderr, stdout = process.stdout } = {},
190
- _flags = {},
191
- ) {
192
- if (paths) {
193
- check(paths, "paths: string[]");
194
- }
195
-
196
- check(cwd, "cwd: directory");
197
- check(environment, "env: objectlike");
198
- check(stdout, "stdout: stream");
199
- check(stderr, "stderr: stream");
200
-
201
- // eslint-disable-next-line no-param-reassign
202
- cwd = cleanPath(cwd);
203
-
204
- const flags = {
205
- deps: {},
206
- ...await getConfigMultiSemrel(cwd, _flags),
207
- };
208
-
209
- const require = createRequire(import.meta.url);
210
- const multisemrelPackageJson = require("../package.json");
211
- const semrelPkgJson = require("semantic-release/package.json");
212
-
213
- // Setup logger.
214
- logger.config.stdio = [stderr, stdout];
215
- logger.config.level = flags.logLevel;
216
-
217
- if (flags.silent) {
218
- logger.config.level = "silent";
219
- }
220
-
221
- if (flags.debug) {
222
- logger.config.level = "debug";
223
- }
224
-
225
- logger.info(`multi-semantic-release version: ${multisemrelPackageJson.version}`);
226
- logger.info(`semantic-release version: ${semrelPkgJson.version}`);
227
-
228
- if (flags.debug) {
229
- logger.info(`flags: ${JSON.stringify(flags, null, 2)}`);
230
- }
231
-
232
- // Vars.
233
- const globalOptions = await getConfig(cwd);
234
- const multiContext = { cwd, env: environment, globalOptions, inputOptions, stderr, stdout };
235
- const { packages: _packages, queue } = await topo({
236
- cwd,
237
- filter: ({ manifest, manifestAbsPath, manifestRelPath }) =>
238
- (!flags.ignorePrivate || !manifest.private) && (paths ? paths.includes(manifestAbsPath) || paths.includes(manifestRelPath) : true),
239
- workspacesExtra: Array.isArray(flags.ignorePackages) ? flags.ignorePackages.map((p) => `!${p}`) : [],
240
- });
241
-
242
- // Get list of package.json paths according to workspaces.
243
- // eslint-disable-next-line no-param-reassign
244
- paths = paths || Object.values(_packages).map((package_) => package_.manifestPath);
245
-
246
- // Start.
247
- logger.complete(`Started multirelease! Loading ${paths.length} packages...`);
248
-
249
- // Load packages from paths.
250
- // eslint-disable-next-line compat/compat
251
- const packages = await Promise.all(paths.map((path) => getPackage(path, multiContext)));
252
-
253
- packages.forEach((package_) => {
254
- // Once we load all the packages we can find their cross refs
255
- // Make a list of local dependencies.
256
- // Map dependency names (e.g. my-awesome-dep) to their actual package objects in the packages array.
257
- // eslint-disable-next-line no-param-reassign
258
- package_.localDeps = [...new Set(package_.deps.map((d) => packages.find((p) => d === p.name)).filter(Boolean))];
259
-
260
- logger.success(`Loaded package ${package_.name}`);
261
- });
262
-
263
- logger.complete(`Queued ${queue.length} packages! Starting release...`);
264
-
265
- // Release all packages.
266
- const createInlinePlugin = createInlinePluginCreator(packages, multiContext, flags);
267
- // eslint-disable-next-line unicorn/no-array-reduce
268
- const released = await queue.reduce(async (_m, _name) => {
269
- const m = await _m;
270
- const package_ = packages.find(({ name }) => name === _name);
271
-
272
- if (package_) {
273
- const { result } = await releasePackage(package_, createInlinePlugin, multiContext, flags);
274
-
275
- if (result) {
276
- return m + 1;
277
- }
278
- }
279
-
280
- return m;
281
- // eslint-disable-next-line compat/compat
282
- }, Promise.resolve(0));
283
-
284
- // Return packages list.
285
- logger.complete(`Released ${released} of ${queue.length} packages, semantically!`);
286
-
287
- return sortBy(packages, ({ name }) => queue.indexOf(name));
288
- }
289
-
290
- // Exports.
291
- export default multiSemanticRelease;
@@ -1,31 +0,0 @@
1
- import { Writable } from "node:stream";
2
-
3
- import { check } from "./utils/blork.js";
4
-
5
- /**
6
- * Create a stream that passes messages through while rewriting scope.
7
- * Replaces `[semantic-release]` with a custom scope (e.g. `[my-awesome-package]`) so output makes more sense.
8
- * @param {stream.Writable} stream The actual stream to write messages to.
9
- * @param {string} scope The string scope for the stream (instances of the text `[semantic-release]` are replaced in the stream).
10
- * @returns {stream.Writable} Object that's compatible with stream.Writable (implements a `write()` property).
11
- * @internal
12
- */
13
- class RescopedStream extends Writable {
14
- // Constructor.
15
- constructor(stream, scope) {
16
- super();
17
- check(scope, "scope: string");
18
- check(stream, "stream: stream");
19
- this._stream = stream;
20
- this._scope = scope;
21
- }
22
-
23
- // Custom write method.
24
- write(message) {
25
- check(message, "msg: string");
26
- this._stream.write(message.replace("[semantic-release]", `[${this._scope}]`));
27
- }
28
- }
29
-
30
- // Exports.
31
- export default RescopedStream;
@@ -1,351 +0,0 @@
1
- import { writeFileSync } from "node:fs";
2
-
3
- import { isEqual, isObject, transform } from "lodash-es";
4
- import semver from "semver";
5
-
6
- import getManifest from "./get-manifest.js";
7
- import logger from "./logger.js";
8
- import { getHighestVersion, getLatestVersion } from "./utils/get-version.js";
9
- import recognizeFormat from "./utils/recognize-format.js";
10
-
11
- const { debug } = logger.withScope("msr:updateDeps");
12
-
13
- /**
14
- * Resolve next prerelease comparing bumped tags versions with last version.
15
- * @param {string|null} latestTag Last released tag from branch or null if non-existent.
16
- * @param {string} lastVersion Last version released.
17
- * @param {string} packagePreRelease Prerelease tag from package to-be-released.
18
- * @returns {string} Next pkg version.
19
- * @internal
20
- */
21
- const _nextPreHighestVersion = (latestTag, lastVersion, packagePreRelease) => {
22
- const bumpFromTags = latestTag ? semver.inc(latestTag, "prerelease", packagePreRelease) : null;
23
- const bumpFromLast = semver.inc(lastVersion, "prerelease", packagePreRelease);
24
-
25
- return bumpFromTags ? getHighestVersion(bumpFromLast, bumpFromTags) : bumpFromLast;
26
- };
27
-
28
- /**
29
- * Resolve next prerelease special cases: highest version from tags or major/minor/patch.#
30
- * @param {Array<string>} tags - if non-empty, we will use these tags as part fo the comparison
31
- * @param {string} lastVersionForCurrentMultiRelease Last package version released from multi-semantic-release
32
- * @param {string} packageNextType Next type evaluated for the next package type.
33
- * @param {string} packagePreRelease Package prerelease suffix.
34
- * @returns {string|undefined} Next pkg version.
35
- * @internal
36
- */
37
- const _nextPreVersionCases = (tags, lastVersionForCurrentMultiRelease, packageNextType, packagePreRelease) => {
38
- // Case 1: Normal release on last version and is now converted to a prerelease
39
- if (!semver.prerelease(lastVersionForCurrentMultiRelease)) {
40
- const { major, minor, patch } = semver.parse(lastVersionForCurrentMultiRelease);
41
-
42
- return `${semver.inc(`${major}.${minor}.${patch}`, packageNextType || "patch")}-${packagePreRelease}.1`;
43
- }
44
-
45
- // Case 2: Validates version with tags
46
- const latestTag = getLatestVersion(tags, true);
47
-
48
- return _nextPreHighestVersion(latestTag, lastVersionForCurrentMultiRelease, packagePreRelease);
49
- };
50
-
51
- /**
52
- * Get dependent release type by recursive scanning and updating pkg deps.
53
- * @param {Package} package_ The package with local deps to check.
54
- * @param {string} bumpStrategy Dependency resolution strategy: override, satisfy, inherit.
55
- * @param {string} releaseStrategy Release type triggered by deps updating: patch, minor, major, inherit.
56
- * @param {Package[]} ignore Packages to ignore (to prevent infinite loops).
57
- * @param {string} prefix Dependency version prefix to be attached if `bumpStrategy='override'`. ^ | ~ | '' (defaults to empty string)
58
- * @returns {string|undefined} Returns the highest release type if found, undefined otherwise
59
- * @internal
60
- */
61
- const getDependentRelease = (package_, bumpStrategy, releaseStrategy, ignore, prefix) => {
62
- const severityOrder = ["patch", "minor", "major"];
63
- const { localDeps, manifest = {} } = package_;
64
- const lastVersion = package_._lastRelease && package_._lastRelease.version;
65
- const { dependencies = {}, devDependencies = {}, optionalDependencies = {}, peerDependencies = {} } = manifest;
66
- const scopes = [dependencies, devDependencies, peerDependencies, optionalDependencies];
67
- const bumpDependency = (scope, name, nextVersion) => {
68
- const currentVersion = scope[name];
69
-
70
- if (!nextVersion || !currentVersion) {
71
- return false;
72
- }
73
-
74
- // eslint-disable-next-line no-use-before-define
75
- const resolvedVersion = resolveNextVersion(currentVersion, nextVersion, bumpStrategy, prefix);
76
-
77
- if (currentVersion !== resolvedVersion) {
78
- // eslint-disable-next-line no-param-reassign
79
- scope[name] = resolvedVersion;
80
-
81
- return true;
82
- }
83
-
84
- return false;
85
- };
86
-
87
- return (
88
- localDeps
89
- .filter((p) => !ignore.includes(p))
90
- // eslint-disable-next-line unicorn/no-array-reduce
91
- .reduce((releaseType, p) => {
92
- // Has changed if...
93
- // 1. Any local dep package itself has changed
94
- // 2. Any local dep package has local deps that have changed.
95
- // eslint-disable-next-line no-use-before-define
96
- const nextType = resolveReleaseType(p, bumpStrategy, releaseStrategy, [...ignore, package_], prefix);
97
- const nextVersion = nextType
98
- ? // Update the nextVersion only if there is a next type to be bumped
99
-
100
- p._preRelease
101
- ? // eslint-disable-next-line no-use-before-define
102
- getNextPreVersion(p)
103
- : // eslint-disable-next-line no-use-before-define
104
- getNextVersion(p)
105
- : // Set the nextVersion fallback to the last local dependency package last version
106
- p._lastRelease && p._lastRelease.version;
107
-
108
- // 3. And this change should correspond to the manifest updating rule.
109
- const requireRelease = scopes
110
- // eslint-disable-next-line unicorn/no-array-reduce
111
- .reduce((result, scope) => bumpDependency(scope, p.name, nextVersion) || result, !lastVersion);
112
-
113
- return requireRelease && severityOrder.indexOf(nextType) > severityOrder.indexOf(releaseType) ? nextType : releaseType;
114
- }, undefined)
115
- );
116
- };
117
-
118
- /**
119
- * Substitute "workspace:" in currentVersion
120
- * See:
121
- * {@link https://yarnpkg.com/features/workspaces#publishing-workspaces}
122
- * {@link https://pnpm.io/workspaces#publishing-workspace-packages}
123
- * @param {string} currentVersion Current version, may start with "workspace:"
124
- * @param {string} nextVersion Next version
125
- * @returns {string} current version without "workspace:"
126
- */
127
- const substituteWorkspaceVersion = (currentVersion, nextVersion) => {
128
- if (currentVersion.startsWith("workspace:")) {
129
- // eslint-disable-next-line regexp/optimal-quantifier-concatenation
130
- const [, range, caret] = /^workspace:(([\^~*])?.*)$/u.exec(currentVersion);
131
-
132
- return caret === range ? caret === "*" ? nextVersion : caret + nextVersion : range;
133
- }
134
-
135
- return currentVersion;
136
- };
137
-
138
- // eslint-disable-next-line no-secrets/no-secrets
139
- // https://gist.github.com/Yimiprod/7ee176597fef230d1451
140
- const difference = (object, base) =>
141
- transform(object, (result, value, key) => {
142
- if (!isEqual(value, base[key])) {
143
- // eslint-disable-next-line no-param-reassign
144
- result[key] = isObject(value) && isObject(base[key]) ? difference(value, base[key]) : `${base[key]} → ${value}`;
145
- }
146
- });
147
-
148
- /**
149
- * Clarify what exactly was changed in manifest file.
150
- * @param {object} actualManifest manifest object
151
- * @param {string} path manifest path
152
- * @returns {boolean} has changed or not
153
- * @internal
154
- */
155
- const auditManifestChanges = (actualManifest, path) => {
156
- const debugPrefix = `[${actualManifest.name}]`;
157
- const oldManifest = getManifest(path);
158
- const depScopes = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"];
159
- // eslint-disable-next-line unicorn/no-array-reduce
160
- const changes = depScopes.reduce((result, scope) => {
161
- const diff = difference(actualManifest[scope], oldManifest[scope]);
162
-
163
- if (Object.keys(diff).length > 0) {
164
- // eslint-disable-next-line no-param-reassign
165
- result[scope] = diff;
166
- }
167
-
168
- return result;
169
- }, {});
170
-
171
- debug(debugPrefix, "package.json path=", path);
172
-
173
- if (Object.keys(changes).length > 0) {
174
- debug(debugPrefix, "changes=", changes);
175
-
176
- return true;
177
- }
178
-
179
- debug(debugPrefix, "no deps changes");
180
-
181
- return false;
182
- };
183
-
184
- /**
185
- * Resolve next package version.
186
- * @param {Package} package_ Package object.
187
- * @returns {string|undefined} Next pkg version.
188
- * @internal
189
- */
190
- export const getNextVersion = (package_) => {
191
- const lastVersion = package_._lastRelease && package_._lastRelease.version;
192
-
193
- return lastVersion && typeof package_._nextType === "string" ? semver.inc(lastVersion, package_._nextType) : lastVersion || "1.0.0";
194
- };
195
-
196
- /**
197
- * Parse the prerelease tag from a semver version.
198
- * @param {string} version Semver version in a string format.
199
- * @returns {string|null} preReleaseTag Version prerelease tag or null.
200
- * @internal
201
- */
202
- export const getPreReleaseTag = (version) => {
203
- const parsed = semver.parse(version);
204
-
205
- if (!parsed) {
206
- return null;
207
- }
208
-
209
- return parsed.prerelease[0] || null;
210
- };
211
-
212
- /**
213
- * Resolve next package version on prereleases.
214
- *
215
- * Will resolve highest next version of either:
216
- *
217
- * 1. The last release for the package during this multi-release cycle
218
- * 2. (if tag options provided):
219
- * a. the highest increment of the tags array provided
220
- * b. the highest increment of the gitTags for the prerelease
221
- * @param {Package} package_ Package object.
222
- * @returns {string|undefined} Next pkg version.
223
- * @internal
224
- */
225
- export const getNextPreVersion = (package_) => {
226
- // Note: this is only set is a current multi-semantic-release released
227
- const lastVersionForCurrentRelease = package_._lastRelease && package_._lastRelease.version;
228
-
229
- const lastPreReleaseTag = getPreReleaseTag(lastVersionForCurrentRelease);
230
- const isNewPreReleaseTag = lastPreReleaseTag && lastPreReleaseTag !== package_._preRelease;
231
-
232
- return isNewPreReleaseTag || !lastVersionForCurrentRelease
233
- ? `1.0.0-${package_._preRelease}.1`
234
- : _nextPreVersionCases([], lastVersionForCurrentRelease, package_._nextType, package_._preRelease);
235
- };
236
-
237
- /**
238
- * Resolve package release type taking into account the cascading dependency update.
239
- * @param {Package} package_ Package object.
240
- * @param {string|undefined} bumpStrategy Dependency resolution strategy: override, satisfy, inherit.
241
- * @param {string|undefined} releaseStrategy Release type triggered by deps updating: patch, minor, major, inherit.
242
- * @param {Package[]} ignore Packages to ignore (to prevent infinite loops).
243
- * @param {string} prefix Dependency version prefix to be attached if `bumpStrategy='override'`. ^ | ~ | '' (defaults to empty string)
244
- * @returns {string|undefined} Resolved release type.
245
- * @internal
246
- */
247
- export const resolveReleaseType = (package_, bumpStrategy = "override", releaseStrategy = "patch", ignore = [], prefix = "") => {
248
- // NOTE This fn also updates pkg deps, so it must be invoked anyway.
249
- const dependentReleaseType = getDependentRelease(package_, bumpStrategy, releaseStrategy, ignore, prefix);
250
-
251
- // Release type found by commitAnalyzer.
252
- if (package_._nextType) {
253
- return package_._nextType;
254
- }
255
-
256
- if (!dependentReleaseType) {
257
- return undefined;
258
- }
259
-
260
- // Define release type for dependent package if any of its deps changes.
261
- // `patch`, `minor`, `major` — strictly declare the release type that occurs when any dependency is updated.
262
- // `inherit` — applies the "highest" release of updated deps to the package.
263
- // For example, if any dep has a breaking change, `major` release will be applied to the all dependants up the chain.
264
-
265
- // eslint-disable-next-line no-param-reassign
266
- package_._nextType = releaseStrategy === "inherit" ? dependentReleaseType : releaseStrategy;
267
-
268
- return package_._nextType;
269
- };
270
-
271
- /**
272
- * Resolve next version of dependency.
273
- * @param {string} currentVersion Current dep version
274
- * @param {string} nextVersion Next release type: patch, minor, major
275
- * @param {string|undefined} bumpStrategy Resolution strategy: inherit, override, satisfy
276
- * @param {string} prefix Dependency version prefix to be attached if `bumpStrategy='override'`. ^ | ~ | '' (defaults to empty string)
277
- * @returns {string} Next dependency version
278
- * @internal
279
- */
280
- export const resolveNextVersion = (currentVersion, nextVersion, bumpStrategy = "override", prefix = "") => {
281
- // handle cases of "workspace protocol" defined in yarn and pnpm workspace, whose version starts with "workspace:"
282
- // eslint-disable-next-line no-param-reassign
283
- currentVersion = substituteWorkspaceVersion(currentVersion, nextVersion);
284
-
285
- // if strategy is ignore, return the current version
286
- if (bumpStrategy === "ignore") {
287
- return currentVersion;
288
- }
289
-
290
- // no change...
291
- if (currentVersion === nextVersion) {
292
- return currentVersion;
293
- }
294
-
295
- // Check the next pkg version against its current references.
296
- // If it matches (`*` matches to any, `1.1.0` matches `1.1.x`, `1.5.0` matches to `^1.0.0` and so on)
297
- // release will not be triggered, if not `override` strategy will be applied instead.
298
- if ((bumpStrategy === "satisfy" || bumpStrategy === "inherit") && semver.satisfies(nextVersion, currentVersion)) {
299
- return currentVersion;
300
- }
301
-
302
- // `inherit` will try to follow the current declaration version/range.
303
- // `~1.0.0` + `minor` turns into `~1.1.0`, `1.x` + `major` gives `2.x`,
304
- // but `1.x` + `minor` gives `1.x` so there will be no release, etc.
305
- if (bumpStrategy === "inherit") {
306
- const separator = ".";
307
- const nextChunks = nextVersion.split(separator);
308
- const currentChunks = currentVersion.split(separator);
309
- const resolvedChunks = currentChunks.map((chunk, index) => (nextChunks[index] ? chunk.replace(/\d+/u, nextChunks[index]) : chunk));
310
-
311
- return resolvedChunks.join(separator);
312
- }
313
-
314
- // "override"
315
- // By default next package version would be set as is for the all dependants.
316
- return prefix + nextVersion;
317
- };
318
-
319
- /**
320
- * Update pkg deps.
321
- * @param {Package} package_ The package this function is being called on.
322
- * @returns {void}
323
- * @internal
324
- */
325
- export const updateManifestDeps = (package_) => {
326
- const { manifest, path } = package_;
327
- const { indent, trailingWhitespace } = recognizeFormat(manifest.__contents__);
328
-
329
- // We need to bump pkg.version for correct yarn.lock update
330
- // https://github.com/qiwi/multi-semantic-release/issues/58
331
- manifest.version = package_._nextRelease.version || manifest.version;
332
-
333
- // Loop through localDeps to verify release consistency.
334
- package_.localDeps.forEach((d) => {
335
- // Get version of dependency.
336
- const release = d._nextRelease || d._lastRelease;
337
-
338
- // Cannot establish version.
339
- if (!release || !release.version) {
340
- throw new Error(`Cannot release ${package_.name} because dependency ${d.name} has not been released yet`);
341
- }
342
- });
343
-
344
- if (!auditManifestChanges(manifest, path)) {
345
- return;
346
- }
347
-
348
- // Write package.json back out.
349
- // eslint-disable-next-line security/detect-non-literal-fs-filename
350
- writeFileSync(path, JSON.stringify(manifest, null, indent) + trailingWhitespace);
351
- };