@williamthorsen/release-kit 2.0.0 → 2.3.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 (75) hide show
  1. package/CHANGELOG.md +77 -58
  2. package/README.md +33 -42
  3. package/bin/release-kit.js +2 -0
  4. package/dist/esm/.cache +1 -1
  5. package/dist/esm/bin/release-kit.js +158 -5
  6. package/dist/esm/bumpAllVersions.d.ts +2 -2
  7. package/dist/esm/bumpAllVersions.js +1 -4
  8. package/dist/esm/component.d.ts +1 -1
  9. package/dist/esm/component.js +2 -3
  10. package/dist/esm/createTags.d.ts +5 -0
  11. package/dist/esm/createTags.js +73 -0
  12. package/dist/esm/detectPackageManager.d.ts +2 -0
  13. package/dist/esm/detectPackageManager.js +44 -0
  14. package/dist/esm/findPackageRoot.d.ts +1 -0
  15. package/dist/esm/findPackageRoot.js +17 -0
  16. package/dist/esm/format.d.ts +3 -0
  17. package/dist/esm/format.js +14 -0
  18. package/dist/esm/generateChangelogs.d.ts +2 -2
  19. package/dist/esm/generateChangelogs.js +14 -13
  20. package/dist/esm/hasPrettierConfig.d.ts +1 -0
  21. package/dist/esm/hasPrettierConfig.js +42 -0
  22. package/dist/esm/index.d.ts +12 -2
  23. package/dist/esm/index.js +11 -2
  24. package/dist/esm/init/checks.d.ts +0 -2
  25. package/dist/esm/init/checks.js +0 -17
  26. package/dist/esm/init/initCommand.d.ts +3 -1
  27. package/dist/esm/init/initCommand.js +35 -40
  28. package/dist/esm/init/prompt.d.ts +0 -1
  29. package/dist/esm/init/prompt.js +3 -14
  30. package/dist/esm/init/scaffold.d.ts +4 -2
  31. package/dist/esm/init/scaffold.js +20 -48
  32. package/dist/esm/init/templates.js +14 -4
  33. package/dist/esm/loadConfig.js +0 -6
  34. package/dist/esm/prepareCommand.d.ts +10 -0
  35. package/dist/esm/prepareCommand.js +97 -21
  36. package/dist/esm/publish.d.ts +7 -0
  37. package/dist/esm/publish.js +49 -0
  38. package/dist/esm/publishCommand.d.ts +1 -0
  39. package/dist/esm/publishCommand.js +54 -0
  40. package/dist/esm/releasePrepare.d.ts +2 -2
  41. package/dist/esm/releasePrepare.js +46 -17
  42. package/dist/esm/releasePrepareMono.d.ts +2 -2
  43. package/dist/esm/releasePrepareMono.js +54 -24
  44. package/dist/esm/reportPrepare.d.ts +2 -0
  45. package/dist/esm/reportPrepare.js +120 -0
  46. package/dist/esm/resolveCliffConfigPath.d.ts +1 -0
  47. package/dist/esm/resolveCliffConfigPath.js +25 -0
  48. package/dist/esm/resolveReleaseTags.d.ts +6 -0
  49. package/dist/esm/resolveReleaseTags.js +42 -0
  50. package/dist/esm/runReleasePrepare.d.ts +1 -1
  51. package/dist/esm/runReleasePrepare.js +4 -3
  52. package/dist/esm/sync-labels/generateCommand.d.ts +4 -0
  53. package/dist/esm/sync-labels/generateCommand.js +51 -0
  54. package/dist/esm/sync-labels/initCommand.d.ts +6 -0
  55. package/dist/esm/sync-labels/initCommand.js +62 -0
  56. package/dist/esm/sync-labels/loadSyncLabelsConfig.d.ts +3 -0
  57. package/dist/esm/sync-labels/loadSyncLabelsConfig.js +59 -0
  58. package/dist/esm/sync-labels/presets.d.ts +2 -0
  59. package/dist/esm/sync-labels/presets.js +40 -0
  60. package/dist/esm/sync-labels/resolveLabels.d.ts +2 -0
  61. package/dist/esm/sync-labels/resolveLabels.js +43 -0
  62. package/dist/esm/sync-labels/scaffold.d.ts +6 -0
  63. package/dist/esm/sync-labels/scaffold.js +25 -0
  64. package/dist/esm/sync-labels/syncCommand.d.ts +1 -0
  65. package/dist/esm/sync-labels/syncCommand.js +33 -0
  66. package/dist/esm/sync-labels/templates.d.ts +4 -0
  67. package/dist/esm/sync-labels/templates.js +50 -0
  68. package/dist/esm/sync-labels/types.d.ts +9 -0
  69. package/dist/esm/sync-labels/types.js +0 -0
  70. package/dist/esm/tagCommand.d.ts +1 -0
  71. package/dist/esm/tagCommand.js +20 -0
  72. package/dist/esm/types.d.ts +29 -1
  73. package/dist/esm/validateConfig.js +11 -6
  74. package/package.json +12 -5
  75. package/presets/labels/common.yaml +52 -0
