@exadev/semantic-release-workspace 1.3.5 → 1.3.6

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.js CHANGED
@@ -4,7 +4,6 @@ import { dirname, relative, resolve, sep } from "node:path";
4
4
  import { glob } from "tinyglobby";
5
5
  import { parse } from "yaml";
6
6
  import { execFile } from "node:child_process";
7
- import { promisify } from "node:util";
8
7
  import validateNpmPackageName from "validate-npm-package-name";
9
8
  import { analyzeCommits } from "@semantic-release/commit-analyzer";
10
9
  import { generateNotes } from "@semantic-release/release-notes-generator";
@@ -45,7 +44,7 @@ var ReleaseConfigurationError = class extends WorkspaceReleaseError {};
45
44
  var GitCommandError = class extends WorkspaceReleaseError {
46
45
  exitCode;
47
46
  constructor(args, cwd, exitCode, detail) {
48
- super(`git ${args.join(" ")} failed in ${cwd}${exitCode === void 0 ? "" : ` (exit ${exitCode})`}: ${detail}`);
47
+ super(`git ${args.join(" ")} failed in ${cwd}${exitCode === void 0 ? "" : ` (exit ${String(exitCode)})`}: ${detail}`);
49
48
  this.exitCode = exitCode;
50
49
  }
51
50
  };
@@ -58,10 +57,30 @@ var PnpmCommandError = class extends WorkspaceReleaseError {
58
57
  }
59
58
  };
60
59
  //#endregion
60
+ //#region src/exec-file.ts
61
+ /**
62
+ * A hand-written promise wrapper around `child_process.execFile`, in place of `util.promisify(execFile)`: `execFile` synchronously returns a `ChildProcess` in addition to invoking its callback, which trips `@typescript-eslint/strict-void-return` when the whole function is handed to `promisify` (a value-returning function used where a void-returning one is contextually expected there) -- exactly the void-return contravariance leniency that rule exists to catch, even though `tsc` itself accepts the pattern. Calling `execFile` directly with our own callback, whose own return type really is `void`, sidesteps the mismatch instead of suppressing it.
63
+ */
64
+ async function execFile$1(command, args, options) {
65
+ return new Promise((resolve, reject) => {
66
+ execFile(command, [...args], {
67
+ cwd: options.cwd,
68
+ env: options.env,
69
+ maxBuffer: options.maxBuffer
70
+ }, (error, stdout, stderr) => {
71
+ if (error) {
72
+ reject(error instanceof Error ? error : new Error(error.message));
73
+ return;
74
+ }
75
+ resolve({
76
+ stdout,
77
+ stderr
78
+ });
79
+ });
80
+ });
81
+ }
82
+ //#endregion
61
83
  //#region src/git.ts
62
- const execFileAsync$1 = promisify(execFile);
63
- /** `git log --name-only` over everything since a package's last release tag can legitimately produce tens of megabytes of path output on a long-lived monorepo, well past execFile's default buffer, failing on exactly the big workspaces this tool exists for. */
64
- const GIT_MAX_BUFFER_BYTES = 104857600;
65
84
  /** Separates one commit's record in `git log --format` output. Chosen from the C0 control range so it can never appear in a hash or a file path. */
66
85
  const COMMIT_RECORD_SEPARATOR = "";
67
86
  /** The identity semantic-release's own core writes release commits under in CI when nothing else is configured (its COMMIT_NAME/COMMIT_EMAIL constants); dependency-bump commits use the same fallback so every commit a release run produces has a consistent author when the repository declares none. */
