@virmator/publish 14.23.1 → 14.25.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/publish.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { type SemVer } from 'semver';
1
2
  import { type PackageJson } from 'type-fest';
2
3
  /** A virmator plugin for publishing a package to npm. */
3
4
  export declare const virmatorPublishPlugin: Readonly<Readonly<{
@@ -43,3 +44,25 @@ export declare function assertValidLicense({ license, isPrivate, displayName, }:
43
44
  isPrivate: PackageJson['private'];
44
45
  displayName: string;
45
46
  }): void;
47
+ /** Commit message version tags that bump the published version. */
48
+ export declare enum ChangeMarker {
49
+ Patch = "patch",
50
+ Minor = "minor",
51
+ Major = "major"
52
+ }
53
+ /**
54
+ * Parses the leading `[tag]` version marker from a commit message. Returns the matching
55
+ * {@link ChangeMarker}, or `undefined` when there is no tag or the tag is an allowed non-bumping one
56
+ * (e.g. `dev`). Throws a `VirmatorNoTraceError` for any tag outside the allowed set so the publish
57
+ * aborts.
58
+ */
59
+ export declare function parseCommitChangeMarker(commitMessage: string): ChangeMarker | undefined;
60
+ /**
61
+ * The next semver version to publish, derived from the highest-priority bump marker found since the
62
+ * last version (major > minor > patch). Returns `undefined` when no version was found or no bump
63
+ * marker is present, in which case the caller asks for a version manually.
64
+ */
65
+ export declare function determineNextVersion({ latestVersion, changeMarkers, }: {
66
+ latestVersion: SemVer | undefined;
67
+ changeMarkers: Readonly<Record<ChangeMarker, number>>;
68
+ }): string | undefined;
package/dist/publish.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { assert, check } from '@augment-vir/assert';
2
- import { awaitedBlockingMap, extractErrorMessage, safeMatch } from '@augment-vir/common';
2
+ import { awaitedBlockingMap, getObjectTypedValues, safeMatch, wrapInTry, } from '@augment-vir/common';
3
3
  import { askQuestionUntilConditionMet, readPackageJson, runShellCommand as runHiddenShellCommand, runShellCommand, } from '@augment-vir/node';
4
4
  import { defineVirmatorPlugin, parseTsConfig, VirmatorNoTraceError, } from '@virmator/core';
5
5
  import mri from 'mri';
@@ -80,23 +80,26 @@ export const virmatorPublishPlugin = defineVirmatorPlugin(import.meta.dirname, {
80
80
  return packageJson;
81
81
  }));
82
82
  const git = simpleGit(monoRepoRootPath);
