@williamthorsen/release-kit 4.5.1 → 4.7.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.
Files changed (58) hide show
  1. package/CHANGELOG.md +122 -99
  2. package/LICENSE +4 -4
  3. package/cliff.toml.template +68 -54
  4. package/dist/esm/.cache +1 -1
  5. package/dist/esm/assertCleanWorkingTree.d.ts +1 -0
  6. package/dist/esm/assertCleanWorkingTree.js +15 -0
  7. package/dist/esm/bin/release-kit.js +29 -6
  8. package/dist/esm/changelogJsonUtils.d.ts +4 -0
  9. package/dist/esm/changelogJsonUtils.js +29 -0
  10. package/dist/esm/createGithubRelease.d.ts +11 -0
  11. package/dist/esm/createGithubRelease.js +60 -0
  12. package/dist/esm/defaults.d.ts +3 -1
  13. package/dist/esm/defaults.js +14 -0
  14. package/dist/esm/generateChangelogJson.d.ts +7 -0
  15. package/dist/esm/generateChangelogJson.js +196 -0
  16. package/dist/esm/generateChangelogs.js +25 -9
  17. package/dist/esm/githubReleaseCommand.d.ts +1 -0
  18. package/dist/esm/githubReleaseCommand.js +35 -0
  19. package/dist/esm/index.d.ts +11 -3
  20. package/dist/esm/index.js +27 -3
  21. package/dist/esm/injectReleaseNotesIntoReadme.d.ts +2 -0
  22. package/dist/esm/injectReleaseNotesIntoReadme.js +42 -0
  23. package/dist/esm/injectSection.d.ts +1 -0
  24. package/dist/esm/injectSection.js +32 -0
  25. package/dist/esm/loadConfig.js +35 -3
  26. package/dist/esm/parseCommitMessage.js +1 -1
  27. package/dist/esm/prepareCommand.d.ts +1 -0
  28. package/dist/esm/prepareCommand.js +54 -20
  29. package/dist/esm/publish.d.ts +1 -1
  30. package/dist/esm/publish.js +7 -26
  31. package/dist/esm/publishCommand.js +49 -29
  32. package/dist/esm/releasePrepare.js +12 -1
  33. package/dist/esm/releasePrepareMono.js +23 -0
  34. package/dist/esm/renderReleaseNotes.d.ts +8 -0
  35. package/dist/esm/renderReleaseNotes.js +40 -0
  36. package/dist/esm/resolveCommandTags.d.ts +2 -0
  37. package/dist/esm/resolveCommandTags.js +36 -0
  38. package/dist/esm/resolveReleaseNotesConfig.d.ts +6 -0
  39. package/dist/esm/resolveReleaseNotesConfig.js +37 -0
  40. package/dist/esm/sync-labels/generateCommand.d.ts +1 -1
  41. package/dist/esm/sync-labels/generateCommand.js +12 -6
  42. package/dist/esm/sync-labels/presets.d.ts +1 -0
  43. package/dist/esm/sync-labels/presets.js +10 -0
  44. package/dist/esm/typeGuards.d.ts +1 -0
  45. package/dist/esm/typeGuards.js +5 -1
  46. package/dist/esm/types.d.ts +29 -0
  47. package/dist/esm/validateConfig.d.ts +1 -0
  48. package/dist/esm/validateConfig.js +89 -6
  49. package/dist/esm/version.d.ts +1 -1
  50. package/dist/esm/version.js +1 -1
  51. package/package.json +4 -3
  52. package/presets/labels/common.yaml +43 -0
  53. package/dist/esm/init/prompt.d.ts +0 -4
  54. package/dist/esm/init/prompt.js +0 -19
  55. package/dist/esm/runReleasePrepare.d.ts +0 -10
  56. package/dist/esm/runReleasePrepare.js +0 -125
  57. package/dist/esm/sync-labels/scaffold.d.ts +0 -6
  58. package/dist/esm/sync-labels/scaffold.js +0 -25
@@ -1,4 +1,4 @@
1
1
  import type { LabelDefinition } from './types.ts';
