@doki-land/nifty 0.0.3 → 0.0.5

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 (44) hide show
  1. package/cli/nifty.mjs +11 -11
  2. package/locales/en-US/cli.json +68 -68
  3. package/locales/locales.json +4 -4
  4. package/package.json +16 -20
  5. package/src/cli/authArgs.ts +52 -52
  6. package/src/cli/bump-cmd.ts +55 -55
  7. package/src/cli/bump.ts +237 -237
  8. package/src/cli/change-logs-cmd.ts +97 -97
  9. package/src/cli/cli.ts +47 -47
  10. package/src/cli/context.ts +36 -36
  11. package/src/cli/format-cmd.ts +23 -23
  12. package/src/cli/format.ts +28 -28
  13. package/src/cli/index.ts +9 -9
  14. package/src/cli/install-native-cmd.ts +22 -22
  15. package/src/cli/install-native.ts +80 -80
  16. package/src/cli/lint-cmd.ts +35 -35
  17. package/src/cli/lint.ts +102 -102
  18. package/src/cli/main.ts +1 -1
  19. package/src/cli/options.ts +40 -40
  20. package/src/cli/publish-cmd.ts +38 -38
  21. package/src/cli/publish.ts +95 -95
  22. package/src/cli/resolveNpm.ts +16 -16
  23. package/src/cli/retime-cmd.ts +62 -62
  24. package/src/cli/reword-cmd.ts +87 -87
  25. package/src/cli/trust-cmd.ts +38 -38
  26. package/src/cli/trust.ts +83 -83
  27. package/src/cli/update-cmd.ts +20 -20
  28. package/src/cli/update.ts +32 -32
  29. package/src/cli/upload-cmd.ts +44 -44
  30. package/src/cli/upload.ts +313 -313
  31. package/src/cli/workspace.ts +124 -124
  32. package/src/config/defineConfig.ts +12 -12
  33. package/src/config/detectProject.ts +189 -189
  34. package/src/config/index.ts +25 -25
  35. package/src/config/loadConfig.ts +129 -129
  36. package/src/config/types.ts +57 -57
  37. package/src/git.ts +61 -61
  38. package/src/github.ts +184 -184
  39. package/src/gitmoji.ts +77 -77
  40. package/src/index.ts +36 -36
  41. package/src/native.ts +665 -665
  42. package/src/nifty.ts +95 -95
  43. package/src/types.ts +40 -40
  44. package/README.md +0 -29