@@ -89,18 +108,22 @@ const GIT_REPOSITORY_DISCOVERY_ENV_KEYS = [
89
108
  */
90
109
  function sanitizeGitEnv(env) {
91
110
  const sanitized = { ...env };
92
- for (const key of GIT_REPOSITORY_DISCOVERY_ENV_KEYS) delete sanitized[key];
111
+ for (const key of GIT_REPOSITORY_DISCOVERY_ENV_KEYS) Reflect.deleteProperty(sanitized, key);
93
112
  return sanitized;
94
113
  }
95
114
  const SANITIZED_PROCESS_GIT_ENV = sanitizeGitEnv(process.env);
115
+ /** `git log --name-only` over everything since a package's last release tag can legitimately produce tens of megabytes of path output on a long-lived monorepo, well past execFile's default buffer, failing on exactly the big workspaces this tool exists for -- hence the generous 100 MiB default, rather than execFile's own. */
116
+ async function execGit(args, cwd, maxBuffer = 104857600) {
117
+ const { stdout } = await execFile$1("git", args, {
118
+ cwd,
119
+ maxBuffer,
120
+ env: SANITIZED_PROCESS_GIT_ENV
121
+ });
122
+ return stdout;
123
+ }
96
124
  async function git(args, options) {
97
125
  try {
98
- const { stdout } = await execFileAsync$1("git", [...args], {
99
- cwd: options.cwd,
100
- maxBuffer: GIT_MAX_BUFFER_BYTES,
101
- env: SANITIZED_PROCESS_GIT_ENV
102
- });
103
- return stdout;
126
+ return await execGit(args, options.cwd);
104
127
  } catch (cause) {
105
128
  throw toGitCommandError(args, options.cwd, cause);
106
129
  }
@@ -214,9 +237,11 @@ async function workingTreeChanges(options) {
214
237
  const paths = [];
215
238
  for (let index = 0; index < tokens.length; index += 1) {
216
239
  const entry = tokens[index];
217
- if (entry === void 0 || entry.length < 4) continue;
240
+ if (entry === void 0) continue;
218
241
  const statusCode = entry.slice(0, 2);
219
- paths.push(entry.slice(3));
242
+ const path = entry.slice(3);
243
+ if (path === "") continue;
244
+ paths.push(path);
220
245
  if (statusCode.includes("R") || statusCode.includes("C")) index += 1;
221
246
  }
222
247
  return paths;
@@ -281,7 +306,7 @@ async function readManifest(path) {
281
306
  const { name, version } = parsed;
282
307
  if (typeof name !== "string" || name.length === 0) throw new WorkspaceDiscoveryError(`${path} has no "name". Every workspace package needs a name: releases are ordered, tagged, and matched to dependents by it.`);
283
308
  const validity = validateNpmPackageName(name);
284
- if (!validity.validForOldPackages) throw new WorkspaceDiscoveryError(`${path} has an invalid "name" ("${name}"): ${(validity.errors ?? []).join("; ")}`);
309
+ if (!validity.validForOldPackages) throw new WorkspaceDiscoveryError(`${path} has an invalid "name" ("${name}"): ${validity.errors.join("; ")}`);
285
310
  if (typeof version !== "string" || version.length === 0) throw new WorkspaceDiscoveryError(`${path} has no "version".`);
286
311
  const dependencies = /* @__PURE__ */ new Map();
287
312
  for (const field of DEPENDENCY_FIELDS) {
@@ -584,6 +609,10 @@ function matchTrailerLine(message, key) {
584
609
  }
585
610
  //#endregion
586
611
  //#region src/plugins.ts
612
+ /** semantic-release's own `getLastRelease` returns `{}` for a package with no prior tag -- not `undefined`, and not a fully-populated `LastRelease` -- contradicting the `gitHead: string` its own type declares. Narrows structurally rather than trusting that declared type, so a first-release context's `lastRelease` (correctly, at runtime) never claims a `gitHead` it does not have. */
613
+ function hasGitHead(lastRelease) {
614
+ return typeof lastRelease === "object" && lastRelease !== null && "gitHead" in lastRelease && typeof lastRelease.gitHead === "string";
615
+ }
587
616
  /** The standard publish pipeline this orchestrator coordinates when a workspace configures none of its own. Every entry reuses the corresponding official plugin -- the orchestrator scopes and sequences them per package, it does not reimplement npm publishing, GitHub release creation, or changelog writing. */
588
617
  const DEFAULT_PUBLISH_PLUGINS = [
589
618
  "@semantic-release/changelog",
@@ -594,7 +623,7 @@ const DEFAULT_PUBLISH_PLUGINS = [
594
623
  message: "chore(release): ${nextRelease.gitTag} [skip ci]"
595
624
  }]
596
625
  ];
597
- /** The standard publish pipeline for `commitStrategy: 'single'`: the same as `DEFAULT_PUBLISH_PLUGINS` minus @semantic-release/git, which that mode never runs -- see `resolvePublishPlugins`'s `forbidGitPlugin` option for why it is rejected outright rather than merely unused. Single-commit mode does its own committing (one combined commit for every released package), so a `prepare`-step git plugin here would create the very per-package commits that mode exists to avoid. */
626
+ /** The standard publish pipeline for `commitStrategy: 'single'`: the same as `DEFAULT_PUBLISH_PLUGINS` minus `@semantic-release/git`, which that mode never runs -- see `resolvePublishPlugins`'s `forbidGitPlugin` option for why it is rejected outright rather than merely unused. Single-commit mode does its own committing (one combined commit for every released package), so a `prepare`-step git plugin here would create the very per-package commits that mode exists to avoid. */
598
627
  const SINGLE_COMMIT_DEFAULT_PUBLISH_PLUGINS = [
599
628
  "@semantic-release/changelog",
600
629
  "@semantic-release/npm",
@@ -604,14 +633,14 @@ const STEP_PLUGINS_THE_ORCHESTRATOR_OWNS = /* @__PURE__ */ new Set(["@semantic-r
604
633
  /**
605
634
  * Builds the per-package `analyzeCommits` and `generateNotes` functions handed to semantic-release as inline plugins.
606
635
  *
607
- * Both apply the same path scoping before delegating to the real @semantic-release/commit-analyzer and @semantic-release/release-notes-generator: the commit list semantic-release already fetched for the release range is filtered down to commits whose `git log --name-only` file list intersects the package's own directory, and only the filtered list reaches the standard plugin. Conventional-commit parsing and changelog formatting stay entirely inside the standard plugins.
636
+ * Both apply the same path scoping before delegating to the real `@semantic-release/commit-analyzer` and `@semantic-release/release-notes-generator`: the commit list semantic-release already fetched for the release range is filtered down to commits whose `git log --name-only` file list intersects the package's own directory, and only the filtered list reaches the standard plugin. Conventional-commit parsing and changelog formatting stay entirely inside the standard plugins.
608
637
  *
609
638
  * The `analyzeCommits` wrapper carries one addition beyond filtering: when the standard analyzer finds no releasable commits but a workspace dependency range of the package's has changed, it returns 'patch' anyway. A dependent whose only change is a dependency bump still needs a release for that range to reach the registry. "Has changed" is read from two sources, merged: bumps recorded in memory earlier in the current run (`scope.bumps`), and bumps recorded in the package's own filtered commit history via the trailer `dependency-bump-commit.ts` writes and reads -- the latter is what lets a run that starts after a previous run already committed and pushed the bump (a crash recovery, or simply a later run) reach the same decision, rather than depending on state that existed only inside the process that made the commit.
610
639
  */
611
640
  function createScopedPlugins(scope) {
612
641
  let cached;
613
642
  async function commitsForPackage(context) {
614
- const from = context.lastRelease?.gitHead ?? void 0;
643
+ const from = hasGitHead(context.lastRelease) ? context.lastRelease.gitHead : void 0;
615
644
  if (cached === void 0) cached = {
616
645
  from,
617
646
  paths: changedPathsSince(from, { cwd: context.cwd })
@@ -631,10 +660,10 @@ function createScopedPlugins(scope) {
631
660
  ...context,
632
661
  commits
633
662
  });
634
- if (type) return type;
663
+ if (typeof type === "string") return type;
635
664
  const bumps = mergeDependencyBumps(scope.bumps.bumpsFor(scope.pkg.name), commits);
636
665
  if (bumps.length === 0) return false;
637
- context.logger.log(`No releasable commits under ${scope.pkg.relativeDirectory}, but ${bumps.length === 1 ? "a workspace dependency range changed" : `${bumps.length} workspace dependency ranges changed`}; forcing a patch release.`);
666
+ context.logger.log(`No releasable commits under ${scope.pkg.relativeDirectory}, but ${bumps.length === 1 ? "a workspace dependency range changed" : `${String(bumps.length)} workspace dependency ranges changed`}; forcing a patch release.`);
638
667
  return "patch";
639
668
  },
640
669
  async generateNotes(_pluginConfig, context) {
@@ -650,7 +679,7 @@ function createScopedPlugins(scope) {
650
679
  "",
651
680
  ...bumps.map((bump) => describeDependencyBump(bump))
652
681
  ].join("\n");
653
- return notes ? `${notes}\n\n${section}` : section;
682
+ return typeof notes === "string" ? `${notes}\n\n${section}` : section;
654
683
  }
655
684
  };
656
685
  }
@@ -722,7 +751,6 @@ function parsePublishPluginSpec(spec) {
722
751
  }
723
752
  //#endregion
724
753
  //#region src/pnpm.ts
725
- const execFileAsync = promisify(execFile);
726
754
  /**
727
755
  * Regenerates `pnpm-lock.yaml` for the whole workspace from the manifests currently on disk, without touching `node_modules` or installing anything -- the same lockfile-refresh step a contributor runs by hand after editing a `package.json` dependency range.
728
756
  *
@@ -730,7 +758,7 @@ const execFileAsync = promisify(execFile);
730
758
  */
731
759
  async function regenerateLockfile(options) {
732
760
  try {
733
- await execFileAsync("pnpm", ["install", "--lockfile-only"], { cwd: options.cwd });
761
+ await execFile$1("pnpm", ["install", "--lockfile-only"], { cwd: options.cwd });
734
762
  } catch (cause) {
735
763
  const stderr = cause instanceof Error && "stderr" in cause && typeof cause.stderr === "string" ? cause.stderr.trim() : "";
736
764
  const detail = stderr !== "" ? stderr : cause instanceof Error ? cause.message : String(cause);
@@ -746,7 +774,7 @@ async function regenerateLockfile(options) {
746
774
  *
747
775
  * 1. **Analyse** (this file's `analysePackage`): for every package, in topological order, run semantic-release with `dryRun: true` forced (regardless of the caller's own `dryRun` option) using the same path-scoped `analyzeCommits`/`generateNotes` wrapper `commitStrategy: 'per-package'` uses -- computing each package's next version and notes without writing, committing, tagging, or publishing anything. Cross-package dependency bumps are tracked purely in memory during this phase (`pendingBumps`), exactly as the per-package strategy tracks them for the span of one run; nothing is committed yet for a later run to recover from, because this strategy never leaves a partial commit for a crash to recover from in the first place -- either the whole combined commit lands, or nothing does.
748
776
  * 2. **Verify** every released package's configured publish plugins' `verifyConditions` step (npm registry auth, GitHub token/repo access), before any file is written -- the same fail-fast-before-anything-releases discipline `validateDependencyRangeShapes` already applies to dependency ranges.
749
- * 3. **Prepare**: for every released package, in topological order, apply any dependency-range bump its own manifest received (writing `package.json` directly, the same `writeDependencyRange` the per-package strategy uses), then run every configured publish plugin's own `prepare` step generically (whichever it defines -- @semantic-release/npm bumps `package.json`'s version, @semantic-release/changelog writes `CHANGELOG.md`). @semantic-release/git is rejected outright from this mode's plugin list (see `resolvePublishPlugins`'s `forbidGitPlugin`), since its own `prepare` step would create exactly the per-package commit this mode exists to avoid. The lockfile is regenerated once at the end, not once per bump, since `pnpm install --lockfile-only` recomputes it from whatever is on disk regardless of how many manifests changed.
777
+ * 3. **Prepare**: for every released package, in topological order, apply any dependency-range bump its own manifest received (writing `package.json` directly, the same `writeDependencyRange` the per-package strategy uses), then run every configured publish plugin's own `prepare` step generically (whichever it defines -- `@semantic-release/npm` bumps `package.json`'s version, `@semantic-release/changelog` writes `CHANGELOG.md`). `@semantic-release/git` is rejected outright from this mode's plugin list (see `resolvePublishPlugins`'s `forbidGitPlugin`), since its own `prepare` step would create exactly the per-package commit this mode exists to avoid. The lockfile is regenerated once at the end, not once per bump, since `pnpm install --lockfile-only` recomputes it from whatever is on disk regardless of how many manifests changed.
750
778
  * 4. **Commit**: discover every file phase 3 touched via `git status` (rather than predicting filenames per plugin), make one commit, tag it once per released package (`name@version`, lightweight, matching semantic-release's own tag form), and push the commit and every tag together.
751
779
  * 5. **Publish**: for every released package, in topological order, call each configured plugin's own `publish` step directly (not through semantic-release's top-level orchestrator -- see the note below), then `success`.
752
780
  *
@@ -763,7 +791,7 @@ async function releaseWorkspaceSingleCommit(options) {
763
791
  const graph = buildDependencyGraph(workspace.packages);
764
792
  validateDependencyRangeShapes(graph);
765
793
  const order = topologicalOrder(graph);
766
- log(`${packageName}: ${order.length} packages in release order: ${order.join(" -> ")} (commitStrategy: single)`);
794
+ log(`${packageName}: ${String(order.length)} packages in release order: ${order.join(" -> ")} (commitStrategy: single)`);
767
795
  const resolvedPlugins = resolvePublishPlugins(options.plugins ?? SINGLE_COMMIT_DEFAULT_PUBLISH_PLUGINS, workspace.root, {
768
796
  requireGitPlugin: false,
769
797
  forbidGitPlugin: true
@@ -789,19 +817,26 @@ async function releaseWorkspaceSingleCommit(options) {
789
817
  bumpsForThisPackage,
790
818
  env,
791
819
  branches: options.branches,
792
- onCommitsResolved: (commits) => capturedCommits.set(name, commits),
820
+ onCommitsResolved: (commits) => {
821
+ capturedCommits.set(name, commits);
822
+ },
793
823
  onContextCaptured: (context) => {
794
824
  captured.branch = context.branch;
795
825
  captured.repositoryUrl = context.repositoryUrl;
796
826
  }
797
827
  });
798
- outcomes.push({
828
+ outcomes.push(nextRelease === void 0 ? {
799
829
  name,
800
830
  directory: pkg.directory,
801
- released: nextRelease !== void 0,
802
- version: nextRelease?.version,
803
- gitTag: nextRelease?.gitTag,
804
- type: nextRelease?.type,
831
+ released: false,
832
+ dependencyBumps: bumpsForThisPackage
833
+ } : {
834
+ name,
835
+ directory: pkg.directory,
836
+ released: true,
837
+ version: nextRelease.version,
838
+ gitTag: nextRelease.gitTag,
839
+ type: nextRelease.type,
805
840
  dependencyBumps: bumpsForThisPackage
806
841
  });
807
842
  if (nextRelease === void 0) {
@@ -829,7 +864,7 @@ async function releaseWorkspaceSingleCommit(options) {
829
864
  };
830
865
  const branch = captured.branch;
831
866
  const repositoryUrl = captured.repositoryUrl;
832
- if (branch === void 0 || repositoryUrl === void 0) throw new ReleaseConfigurationError(`Internal error: ${packageName} analysed ${planned.length} package release(s) but never captured a branch/repositoryUrl from semantic-release's own context. This should be impossible when at least one package releases.`);
867
+ if (branch === void 0 || repositoryUrl === void 0) throw new ReleaseConfigurationError(`Internal error: ${packageName} analysed ${String(planned.length)} package release(s) but never captured a branch/repositoryUrl from semantic-release's own context. This should be impossible when at least one package releases.`);
833
868
  const shared = {
834
869
  env,
835
870
  branch,
@@ -855,7 +890,7 @@ async function releaseWorkspaceSingleCommit(options) {
855
890
  }
856
891
  if (anyRangeRewritten) await regenerateLockfile({ cwd: workspace.root });
857
892
  const touchedPaths = await workingTreeChanges({ cwd: repoRoot });
858
- if (touchedPaths.length === 0) throw new ReleaseConfigurationError(`${packageName}: analysis planned ${planned.length} release(s), but no files changed while preparing them. Every configured publish plugin's own "prepare" step (bumping package.json, writing CHANGELOG.md) produced nothing to commit -- check the plugin list includes something that writes the version, e.g. @semantic-release/npm.`);
893
+ if (touchedPaths.length === 0) throw new ReleaseConfigurationError(`${packageName}: analysis planned ${String(planned.length)} release(s), but no files changed while preparing them. Every configured publish plugin's own "prepare" step (bumping package.json, writing CHANGELOG.md) produced nothing to commit -- check the plugin list includes something that writes the version, e.g. @semantic-release/npm.`);
859
894
  const identity = await resolveCommitIdentity({ cwd: repoRoot });
860
895
  await commitFiles(touchedPaths, describeCombinedCommit(planned), {
861
896
  cwd: repoRoot,
@@ -865,7 +900,7 @@ async function releaseWorkspaceSingleCommit(options) {
865
900
  const tagNames = planned.map((release) => release.gitTag);
866
901
  for (const tagName of tagNames) await createTag(tagName, commitSha, { cwd: repoRoot });
867
902
  await pushHeadAndTags(tagNames, { cwd: repoRoot });
868
- log(`${packageName}: committed ${commitSha} and pushed ${tagNames.length} tag(s): ${tagNames.join(", ")}`);
903
+ log(`${packageName}: committed ${commitSha} and pushed ${String(tagNames.length)} tag(s): ${tagNames.join(", ")}`);
869
904
  for (const release of planned) {
870
905
  const releases = [];
871
906
  for (const [modulePath, pluginConfig] of resolvedPlugins) {
@@ -957,10 +992,18 @@ function describeCombinedCommit(planned) {
957
992
  }
958
993
  function buildPluginContext(release, shared, capturedCommits, releases) {
959
994
  const logger = {
960
- log: (...args) => shared.log(`[${release.pkg.name}] ${args.map(String).join(" ")}`),
961
- warn: (...args) => shared.log(`[${release.pkg.name}] warn: ${args.map(String).join(" ")}`),
962
- error: (...args) => shared.log(`[${release.pkg.name}] error: ${args.map(String).join(" ")}`),
963
- success: (...args) => shared.log(`[${release.pkg.name}] ${args.map(String).join(" ")}`)
995
+ log: (...args) => {
996
+ shared.log(`[${release.pkg.name}] ${args.map(String).join(" ")}`);
997
+ },
998
+ warn: (...args) => {
999
+ shared.log(`[${release.pkg.name}] warn: ${args.map(String).join(" ")}`);
1000
+ },
1001
+ error: (...args) => {
1002
+ shared.log(`[${release.pkg.name}] error: ${args.map(String).join(" ")}`);
1003
+ },
1004
+ success: (...args) => {
1005
+ shared.log(`[${release.pkg.name}] ${args.map(String).join(" ")}`);
1006
+ }
964
1007
  };
965
1008
  return {
966
1009
  cwd: release.pkg.directory,
@@ -1020,7 +1063,7 @@ async function detachWorkspaceRelease(options) {
1020
1063
  const graph = buildDependencyGraph(workspace.packages);
1021
1064
  validateDependencyRangeShapes(graph);
1022
1065
  const order = topologicalOrder(graph);
1023
- log(`${packageName}: ${order.length} packages in release order (gated -- tag only, publish deferred): ${order.join(" -> ")}`);
1066
+ log(`${packageName}: ${String(order.length)} packages in release order (gated -- tag only, publish deferred): ${order.join(" -> ")}`);
1024
1067
  const publishPlugins = resolvePublishPlugins(options.plugins ?? DEFAULT_PUBLISH_PLUGINS, workspace.root, { requireGitPlugin: !dryRun });
1025
1068
  const analyzeCommitsConfig = options.analyzeCommits ?? {};
1026
1069
  const generateNotesConfig = options.generateNotes ?? {};
@@ -1042,15 +1085,20 @@ async function detachWorkspaceRelease(options) {
1042
1085
  });
1043
1086
  return {
1044
1087
  order,
1045
- packages: entries.map((entry) => ({
1088
+ packages: entries.map((entry) => entry.result === null ? {
1089
+ name: entry.name,
1090
+ directory: entry.directory,
1091
+ released: false,
1092
+ dependencyBumps: entry.dependencyBumps
1093
+ } : {
1046
1094
  name: entry.name,
1047
1095
  directory: entry.directory,
1048
- released: entry.result !== null,
1049
- version: entry.result?.nextRelease.version,
1050
- gitTag: entry.result?.nextRelease.gitTag,
1051
- type: entry.result?.nextRelease.type,
1096
+ released: true,
1097
+ version: entry.result.nextRelease.version,
1098
+ gitTag: entry.result.nextRelease.gitTag,
1099
+ type: entry.result.nextRelease.type,
1052
1100
  dependencyBumps: entry.dependencyBumps
1053
- })),
1101
+ }),
1054
1102
  detached: entries.map((entry) => ({
1055
1103
  name: entry.name,
1056
1104
  relativeDirectory: entry.relativeDirectory,
@@ -1100,9 +1148,6 @@ async function resumeWorkspaceRelease(options) {
1100
1148
  name: entry.name,
1101
1149
  directory: resolve(root, entry.relativeDirectory),
1102
1150
  released: false,
1103
- version: void 0,
1104
- gitTag: void 0,
1105
- type: void 0,
1106
1151
  dependencyBumps: entry.dependencyBumps
1107
1152
  });
1108
1153
  continue;
@@ -1118,7 +1163,7 @@ async function resumeWorkspaceRelease(options) {
1118
1163
  } catch (cause) {
1119
1164
  throw new WorkspaceReleaseError(`Resuming ${entry.name} failed: ${cause instanceof Error ? cause.message : String(cause)}`);
1120
1165
  }
1121
- log(`${entry.name}: published ${entry.state.nextRelease.gitTag} (${releases.length} publish plugin${releases.length === 1 ? "" : "s"} ran)`);
1166
+ log(`${entry.name}: published ${entry.state.nextRelease.gitTag} (${String(releases.length)} publish plugin${releases.length === 1 ? "" : "s"} ran)`);
1122
1167
  packages.push({
1123
1168
  name: entry.name,
1124
1169
  directory,
@@ -1155,7 +1200,7 @@ async function releaseWorkspace(options = {}) {
1155
1200
  const graph = buildDependencyGraph(workspace.packages);
1156
1201
  validateDependencyRangeShapes(graph);
1157
1202
  const order = topologicalOrder(graph);
1158
- log(`${packageName}: ${order.length} packages in release order: ${order.join(" -> ")}`);
1203
+ log(`${packageName}: ${String(order.length)} packages in release order: ${order.join(" -> ")}`);
1159
1204
  const publishPlugins = resolvePublishPlugins(options.plugins ?? DEFAULT_PUBLISH_PLUGINS, workspace.root, { requireGitPlugin: !dryRun });
1160
1205
  const analyzeCommitsConfig = options.analyzeCommits ?? {};
1161
1206
  const generateNotesConfig = options.generateNotes ?? {};
@@ -1179,13 +1224,18 @@ async function releaseWorkspace(options = {}) {
1179
1224
  };
1180
1225
  })).map((entry) => {
1181
1226
  const nextRelease = entry.result === false ? void 0 : entry.result.nextRelease;
1182
- return {
1227
+ return nextRelease === void 0 ? {
1183
1228
  name: entry.name,
1184
1229
  directory: entry.directory,
1185
- released: nextRelease !== void 0,
1186
- version: nextRelease?.version,
1187
- gitTag: nextRelease?.gitTag,
1188
- type: nextRelease?.type,
1230
+ released: false,
1231
+ dependencyBumps: entry.dependencyBumps
1232
+ } : {
1233
+ name: entry.name,
1234
+ directory: entry.directory,
1235
+ released: true,
1236
+ version: nextRelease.version,
1237
+ gitTag: nextRelease.gitTag,
1238
+ type: nextRelease.type,
1189
1239
  dependencyBumps: entry.dependencyBumps
1190
1240
  };
1191
1241
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exadev/semantic-release-workspace",
3
- "version": "1.3.5",
3
+ "version": "1.3.6",
4
4
  "description": "Independent per-package semantic-release orchestration for pnpm workspaces, without lockstep versioning.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -75,7 +75,7 @@
75
75
  "ci"
76
76
  ],
77
77
  "license": "MIT",
78
- "packageManager": "pnpm@11.6.0",
78
+ "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c",
79
79
  "dependencies": {
80
80
  "@exadev/release-gate": "^1.0.0",
81
81
  "commander": "^15.0.0",
@@ -89,7 +89,7 @@
89
89
  "@commitlint/cli": "^21.2.2",
90
90
  "@commitlint/config-conventional": "^21.2.2",
91
91
  "@eslint/js": "^10.0.1",
92
- "@exadev/eslint-config": "^2.1.1",
92
+ "@exadev/eslint-config": "2.12.1",
93
93
  "@semantic-release/changelog": "^7.0.0",
94
94
  "@semantic-release/commit-analyzer": "^13.0.1",
95
95
  "@semantic-release/git": "^11.0.1",