2
2
  export declare const LABELS_OUTPUT_PATH = ".github/labels.yaml";
3
- export declare function formatLabelsYaml(labels: LabelDefinition[]): string;
3
+ export declare function formatLabelsYaml(labels: LabelDefinition[], presetHashes: Map<string, string>): string;
4
4
  export declare function generateCommand(): Promise<number>;
@@ -2,14 +2,16 @@ import { mkdirSync, writeFileSync } from "node:fs";
2
2
  import { dirname } from "node:path";
3
3
  import { dump } from "js-yaml";
4
4
  import { loadSyncLabelsConfig, SYNC_LABELS_CONFIG_PATH } from "./loadSyncLabelsConfig.js";
5
+ import { hashPresetFile } from "./presets.js";
5
6
  import { resolveLabels } from "./resolveLabels.js";
6
7
  const LABELS_OUTPUT_PATH = ".github/labels.yaml";
7
- const GENERATED_HEADER = `# Generated by release-kit sync-labels \u2014 do not edit.
8
- # Source: ${SYNC_LABELS_CONFIG_PATH}
9
- `;
10
- function formatLabelsYaml(labels) {
8
+ function formatLabelsYaml(labels, presetHashes) {
9
+ const headerLines = ["# Generated by release-kit sync-labels \u2014 do not edit.", `# Source: ${SYNC_LABELS_CONFIG_PATH}`];
10
+ for (const [name, hash] of [...presetHashes.entries()].sort(([a], [b]) => a.localeCompare(b))) {
11
+ headerLines.push(`# ${name} preset hash: ${hash}`);
12
+ }
11
13
  const yamlBody = dump(labels, { quotingType: "'", forceQuotes: false, lineWidth: -1 });
12
- return GENERATED_HEADER + yamlBody;
14
+ return headerLines.join("\n") + "\n" + yamlBody;
13
15
  }
14
16
  async function generateCommand() {
15
17
  let config;
@@ -32,7 +34,11 @@ async function generateCommand() {
32
34
  console.error(`Error resolving labels: ${message}`);
33
35
  return 1;
34
36
  }
35
- const content = formatLabelsYaml(labels);
37
+ const presetHashes = /* @__PURE__ */ new Map();
38
+ for (const presetName of config.presets ?? []) {
39
+ presetHashes.set(presetName, hashPresetFile(presetName));
40
+ }
41
+ const content = formatLabelsYaml(labels, presetHashes);
36
42
  try {
37
43
  mkdirSync(dirname(LABELS_OUTPUT_PATH), { recursive: true });
38
44
  writeFileSync(LABELS_OUTPUT_PATH, content, "utf8");
@@ -1,2 +1,3 @@
1
1
  import type { LabelDefinition } from './types.ts';
2
+ export declare function hashPresetFile(presetName: string): string;
2
3
  export declare function loadPreset(presetName: string): LabelDefinition[];
@@ -1,3 +1,4 @@
1
+ import { createHash } from "node:crypto";
1
2
  import { existsSync, readFileSync } from "node:fs";
2
3
  import { resolve } from "node:path";
3
4
  import { load } from "js-yaml";
@@ -7,6 +8,14 @@ function resolvePresetPath(presetName) {
7
8
  const root = findPackageRoot(import.meta.url);
8
9
  return resolve(root, "presets", "labels", `${presetName}.yaml`);
9
10
  }
11
+ function hashPresetFile(presetName) {
12
+ const presetPath = resolvePresetPath(presetName);
13
+ if (!existsSync(presetPath)) {
14
+ throw new Error(`Unknown preset "${presetName}". No file found at ${presetPath}`);
15
+ }
16
+ const content = readFileSync(presetPath, "utf8");
17
+ return createHash("sha256").update(content).digest("hex");
18
+ }
10
19
  function loadPreset(presetName) {
11
20
  const presetPath = resolvePresetPath(presetName);
12
21
  if (!existsSync(presetPath)) {
@@ -36,5 +45,6 @@ function loadPreset(presetName) {
36
45
  return labels;
37
46
  }
38
47
  export {
48
+ hashPresetFile,
39
49
  loadPreset
40
50
  };
@@ -1 +1,2 @@
1
1
  export declare function isRecord(value: unknown): value is Record<string, unknown>;
2
+ export declare function isUnknownArray(value: unknown): value is unknown[];
@@ -1,6 +1,10 @@
1
1
  function isRecord(value) {
2
2
  return typeof value === "object" && value !== null && !Array.isArray(value);
3
3
  }
4
+ function isUnknownArray(value) {
5
+ return Array.isArray(value);
6
+ }
4
7
  export {
5
- isRecord
8
+ isRecord,
9
+ isUnknownArray
6
10
  };
@@ -1,4 +1,27 @@
1
1
  export type ReleaseType = 'major' | 'minor' | 'patch';
2
+ export type ChangelogAudience = 'all' | 'dev';
3
+ export interface ChangelogItem {
4
+ description: string;
5
+ }
6
+ export interface ChangelogSection {
7
+ title: string;
8
+ audience: ChangelogAudience;
9
+ items: ChangelogItem[];
10
+ }
11
+ export interface ChangelogEntry {
12
+ version: string;
13
+ date: string;
14
+ sections: ChangelogSection[];
15
+ }
16
+ export interface ChangelogJsonConfig {
17
+ enabled: boolean;
18
+ outputPath: string;
19
+ devOnlySections: string[];
20
+ }
21
+ export interface ReleaseNotesConfig {
22
+ shouldInjectIntoReadme: boolean;
23
+ shouldCreateGithubRelease: boolean;
24
+ }
2
25
  export interface PropagationSource {
3
26
  packageName: string;
4
27
  newVersion: string;
@@ -51,6 +74,8 @@ export interface ReleaseKitConfig {
51
74
  formatCommand?: string;
52
75
  cliffConfigPath?: string;
53
76
  scopeAliases?: Record<string, string>;
77
+ changelogJson?: Partial<ChangelogJsonConfig>;
78
+ releaseNotes?: Partial<ReleaseNotesConfig>;
54
79
  }
55
80
  export interface ComponentOverride {
56
81
  dir: string;
@@ -82,6 +107,8 @@ export interface MonorepoReleaseConfig {
82
107
  formatCommand?: string;
83
108
  cliffConfigPath?: string;
84
109
  scopeAliases?: Record<string, string>;
110
+ changelogJson: ChangelogJsonConfig;
111
+ releaseNotes: ReleaseNotesConfig;
85
112
  }
86
113
  export interface ReleaseConfig {
87
114
  tagPrefix: string;
@@ -92,4 +119,6 @@ export interface ReleaseConfig {
92
119
  formatCommand?: string;
93
120
  cliffConfigPath?: string;
94
121
  scopeAliases?: Record<string, string>;
122
+ changelogJson: ChangelogJsonConfig;
123
+ releaseNotes: ReleaseNotesConfig;
95
124
  }
@@ -2,4 +2,5 @@ import type { ReleaseKitConfig } from './types.ts';
2
2
  export declare function validateConfig(raw: unknown): {
3
3
  config: ReleaseKitConfig;
4
4
  errors: string[];
5
+ warnings: string[];
5
6
  };
@@ -2,29 +2,112 @@ import { isRecord } from "./typeGuards.js";
2
2
  function validateConfig(raw) {
3
3
  const errors = [];
4
4
  if (!isRecord(raw)) {
5
- return { config: {}, errors: ["Config must be an object"] };
5
+ return { config: {}, errors: ["Config must be an object"], warnings: [] };
6
6
  }
7
7
  const config = {};
8
8
  const knownFields = /* @__PURE__ */ new Set([
9
+ "changelogJson",
10
+ "cliffConfigPath",
9
11
  "components",
10
- "versionPatterns",
11
- "workTypes",
12
12
  "formatCommand",
13
- "cliffConfigPath",
14
- "scopeAliases"
13
+ "releaseNotes",
14
+ "scopeAliases",
15
+ "versionPatterns",
16
+ "workTypes"
15
17
  ]);
16
18
  for (const key of Object.keys(raw)) {
17
19
  if (!knownFields.has(key)) {
18
20
  errors.push(`Unknown field: '${key}'`);
19
21
  }
20
22
  }
23
+ validateChangelogJson(raw.changelogJson, config, errors);
21
24
  validateComponents(raw.components, config, errors);
25
+ validateReleaseNotes(raw.releaseNotes, config, errors);
22
26
  validateVersionPatterns(raw.versionPatterns, config, errors);
23
27
  validateWorkTypes(raw.workTypes, config, errors);
24
28
  validateStringField("formatCommand", raw.formatCommand, config, errors);
25
29
  validateStringField("cliffConfigPath", raw.cliffConfigPath, config, errors);
26
30
  validateScopeAliases(raw.scopeAliases, config, errors);
27
- return { config, errors };
31
+ const warnings = [];
32
+ const changelogJsonEnabled = config.changelogJson?.enabled ?? true;
33
+ if (!changelogJsonEnabled) {
34
+ if (config.releaseNotes?.shouldCreateGithubRelease) {
35
+ warnings.push(
36
+ "releaseNotes.shouldCreateGithubRelease is enabled but changelogJson.enabled is false; GitHub Releases will be skipped at runtime"
37
+ );
38
+ }
39
+ if (config.releaseNotes?.shouldInjectIntoReadme) {
40
+ warnings.push(
41
+ "releaseNotes.shouldInjectIntoReadme is enabled but changelogJson.enabled is false; README injection will be skipped at runtime"
42
+ );
43
+ }
44
+ }
45
+ return { config, errors, warnings };
46
+ }
47
+ function validateChangelogJson(value, config, errors) {
48
+ if (value === void 0) return;
49
+ if (!isRecord(value)) {
50
+ errors.push("'changelogJson' must be an object");
51
+ return;
52
+ }
53
+ const knownChangelogJsonFields = /* @__PURE__ */ new Set(["enabled", "outputPath", "devOnlySections"]);
54
+ for (const key of Object.keys(value)) {
55
+ if (!knownChangelogJsonFields.has(key)) {
56
+ errors.push(`changelogJson: unknown field '${key}'`);
57
+ }
58
+ }
59
+ const result = {};
60
+ if (value.enabled !== void 0) {
61
+ if (typeof value.enabled === "boolean") {
62
+ result.enabled = value.enabled;
63
+ } else {
64
+ errors.push("changelogJson.enabled: must be a boolean");
65
+ }
66
+ }
67
+ if (value.outputPath !== void 0) {
68
+ if (typeof value.outputPath === "string") {
69
+ result.outputPath = value.outputPath;
70
+ } else {
71
+ errors.push("changelogJson.outputPath: must be a string");
72
+ }
73
+ }
74
+ if (value.devOnlySections !== void 0) {
75
+ if (isStringArray(value.devOnlySections)) {
76
+ result.devOnlySections = value.devOnlySections;
77
+ } else {
78
+ errors.push("changelogJson.devOnlySections: must be a string array");
79
+ }
80
+ }
81
+ config.changelogJson = result;
82
+ }
83
+ function validateReleaseNotes(value, config, errors) {
84
+ if (value === void 0) return;
85
+ if (!isRecord(value)) {
86
+ errors.push("'releaseNotes' must be an object");
87
+ return;
88
+ }
89
+ const knownReleaseNotesFields = /* @__PURE__ */ new Set(["shouldInjectIntoReadme", "shouldCreateGithubRelease"]);
90
+ for (const key of Object.keys(value)) {
91
+ if (!knownReleaseNotesFields.has(key)) {
92
+ errors.push(`releaseNotes: unknown field '${key}'`);
93
+ }
94
+ }
95
+ const result = {};
96
+ if (value.shouldInjectIntoReadme !== void 0) {
97
+ if (typeof value.shouldInjectIntoReadme === "boolean") {
98
+ result.shouldInjectIntoReadme = value.shouldInjectIntoReadme;
99
+ } else {
100
+ errors.push("releaseNotes.shouldInjectIntoReadme: must be a boolean");
101
+ }
102
+ }
103
+ if (value.shouldCreateGithubRelease !== void 0) {
104
+ if (typeof value.shouldCreateGithubRelease === "boolean") {
105
+ result.shouldCreateGithubRelease = value.shouldCreateGithubRelease;
106
+ } else {
107
+ errors.push("releaseNotes.shouldCreateGithubRelease: must be a boolean");
108
+ }
109
+ }
110
+ config.releaseNotes = result;
28
111
  }
29
112
  function isStringArray(value) {
30
113
  return Array.isArray(value) && value.every((item) => typeof item === "string");
@@ -1 +1 @@
1
- export declare const VERSION = "4.5.1";
1
+ export declare const VERSION = "4.7.0";
@@ -1,4 +1,4 @@
1
- const VERSION = "4.5.1";
1
+ const VERSION = "4.7.0";
2
2
  export {
3
3
  VERSION
4
4
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@williamthorsen/release-kit",
3
- "version": "4.5.1",
3
+ "version": "4.7.0",
4
4
  "description": "Version-bumping and changelog-generation toolkit for release workflows",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/williamthorsen/node-monorepo-tools/tree/main/packages/release-kit#readme",
@@ -12,7 +12,7 @@
12
12
  "url": "https://github.com/williamthorsen/node-monorepo-tools.git",
13
13
  "directory": "packages/release-kit"
14
14
  },
15
- "license": "UNLICENSED",
15
+ "license": "ISC",
16
16
  "author": "William Thorsen <william@thorsen.dev> (https://github.com/williamthorsen)",
17
17
  "type": "module",
18
18
  "exports": {
@@ -34,7 +34,8 @@
34
34
  "glob": "13.0.6",
35
35
  "jiti": "2.6.1",
36
36
  "js-yaml": "4.1.1",
37
- "@williamthorsen/node-monorepo-core": "0.2.5"
37
+ "json-stringify-pretty-compact": "4.0.0",
38
+ "@williamthorsen/node-monorepo-core": "0.2.6"
38
39
  },
39
40
  "devDependencies": {
40
41
  "@types/js-yaml": "4.0.9",
@@ -11,9 +11,21 @@
11
11
  - name: documentation
12
12
  color: a2eeef
13
13
  description: Improvements or additions to documentation
14
+ - name: deprecation
15
+ color: fbca04
16
+ description: Marks functionality for future removal
14
17
  - name: feature
15
18
  color: '0075ca'
16
19
  description: Added or improved external functionality
20
+ - name: fix
21
+ color: d73a4a
22
+ description: Fixes a bug
23
+ - name: performance
24
+ color: '0075ca'
25
+ description: Improves performance without changing behavior
26
+ - name: security
27
+ color: b60205
28
+ description: Security vulnerability or hardening
17
29
  - name: spike
18
30
  color: '000000'
19
31
  description: Investigation
@@ -30,6 +42,9 @@
30
42
  description: Further information is needed
31
43
 
32
44
  # Refactoring
45
+ - name: ci
46
+ color: edc287
47
+ description: CI/CD tooling & configuration
33
48
  - name: dependencies
34
49
  color: edc287
35
50
  description: Change to dependencies
@@ -46,6 +61,34 @@
46
61
  color: 1d76db
47
62
  description: Changed internal functionality
48
63
 
64
+ # Priority
65
+ - name: 'priority:critical'
66
+ color: b60205
67
+ description: Demands immediate attention
68
+ - name: 'priority:high'
69
+ color: d93f0b
70
+ description: Address soon
71
+ - name: 'priority:medium'
72
+ color: fbca04
73
+ description: No rush but don't forget
74
+ - name: 'priority:low'
75
+ color: cfd3d7
76
+ description: Get to it eventually
77
+
78
+ # Value
79
+ - name: 'value:must-have'
80
+ color: 7057ff
81
+ description: Essential for the application to fulfill its purpose
82
+ - name: 'value:should-have'
83
+ color: 8d83fc
84
+ description: Important but the application is usable without it
85
+ - name: 'value:nice-to-have'
86
+ color: a8b2f8
87
+ description: A nice-to-have enhancement
88
+ - name: 'value:questionable'
89
+ color: c5def5
90
+ description: Decision needed on whether this would add sufficient value
91
+
49
92
  # Triage
50
93
  - name: duplicate
51
94
  color: cfd3d7
@@ -1,4 +0,0 @@
1
- export declare function printStep(message: string): void;
2
- export declare function printSuccess(message: string): void;
3
- export declare function printSkip(message: string): void;
4
- export declare function printError(message: string): void;
@@ -1,19 +0,0 @@
1
- function printStep(message) {
2
- console.info(`
3
- > ${message}`);
4
- }
5
- function printSuccess(message) {
6
- console.info(` \u2705 ${message}`);
7
- }
8
- function printSkip(message) {
9
- console.info(` \u26A0\uFE0F ${message}`);
10
- }
11
- function printError(message) {
12
- console.error(` \u274C ${message}`);
13
- }
14
- export {
15
- printError,
16
- printSkip,
17
- printStep,
18
- printSuccess
19
- };
@@ -1,10 +0,0 @@
1
- import type { MonorepoReleaseConfig, ReleaseConfig, ReleaseType } from './types.ts';
2
- export declare const RELEASE_TAGS_FILE = "tmp/.release-tags";
3
- export declare function parseArgs(argv: string[]): {
4
- dryRun: boolean;
5
- force: boolean;
6
- bumpOverride: ReleaseType | undefined;
7
- only: string[] | undefined;
8
- };
9
- export declare function runReleasePrepare(config: MonorepoReleaseConfig | ReleaseConfig): void;
10
- export declare function writeReleaseTags(tags: string[], dryRun: boolean): void;
@@ -1,125 +0,0 @@
1
- import { mkdirSync, writeFileSync } from "node:fs";
2
- import { dirname } from "node:path";
3
- import { dim } from "./format.js";
4
- import { releasePrepare } from "./releasePrepare.js";
5
- import { releasePrepareMono } from "./releasePrepareMono.js";
6
- const RELEASE_TAGS_FILE = "tmp/.release-tags";
7
- const VALID_BUMP_TYPES = ["major", "minor", "patch"];
8
- function isReleaseType(value) {
9
- return VALID_BUMP_TYPES.includes(value);
10
- }
11
- function isMonorepoConfig(config) {
12
- return "components" in config;
13
- }
14
- function showHelp() {
15
- console.info(`
16
- Usage: runReleasePrepare [options]
17
-
18
- Legacy entry point for release preparation. Prefer the CLI:
19
- npx @williamthorsen/release-kit prepare
20
-
21
- Options:
22
- --dry-run Run without modifying any files
23
- --bump=major|minor|patch Override the bump type for all components
24
- --force Bypass the "no commits since last tag" check (monorepo only, requires --bump)
25
- --only=name1,name2 Only process the named components (comma-separated, monorepo only)
26
- --help Show this help message
27
- `);
28
- }
29
- function parseArgs(argv) {
30
- let dryRun = false;
31
- let force = false;
32
- let bumpOverride;
33
- let only;
34
- for (const arg of argv) {
35
- if (arg === "--dry-run") {
36
- dryRun = true;
37
- } else if (arg === "--force") {
38
- force = true;
39
- } else if (arg.startsWith("--bump=")) {
40
- const value = arg.slice("--bump=".length);
41
- if (!isReleaseType(value)) {
42
- console.error(`Error: Invalid bump type "${value}". Must be one of: ${VALID_BUMP_TYPES.join(", ")}`);
43
- process.exit(1);
44
- }
45
- bumpOverride = value;
46
- } else if (arg.startsWith("--only=")) {
47
- const value = arg.slice("--only=".length);
48
- if (!value) {
49
- console.error("Error: --only requires a comma-separated list of component names");
50
- process.exit(1);
51
- }
52
- only = value.split(",");
53
- } else if (arg === "--help" || arg === "-h") {
54
- showHelp();
55
- process.exit(0);
56
- } else {
57
- console.error(`Error: Unknown argument: ${arg}`);
58
- process.exit(1);
59
- }
60
- }
61
- if (force && bumpOverride === void 0) {
62
- console.error("Error: --force requires --bump to specify the version bump type");
63
- process.exit(1);
64
- }
65
- return { dryRun, force, bumpOverride, only };
66
- }
67
- function runReleasePrepare(config) {
68
- const { dryRun, force, bumpOverride, only } = parseArgs(process.argv.slice(2));
69
- const options = {
70
- dryRun,
71
- force,
72
- ...bumpOverride === void 0 ? {} : { bumpOverride }
73
- };
74
- if (!isMonorepoConfig(config)) {
75
- if (only !== void 0) {
76
- console.error("Error: --only is only supported for monorepo configurations");
77
- process.exit(1);
78
- }
79
- try {
80
- const tags = releasePrepare(config, options);
81
- writeReleaseTags(tags, dryRun);
82
- } catch (error) {
83
- console.error("Error preparing release:", error instanceof Error ? error.message : String(error));
84
- process.exit(1);
85
- }
86
- return;
87
- }
88
- let effectiveConfig = config;
89
- if (only !== void 0) {
90
- const knownNames = config.components.map((c) => c.dir);
91
- for (const name of only) {
92
- if (!knownNames.includes(name)) {
93
- console.error(`Error: Unknown component "${name}". Known components: ${knownNames.join(", ")}`);
94
- process.exit(1);
95
- }
96
- }
97
- const filtered = config.components.filter((c) => only.includes(c.dir));
98
- effectiveConfig = { ...config, components: filtered };
99
- }
100
- try {
101
- const tags = releasePrepareMono(effectiveConfig, options);
102
- writeReleaseTags(tags, dryRun);
103
- } catch (error) {
104
- console.error("Error preparing release:", error instanceof Error ? error.message : String(error));
105
- process.exit(1);
106
- }
107
- }
108
- function writeReleaseTags(tags, dryRun) {
109
- if (tags.length === 0) {
110
- return;
111
- }
112
- if (dryRun) {
113
- console.info(dim(` [dry-run] Would write ${RELEASE_TAGS_FILE}: ${tags.join(" ")}`));
114
- return;
115
- }
116
- mkdirSync(dirname(RELEASE_TAGS_FILE), { recursive: true });
117
- writeFileSync(RELEASE_TAGS_FILE, tags.join("\n"), "utf8");
118
- console.info(dim(` Wrote ${RELEASE_TAGS_FILE}: ${tags.join(" ")}`));
119
- }
120
- export {
121
- RELEASE_TAGS_FILE,
122
- parseArgs,
123
- runReleasePrepare,
124
- writeReleaseTags
125
- };
@@ -1,6 +0,0 @@
1
- interface WriteResult {
2
- action: 'created' | 'skipped' | 'dry-run' | 'failed';
3
- filePath: string;
4
- }
5
- export declare function writeIfAbsent(filePath: string, content: string, dryRun: boolean, overwrite: boolean): WriteResult;
6
- export {};
@@ -1,25 +0,0 @@
1
- import { existsSync, mkdirSync, writeFileSync } from "node:fs";
2
- import { dirname } from "node:path";
3
- function writeIfAbsent(filePath, content, dryRun, overwrite) {
4
- if (existsSync(filePath) && !overwrite) {
5
- console.info(` Skipped ${filePath} (already exists)`);
6
- return { action: "skipped", filePath };
7
- }
8
- if (dryRun) {
9
- console.info(` [dry-run] Would create ${filePath}`);
10
- return { action: "dry-run", filePath };
11
- }
12
- try {
13
- mkdirSync(dirname(filePath), { recursive: true });
14
- writeFileSync(filePath, content, "utf8");
15
- } catch (error) {
16
- const message = error instanceof Error ? error.message : String(error);
17
- console.error(` Failed to write ${filePath}: ${message}`);
18
- return { action: "failed", filePath };
19
- }
20
- console.info(` Created ${filePath}`);
21
- return { action: "created", filePath };
22
- }
23
- export {
24
- writeIfAbsent
25
- };