@@ -11,10 +11,8 @@ function bumpAllVersions(packageFiles, releaseType, dryRun) {
11
11
  const firstPkg = readPackageJson(firstFile);
12
12
  const currentVersion = firstPkg.version;
13
13
  const newVersion = bumpVersion(currentVersion, releaseType);
14
- console.info(`Bumping version: ${currentVersion} -> ${newVersion} (${releaseType})`);
15
14
  for (const filePath of packageFiles) {
16
15
  if (dryRun) {
17
- console.info(` [dry-run] Would bump ${filePath}`);
18
16
  continue;
19
17
  }
20
18
  const pkg = filePath === firstFile ? firstPkg : readPackageJson(filePath);
@@ -24,9 +22,8 @@ function bumpAllVersions(packageFiles, releaseType, dryRun) {
24
22
  } catch (error) {
25
23
  throw new Error(`Failed to write ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
26
24
  }
27
- console.info(` Bumped ${filePath}`);
28
25
  }
29
- return newVersion;
26
+ return { currentVersion, newVersion, files: [...packageFiles] };
30
27
  }
31
28
  function readPackageJson(filePath) {
32
29
  let content;
@@ -1,2 +1,2 @@
1
1
  import type { ComponentConfig } from './types.ts';
2
- export declare function component(workspacePath: string, tagPrefix?: string): ComponentConfig;
2
+ export declare function component(workspacePath: string): ComponentConfig;
@@ -1,10 +1,9 @@
1
1
  import { basename } from "node:path";
2
- function component(workspacePath, tagPrefix) {
2
+ function component(workspacePath) {
3
3
  const dir = basename(workspacePath);
4
- const prefix = tagPrefix ?? `${dir}-v`;
5
4
  return {
6
5
  dir,
7
- tagPrefix: prefix,
6
+ tagPrefix: `${dir}-v`,
8
7
  packageFiles: [`${workspacePath}/package.json`],
9
8
  changelogPaths: [workspacePath],
10
9
  paths: [`${workspacePath}/**`]
@@ -0,0 +1,5 @@
1
+ export interface CreateTagsOptions {
2
+ dryRun: boolean;
3
+ noGitChecks: boolean;
4
+ }
5
+ export declare function createTags(options: CreateTagsOptions): string[];
@@ -0,0 +1,73 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { readFileSync, unlinkSync } from "node:fs";
3
+ import { RELEASE_TAGS_FILE } from "./prepareCommand.js";
4
+ function createTags(options) {
5
+ const { dryRun, noGitChecks } = options;
6
+ let content;
7
+ try {
8
+ content = readFileSync(RELEASE_TAGS_FILE, "utf8");
9
+ } catch {
10
+ throw new Error("No tags file found. Run `release-kit prepare` first.");
11
+ }
12
+ const tags = content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
13
+ if (tags.length === 0) {
14
+ return [];
15
+ }
16
+ if (!dryRun && !noGitChecks) {
17
+ assertCleanWorkingTree();
18
+ }
19
+ if (dryRun) {
20
+ console.info("[dry-run] Would create tags:");
21
+ for (const tag of tags) {
22
+ console.info(`\u{1F3F7}\uFE0F ${tag}`);
23
+ }
24
+ return tags;
25
+ }
26
+ const created = [];
27
+ for (const tag of tags) {
28
+ try {
29
+ execFileSync("git", ["tag", "-a", tag, "-m", tag]);
30
+ created.push(tag);
31
+ } catch (error) {
32
+ if (created.length > 0) {
33
+ console.warn("Tags created before failure:");
34
+ for (const t of created) {
35
+ console.warn(` ${t}`);
36
+ }
37
+ }
38
+ throw error;
39
+ }
40
+ }
41
+ console.info("Created tags:");
42
+ for (const tag of tags) {
43
+ console.info(`\u{1F3F7}\uFE0F ${tag}`);
44
+ }
45
+ deleteTagsFile();
46
+ return tags;
47
+ }
48
+ function deleteTagsFile() {
49
+ try {
50
+ unlinkSync(RELEASE_TAGS_FILE);
51
+ } catch (error) {
52
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
53
+ return;
54
+ }
55
+ throw error;
56
+ }
57
+ }
58
+ function assertCleanWorkingTree() {
59
+ try {
60
+ execFileSync("git", ["diff", "--quiet"]);
61
+ execFileSync("git", ["diff", "--quiet", "--cached"]);
62
+ } catch (error) {
63
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
64
+ throw error;
65
+ }
66
+ throw new Error(
67
+ "Working tree is dirty. Commit or stash changes before tagging, or use `--no-git-checks` to skip this check."
68
+ );
69
+ }
70
+ }
71
+ export {
72
+ createTags
73
+ };
@@ -0,0 +1,2 @@
1
+ export type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'yarn-berry';
2
+ export declare function detectPackageManager(): PackageManager;
@@ -0,0 +1,44 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { isRecord } from "./typeGuards.js";
4
+ function detectPackageManager() {
5
+ const packageJsonPath = join(process.cwd(), "package.json");
6
+ try {
7
+ const content = readFileSync(packageJsonPath, "utf8");
8
+ const parsed = JSON.parse(content);
9
+ if (isRecord(parsed) && typeof parsed.packageManager === "string") {
10
+ const [name, version] = parsed.packageManager.split("@");
11
+ if (name === "pnpm" || name === "npm") {
12
+ return name;
13
+ }
14
+ if (name === "yarn") {
15
+ return isYarnBerry(version) ? "yarn-berry" : "yarn";
16
+ }
17
+ }
18
+ } catch {
19
+ }
20
+ return detectFromLockfile();
21
+ }
22
+ function isYarnBerry(version) {
23
+ if (version === void 0) {
24
+ return false;
25
+ }
26
+ const major = Number.parseInt(version, 10);
27
+ return !Number.isNaN(major) && major >= 2;
28
+ }
29
+ function detectFromLockfile() {
30
+ const cwd = process.cwd();
31
+ if (existsSync(join(cwd, "pnpm-lock.yaml"))) {
32
+ return "pnpm";
33
+ }
34
+ if (existsSync(join(cwd, "package-lock.json"))) {
35
+ return "npm";
36
+ }
37
+ if (existsSync(join(cwd, "yarn.lock"))) {
38
+ return "yarn";
39
+ }
40
+ return "npm";
41
+ }
42
+ export {
43
+ detectPackageManager
44
+ };
@@ -0,0 +1 @@
1
+ export declare function findPackageRoot(fromUrl: string): string;
@@ -0,0 +1,17 @@
1
+ import { existsSync } from "node:fs";
2
+ import { dirname, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ function findPackageRoot(fromUrl) {
5
+ let dir = dirname(fileURLToPath(fromUrl));
6
+ while (!existsSync(resolve(dir, "package.json"))) {
7
+ const parent = dirname(dir);
8
+ if (parent === dir) {
9
+ throw new Error("Could not find package root from " + fromUrl);
10
+ }
11
+ dir = parent;
12
+ }
13
+ return dir;
14
+ }
15
+ export {
16
+ findPackageRoot
17
+ };
@@ -0,0 +1,3 @@
1
+ export declare function bold(text: string): string;
2
+ export declare function dim(text: string): string;
3
+ export declare function sectionHeader(name: string): string;
@@ -0,0 +1,14 @@
1
+ function bold(text) {
2
+ return `\x1B[1m${text}\x1B[0m`;
3
+ }
4
+ function dim(text) {
5
+ return `\x1B[2m${text}\x1B[0m`;
6
+ }
7
+ function sectionHeader(name) {
8
+ return `\u2501\u2501\u2501 ${name} \u2501\u2501\u2501`;
9
+ }
10
+ export {
11
+ bold,
12
+ dim,
13
+ sectionHeader
14
+ };
@@ -2,5 +2,5 @@ import type { ReleaseConfig } from './types.ts';
2
2
  export interface GenerateChangelogOptions {
3
3
  includePaths?: string[];
4
4
  }
5
- export declare function generateChangelog(config: Pick<ReleaseConfig, 'cliffConfigPath'>, changelogPath: string, tag: string, dryRun: boolean, options?: GenerateChangelogOptions): void;
6
- export declare function generateChangelogs(config: ReleaseConfig, tag: string, dryRun: boolean): void;
5
+ export declare function generateChangelog(config: Pick<ReleaseConfig, 'cliffConfigPath'>, changelogPath: string, tag: string, dryRun: boolean, options?: GenerateChangelogOptions): string[];
6
+ export declare function generateChangelogs(config: ReleaseConfig, tag: string, dryRun: boolean): string[];
@@ -1,28 +1,29 @@
1
1
  import { execFileSync } from "node:child_process";
2
+ import { resolveCliffConfigPath } from "./resolveCliffConfigPath.js";
2
3
  function generateChangelog(config, changelogPath, tag, dryRun, options) {
3
- const cliffConfigPath = config.cliffConfigPath ?? "cliff.toml";
4
+ const cliffConfigPath = resolveCliffConfigPath(config.cliffConfigPath, import.meta.url);
4
5
  const outputFile = `${changelogPath}/CHANGELOG.md`;
5
6
  const args = ["--config", cliffConfigPath, "--output", outputFile, "--tag", tag];
6
7
  for (const includePath of options?.includePaths ?? []) {
7
8
  args.push("--include-path", includePath);
8
9
  }
9
- if (dryRun) {
10
- console.info(` [dry-run] Would run: npx --yes git-cliff ${args.join(" ")}`);
11
- return;
12
- }
13
- console.info(` Generating changelog: ${outputFile}`);
14
- try {
15
- execFileSync("npx", ["--yes", "git-cliff", ...args], { stdio: "inherit" });
16
- } catch (error) {
17
- throw new Error(
18
- `Failed to generate changelog for ${outputFile}: ${error instanceof Error ? error.message : String(error)}`
19
- );
10
+ if (!dryRun) {
11
+ try {
12
+ execFileSync("npx", ["--yes", "git-cliff", ...args], { stdio: "inherit" });
13
+ } catch (error) {
14
+ throw new Error(
15
+ `Failed to generate changelog for ${outputFile}: ${error instanceof Error ? error.message : String(error)}`
16
+ );
17
+ }
20
18
  }
19
+ return [outputFile];
21
20
  }
22
21
  function generateChangelogs(config, tag, dryRun) {
22
+ const results = [];
23
23
  for (const changelogPath of config.changelogPaths) {
24
- generateChangelog(config, changelogPath, tag, dryRun);
24
+ results.push(...generateChangelog(config, changelogPath, tag, dryRun));
25
25
  }
26
+ return results;
26
27
  }
27
28
  export {
28
29
  generateChangelog,
@@ -0,0 +1 @@
1
+ export declare function hasPrettierConfig(): boolean;
@@ -0,0 +1,42 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { isRecord } from "./typeGuards.js";
4
+ function hasPrettierConfig() {
5
+ const cwd = process.cwd();
6
+ for (const file of PRETTIER_CONFIG_FILES) {
7
+ if (existsSync(path.join(cwd, file))) {
8
+ return true;
9
+ }
10
+ }
11
+ return hasPrettierKeyInPackageJson(path.join(cwd, "package.json"));
12
+ }
13
+ function hasPrettierKeyInPackageJson(packageJsonPath) {
14
+ if (!existsSync(packageJsonPath)) {
15
+ return false;
16
+ }
17
+ try {
18
+ const parsed = JSON.parse(readFileSync(packageJsonPath, "utf8"));
19
+ return isRecord(parsed) && "prettier" in parsed;
20
+ } catch {
21
+ return false;
22
+ }
23
+ }
24
+ const PRETTIER_CONFIG_FILES = [
25
+ ".prettierrc",
26
+ ".prettierrc.cjs",
27
+ ".prettierrc.js",
28
+ ".prettierrc.json",
29
+ ".prettierrc.json5",
30
+ ".prettierrc.mjs",
31
+ ".prettierrc.toml",
32
+ ".prettierrc.ts",
33
+ ".prettierrc.yaml",
34
+ ".prettierrc.yml",
35
+ "prettier.config.cjs",
36
+ "prettier.config.js",
37
+ "prettier.config.mjs",
38
+ "prettier.config.ts"
39
+ ];
40
+ export {
41
+ hasPrettierConfig
42
+ };
@@ -1,15 +1,25 @@
1
+ export type { CreateTagsOptions } from './createTags.ts';
2
+ export type { PackageManager } from './detectPackageManager.ts';
1
3
  export type { GenerateChangelogOptions } from './generateChangelogs.ts';
4
+ export type { PublishOptions } from './publish.ts';
2
5
  export type { ReleasePrepareOptions } from './releasePrepare.ts';
3
- export type { Commit, ComponentConfig, ComponentOverride, MonorepoReleaseConfig, ParsedCommit, ReleaseConfig, ReleaseKitConfig, ReleaseType, VersionPatterns, WorkTypeConfig, } from './types.ts';
6
+ export type { ResolvedTag } from './resolveReleaseTags.ts';
7
+ export type { LabelDefinition, SyncLabelsConfig } from './sync-labels/types.ts';
8
+ export type { BumpResult, Commit, ComponentConfig, ComponentOverride, ComponentPrepareResult, MonorepoReleaseConfig, ParsedCommit, PrepareResult, ReleaseConfig, ReleaseKitConfig, ReleaseType, VersionPatterns, WorkTypeConfig, } from './types.ts';
4
9
  export { DEFAULT_VERSION_PATTERNS, DEFAULT_WORK_TYPES } from './defaults.ts';
5
10
  export { bumpAllVersions } from './bumpAllVersions.ts';
6
11
  export { bumpVersion } from './bumpVersion.ts';
7
12
  export { component } from './component.ts';
13
+ export { createTags } from './createTags.ts';
14
+ export { detectPackageManager } from './detectPackageManager.ts';
8
15
  export { determineBumpType } from './determineBumpType.ts';
9
16
  export { discoverWorkspaces } from './discoverWorkspaces.ts';
10
17
  export { generateChangelog, generateChangelogs } from './generateChangelogs.ts';
11
18
  export { getCommitsSinceTarget } from './getCommitsSinceTarget.ts';
12
19
  export { parseCommitMessage } from './parseCommitMessage.ts';
20
+ export { RELEASE_TAGS_FILE, writeReleaseTags } from './prepareCommand.ts';
21
+ export { publish } from './publish.ts';
13
22
  export { releasePrepare } from './releasePrepare.ts';
14
23
  export { releasePrepareMono } from './releasePrepareMono.ts';
15
- export { RELEASE_TAGS_FILE, runReleasePrepare, writeReleaseTags } from './runReleasePrepare.ts';
24
+ export { reportPrepare } from './reportPrepare.ts';
25
+ export { resolveReleaseTags } from './resolveReleaseTags.ts';
package/dist/esm/index.js CHANGED
@@ -2,14 +2,19 @@ import { DEFAULT_VERSION_PATTERNS, DEFAULT_WORK_TYPES } from "./defaults.js";
2
2
  import { bumpAllVersions } from "./bumpAllVersions.js";
3
3
  import { bumpVersion } from "./bumpVersion.js";
4
4
  import { component } from "./component.js";
5
+ import { createTags } from "./createTags.js";
6
+ import { detectPackageManager } from "./detectPackageManager.js";
5
7
  import { determineBumpType } from "./determineBumpType.js";
6
8
  import { discoverWorkspaces } from "./discoverWorkspaces.js";
7
9
  import { generateChangelog, generateChangelogs } from "./generateChangelogs.js";
8
10
  import { getCommitsSinceTarget } from "./getCommitsSinceTarget.js";
9
11
  import { parseCommitMessage } from "./parseCommitMessage.js";
12
+ import { RELEASE_TAGS_FILE, writeReleaseTags } from "./prepareCommand.js";
13
+ import { publish } from "./publish.js";
10
14
  import { releasePrepare } from "./releasePrepare.js";
11
15
  import { releasePrepareMono } from "./releasePrepareMono.js";
12
- import { RELEASE_TAGS_FILE, runReleasePrepare, writeReleaseTags } from "./runReleasePrepare.js";
16
+ import { reportPrepare } from "./reportPrepare.js";
17
+ import { resolveReleaseTags } from "./resolveReleaseTags.js";
13
18
  export {
14
19
  DEFAULT_VERSION_PATTERNS,
15
20
  DEFAULT_WORK_TYPES,
@@ -17,14 +22,18 @@ export {
17
22
  bumpAllVersions,
18
23
  bumpVersion,
19
24
  component,
25
+ createTags,
26
+ detectPackageManager,
20
27
  determineBumpType,
21
28
  discoverWorkspaces,
22
29
  generateChangelog,
23
30
  generateChangelogs,
24
31
  getCommitsSinceTarget,
25
32
  parseCommitMessage,
33
+ publish,
26
34
  releasePrepare,
27
35
  releasePrepareMono,
28
- runReleasePrepare,
36
+ reportPrepare,
37
+ resolveReleaseTags,
29
38
  writeReleaseTags
30
39
  };
@@ -5,5 +5,3 @@ export interface CheckResult {
5
5
  export declare function isGitRepo(): CheckResult;
6
6
  export declare function hasPackageJson(): CheckResult;
7
7
  export declare function usesPnpm(): CheckResult;
8
- export declare function hasCliffToml(): CheckResult;
9
- export declare function notAlreadyInitialized(): CheckResult;
@@ -29,25 +29,8 @@ function usesPnpm() {
29
29
  message: "This project does not appear to use pnpm. A pnpm-lock.yaml or packageManager field is required."
30
30
  };
31
31
  }
32
- function hasCliffToml() {
33
- if (existsSync("cliff.toml")) {
34
- return { ok: true };
35
- }
36
- return { ok: false, message: "No cliff.toml found. This file is required for changelog generation." };
37
- }
38
- function notAlreadyInitialized() {
39
- if (!existsSync(".config/release-kit.config.ts") && !existsSync(".github/scripts/release.config.ts")) {
40
- return { ok: true };
41
- }
42
- return {
43
- ok: false,
44
- message: "release-kit appears to be already initialized."
45
- };
46
- }
47
32
  export {
48
- hasCliffToml,
49
33
  hasPackageJson,
50
34
  isGitRepo,
51
- notAlreadyInitialized,
52
35
  usesPnpm
53
36
  };
@@ -1,5 +1,7 @@
1
1
  interface InitOptions {
2
2
  dryRun: boolean;
3
+ force: boolean;
4
+ withConfig: boolean;
3
5
  }
4
- export declare function initCommand({ dryRun }: InitOptions): Promise<number>;
6
+ export declare function initCommand({ dryRun, force, withConfig }: InitOptions): number;
5
7
  export {};
@@ -1,7 +1,7 @@
1
- import { hasCliffToml, hasPackageJson, isGitRepo, notAlreadyInitialized, usesPnpm } from "./checks.js";
1
+ import { printError, printStep, printSuccess, reportWriteResult } from "@williamthorsen/node-monorepo-core";
2
+ import { hasPackageJson, isGitRepo, usesPnpm } from "./checks.js";
2
3
  import { detectRepoType } from "./detectRepoType.js";
3
- import { confirm, printError, printStep, printSuccess } from "./prompt.js";
4
- import { copyCliffTemplate, scaffoldFiles } from "./scaffold.js";
4
+ import { scaffoldFiles } from "./scaffold.js";
5
5
  function runRequiredCheck(label, result) {
6
6
  if (result.ok) {
7
7
  printSuccess(label);
@@ -10,59 +10,54 @@ function runRequiredCheck(label, result) {
10
10
  printError(result.message ?? `${label} failed`);
11
11
  return false;
12
12
  }
13
- async function checkEligibility(dryRun) {
13
+ function checkEligibility() {
14
14
  printStep("Checking eligibility");
15
- if (!runRequiredCheck("Git repository detected", isGitRepo())) return { status: "fail", overwrite: false };
16
- if (!runRequiredCheck("package.json found", hasPackageJson())) return { status: "fail", overwrite: false };
17
- if (!runRequiredCheck("pnpm detected", usesPnpm())) return { status: "fail", overwrite: false };
18
- const cliffCheck = hasCliffToml();
19
- if (cliffCheck.ok) {
20
- printSuccess("cliff.toml found");
21
- } else {
22
- console.info("");
23
- const shouldCreate = await confirm("No cliff.toml found. Create one from the bundled template?");
24
- if (shouldCreate) {
25
- copyCliffTemplate(dryRun);
26
- } else {
27
- printError("cliff.toml is required for changelog generation. Aborting.");
28
- return { status: "fail", overwrite: false };
29
- }
30
- }
31
- const initCheck = notAlreadyInitialized();
32
- if (!initCheck.ok) {
33
- console.info("");
34
- const shouldOverwrite = await confirm("release-kit appears to be already initialized. Overwrite existing files?");
35
- if (!shouldOverwrite) {
36
- console.info("Aborting.");
37
- return { status: "abort", overwrite: false };
38
- }
39
- return { status: "pass", overwrite: true };
40
- }
41
- return { status: "pass", overwrite: false };
15
+ if (!runRequiredCheck("Git repository detected", isGitRepo())) return false;
16
+ if (!runRequiredCheck("package.json found", hasPackageJson())) return false;
17
+ if (!runRequiredCheck("pnpm detected", usesPnpm())) return false;
18
+ return true;
42
19
  }
43
- async function initCommand({ dryRun }) {
20
+ function initCommand({ dryRun, force, withConfig }) {
44
21
  if (dryRun) {
45
22
  console.info("[dry-run mode]");
46
23
  }
47
- let eligibility;
24
+ let eligible;
48
25
  try {
49
- eligibility = await checkEligibility(dryRun);
26
+ eligible = checkEligibility();
50
27
  } catch (error) {
51
28
  printError(`Eligibility check failed: ${error instanceof Error ? error.message : String(error)}`);
52
29
  return 1;
53
30
  }
54
- if (eligibility.status === "fail") return 1;
55
- if (eligibility.status === "abort") return 0;
31
+ if (!eligible) return 1;
56
32
  printStep("Detecting repo type");
57
- const repoType = detectRepoType();
33
+ let repoType;
34
+ try {
35
+ repoType = detectRepoType();
36
+ } catch (error) {
37
+ printError(`Failed to detect repo type: ${error instanceof Error ? error.message : String(error)}`);
38
+ return 1;
39
+ }
58
40
  printSuccess(`Detected: ${repoType}`);
59
41
  printStep("Scaffolding files");
60
- scaffoldFiles({ repoType, dryRun, overwrite: eligibility.overwrite });
42
+ let results;
43
+ try {
44
+ results = scaffoldFiles({ repoType, dryRun, overwrite: force, withConfig });
45
+ } catch (error) {
46
+ printError(`Failed to scaffold files: ${error instanceof Error ? error.message : String(error)}`);
47
+ return 1;
48
+ }
49
+ for (const result of results) {
50
+ reportWriteResult(result, dryRun);
51
+ }
52
+ if (results.some((r) => r.outcome === "failed")) {
53
+ return 1;
54
+ }
61
55
  printStep("Next steps");
56
+ const configHint = withConfig ? "1. (Optional) Customize .config/release-kit.config.ts and .config/git-cliff.toml." : "1. (Optional) Run again with --with-config to scaffold config files.";
62
57
  console.info(`
63
- 1. (Optional) Customize .config/release-kit.config.ts to exclude components, override version patterns, add custom work types, etc.
58
+ ${configHint}
64
59
  2. Test by running: npx @williamthorsen/release-kit prepare --dry-run
65
- 3. Commit the generated workflow file (and config file if created).
60
+ 3. Commit the generated files.
66
61
  `);
67
62
  return 0;
68
63
  }
@@ -1,4 +1,3 @@
1
- export declare function confirm(question: string): Promise<boolean>;
2
1
  export declare function printStep(message: string): void;
3
2
  export declare function printSuccess(message: string): void;
4
3
  export declare function printSkip(message: string): void;
@@ -1,28 +1,17 @@
1
- import { createInterface } from "node:readline/promises";
2
- async function confirm(question) {
3
- const rl = createInterface({ input: process.stdin, output: process.stdout });
4
- try {
5
- const answer = await rl.question(`${question} (y/n) `);
6
- return answer.trim().toLowerCase().startsWith("y");
7
- } finally {
8
- rl.close();
9
- }
10
- }
11
1
  function printStep(message) {
12
2
  console.info(`
13
3
  > ${message}`);
14
4
  }
15
5
  function printSuccess(message) {
16
- console.info(` [ok] ${message}`);
6
+ console.info(` \u2705 ${message}`);
17
7
  }
18
8
  function printSkip(message) {
19
- console.info(` [skip] ${message}`);
9
+ console.info(` \u26A0\uFE0F ${message}`);
20
10
  }
21
11
  function printError(message) {
22
- console.error(` [error] ${message}`);
12
+ console.error(` \u274C ${message}`);
23
13
  }
24
14
  export {
25
- confirm,
26
15
  printError,
27
16
  printSkip,
28
17
  printStep,
@@ -1,9 +1,11 @@
1
+ import type { WriteResult } from '@williamthorsen/node-monorepo-core';
1
2
  import type { RepoType } from './detectRepoType.ts';
2
3
  interface ScaffoldOptions {
3
4
  repoType: RepoType;
4
5
  dryRun: boolean;
5
6
  overwrite: boolean;
7
+ withConfig: boolean;
6
8
  }
7
- export declare function copyCliffTemplate(dryRun: boolean): void;
8
- export declare function scaffoldFiles({ repoType, dryRun, overwrite }: ScaffoldOptions): void;
9
+ export declare function copyCliffTemplate(dryRun: boolean, overwrite: boolean): WriteResult;
10
+ export declare function scaffoldFiles({ repoType, dryRun, overwrite, withConfig }: ScaffoldOptions): WriteResult[];
9
11
  export {};