@doki-land/nifty 0.1.2 → 0.1.3
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/cli/nifty.mjs +1 -1
- package/package.json +8 -7
- package/src/cli/authArgs.ts +3 -3
- package/src/cli/bump-cmd.ts +2 -2
- package/src/cli/bump.ts +122 -33
- package/src/cli/change-logs-cmd.ts +5 -5
- package/src/cli/cli.ts +1 -1
- package/src/cli/commit-cmd.ts +16 -14
- package/src/cli/commit-lint-report.ts +9 -7
- package/src/cli/context.ts +2 -2
- package/src/cli/format-cmd.ts +2 -2
- package/src/cli/format.ts +1 -1
- package/src/cli/install-native-cmd.ts +2 -2
- package/src/cli/install-native.ts +1 -1
- package/src/cli/lint-cmd.ts +2 -2
- package/src/cli/lint.ts +7 -3
- package/src/cli/options.ts +2 -2
- package/src/cli/publish-cmd.ts +9 -3
- package/src/cli/publish.ts +2 -2
- package/src/cli/retime-cmd.ts +4 -4
- package/src/cli/trust-cmd.ts +3 -3
- package/src/cli/trust.ts +1 -1
- package/src/cli/update-cmd.ts +2 -2
- package/src/cli/update.ts +2 -2
- package/src/cli/upload-cmd.ts +2 -2
- package/src/cli/upload.ts +40 -40
- package/src/cli/workspace.ts +13 -13
- package/src/config/authorMap.ts +6 -2
- package/src/config/defineConfig.ts +10 -8
- package/src/config/detectProject.ts +2 -2
- package/src/config/formatPresets.ts +3 -3
- package/src/config/loadConfig.ts +7 -7
- package/src/config/trustPayload.ts +4 -4
- package/src/config/types.ts +3 -3
- package/src/git.ts +2 -2
- package/src/github.ts +20 -20
- package/src/gitmoji.ts +2 -2
- package/src/native.ts +97 -93
- package/src/nifty.ts +10 -8
package/cli/nifty.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@doki-land/nifty",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Gitmoji-first commit conventions and release helpers (Node-API + TypeScript)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MPL-2.0",
|
|
@@ -17,18 +17,19 @@
|
|
|
17
17
|
"src"
|
|
18
18
|
],
|
|
19
19
|
"scripts": {
|
|
20
|
-
"typecheck": "tsc --noEmit"
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
21
|
+
"test": "node --test tests/*.test.mjs"
|
|
21
22
|
},
|
|
22
23
|
"dependencies": {
|
|
23
24
|
"@vmz/commander": "^0.2.0",
|
|
24
25
|
"jiti": "^2.4.2"
|
|
25
26
|
},
|
|
26
27
|
"optionalDependencies": {
|
|
27
|
-
"@doki-land/nifty-win32-x64": "0.1.
|
|
28
|
-
"@doki-land/nifty-linux-x64": "0.1.
|
|
29
|
-
"@doki-land/nifty-linux-arm64": "0.1.
|
|
30
|
-
"@doki-land/nifty-darwin-arm64": "0.1.
|
|
31
|
-
"@doki-land/nifty-darwin-x64": "0.1.
|
|
28
|
+
"@doki-land/nifty-win32-x64": "0.1.3",
|
|
29
|
+
"@doki-land/nifty-linux-x64": "0.1.3",
|
|
30
|
+
"@doki-land/nifty-linux-arm64": "0.1.3",
|
|
31
|
+
"@doki-land/nifty-darwin-arm64": "0.1.3",
|
|
32
|
+
"@doki-land/nifty-darwin-x64": "0.1.3"
|
|
32
33
|
},
|
|
33
34
|
"publishConfig": {
|
|
34
35
|
"access": "public"
|
package/src/cli/authArgs.ts
CHANGED
|
@@ -44,9 +44,9 @@ export function authPayload(options: AuthCliOptions): {
|
|
|
44
44
|
npm: string;
|
|
45
45
|
} {
|
|
46
46
|
return {
|
|
47
|
-
...(options.otp !== undefined ? { otp: options.otp } : {
|
|
48
|
-
...(options.totpSecret !== undefined ? { totpSecret: options.totpSecret } : {
|
|
49
|
-
...(options.token !== undefined ? { token: options.token } : {
|
|
47
|
+
...(options.otp !== undefined ? { otp: options.otp } : {}),
|
|
48
|
+
...(options.totpSecret !== undefined ? { totpSecret: options.totpSecret } : {}),
|
|
49
|
+
...(options.token !== undefined ? { token: options.token } : {}),
|
|
50
50
|
npm: options.npm,
|
|
51
51
|
};
|
|
52
52
|
}
|
package/src/cli/bump-cmd.ts
CHANGED
|
@@ -11,10 +11,10 @@ export function registerBumpCommand(cli: Cli): void {
|
|
|
11
11
|
.option("--patch", "cli.opt.patch")
|
|
12
12
|
.option("--minor", "cli.opt.minor")
|
|
13
13
|
.option("--major", "cli.opt.major")
|
|
14
|
-
.action(async(options) => cmdBump(options));
|
|
14
|
+
.action(async (options) => cmdBump(options));
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export async function cmdBump(options: ParsedOptions): Promise
|
|
17
|
+
export async function cmdBump(options: ParsedOptions): Promise<number> {
|
|
18
18
|
await bootstrapFromOptions(options);
|
|
19
19
|
const bumpOptions = bumpOptionsFromParsed(options);
|
|
20
20
|
const report = bumpWorkspace(bumpOptions);
|
package/src/cli/bump.ts
CHANGED
|
@@ -24,7 +24,7 @@ export type BumpReport = {
|
|
|
24
24
|
|
|
25
25
|
const DEFAULT_KIND: BumpKind = "patch";
|
|
26
26
|
|
|
27
|
-
export async function runBump(argv: string[]): Promise
|
|
27
|
+
export async function runBump(argv: string[]): Promise<void> {
|
|
28
28
|
const options = parseBumpArgs(argv);
|
|
29
29
|
const report = bumpWorkspace(options);
|
|
30
30
|
printBumpReport(report, options.dryRun);
|
|
@@ -85,27 +85,36 @@ export function bumpWorkspace(options: BumpOptions): BumpReport {
|
|
|
85
85
|
|
|
86
86
|
const cargoPaths = collectCargoManifests(cratesDir);
|
|
87
87
|
const npmPaths = collectPackageManifests(packagesDir);
|
|
88
|
-
const from = readWorkspaceVersion(cargoPaths, npmPaths);
|
|
88
|
+
const from = readWorkspaceVersion(root, cargoPaths, npmPaths);
|
|
89
89
|
const to = options.version ?? bumpSemver(from, options.kind ?? DEFAULT_KIND);
|
|
90
90
|
assertVersion(to);
|
|
91
91
|
|
|
92
|
-
const
|
|
93
|
-
const
|
|
92
|
+
const cargoUpdates = prepareCargoUpdates(root, cargoPaths, to);
|
|
93
|
+
const npmUpdates = npmPaths.map((path) => preparePackageUpdate(path, to));
|
|
94
|
+
if (!options.dryRun) {
|
|
95
|
+
cargoUpdates.concat(npmUpdates).forEach((update) => writeFileSync(update.path, update.next, "utf8"));
|
|
96
|
+
}
|
|
94
97
|
|
|
95
98
|
return {
|
|
96
99
|
root,
|
|
97
100
|
from,
|
|
98
101
|
to,
|
|
99
102
|
kind: options.version ? undefined : options.kind ?? DEFAULT_KIND,
|
|
100
|
-
cargo,
|
|
101
|
-
npm,
|
|
103
|
+
cargo: cargoUpdates.map((update) => update.path),
|
|
104
|
+
npm: npmUpdates.map((update) => update.path),
|
|
102
105
|
};
|
|
103
106
|
}
|
|
104
107
|
|
|
105
|
-
function readWorkspaceVersion(cargoPaths: string[], npmPaths: string[]): string {
|
|
106
|
-
const versions = new Set
|
|
108
|
+
function readWorkspaceVersion(root: string, cargoPaths: string[], npmPaths: string[]): string {
|
|
109
|
+
const versions = new Set<string>();
|
|
110
|
+
let usesWorkspaceVersion = false;
|
|
107
111
|
for (const path of cargoPaths) {
|
|
108
|
-
|
|
112
|
+
const info = readCargoVersion(path);
|
|
113
|
+
if (info.inherited) usesWorkspaceVersion = true;
|
|
114
|
+
else versions.add(info.version);
|
|
115
|
+
}
|
|
116
|
+
if (usesWorkspaceVersion) {
|
|
117
|
+
versions.add(readWorkspacePackageVersion(join(root, "Cargo.toml")).version);
|
|
109
118
|
}
|
|
110
119
|
for (const path of npmPaths) {
|
|
111
120
|
versions.add(readPackageVersion(path));
|
|
@@ -116,20 +125,25 @@ function readWorkspaceVersion(cargoPaths: string[], npmPaths: string[]): string
|
|
|
116
125
|
if (versions.size > 1) {
|
|
117
126
|
throw new Error(`workspace versions are out of sync: ${[...versions].sort().join(", ")}`);
|
|
118
127
|
}
|
|
119
|
-
return[...versions][0];
|
|
128
|
+
return [...versions][0];
|
|
120
129
|
}
|
|
121
130
|
|
|
122
|
-
|
|
131
|
+
type CargoVersion = { version: string; inherited: boolean };
|
|
132
|
+
|
|
133
|
+
type FileUpdate = { path: string; next: string };
|
|
134
|
+
|
|
135
|
+
function readCargoVersion(path: string): CargoVersion {
|
|
123
136
|
const original = readFileSync(path, "utf8");
|
|
124
|
-
const
|
|
125
|
-
if (!
|
|
126
|
-
|
|
137
|
+
const field = findTomlField(original, "package", "version.workspace") ?? findTomlField(original, "package", "version");
|
|
138
|
+
if (!field) throw new Error(`no [package].version found in ${path}`);
|
|
139
|
+
if (isWorkspaceInheritedVersion(field)) {
|
|
140
|
+
return { version: "", inherited: true };
|
|
127
141
|
}
|
|
128
|
-
return
|
|
142
|
+
return { version: parseTomlString(field.value, `[package].version in ${path}`), inherited: false };
|
|
129
143
|
}
|
|
130
144
|
|
|
131
145
|
function readPackageVersion(path: string): string {
|
|
132
|
-
const parsed = JSON.parse(readFileSync(path, "utf8"))as { version?: string };
|
|
146
|
+
const parsed = JSON.parse(readFileSync(path, "utf8")) as { version?: string };
|
|
133
147
|
if (!parsed.version) {
|
|
134
148
|
throw new Error(`no version field in ${path}`);
|
|
135
149
|
}
|
|
@@ -165,7 +179,7 @@ export function bumpSemver(version: string, kind: BumpKind): string {
|
|
|
165
179
|
|
|
166
180
|
function collectCargoManifests(cratesDir: string): string[] {
|
|
167
181
|
if (!existsSync(cratesDir)) {
|
|
168
|
-
return[];
|
|
182
|
+
return [];
|
|
169
183
|
}
|
|
170
184
|
return readdirSync(cratesDir, { withFileTypes: true })
|
|
171
185
|
.filter((entry) => entry.isDirectory())
|
|
@@ -175,7 +189,7 @@ function collectCargoManifests(cratesDir: string): string[] {
|
|
|
175
189
|
|
|
176
190
|
function collectPackageManifests(packagesDir: string): string[] {
|
|
177
191
|
if (!existsSync(packagesDir)) {
|
|
178
|
-
return[];
|
|
192
|
+
return [];
|
|
179
193
|
}
|
|
180
194
|
return readdirSync(packagesDir, { withFileTypes: true })
|
|
181
195
|
.filter((entry) => entry.isDirectory() && entry.name !== "node_modules")
|
|
@@ -183,33 +197,106 @@ function collectPackageManifests(packagesDir: string): string[] {
|
|
|
183
197
|
.filter((path) => existsSync(path));
|
|
184
198
|
}
|
|
185
199
|
|
|
186
|
-
function
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
200
|
+
function prepareCargoUpdates(root: string, cargoPaths: string[], version: string): FileUpdate[] {
|
|
201
|
+
const updates: FileUpdate[] = [];
|
|
202
|
+
let usesWorkspaceVersion = false;
|
|
203
|
+
for (const path of cargoPaths) {
|
|
204
|
+
const original = readFileSync(path, "utf8");
|
|
205
|
+
const field = findTomlField(original, "package", "version.workspace") ?? findTomlField(original, "package", "version");
|
|
206
|
+
if (!field) throw new Error(`no [package].version found in ${path}`);
|
|
207
|
+
if (isWorkspaceInheritedVersion(field)) {
|
|
208
|
+
usesWorkspaceVersion = true;
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
parseTomlString(field.value, `[package].version in ${path}`);
|
|
212
|
+
updates.push({ path, next: replaceTomlField(original, field, version) });
|
|
191
213
|
}
|
|
192
|
-
if (
|
|
193
|
-
|
|
214
|
+
if (usesWorkspaceVersion) {
|
|
215
|
+
const path = join(root, "Cargo.toml");
|
|
216
|
+
const original = readFileSync(path, "utf8");
|
|
217
|
+
const field = findTomlField(original, "workspace.package", "version");
|
|
218
|
+
if (!field) {
|
|
219
|
+
throw new Error(`Cargo members inherit version.workspace but ${path} has no [workspace.package].version`);
|
|
220
|
+
}
|
|
221
|
+
parseTomlString(field.value, `[workspace.package].version in ${path}`);
|
|
222
|
+
updates.push({ path, next: replaceTomlField(original, field, version) });
|
|
194
223
|
}
|
|
195
|
-
return
|
|
224
|
+
return updates;
|
|
196
225
|
}
|
|
197
226
|
|
|
198
|
-
function
|
|
227
|
+
function readWorkspacePackageVersion(path: string): CargoVersion {
|
|
199
228
|
const original = readFileSync(path, "utf8");
|
|
200
|
-
const
|
|
229
|
+
const field = findTomlField(original, "workspace.package", "version");
|
|
230
|
+
if (!field) throw new Error(`Cargo members inherit version.workspace but ${path} has no [workspace.package].version`);
|
|
231
|
+
return { version: parseTomlString(field.value, `[workspace.package].version in ${path}`), inherited: false };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function isWorkspaceInheritedVersion(field: NonNullable<ReturnType<typeof findTomlField>>): boolean {
|
|
235
|
+
if (field.key === "version.workspace") return true;
|
|
236
|
+
const value = field.value.trim();
|
|
237
|
+
if (!value.startsWith("{") || !value.endsWith("}")) return false;
|
|
238
|
+
return value
|
|
239
|
+
.slice(1, -1)
|
|
240
|
+
.split(",")
|
|
241
|
+
.some((assignment) => {
|
|
242
|
+
const equals = assignment.indexOf("=");
|
|
243
|
+
return equals >= 0 && assignment.slice(0, equals).trim() === "workspace" && assignment.slice(equals + 1).trim() === "true";
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function findTomlField(contents: string, table: string, name: string) {
|
|
248
|
+
let currentTable = "";
|
|
249
|
+
const lines = contents.split(/(?<=\n)/);
|
|
250
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
251
|
+
const line = lines[index];
|
|
252
|
+
const trimmed = line.trim();
|
|
253
|
+
const tableMatch = trimmed.match(/^\[([^\]]+)\]$/);
|
|
254
|
+
if (tableMatch) {
|
|
255
|
+
currentTable = tableMatch[1].trim();
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
if (trimmed.startsWith("[[")) {
|
|
259
|
+
currentTable = "";
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
if (currentTable !== table) continue;
|
|
263
|
+
const fieldMatch = line.match(new RegExp(`^(\\s*)(${name.replace(".", "\\.")})\\s*=\\s*(.*?)(\\r?\\n?)$`));
|
|
264
|
+
if (fieldMatch) return { index, line, indent: fieldMatch[1], key: fieldMatch[2], value: fieldMatch[3], ending: fieldMatch[4] };
|
|
265
|
+
}
|
|
266
|
+
return undefined;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function parseTomlString(value: string, description: string): string {
|
|
270
|
+
const trimmed = value.trim();
|
|
271
|
+
const quote = trimmed[0];
|
|
272
|
+
if (quote !== '"' && quote !== "'") throw new Error(`expected a quoted string for ${description}`);
|
|
273
|
+
const closingQuote = trimmed.indexOf(quote, 1);
|
|
274
|
+
if (closingQuote < 0) throw new Error(`expected a quoted string for ${description}`);
|
|
275
|
+
const trailing = trimmed.slice(closingQuote + 1).trim();
|
|
276
|
+
if (trailing && !trailing.startsWith("#")) throw new Error(`expected a quoted string for ${description}`);
|
|
277
|
+
return trimmed.slice(1, closingQuote);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function replaceTomlField(contents: string, field: NonNullable<ReturnType<typeof findTomlField>>, version: string): string {
|
|
281
|
+
const value = field.value.trimStart();
|
|
282
|
+
const closingQuote = value.indexOf(value[0], 1);
|
|
283
|
+
const suffix = closingQuote >= 0 ? value.slice(closingQuote + 1) : "";
|
|
284
|
+
const line = `${field.indent}${field.key} = "${version}"${suffix}${field.ending}`;
|
|
285
|
+
return contents.split(/(?<=\n)/).map((current, index) => index === field.index ? line : current).join("");
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function preparePackageUpdate(path: string, version: string): FileUpdate {
|
|
289
|
+
const original = readFileSync(path, "utf8");
|
|
290
|
+
const parsed = JSON.parse(original) as { version?: string };
|
|
201
291
|
if (!parsed.version) {
|
|
202
292
|
throw new Error(`no version field in ${path}`);
|
|
203
293
|
}
|
|
204
294
|
parsed.version = version;
|
|
205
295
|
const next = `${JSON.stringify(parsed, null, 4)}\n`;
|
|
206
|
-
|
|
207
|
-
writeFileSync(path, next, "utf8");
|
|
208
|
-
}
|
|
209
|
-
return path;
|
|
296
|
+
return { path, next };
|
|
210
297
|
}
|
|
211
298
|
|
|
212
|
-
export function printBumpReport(report: BumpReport, dryRun?: boolean)
|
|
299
|
+
export function printBumpReport(report: BumpReport, dryRun?: boolean): void {
|
|
213
300
|
const prefix = dryRun ? "would bump" : "bumped";
|
|
214
301
|
const kind = report.kind ? ` (${report.kind})` : "";
|
|
215
302
|
console.log(`${prefix} workspace ${report.root} ${report.from} -> ${report.to}${kind}`);
|
|
@@ -233,5 +320,7 @@ Usage:
|
|
|
233
320
|
nifty bump --version 1.2.3
|
|
234
321
|
nifty bump --dry-run
|
|
235
322
|
nifty bump -C <cwd>
|
|
323
|
+
|
|
324
|
+
Cargo members using version.workspace = true are updated through the root [workspace.package].version.
|
|
236
325
|
`);
|
|
237
326
|
}
|
|
@@ -27,12 +27,12 @@ export function registerChangeLogsCommand(cli: Cli): void {
|
|
|
27
27
|
.option("--map <path>", "cli.opt.author-map")
|
|
28
28
|
.option("--github-token <token>", "cli.opt.github-token")
|
|
29
29
|
.option("--fetch", "cli.opt.fetch")
|
|
30
|
-
.action(async(options) => cmdChangeLogsLookup(options));
|
|
30
|
+
.action(async (options) => cmdChangeLogsLookup(options));
|
|
31
31
|
|
|
32
|
-
changeLogs.action(async(options) => cmdChangeLogsRender(options));
|
|
32
|
+
changeLogs.action(async (options) => cmdChangeLogsRender(options));
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
export async function cmdChangeLogsRender(options: ParsedOptions): Promise
|
|
35
|
+
export async function cmdChangeLogsRender(options: ParsedOptions): Promise<number> {
|
|
36
36
|
await bootstrapFromOptions(options);
|
|
37
37
|
const cwd = cwdFrom(options) ?? process.cwd();
|
|
38
38
|
const { config } = await loadConfig({ cwd, createIfMissing: false });
|
|
@@ -66,7 +66,7 @@ export async function cmdChangeLogsRender(options: ParsedOptions): Promise < num
|
|
|
66
66
|
return 0;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
export async function cmdChangeLogsLookup(options: ParsedOptions): Promise
|
|
69
|
+
export async function cmdChangeLogsLookup(options: ParsedOptions): Promise<number> {
|
|
70
70
|
await bootstrapFromOptions(options);
|
|
71
71
|
const cwd = cwdFrom(options) ?? process.cwd();
|
|
72
72
|
const { config } = await loadConfig({ cwd, createIfMissing: false });
|
|
@@ -86,7 +86,7 @@ export async function cmdChangeLogsLookup(options: ParsedOptions): Promise < num
|
|
|
86
86
|
githubToken: str(options, "github-token") ?? config.githubToken ?? process.env.GITHUB_TOKEN,
|
|
87
87
|
fetch: flag(options, "fetch"),
|
|
88
88
|
});
|
|
89
|
-
const json: Record
|
|
89
|
+
const json: Record<string, unknown> = {};
|
|
90
90
|
if (author.id !== undefined) {
|
|
91
91
|
json.id = author.id;
|
|
92
92
|
}
|
package/src/cli/cli.ts
CHANGED
|
@@ -37,7 +37,7 @@ function buildNiftyCli() {
|
|
|
37
37
|
return cli;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
export async function runCli(argv: string[]): Promise
|
|
40
|
+
export async function runCli(argv: string[]): Promise<number> {
|
|
41
41
|
try {
|
|
42
42
|
return await buildNiftyCli().parse(argv);
|
|
43
43
|
} catch (error) {
|
package/src/cli/commit-cmd.ts
CHANGED
|
@@ -17,7 +17,7 @@ export function registerCommitCommand(cli: Cli): void {
|
|
|
17
17
|
.option("--path <file>", "cli.opt.path")
|
|
18
18
|
.option("--with-lint", "cli.opt.with-lint")
|
|
19
19
|
.option("--errors-only", "cli.opt.errors-only")
|
|
20
|
-
.action(async(options) => cmdCommitExport(options));
|
|
20
|
+
.action(async (options) => cmdCommitExport(options));
|
|
21
21
|
|
|
22
22
|
commit
|
|
23
23
|
.command("apply", "cli.cmd.commit.apply")
|
|
@@ -25,7 +25,7 @@ export function registerCommitCommand(cli: Cli): void {
|
|
|
25
25
|
.option("--ref <ref>", "cli.opt.ref")
|
|
26
26
|
.option("--path <file>", "cli.opt.path")
|
|
27
27
|
.option("--dry-run", "cli.opt.dry-run")
|
|
28
|
-
.action(async(options) => cmdCommitApply(options));
|
|
28
|
+
.action(async (options) => cmdCommitApply(options));
|
|
29
29
|
|
|
30
30
|
commit
|
|
31
31
|
.command("scan", "cli.cmd.commit.scan")
|
|
@@ -34,7 +34,7 @@ export function registerCommitCommand(cli: Cli): void {
|
|
|
34
34
|
.option("-s, --subject <text>...", "cli.opt.subject")
|
|
35
35
|
.option("--json", "cli.opt.json")
|
|
36
36
|
.option("--errors-only", "cli.opt.errors-only")
|
|
37
|
-
.action(async(options) => cmdCommitScan(options));
|
|
37
|
+
.action(async (options) => cmdCommitScan(options));
|
|
38
38
|
|
|
39
39
|
commit
|
|
40
40
|
.command("audit", "cli.cmd.commit.audit")
|
|
@@ -43,10 +43,10 @@ export function registerCommitCommand(cli: Cli): void {
|
|
|
43
43
|
.option("-s, --subject <text>...", "cli.opt.subject")
|
|
44
44
|
.option("--json", "cli.opt.json")
|
|
45
45
|
.option("--errors-only", "cli.opt.errors-only")
|
|
46
|
-
.action(async(options) => cmdCommitAudit(options));
|
|
46
|
+
.action(async (options) => cmdCommitAudit(options));
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
export async function cmdCommitExport(options: ParsedOptions): Promise
|
|
49
|
+
export async function cmdCommitExport(options: ParsedOptions): Promise<number> {
|
|
50
50
|
await bootstrapFromOptions(options);
|
|
51
51
|
const base = requireStr(options, "base");
|
|
52
52
|
const path = str(options, "path") ?? "commit.pending.json";
|
|
@@ -77,7 +77,7 @@ export async function cmdCommitExport(options: ParsedOptions): Promise < number>
|
|
|
77
77
|
return 0;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
export async function cmdCommitApply(options: ParsedOptions): Promise
|
|
80
|
+
export async function cmdCommitApply(options: ParsedOptions): Promise<number> {
|
|
81
81
|
await bootstrapFromOptions(options);
|
|
82
82
|
const base = requireStr(options, "base");
|
|
83
83
|
const path = requireStr(options, "path");
|
|
@@ -117,13 +117,13 @@ export async function cmdCommitApply(options: ParsedOptions): Promise < number>
|
|
|
117
117
|
return 0;
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
export async function cmdCommitScan(options: ParsedOptions): Promise
|
|
120
|
+
export async function cmdCommitScan(options: ParsedOptions): Promise<number> {
|
|
121
121
|
await bootstrapFromOptions(options);
|
|
122
122
|
await lintCommits({ ...commitLintOptions(options), reportTitle: "commit scan" }, false, true);
|
|
123
123
|
return 0;
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
export async function cmdCommitAudit(options: ParsedOptions): Promise
|
|
126
|
+
export async function cmdCommitAudit(options: ParsedOptions): Promise<number> {
|
|
127
127
|
await bootstrapFromOptions(options);
|
|
128
128
|
const errorCount = await lintCommits(commitLintOptions(options), false, true);
|
|
129
129
|
return errorCount > 0 ? 1 : 0;
|
|
@@ -145,10 +145,10 @@ function commitLintOptions(options: ParsedOptions): LintOptions {
|
|
|
145
145
|
|
|
146
146
|
type ExportDocument = {
|
|
147
147
|
version: number;
|
|
148
|
-
entries: Array
|
|
148
|
+
entries: Array<{
|
|
149
149
|
hash: string;
|
|
150
150
|
message: string;
|
|
151
|
-
violations?: Array
|
|
151
|
+
violations?: Array<{
|
|
152
152
|
rule: string;
|
|
153
153
|
severity: "error" | "warning" | "info";
|
|
154
154
|
message: string;
|
|
@@ -164,7 +164,7 @@ async function lintAndAnnotateExport(options: {
|
|
|
164
164
|
json: boolean;
|
|
165
165
|
errorsOnly: boolean;
|
|
166
166
|
reportTitle: string;
|
|
167
|
-
}): Promise
|
|
167
|
+
}): Promise<number> {
|
|
168
168
|
const native = loadNiftyNative();
|
|
169
169
|
const report = native.lint.run({
|
|
170
170
|
cwd: options.cwd,
|
|
@@ -173,7 +173,9 @@ async function lintAndAnnotateExport(options: {
|
|
|
173
173
|
commitOnly: true,
|
|
174
174
|
scanCargo: false,
|
|
175
175
|
});
|
|
176
|
-
const diagnostics = options.errorsOnly
|
|
176
|
+
const diagnostics = options.errorsOnly
|
|
177
|
+
? report.diagnostics.filter((item) => item.severity === "error")
|
|
178
|
+
: report.diagnostics;
|
|
177
179
|
|
|
178
180
|
if (options.json) {
|
|
179
181
|
const { printCommitLintReport } = await import("./commit-lint-report.js");
|
|
@@ -190,7 +192,7 @@ async function lintAndAnnotateExport(options: {
|
|
|
190
192
|
});
|
|
191
193
|
}
|
|
192
194
|
|
|
193
|
-
const byHash = new Map
|
|
195
|
+
const byHash = new Map<string, Array<{ rule: string; severity: "error" | "warning" | "info"; message: string }>>();
|
|
194
196
|
for (const item of diagnostics) {
|
|
195
197
|
if (!item.hash) {
|
|
196
198
|
continue;
|
|
@@ -201,7 +203,7 @@ async function lintAndAnnotateExport(options: {
|
|
|
201
203
|
}
|
|
202
204
|
|
|
203
205
|
const text = await readFile(options.path, "utf8");
|
|
204
|
-
const document = JSON.parse(text)as ExportDocument;
|
|
206
|
+
const document = JSON.parse(text) as ExportDocument;
|
|
205
207
|
document.entries = document.entries.map((entry) => {
|
|
206
208
|
const violations = byHash.get(entry.hash);
|
|
207
209
|
if (!violations || violations.length === 0) {
|
|
@@ -24,9 +24,11 @@ export function printCommitLintReport(
|
|
|
24
24
|
diagnostics: CommitLintDiagnostic[],
|
|
25
25
|
errorCount: number,
|
|
26
26
|
warningCount: number,
|
|
27
|
-
options: CommitLintReportOptions = {
|
|
27
|
+
options: CommitLintReportOptions = {},
|
|
28
28
|
): void {
|
|
29
|
-
const filtered = options.errorsOnly
|
|
29
|
+
const filtered = options.errorsOnly
|
|
30
|
+
? diagnostics.filter((item) => item.severity === "error")
|
|
31
|
+
: diagnostics;
|
|
30
32
|
|
|
31
33
|
if (options.json) {
|
|
32
34
|
const payload = {
|
|
@@ -57,7 +59,7 @@ export function printCommitLintReport(
|
|
|
57
59
|
const summary = summarizeByRule(filtered.filter((item) => item.severity === "error"));
|
|
58
60
|
if (summary.length > 0) {
|
|
59
61
|
console.log("summary by rule:");
|
|
60
|
-
for (const[rule, count]of summary) {
|
|
62
|
+
for (const [rule, count] of summary) {
|
|
61
63
|
console.log(` ${rule} ${count}`);
|
|
62
64
|
}
|
|
63
65
|
console.log();
|
|
@@ -65,7 +67,7 @@ export function printCommitLintReport(
|
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
function groupCommitDiagnostics(diagnostics: CommitLintDiagnostic[]): CommitLintGroup[] {
|
|
68
|
-
const groups = new Map
|
|
70
|
+
const groups = new Map<string, CommitLintGroup>();
|
|
69
71
|
const order: string[] = [];
|
|
70
72
|
|
|
71
73
|
for (const item of diagnostics) {
|
|
@@ -84,12 +86,12 @@ function groupCommitDiagnostics(diagnostics: CommitLintDiagnostic[]): CommitLint
|
|
|
84
86
|
return order.map((key) => groups.get(key)!);
|
|
85
87
|
}
|
|
86
88
|
|
|
87
|
-
function summarizeByRule(diagnostics: CommitLintDiagnostic[]): Array
|
|
88
|
-
const counts = new Map
|
|
89
|
+
function summarizeByRule(diagnostics: CommitLintDiagnostic[]): Array<[string, number]> {
|
|
90
|
+
const counts = new Map<string, number>();
|
|
89
91
|
for (const item of diagnostics) {
|
|
90
92
|
counts.set(item.rule, (counts.get(item.rule) ?? 0) + 1);
|
|
91
93
|
}
|
|
92
|
-
return[...counts.entries()].sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0]));
|
|
94
|
+
return [...counts.entries()].sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0]));
|
|
93
95
|
}
|
|
94
96
|
|
|
95
97
|
function printCommitGroup(group: CommitLintGroup): void {
|
package/src/cli/context.ts
CHANGED
|
@@ -23,13 +23,13 @@ export function parseCliContext(argv: string[]): { cwd: string; dryRun: boolean
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
/** Ensure `nifty.config.*` exists before running a command (skip create on `--dry-run`). */
|
|
26
|
-
export async function bootstrapNiftyConfig(argv: string[]): Promise
|
|
26
|
+
export async function bootstrapNiftyConfig(argv: string[]): Promise<void> {
|
|
27
27
|
const { cwd, dryRun } = parseCliContext(argv);
|
|
28
28
|
await loadConfig({ cwd, createIfMissing: !dryRun });
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
/** Commander action hook: load config from parsed global/command flags. */
|
|
32
|
-
export async function bootstrapFromOptions(options: ParsedOptions): Promise
|
|
32
|
+
export async function bootstrapFromOptions(options: ParsedOptions): Promise<void> {
|
|
33
33
|
const cwd = cwdFrom(options) ?? process.cwd();
|
|
34
34
|
const dryRun = options["dry-run"] === true;
|
|
35
35
|
await loadConfig({ cwd, createIfMissing: !dryRun });
|
package/src/cli/format-cmd.ts
CHANGED
|
@@ -7,10 +7,10 @@ import { cwdFrom, flag } from "./options.js";
|
|
|
7
7
|
export function registerFormatCommand(cli: Cli): void {
|
|
8
8
|
cli.command("format", "cli.cmd.format")
|
|
9
9
|
.option("--check", "cli.opt.check")
|
|
10
|
-
.action(async(options) => cmdFormat(options));
|
|
10
|
+
.action(async (options) => cmdFormat(options));
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export async function cmdFormat(options: ParsedOptions): Promise
|
|
13
|
+
export async function cmdFormat(options: ParsedOptions): Promise<number> {
|
|
14
14
|
await bootstrapFromOptions(options);
|
|
15
15
|
const exitCode = await formatWorkspace({
|
|
16
16
|
check: flag(options, "check"),
|
package/src/cli/format.ts
CHANGED
|
@@ -6,7 +6,7 @@ export type FormatOptions = {
|
|
|
6
6
|
cwd?: string;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
-
export async function formatWorkspace(options: FormatOptions): Promise
|
|
9
|
+
export async function formatWorkspace(options: FormatOptions): Promise<number> {
|
|
10
10
|
const cwd = options.cwd ?? process.cwd();
|
|
11
11
|
const { config } = await loadConfig({ cwd, createIfMissing: false, env: { command: "format" } });
|
|
12
12
|
const resolved = resolveFormatConfig(config.format);
|
|
@@ -6,10 +6,10 @@ import { cwdFrom, str } from "./options.js";
|
|
|
6
6
|
export function registerInstallNativeCommand(cli: Cli): void {
|
|
7
7
|
cli.command("install-native", "cli.cmd.install-native")
|
|
8
8
|
.option("--from <dir>", "cli.opt.from-artifacts")
|
|
9
|
-
.action(async(options) => cmdInstallNative(options));
|
|
9
|
+
.action(async (options) => cmdInstallNative(options));
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export async function cmdInstallNative(options: ParsedOptions): Promise
|
|
12
|
+
export async function cmdInstallNative(options: ParsedOptions): Promise<number> {
|
|
13
13
|
const from =
|
|
14
14
|
str(options, "from") ??
|
|
15
15
|
process.env.NATIVE_ARTIFACT_ROOT ??
|
|
@@ -4,7 +4,7 @@ import { basename, dirname, join, relative, resolve } from "node:path";
|
|
|
4
4
|
import { detectProjectLayout } from "../config/detectProject.js";
|
|
5
5
|
|
|
6
6
|
/** Basename of a built .node file → platform package directory under projects/packages/. */
|
|
7
|
-
const PACKAGE_BY_ASSET: Record
|
|
7
|
+
const PACKAGE_BY_ASSET: Record<string, string> = {
|
|
8
8
|
"nifty-win32-x64-msvc.node": "nifty-win32-x64",
|
|
9
9
|
"nifty-linux-x64-gnu.node": "nifty-linux-x64",
|
|
10
10
|
"nifty-linux-arm64-gnu.node": "nifty-linux-arm64",
|
package/src/cli/lint-cmd.ts
CHANGED
|
@@ -15,10 +15,10 @@ function registerLintLike(cli: Cli, name: string, helpId: string, check: boolean
|
|
|
15
15
|
.option("--to <ref>", "cli.opt.to")
|
|
16
16
|
.option("-s, --subject <text>...", "cli.opt.subject")
|
|
17
17
|
.option("--no-cargo", "cli.opt.no-cargo")
|
|
18
|
-
.action(async(options) => cmdLint(options, check));
|
|
18
|
+
.action(async (options) => cmdLint(options, check));
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
export async function cmdLint(options: ParsedOptions, check: boolean): Promise
|
|
21
|
+
export async function cmdLint(options: ParsedOptions, check: boolean): Promise<number> {
|
|
22
22
|
await bootstrapFromOptions(options);
|
|
23
23
|
const lintOptions: LintOptions = {
|
|
24
24
|
subjects: strList(options, "subject"),
|
package/src/cli/lint.ts
CHANGED
|
@@ -22,7 +22,7 @@ type LintRuleConfig = {
|
|
|
22
22
|
severity?: "error" | "warning" | "info";
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
export async function runLint(argv: string[], check: boolean): Promise
|
|
25
|
+
export async function runLint(argv: string[], check: boolean): Promise<void> {
|
|
26
26
|
const options = parseLintArgs(argv);
|
|
27
27
|
const errorCount = await lintCommits(options, check);
|
|
28
28
|
if (check && errorCount > 0) {
|
|
@@ -34,7 +34,7 @@ export async function lintCommits(
|
|
|
34
34
|
options: LintOptions,
|
|
35
35
|
check: boolean,
|
|
36
36
|
commitOnly = options.commitOnly ?? false,
|
|
37
|
-
): Promise
|
|
37
|
+
): Promise<number> {
|
|
38
38
|
const native = loadNiftyNative();
|
|
39
39
|
const cwd = options.cwd ?? process.cwd();
|
|
40
40
|
const { config } = await loadConfig({ cwd, createIfMissing: false });
|
|
@@ -84,7 +84,11 @@ function printWorkspaceDiagnostic(item: LintDiagnostic): void {
|
|
|
84
84
|
return;
|
|
85
85
|
}
|
|
86
86
|
const location =
|
|
87
|
-
item.path !== undefined
|
|
87
|
+
item.path !== undefined
|
|
88
|
+
? item.line !== undefined
|
|
89
|
+
? `${item.path}:${item.line}`
|
|
90
|
+
: item.path
|
|
91
|
+
: undefined;
|
|
88
92
|
const suffix = location ? ` @ ${location}` : "";
|
|
89
93
|
console.log(`${prefix} [${item.rule}] ${item.message}${suffix}`);
|
|
90
94
|
}
|
package/src/cli/options.ts
CHANGED
|
@@ -25,9 +25,9 @@ export function strList(options: ParsedOptions, key: string): string[] {
|
|
|
25
25
|
return value.filter((entry): entry is string => typeof entry === "string" && entry.length > 0);
|
|
26
26
|
}
|
|
27
27
|
if (typeof value === "string" && value.length > 0) {
|
|
28
|
-
return[value];
|
|
28
|
+
return [value];
|
|
29
29
|
}
|
|
30
|
-
return[];
|
|
30
|
+
return [];
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export function authFromOptions(options: ParsedOptions): AuthCliOptions {
|