package/src/cli/bump.ts CHANGED
@@ -1,237 +1,237 @@
1
- import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
2
- import { join } from "node:path";
3
-
4
- import { findWorkspaceRoot } from "./workspace.js";
5
-
6
- export type BumpKind = "patch" | "minor" | "major";
7
-
8
- export type BumpOptions = {
9
- /** Explicit target version. When set, overrides `kind`. */
10
- version?: string;
11
- kind?: BumpKind;
12
- cwd?: string;
13
- dryRun?: boolean;
14
- };
15
-
16
- export type BumpReport = {
17
- root: string;
18
- from: string;
19
- to: string;
20
- kind?: BumpKind;
21
- cargo: string[];
22
- npm: string[];
23
- };
24
-
25
- const DEFAULT_KIND: BumpKind = "patch";
26
-
27
- export async function runBump(argv: string[]): Promise<void> {
28
- const options = parseBumpArgs(argv);
29
- const report = bumpWorkspace(options);
30
- printBumpReport(report, options.dryRun);
31
- }
32
-
33
- function parseBumpArgs(argv: string[]): BumpOptions {
34
- let version: string | undefined;
35
- let kind: BumpKind = DEFAULT_KIND;
36
- let cwd: string | undefined;
37
- let dryRun = false;
38
-
39
- for (let i = 0; i < argv.length; i += 1) {
40
- const arg = argv[i];
41
- if (arg === "--dry-run") {
42
- dryRun = true;
43
- } else if (arg === "-C" || arg === "--cwd") {
44
- cwd = argv[++i];
45
- } else if (arg === "--version" || arg === "-v") {
46
- version = argv[++i];
47
- } else if (arg === "--patch") {
48
- kind = "patch";
49
- } else if (arg === "--minor") {
50
- kind = "minor";
51
- } else if (arg === "--major") {
52
- kind = "major";
53
- } else if (arg === "-h" || arg === "--help") {
54
- printBumpHelp();
55
- process.exit(0);
56
- } else if (!arg.startsWith("-")) {
57
- if (isBumpKind(arg)) {
58
- kind = arg;
59
- } else {
60
- version = arg;
61
- }
62
- }
63
- }
64
-
65
- if (version) {
66
- assertVersion(version);
67
- }
68
- return { version, kind, cwd, dryRun };
69
- }
70
-
71
- function isBumpKind(value: string): value is BumpKind {
72
- return value === "patch" || value === "minor" || value === "major";
73
- }
74
-
75
- function assertVersion(version: string): void {
76
- if (!/^\d+\.\d+\.\d+(-[\w.-]+)?$/.test(version)) {
77
- throw new Error(`invalid version ${version}, expected semver like 0.0.0`);
78
- }
79
- }
80
-
81
- export function bumpWorkspace(options: BumpOptions): BumpReport {
82
- const root = findWorkspaceRoot(options.cwd ?? process.cwd());
83
- const cratesDir = join(root, "projects", "crates");
84
- const packagesDir = join(root, "projects", "packages");
85
-
86
- const cargoPaths = collectCargoManifests(cratesDir);
87
- const npmPaths = collectPackageManifests(packagesDir);
88
- const from = readWorkspaceVersion(cargoPaths, npmPaths);
89
- const to = options.version ?? bumpSemver(from, options.kind ?? DEFAULT_KIND);
90
- assertVersion(to);
91
-
92
- const cargo = cargoPaths.map((path) => bumpCargoManifest(path, to, options.dryRun));
93
- const npm = npmPaths.map((path) => bumpPackageManifest(path, to, options.dryRun));
94
-
95
- return {
96
- root,
97
- from,
98
- to,
99
- kind: options.version ? undefined : options.kind ?? DEFAULT_KIND,
100
- cargo,
101
- npm,
102
- };
103
- }
104
-
105
- function readWorkspaceVersion(cargoPaths: string[], npmPaths: string[]): string {
106
- const versions = new Set<string>();
107
- for (const path of cargoPaths) {
108
- versions.add(readCargoVersion(path));
109
- }
110
- for (const path of npmPaths) {
111
- versions.add(readPackageVersion(path));
112
- }
113
- if (versions.size === 0) {
114
- throw new Error("no crate or package versions found in workspace");
115
- }
116
- if (versions.size > 1) {
117
- throw new Error(`workspace versions are out of sync: ${[...versions].sort().join(", ")}`);
118
- }
119
- return [...versions][0];
120
- }
121
-
122
- function readCargoVersion(path: string): string {
123
- const original = readFileSync(path, "utf8");
124
- const match = original.match(/^version\s*=\s*"([^"]*)"/m);
125
- if (!match) {
126
- throw new Error(`no [package].version found in ${path}`);
127
- }
128
- return match[1];
129
- }
130
-
131
- function readPackageVersion(path: string): string {
132
- const parsed = JSON.parse(readFileSync(path, "utf8")) as { version?: string };
133
- if (!parsed.version) {
134
- throw new Error(`no version field in ${path}`);
135
- }
136
- return parsed.version;
137
- }
138
-
139
- export function bumpSemver(version: string, kind: BumpKind): string {
140
- const match = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-.*)?$/);
141
- if (!match) {
142
- throw new Error(`cannot bump non-semver version ${version}`);
143
- }
144
- let major = Number(match[1]);
145
- let minor = Number(match[2]);
146
- let patch = Number(match[3]);
147
-
148
- switch (kind) {
149
- case "patch":
150
- patch += 1;
151
- break;
152
- case "minor":
153
- minor += 1;
154
- patch = 0;
155
- break;
156
- case "major":
157
- major += 1;
158
- minor = 0;
159
- patch = 0;
160
- break;
161
- }
162
-
163
- return `${major}.${minor}.${patch}`;
164
- }
165
-
166
- function collectCargoManifests(cratesDir: string): string[] {
167
- if (!existsSync(cratesDir)) {
168
- return [];
169
- }
170
- return readdirSync(cratesDir, { withFileTypes: true })
171
- .filter((entry) => entry.isDirectory())
172
- .map((entry) => join(cratesDir, entry.name, "Cargo.toml"))
173
- .filter((path) => existsSync(path));
174
- }
175
-
176
- function collectPackageManifests(packagesDir: string): string[] {
177
- if (!existsSync(packagesDir)) {
178
- return [];
179
- }
180
- return readdirSync(packagesDir, { withFileTypes: true })
181
- .filter((entry) => entry.isDirectory() && entry.name !== "node_modules")
182
- .map((entry) => join(packagesDir, entry.name, "package.json"))
183
- .filter((path) => existsSync(path));
184
- }
185
-
186
- function bumpCargoManifest(path: string, version: string, dryRun?: boolean): string {
187
- const original = readFileSync(path, "utf8");
188
- const next = original.replace(/^version\s*=\s*"[^"]*"/m, `version = "${version}"`);
189
- if (next === original) {
190
- throw new Error(`no [package].version found in ${path}`);
191
- }
192
- if (!dryRun) {
193
- writeFileSync(path, next, "utf8");
194
- }
195
- return path;
196
- }
197
-
198
- function bumpPackageManifest(path: string, version: string, dryRun?: boolean): string {
199
- const original = readFileSync(path, "utf8");
200
- const parsed = JSON.parse(original) as { version?: string };
201
- if (!parsed.version) {
202
- throw new Error(`no version field in ${path}`);
203
- }
204
- parsed.version = version;
205
- const next = `${JSON.stringify(parsed, null, 4)}\n`;
206
- if (!dryRun) {
207
- writeFileSync(path, next, "utf8");
208
- }
209
- return path;
210
- }
211
-
212
- export function printBumpReport(report: BumpReport, dryRun?: boolean): void {
213
- const prefix = dryRun ? "would bump" : "bumped";
214
- const kind = report.kind ? ` (${report.kind})` : "";
215
- console.log(`${prefix} workspace ${report.root} ${report.from} -> ${report.to}${kind}`);
216
- for (const path of report.cargo) {
217
- console.log(` cargo: ${path}`);
218
- }
219
- for (const path of report.npm) {
220
- console.log(` npm: ${path}`);
221
- }
222
- }
223
-
224
- function printBumpHelp(): void {
225
- console.log(`nifty bump — align all crate and package versions in the workspace
226
-
227
- Usage:
228
- nifty bump # bump patch (default)
229
- nifty bump patch
230
- nifty bump minor
231
- nifty bump major
232
- nifty bump 1.2.3 # set explicit version
233
- nifty bump --version 1.2.3
234
- nifty bump --dry-run
235
- nifty bump -C <cwd>
236
- `);
237
- }
1
+ import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ import { findWorkspaceRoot } from "./workspace.js";
5
+
6
+ export type BumpKind = "patch" | "minor" | "major";
7
+
8
+ export type BumpOptions = {
9
+ /** Explicit target version. When set, overrides `kind`. */
10
+ version?: string;
11
+ kind?: BumpKind;
12
+ cwd?: string;
13
+ dryRun?: boolean;
14
+ };
15
+
16
+ export type BumpReport = {
17
+ root: string;
18
+ from: string;
19
+ to: string;
20
+ kind?: BumpKind;
21
+ cargo: string[];
22
+ npm: string[];
23
+ };
24
+
25
+ const DEFAULT_KIND: BumpKind = "patch";
26
+
27
+ export async function runBump(argv: string[]): Promise<void> {
28
+ const options = parseBumpArgs(argv);
29
+ const report = bumpWorkspace(options);
30
+ printBumpReport(report, options.dryRun);
31
+ }
32
+
33
+ function parseBumpArgs(argv: string[]): BumpOptions {
34
+ let version: string | undefined;
35
+ let kind: BumpKind = DEFAULT_KIND;
36
+ let cwd: string | undefined;
37
+ let dryRun = false;
38
+
39
+ for (let i = 0; i < argv.length; i += 1) {
40
+ const arg = argv[i];
41
+ if (arg === "--dry-run") {
42
+ dryRun = true;
43
+ } else if (arg === "-C" || arg === "--cwd") {
44
+ cwd = argv[++i];
45
+ } else if (arg === "--version" || arg === "-v") {
46
+ version = argv[++i];
47
+ } else if (arg === "--patch") {
48
+ kind = "patch";
49
+ } else if (arg === "--minor") {
50
+ kind = "minor";
51
+ } else if (arg === "--major") {
52
+ kind = "major";
53
+ } else if (arg === "-h" || arg === "--help") {
54
+ printBumpHelp();
55
+ process.exit(0);
56
+ } else if (!arg.startsWith("-")) {
57
+ if (isBumpKind(arg)) {
58
+ kind = arg;
59
+ } else {
60
+ version = arg;
61
+ }
62
+ }
63
+ }
64
+
65
+ if (version) {
66
+ assertVersion(version);
67
+ }
68
+ return { version, kind, cwd, dryRun };
69
+ }
70
+
71
+ function isBumpKind(value: string): value is BumpKind {
72
+ return value === "patch" || value === "minor" || value === "major";
73
+ }
74
+
75
+ function assertVersion(version: string): void {
76
+ if (!/^\d+\.\d+\.\d+(-[\w.-]+)?$/.test(version)) {
77
+ throw new Error(`invalid version ${version}, expected semver like 0.0.0`);
78
+ }
79
+ }
80
+
81
+ export function bumpWorkspace(options: BumpOptions): BumpReport {
82
+ const root = findWorkspaceRoot(options.cwd ?? process.cwd());
83
+ const cratesDir = join(root, "projects", "crates");
84
+ const packagesDir = join(root, "projects", "packages");
85
+
86
+ const cargoPaths = collectCargoManifests(cratesDir);
87
+ const npmPaths = collectPackageManifests(packagesDir);
88
+ const from = readWorkspaceVersion(cargoPaths, npmPaths);
89
+ const to = options.version ?? bumpSemver(from, options.kind ?? DEFAULT_KIND);
90
+ assertVersion(to);
91
+
92
+ const cargo = cargoPaths.map((path) => bumpCargoManifest(path, to, options.dryRun));
93
+ const npm = npmPaths.map((path) => bumpPackageManifest(path, to, options.dryRun));
94
+
95
+ return {
96
+ root,
97
+ from,
98
+ to,
99
+ kind: options.version ? undefined : options.kind ?? DEFAULT_KIND,
100
+ cargo,
101
+ npm,
102
+ };
103
+ }
104
+
105
+ function readWorkspaceVersion(cargoPaths: string[], npmPaths: string[]): string {
106
+ const versions = new Set<string>();
107
+ for (const path of cargoPaths) {
108
+ versions.add(readCargoVersion(path));
109
+ }
110
+ for (const path of npmPaths) {
111
+ versions.add(readPackageVersion(path));
112
+ }
113
+ if (versions.size === 0) {
114
+ throw new Error("no crate or package versions found in workspace");
115
+ }
116
+ if (versions.size > 1) {
117
+ throw new Error(`workspace versions are out of sync: ${[...versions].sort().join(", ")}`);
118
+ }
119
+ return [...versions][0];
120
+ }
121
+
122
+ function readCargoVersion(path: string): string {
123
+ const original = readFileSync(path, "utf8");
124
+ const match = original.match(/^version\s*=\s*"([^"]*)"/m);
125
+ if (!match) {
126
+ throw new Error(`no [package].version found in ${path}`);
127
+ }
128
+ return match[1];
129
+ }
130
+
131
+ function readPackageVersion(path: string): string {
132
+ const parsed = JSON.parse(readFileSync(path, "utf8")) as { version?: string };
133
+ if (!parsed.version) {
134
+ throw new Error(`no version field in ${path}`);
135
+ }
136
+ return parsed.version;
137
+ }
138
+
139
+ export function bumpSemver(version: string, kind: BumpKind): string {
140
+ const match = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-.*)?$/);
141
+ if (!match) {
142
+ throw new Error(`cannot bump non-semver version ${version}`);
143
+ }
144
+ let major = Number(match[1]);
145
+ let minor = Number(match[2]);
146
+ let patch = Number(match[3]);
147
+
148
+ switch (kind) {
149
+ case "patch":
150
+ patch += 1;
151
+ break;
152
+ case "minor":
153
+ minor += 1;
154
+ patch = 0;
155
+ break;
156
+ case "major":
157
+ major += 1;
158
+ minor = 0;
159
+ patch = 0;
160
+ break;
161
+ }
162
+
163
+ return `${major}.${minor}.${patch}`;
164
+ }
165
+
166
+ function collectCargoManifests(cratesDir: string): string[] {
167
+ if (!existsSync(cratesDir)) {
168
+ return [];
169
+ }
170
+ return readdirSync(cratesDir, { withFileTypes: true })
171
+ .filter((entry) => entry.isDirectory())
172
+ .map((entry) => join(cratesDir, entry.name, "Cargo.toml"))
173
+ .filter((path) => existsSync(path));
174
+ }
175
+
176
+ function collectPackageManifests(packagesDir: string): string[] {
177
+ if (!existsSync(packagesDir)) {
178
+ return [];
179
+ }
180
+ return readdirSync(packagesDir, { withFileTypes: true })
181
+ .filter((entry) => entry.isDirectory() && entry.name !== "node_modules")
182
+ .map((entry) => join(packagesDir, entry.name, "package.json"))
183
+ .filter((path) => existsSync(path));
184
+ }
185
+
186
+ function bumpCargoManifest(path: string, version: string, dryRun?: boolean): string {
187
+ const original = readFileSync(path, "utf8");
188
+ const next = original.replace(/^version\s*=\s*"[^"]*"/m, `version = "${version}"`);
189
+ if (next === original) {
190
+ throw new Error(`no [package].version found in ${path}`);
191
+ }
192
+ if (!dryRun) {
193
+ writeFileSync(path, next, "utf8");
194
+ }
195
+ return path;
196
+ }
197
+
198
+ function bumpPackageManifest(path: string, version: string, dryRun?: boolean): string {
199
+ const original = readFileSync(path, "utf8");
200
+ const parsed = JSON.parse(original) as { version?: string };
201
+ if (!parsed.version) {
202
+ throw new Error(`no version field in ${path}`);
203
+ }
204
+ parsed.version = version;
205
+ const next = `${JSON.stringify(parsed, null, 4)}\n`;
206
+ if (!dryRun) {
207
+ writeFileSync(path, next, "utf8");
208
+ }
209
+ return path;
210
+ }
211
+
212
+ export function printBumpReport(report: BumpReport, dryRun?: boolean): void {
213
+ const prefix = dryRun ? "would bump" : "bumped";
214
+ const kind = report.kind ? ` (${report.kind})` : "";
215
+ console.log(`${prefix} workspace ${report.root} ${report.from} -> ${report.to}${kind}`);
216
+ for (const path of report.cargo) {
217
+ console.log(` cargo: ${path}`);
218
+ }
219
+ for (const path of report.npm) {
220
+ console.log(` npm: ${path}`);
221
+ }
222
+ }
223
+
224
+ function printBumpHelp(): void {
225
+ console.log(`nifty bump — align all crate and package versions in the workspace
226
+
227
+ Usage:
228
+ nifty bump # bump patch (default)
229
+ nifty bump patch
230
+ nifty bump minor
231
+ nifty bump major
232
+ nifty bump 1.2.3 # set explicit version
233
+ nifty bump --version 1.2.3
234
+ nifty bump --dry-run
235
+ nifty bump -C <cwd>
236
+ `);
237
+ }
@@ -1,97 +1,97 @@
1
- import { join } from "node:path";
2
-
3
- import type { Cli, ParsedOptions } from "@vmz/commander";
4
-
5
- import { loadConfig } from "../config/loadConfig.js";
6
- import { loadNiftyNative } from "../native.js";
7
- import { bootstrapFromOptions } from "./context.js";
8
- import { cwdFrom, flag, str } from "./options.js";
9
-
10
- export function registerChangeLogsCommand(cli: Cli): void {
11
- const changeLogs = cli.command("change-logs", "cli.cmd.change-logs")
12
- .option("--version <version>", "cli.opt.version")
13
- .option("--from <ref>", "cli.opt.from")
14
- .option("--to <ref>", "cli.opt.to")
15
- .option("--write", "cli.opt.write")
16
- .option("--tags", "cli.opt.tags")
17
- .option("--repo <owner/name>", "cli.opt.repo")
18
- .option("--author-map <path>", "cli.opt.author-map")
19
- .option("--releases-dir <path>", "cli.opt.releases-dir");
20
-
21
- changeLogs
22
- .command("lookup", "cli.cmd.change-logs.lookup")
23
- .option("--email <email>", "cli.opt.email")
24
- .option("--login <login>", "cli.opt.login")
25
- .option("--map <path>", "cli.opt.author-map")
26
- .option("--github-token <token>", "cli.opt.github-token")
27
- .option("--fetch", "cli.opt.fetch")
28
- .action(async (options) => cmdChangeLogsLookup(options));
29
-
30
- changeLogs.action(async (options) => cmdChangeLogsRender(options));
31
- }
32
-
33
- export async function cmdChangeLogsRender(options: ParsedOptions): Promise<number> {
34
- await bootstrapFromOptions(options);
35
- const cwd = cwdFrom(options) ?? process.cwd();
36
- const { config } = await loadConfig({ cwd, createIfMissing: false });
37
- const native = loadNiftyNative();
38
- const report = native.history["changelog-render"]({
39
- cwd,
40
- version: str(options, "version"),
41
- fromRef: str(options, "from"),
42
- toRef: str(options, "to"),
43
- write: flag(options, "write"),
44
- tags: flag(options, "tags"),
45
- repo: str(options, "repo") ?? config.changelog?.repo,
46
- authorMap: str(options, "author-map") ?? config.changelog?.authorMap,
47
- releasesDir: str(options, "releases-dir") ?? config.changelog?.releasesDir,
48
- });
49
-
50
- if (flag(options, "tags")) {
51
- console.log(report.notes);
52
- return 0;
53
- }
54
-
55
- if (report.rangeLabel) {
56
- console.error(`change-logs: ${report.rangeLabel} — ${report.commitCount} commit(s)`);
57
- }
58
- if (report.writtenPath) {
59
- console.error(`change-logs: wrote ${report.writtenPath}`);
60
- }
61
- process.stdout.write(report.notes);
62
- return 0;
63
- }
64
-
65
- export async function cmdChangeLogsLookup(options: ParsedOptions): Promise<number> {
66
- await bootstrapFromOptions(options);
67
- const cwd = cwdFrom(options) ?? process.cwd();
68
- const { config } = await loadConfig({ cwd, createIfMissing: false });
69
- const email = str(options, "email");
70
- const login = str(options, "login");
71
- if (!email && !login) {
72
- throw new Error("lookup requires --email or --login");
73
- }
74
- const native = loadNiftyNative();
75
- const author = native.history["changelog-lookup"]({
76
- cwd,
77
- email,
78
- login,
79
- map: str(options, "map") ?? config.changelog?.authorMap,
80
- githubToken: str(options, "github-token") ?? config.githubToken ?? process.env.GITHUB_TOKEN,
81
- fetch: flag(options, "fetch"),
82
- });
83
- const json: Record<string, unknown> = {};
84
- if (author.id !== undefined) {
85
- json.id = author.id;
86
- }
87
- if (author.login) {
88
- json.login = author.login;
89
- }
90
- console.log(JSON.stringify(json, null, 2));
91
- return 0;
92
- }
93
-
94
- /** Default author map path relative to repo root (matches git-change-logs). */
95
- export function defaultAuthorMapPath(repoRoot: string): string {
96
- return join(repoRoot, "documentation/maintenance/author-github.json");
97
- }
1
+ import { join } from "node:path";
2
+
3
+ import type { Cli, ParsedOptions } from "@vmz/commander";
4
+
5
+ import { loadConfig } from "../config/loadConfig.js";
6
+ import { loadNiftyNative } from "../native.js";
7
+ import { bootstrapFromOptions } from "./context.js";
8
+ import { cwdFrom, flag, str } from "./options.js";
9
+
10
+ export function registerChangeLogsCommand(cli: Cli): void {
11
+ const changeLogs = cli.command("change-logs", "cli.cmd.change-logs")
12
+ .option("--version <version>", "cli.opt.version")
13
+ .option("--from <ref>", "cli.opt.from")
14
+ .option("--to <ref>", "cli.opt.to")
15
+ .option("--write", "cli.opt.write")
16
+ .option("--tags", "cli.opt.tags")
17
+ .option("--repo <owner/name>", "cli.opt.repo")
18
+ .option("--author-map <path>", "cli.opt.author-map")
19
+ .option("--releases-dir <path>", "cli.opt.releases-dir");
20
+
21
+ changeLogs
22
+ .command("lookup", "cli.cmd.change-logs.lookup")
23
+ .option("--email <email>", "cli.opt.email")
24
+ .option("--login <login>", "cli.opt.login")
25
+ .option("--map <path>", "cli.opt.author-map")
26
+ .option("--github-token <token>", "cli.opt.github-token")
27
+ .option("--fetch", "cli.opt.fetch")
28
+ .action(async (options) => cmdChangeLogsLookup(options));
29
+
30
+ changeLogs.action(async (options) => cmdChangeLogsRender(options));
31
+ }
32
+
33
+ export async function cmdChangeLogsRender(options: ParsedOptions): Promise<number> {
34
+ await bootstrapFromOptions(options);
35
+ const cwd = cwdFrom(options) ?? process.cwd();
36
+ const { config } = await loadConfig({ cwd, createIfMissing: false });
37
+ const native = loadNiftyNative();
38
+ const report = native.history["changelog-render"]({
39
+ cwd,
40
+ version: str(options, "version"),
41
+ fromRef: str(options, "from"),
42
+ toRef: str(options, "to"),
43
+ write: flag(options, "write"),
44
+ tags: flag(options, "tags"),
45
+ repo: str(options, "repo") ?? config.changelog?.repo,
46
+ authorMap: str(options, "author-map") ?? config.changelog?.authorMap,
47
+ releasesDir: str(options, "releases-dir") ?? config.changelog?.releasesDir,
48
+ });
49
+
50
+ if (flag(options, "tags")) {
51
+ console.log(report.notes);
52
+ return 0;
53
+ }
54
+
55
+ if (report.rangeLabel) {
56
+ console.error(`change-logs: ${report.rangeLabel} — ${report.commitCount} commit(s)`);
57
+ }
58
+ if (report.writtenPath) {
59
+ console.error(`change-logs: wrote ${report.writtenPath}`);
60
+ }
61
+ process.stdout.write(report.notes);
62
+ return 0;
63
+ }
64
+
65
+ export async function cmdChangeLogsLookup(options: ParsedOptions): Promise<number> {
66
+ await bootstrapFromOptions(options);
67
+ const cwd = cwdFrom(options) ?? process.cwd();
68
+ const { config } = await loadConfig({ cwd, createIfMissing: false });
69
+ const email = str(options, "email");
70
+ const login = str(options, "login");
71
+ if (!email && !login) {
72
+ throw new Error("lookup requires --email or --login");
73
+ }
74
+ const native = loadNiftyNative();
75
+ const author = native.history["changelog-lookup"]({
76
+ cwd,
77
+ email,
78
+ login,
79
+ map: str(options, "map") ?? config.changelog?.authorMap,
80
+ githubToken: str(options, "github-token") ?? config.githubToken ?? process.env.GITHUB_TOKEN,
81
+ fetch: flag(options, "fetch"),
82
+ });
83
+ const json: Record<string, unknown> = {};
84
+ if (author.id !== undefined) {
85
+ json.id = author.id;
86
+ }
87
+ if (author.login) {
88
+ json.login = author.login;
89
+ }
90
+ console.log(JSON.stringify(json, null, 2));
91
+ return 0;
92
+ }
93
+
94
+ /** Default author map path relative to repo root (matches git-change-logs). */
95
+ export function defaultAuthorMapPath(repoRoot: string): string {
96
+ return join(repoRoot, "documentation/maintenance/author-github.json");
97
+ }