83
+ /**
84
+ * Always read the version tags on commits since the last release so a disallowed tag (e.g.
85
+ * `[wip]`) aborts the publish, even when the current version needs no bump.
86
+ */
87
+ const { latestVersion, changeMarkers } = await findChangeMarkersSinceVersion(git);
83
88
  if (await isVersionPublished(version, [
84
89
  cwdValidPackageJson,
85
90
  ...monoRepoPackageJsonFiles,
86
91
  ])) {
87
- let nextVersion;
88
- try {
89
- nextVersion = await determineNextVersion(git);
90
- }
91
- catch (error) {
92
- log.faint(extractErrorMessage(error));
93
- }
94
92
  const allPackageJsonFiles = [
95
93
  cwdValidPackageJson,
96
94
  ...monoRepoPackageJsonFiles,
97
95
  ];
98
- if (!nextVersion || (await isVersionPublished(nextVersion, allPackageJsonFiles))) {
99
- nextVersion = await askQuestionUntilConditionMet({
96
+ const autoNextVersion = determineNextVersion({
97
+ latestVersion,
98
+ changeMarkers,
99
+ });
100
+ const nextVersion = autoNextVersion && !(await isVersionPublished(autoNextVersion, allPackageJsonFiles))
101
+ ? autoNextVersion
102
+ : await askQuestionUntilConditionMet({
100
103
  async verifyResponseCallback(response) {
101
104
  const version = semver.coerce(response)?.raw;
102
105
  if (!version) {
@@ -107,7 +110,6 @@ export const virmatorPublishPlugin = defineVirmatorPlugin(import.meta.dirname, {
107
110
  invalidInputMessage: 'Invalid semver version.',
108
111
  questionToAsk: 'Failed to automatically determine next publish version. Please enter one:',
109
112
  });
110
- }
111
113
  log.info(`Publishing version ${nextVersion}...`);
112
114
  await updateVersions(nextVersion, monoRepoRootPath, monoRepoPackages, log);
113
115
  await runHiddenShellCommand('npm i');
@@ -248,12 +250,38 @@ async function isPublished({ name, version }) {
248
250
  return output.exitCode === 0;
249
251
  }
250
252
  const gitCommitFormatDelimiter = '<**..**>';
251
- var ChangeMarker;
253
+ /** Commit message version tags that bump the published version. */
254
+ export var ChangeMarker;
252
255
  (function (ChangeMarker) {
253
256
  ChangeMarker["Patch"] = "patch";
254
257
  ChangeMarker["Minor"] = "minor";
255
258
  ChangeMarker["Major"] = "major";
256
259
  })(ChangeMarker || (ChangeMarker = {}));
260
+ /**
261
+ * Every commit-message version tag `virmator publish` accepts. The {@link ChangeMarker} values each
262
+ * bump their respective semver part; `dev` is allowed but does not bump the version. Any other tag
263
+ * (e.g. `wip`) aborts the publish.
264
+ */
265
+ const allowedVersionTags = [
266
+ ...getObjectTypedValues(ChangeMarker),
267
+ 'dev',
268
+ ];
269
+ /**
270
+ * Parses the leading `[tag]` version marker from a commit message. Returns the matching
271
+ * {@link ChangeMarker}, or `undefined` when there is no tag or the tag is an allowed non-bumping one
272
+ * (e.g. `dev`). Throws a `VirmatorNoTraceError` for any tag outside the allowed set so the publish
273
+ * aborts.
274
+ */
275
+ export function parseCommitChangeMarker(commitMessage) {
276
+ const [, rawChangeMarker,] = safeMatch(commitMessage.trim(), /^\[([^\]]+)]/);
277
+ if (!rawChangeMarker) {
278
+ return undefined;
279
+ }
280
+ else if (!allowedVersionTags.includes(rawChangeMarker)) {
281
+ throw new VirmatorNoTraceError(`${rawChangeMarker} version tag not allowed`);
282
+ }
283
+ return check.isEnumValue(rawChangeMarker, ChangeMarker) ? rawChangeMarker : undefined;
284
+ }
257
285
  async function getGitCommitVersion(decrement, git) {
258
286
  const output = await git.raw(`show HEAD~${decrement} --pretty='%d${gitCommitFormatDelimiter}%s' -s`.split(' '));
259
287
  const [maybeTag, message,] = output.split(gitCommitFormatDelimiter);
@@ -269,16 +297,19 @@ async function getGitCommitVersion(decrement, git) {
269
297
  .filter(check.isTruthy);
270
298
  const sortedVersionTags = semver.sort(versionTags);
271
299
  const latestVersionTag = sortedVersionTags.slice(-1)[0];
272
- const [, rawChangeMarker,] = message ? safeMatch(message.trim(), /^\[([^\]]+)]/) : [];
273
- const changeMarker = rawChangeMarker && check.isEnumValue(rawChangeMarker, ChangeMarker)
274
- ? rawChangeMarker
275
- : undefined;
276
300
  return {
277
301
  version: latestVersionTag,
278
- changeMarker,
302
+ changeMarker: message ? parseCommitChangeMarker(message) : undefined,
279
303
  };
280
304
  }
281
- async function determineNextVersion(git) {
305
+ const maxCommitLookBack = 100;
306
+ /**
307
+ * Walks backward from HEAD until the most recent version git-tag (or `maxCommitLookBack` commits /
308
+ * the start of history), tallying the bump markers found on commits since that version. Reading
309
+ * each commit validates its version tag, so a disallowed tag (e.g. `[wip]`) aborts here — even when
310
+ * the current version needs no bump.
311
+ */
312
+ async function findChangeMarkersSinceVersion(git) {
282
313
  const changeMarkers = {
283
314
  [ChangeMarker.Patch]: 0,
284
315
  [ChangeMarker.Minor]: 0,
@@ -286,20 +317,42 @@ async function determineNextVersion(git) {
286
317
  };
287
318
  let decrement = 0;
288
319
  let latestVersion = undefined;
289
- while (!latestVersion) {
290
- if (decrement > 100) {
291
- throw new Error("Couldn't find a version tag in the past 100 commits.");
320
+ while (!latestVersion && decrement <= maxCommitLookBack) {
321
+ const commitVersion = await wrapInTry(() => getGitCommitVersion(decrement, git), {
322
+ handleError(error) {
323
+ /** A disallowed version tag must abort; any other git error just ends the walk. */
324
+ if (error instanceof VirmatorNoTraceError) {
325
+ throw error;
326
+ }
327
+ return undefined;
328
+ },
329
+ });
330
+ if (!commitVersion) {
331
+ break;
292
332
  }
293
- const { changeMarker, version } = await getGitCommitVersion(decrement, git);
294
- if (version) {
295
- latestVersion = version;
333
+ else if (commitVersion.version) {
334
+ latestVersion = commitVersion.version;
296
335
  }
297
- else if (changeMarker) {
298
- changeMarkers[changeMarker]++;
336
+ else if (commitVersion.changeMarker) {
337
+ changeMarkers[commitVersion.changeMarker]++;
299
338
  }
300
339
  decrement++;
301
340
  }
302
- if (changeMarkers[ChangeMarker.Major]) {
341
+ return {
342
+ latestVersion,
343
+ changeMarkers,
344
+ };
345
+ }
346
+ /**
347
+ * The next semver version to publish, derived from the highest-priority bump marker found since the
348
+ * last version (major > minor > patch). Returns `undefined` when no version was found or no bump
349
+ * marker is present, in which case the caller asks for a version manually.
350
+ */
351
+ export function determineNextVersion({ latestVersion, changeMarkers, }) {
352
+ if (!latestVersion) {
353
+ return undefined;
354
+ }
355
+ else if (changeMarkers[ChangeMarker.Major]) {
303
356
  return latestVersion.inc('major').raw;
304
357
  }
305
358
  else if (changeMarkers[ChangeMarker.Minor]) {
@@ -309,7 +362,7 @@ async function determineNextVersion(git) {
309
362
  return latestVersion.inc('patch').raw;
310
363
  }
311
364
  else {
312
- throw new Error('No change markers fround since last tagged version.');
365
+ return undefined;
313
366
  }
314
367
  }
315
368
  async function updateVersions(version, monoRepoRootPath, monoPackages, log) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@virmator/publish",
3
- "version": "14.23.1",
3
+ "version": "14.25.0",
4
4
  "description": "Default publish plugin for virmator.",
5
5
  "keywords": [
6
6
  "virmator",
@@ -34,22 +34,22 @@
34
34
  "test:update": "npm test"
35
35
  },
36
36
  "dependencies": {
37
- "@augment-vir/assert": "^31.71.3",
38
- "@augment-vir/common": "^31.71.3",
39
- "@augment-vir/node": "^31.71.3",
40
- "@virmator/core": "^14.23.1",
37
+ "@augment-vir/assert": "^31.72.1",
38
+ "@augment-vir/common": "^31.72.1",
39
+ "@augment-vir/node": "^31.72.1",
40
+ "@virmator/core": "^14.25.0",
41
41
  "mri": "^1.2.0",
42
- "semver": "^7.8.1",
42
+ "semver": "^7.8.3",
43
43
  "simple-git": "^3.36.0",
44
44
  "spdx-vir": "^0.0.1",
45
45
  "url-vir": "^2.1.9"
46
46
  },
47
47
  "devDependencies": {
48
- "@augment-vir/test": "^31.71.3",
49
- "@types/node": "^25.9.1",
48
+ "@augment-vir/test": "^31.72.1",
49
+ "@types/node": "^25.9.2",
50
50
  "@types/semver": "^7.7.1",
51
51
  "markdown-code-example-inserter": "^3.0.5",
52
- "type-fest": "^5.6.0",
52
+ "type-fest": "^5.7.0",
53
53
  "typedoc": "^0.28.19"
54
54
  },
55
55
  "engines": {