@doki-land/nifty 0.1.1 → 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 +2 -2
- package/package.json +8 -7
- package/src/cli/authArgs.ts +29 -29
- package/src/cli/bump-cmd.ts +15 -15
- package/src/cli/bump.ts +183 -94
- package/src/cli/change-logs-cmd.ts +26 -26
- package/src/cli/cli.ts +3 -3
- package/src/cli/commit-cmd.ts +77 -77
- package/src/cli/commit-lint-report.ts +44 -44
- package/src/cli/context.ts +9 -9
- package/src/cli/format-cmd.ts +6 -6
- package/src/cli/format.ts +15 -15
- package/src/cli/install-native-cmd.ts +4 -4
- package/src/cli/install-native.ts +24 -24
- package/src/cli/lint-cmd.ts +11 -11
- package/src/cli/lint.ts +49 -49
- package/src/cli/options.ts +16 -16
- package/src/cli/publish-cmd.ts +16 -16
- package/src/cli/publish.ts +38 -38
- package/src/cli/resolveNpm.ts +3 -3
- package/src/cli/retime-cmd.ts +18 -18
- package/src/cli/trust-cmd.ts +11 -11
- package/src/cli/trust.ts +26 -26
- package/src/cli/update-cmd.ts +5 -5
- package/src/cli/update.ts +12 -12
- package/src/cli/upload-cmd.ts +17 -17
- package/src/cli/upload.ts +132 -132
- package/src/cli/workspace.ts +49 -49
- package/src/config/authorMap.ts +4 -4
- package/src/config/defineConfig.ts +10 -8
- package/src/config/detectProject.ts +46 -46
- package/src/config/formatPresets.ts +26 -26
- package/src/config/loadConfig.ts +35 -35
- package/src/config/trustPayload.ts +7 -7
- package/src/config/types.ts +38 -38
- package/src/git.ts +14 -14
- package/src/github.ts +54 -54
- package/src/gitmoji.ts +18 -18
- package/src/native.ts +600 -600
- package/src/nifty.ts +30 -30
- package/src/types.ts +17 -17
package/cli/nifty.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { createJiti } from "jiti";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
|
|
7
7
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
8
|
-
const jiti = createJiti(import.meta.url, { interopDefault
|
|
8
|
+
const jiti = createJiti(import.meta.url, { interopDefault: true });
|
|
9
9
|
const { runCli } = await jiti.import(join(here, "../src/cli/main.ts"));
|
|
10
10
|
|
|
11
11
|
process.exit(await runCli(process.argv.slice(2)));
|
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
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
import { resolveNpmExecutable } from "./resolveNpm.js";
|
|
2
2
|
|
|
3
3
|
export type AuthCliOptions = {
|
|
4
|
-
otp
|
|
5
|
-
totpSecret
|
|
6
|
-
token
|
|
7
|
-
npm
|
|
4
|
+
otp?: string;
|
|
5
|
+
totpSecret?: string;
|
|
6
|
+
token?: string;
|
|
7
|
+
npm: string;
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
-
export function parseAuthArgs(argv
|
|
11
|
-
let otp
|
|
12
|
-
let totpSecret
|
|
13
|
-
let token
|
|
10
|
+
export function parseAuthArgs(argv: string[]): AuthCliOptions {
|
|
11
|
+
let otp: string | undefined;
|
|
12
|
+
let totpSecret: string | undefined;
|
|
13
|
+
let token: string | undefined;
|
|
14
14
|
|
|
15
|
-
for(let i = 0; i<argv.length; i += 1){
|
|
15
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
16
16
|
const arg = argv[i];
|
|
17
|
-
if(arg === "--otp"){
|
|
18
|
-
otp = argv[
|
|
19
|
-
} else if(arg === "--totp-secret"){
|
|
20
|
-
totpSecret = argv[
|
|
21
|
-
} else if(arg === "--token"){
|
|
22
|
-
token = argv[
|
|
23
|
-
} else if(arg.startsWith("--otp=")){
|
|
17
|
+
if (arg === "--otp") {
|
|
18
|
+
otp = argv[++i];
|
|
19
|
+
} else if (arg === "--totp-secret") {
|
|
20
|
+
totpSecret = argv[++i];
|
|
21
|
+
} else if (arg === "--token") {
|
|
22
|
+
token = argv[++i];
|
|
23
|
+
} else if (arg.startsWith("--otp=")) {
|
|
24
24
|
otp = arg.slice("--otp=".length);
|
|
25
|
-
} else if(arg.startsWith("--totp-secret=")){
|
|
25
|
+
} else if (arg.startsWith("--totp-secret=")) {
|
|
26
26
|
totpSecret = arg.slice("--totp-secret=".length);
|
|
27
|
-
} else if(arg.startsWith("--token=")){
|
|
27
|
+
} else if (arg.startsWith("--token=")) {
|
|
28
28
|
token = arg.slice("--token=".length);
|
|
29
29
|
}
|
|
30
30
|
}
|
|
@@ -33,20 +33,20 @@ export function parseAuthArgs(argv : string[]) : AuthCliOptions {
|
|
|
33
33
|
otp,
|
|
34
34
|
totpSecret,
|
|
35
35
|
token,
|
|
36
|
-
npm
|
|
36
|
+
npm: resolveNpmExecutable(),
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
export function authPayload(options
|
|
41
|
-
otp
|
|
42
|
-
totpSecret
|
|
43
|
-
token
|
|
44
|
-
npm
|
|
45
|
-
}{
|
|
40
|
+
export function authPayload(options: AuthCliOptions): {
|
|
41
|
+
otp?: string;
|
|
42
|
+
totpSecret?: string;
|
|
43
|
+
token?: string;
|
|
44
|
+
npm: string;
|
|
45
|
+
} {
|
|
46
46
|
return {
|
|
47
|
-
...(options.otp!== undefined ? { otp
|
|
48
|
-
...(options.totpSecret!== undefined ? { totpSecret
|
|
49
|
-
...(options.token!== undefined ? { token
|
|
50
|
-
npm
|
|
47
|
+
...(options.otp !== undefined ? { otp: options.otp } : {}),
|
|
48
|
+
...(options.totpSecret !== undefined ? { totpSecret: options.totpSecret } : {}),
|
|
49
|
+
...(options.token !== undefined ? { token: options.token } : {}),
|
|
50
|
+
npm: options.npm,
|
|
51
51
|
};
|
|
52
52
|
}
|
package/src/cli/bump-cmd.ts
CHANGED
|
@@ -4,17 +4,17 @@ import { bumpWorkspace, printBumpReport, type BumpKind, type BumpOptions } from
|
|
|
4
4
|
import { bootstrapFromOptions } from "./context.js";
|
|
5
5
|
import { cwdFrom, flag, str } from "./options.js";
|
|
6
6
|
|
|
7
|
-
export function registerBumpCommand(cli
|
|
7
|
+
export function registerBumpCommand(cli: Cli): void {
|
|
8
8
|
cli.command("bump", "cli.cmd.bump")
|
|
9
9
|
.option("--dry-run", "cli.opt.dry-run")
|
|
10
10
|
.option("--version, -v <version>", "cli.opt.version")
|
|
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
|
|
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);
|
|
@@ -22,34 +22,34 @@ export async function cmdBump(options : ParsedOptions) : Promise<number>{
|
|
|
22
22
|
return 0;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
function bumpOptionsFromParsed(options
|
|
25
|
+
function bumpOptionsFromParsed(options: ParsedOptions): BumpOptions {
|
|
26
26
|
let version = str(options, "version");
|
|
27
|
-
let kind
|
|
28
|
-
if(flag(options, "major")){
|
|
27
|
+
let kind: BumpKind = "patch";
|
|
28
|
+
if (flag(options, "major")) {
|
|
29
29
|
kind = "major";
|
|
30
|
-
} else if(flag(options, "minor")){
|
|
30
|
+
} else if (flag(options, "minor")) {
|
|
31
31
|
kind = "minor";
|
|
32
|
-
} else if(flag(options, "patch")){
|
|
32
|
+
} else if (flag(options, "patch")) {
|
|
33
33
|
kind = "patch";
|
|
34
34
|
}
|
|
35
|
-
for(const positional of options._){
|
|
36
|
-
if(isBumpKind(positional)){
|
|
35
|
+
for (const positional of options._) {
|
|
36
|
+
if (isBumpKind(positional)) {
|
|
37
37
|
kind = positional;
|
|
38
|
-
} else if(positional){
|
|
38
|
+
} else if (positional) {
|
|
39
39
|
version = positional;
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
-
if(version &&
|
|
42
|
+
if (version && !/^\d+\.\d+\.\d+(-[\w.-]+)?$/.test(version)) {
|
|
43
43
|
throw new Error(`invalid version ${version}, expected semver like 0.0.0`);
|
|
44
44
|
}
|
|
45
45
|
return {
|
|
46
46
|
version,
|
|
47
47
|
kind,
|
|
48
|
-
cwd
|
|
49
|
-
dryRun
|
|
48
|
+
cwd: cwdFrom(options),
|
|
49
|
+
dryRun: flag(options, "dry-run"),
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
function isBumpKind(value
|
|
53
|
+
function isBumpKind(value: string): value is BumpKind {
|
|
54
54
|
return value === "patch" || value === "minor" || value === "major";
|
|
55
55
|
}
|
package/src/cli/bump.ts
CHANGED
|
@@ -7,54 +7,54 @@ export type BumpKind = "patch" | "minor" | "major";
|
|
|
7
7
|
|
|
8
8
|
export type BumpOptions = {
|
|
9
9
|
/** Explicit target version. When set, overrides `kind`. */
|
|
10
|
-
version
|
|
11
|
-
kind
|
|
12
|
-
cwd
|
|
13
|
-
dryRun
|
|
10
|
+
version?: string;
|
|
11
|
+
kind?: BumpKind;
|
|
12
|
+
cwd?: string;
|
|
13
|
+
dryRun?: boolean;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
export type BumpReport = {
|
|
17
|
-
root
|
|
18
|
-
from
|
|
19
|
-
to
|
|
20
|
-
kind
|
|
21
|
-
cargo
|
|
22
|
-
npm
|
|
17
|
+
root: string;
|
|
18
|
+
from: string;
|
|
19
|
+
to: string;
|
|
20
|
+
kind?: BumpKind;
|
|
21
|
+
cargo: string[];
|
|
22
|
+
npm: string[];
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
const DEFAULT_KIND
|
|
25
|
+
const DEFAULT_KIND: BumpKind = "patch";
|
|
26
26
|
|
|
27
|
-
export async function runBump(argv
|
|
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);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
function parseBumpArgs(argv
|
|
34
|
-
let version
|
|
35
|
-
let kind
|
|
36
|
-
let cwd
|
|
33
|
+
function parseBumpArgs(argv: string[]): BumpOptions {
|
|
34
|
+
let version: string | undefined;
|
|
35
|
+
let kind: BumpKind = DEFAULT_KIND;
|
|
36
|
+
let cwd: string | undefined;
|
|
37
37
|
let dryRun = false;
|
|
38
38
|
|
|
39
|
-
for(let i = 0; i<argv.length; i += 1){
|
|
39
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
40
40
|
const arg = argv[i];
|
|
41
|
-
if(arg === "--dry-run"){
|
|
41
|
+
if (arg === "--dry-run") {
|
|
42
42
|
dryRun = true;
|
|
43
|
-
} else if(arg === "-C" || arg === "--cwd"){
|
|
44
|
-
cwd = argv[
|
|
45
|
-
} else if(arg === "--version" || arg === "-v"){
|
|
46
|
-
version = argv[
|
|
47
|
-
} else if(arg === "--patch"){
|
|
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
48
|
kind = "patch";
|
|
49
|
-
} else if(arg === "--minor"){
|
|
49
|
+
} else if (arg === "--minor") {
|
|
50
50
|
kind = "minor";
|
|
51
|
-
} else if(arg === "--major"){
|
|
51
|
+
} else if (arg === "--major") {
|
|
52
52
|
kind = "major";
|
|
53
|
-
} else if(arg === "-h" || arg === "--help"){
|
|
53
|
+
} else if (arg === "-h" || arg === "--help") {
|
|
54
54
|
printBumpHelp();
|
|
55
55
|
process.exit(0);
|
|
56
|
-
} else if(!arg.startsWith("-")){
|
|
57
|
-
if(isBumpKind(arg)){
|
|
56
|
+
} else if (!arg.startsWith("-")) {
|
|
57
|
+
if (isBumpKind(arg)) {
|
|
58
58
|
kind = arg;
|
|
59
59
|
} else {
|
|
60
60
|
version = arg;
|
|
@@ -62,98 +62,112 @@ function parseBumpArgs(argv : string[]) : BumpOptions {
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
if(version){
|
|
65
|
+
if (version) {
|
|
66
66
|
assertVersion(version);
|
|
67
67
|
}
|
|
68
68
|
return { version, kind, cwd, dryRun };
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
function isBumpKind(value
|
|
71
|
+
function isBumpKind(value: string): value is BumpKind {
|
|
72
72
|
return value === "patch" || value === "minor" || value === "major";
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
function assertVersion(version
|
|
76
|
-
if(
|
|
75
|
+
function assertVersion(version: string): void {
|
|
76
|
+
if (!/^\d+\.\d+\.\d+(-[\w.-]+)?$/.test(version)) {
|
|
77
77
|
throw new Error(`invalid version ${version}, expected semver like 0.0.0`);
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
export function bumpWorkspace(options
|
|
81
|
+
export function bumpWorkspace(options: BumpOptions): BumpReport {
|
|
82
82
|
const root = findWorkspaceRoot(options.cwd ?? process.cwd());
|
|
83
83
|
const cratesDir = join(root, "projects", "crates");
|
|
84
84
|
const packagesDir = join(root, "projects", "packages");
|
|
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
|
-
kind
|
|
100
|
-
cargo,
|
|
101
|
-
npm,
|
|
102
|
+
kind: options.version ? undefined : options.kind ?? DEFAULT_KIND,
|
|
103
|
+
cargo: cargoUpdates.map((update) => update.path),
|
|
104
|
+
npm: npmUpdates.map((update) => update.path),
|
|
102
105
|
};
|
|
103
106
|
}
|
|
104
107
|
|
|
105
|
-
function readWorkspaceVersion(cargoPaths
|
|
108
|
+
function readWorkspaceVersion(root: string, cargoPaths: string[], npmPaths: string[]): string {
|
|
106
109
|
const versions = new Set<string>();
|
|
107
|
-
|
|
108
|
-
|
|
110
|
+
let usesWorkspaceVersion = false;
|
|
111
|
+
for (const path of cargoPaths) {
|
|
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
|
-
for(const path of npmPaths){
|
|
119
|
+
for (const path of npmPaths) {
|
|
111
120
|
versions.add(readPackageVersion(path));
|
|
112
121
|
}
|
|
113
|
-
if(versions.size === 0){
|
|
122
|
+
if (versions.size === 0) {
|
|
114
123
|
throw new Error("no crate or package versions found in workspace");
|
|
115
124
|
}
|
|
116
|
-
if(versions.size>1){
|
|
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
|
-
function readPackageVersion(path
|
|
132
|
-
const parsed = JSON.parse(readFileSync(path, "utf8"))as { version
|
|
133
|
-
if(!parsed.version){
|
|
145
|
+
function readPackageVersion(path: string): string {
|
|
146
|
+
const parsed = JSON.parse(readFileSync(path, "utf8")) as { version?: string };
|
|
147
|
+
if (!parsed.version) {
|
|
134
148
|
throw new Error(`no version field in ${path}`);
|
|
135
149
|
}
|
|
136
150
|
return parsed.version;
|
|
137
151
|
}
|
|
138
152
|
|
|
139
|
-
export function bumpSemver(version
|
|
140
|
-
const match = version.match(
|
|
141
|
-
if(!match){
|
|
153
|
+
export function bumpSemver(version: string, kind: BumpKind): string {
|
|
154
|
+
const match = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-.*)?$/);
|
|
155
|
+
if (!match) {
|
|
142
156
|
throw new Error(`cannot bump non-semver version ${version}`);
|
|
143
157
|
}
|
|
144
158
|
let major = Number(match[1]);
|
|
145
159
|
let minor = Number(match[2]);
|
|
146
160
|
let patch = Number(match[3]);
|
|
147
161
|
|
|
148
|
-
switch(kind){
|
|
149
|
-
case "patch"
|
|
162
|
+
switch (kind) {
|
|
163
|
+
case "patch":
|
|
150
164
|
patch += 1;
|
|
151
165
|
break;
|
|
152
|
-
case "minor"
|
|
166
|
+
case "minor":
|
|
153
167
|
minor += 1;
|
|
154
168
|
patch = 0;
|
|
155
169
|
break;
|
|
156
|
-
case "major"
|
|
170
|
+
case "major":
|
|
157
171
|
major += 1;
|
|
158
172
|
minor = 0;
|
|
159
173
|
patch = 0;
|
|
@@ -163,65 +177,138 @@ export function bumpSemver(version : string, kind : BumpKind) : string {
|
|
|
163
177
|
return `${major}.${minor}.${patch}`;
|
|
164
178
|
}
|
|
165
179
|
|
|
166
|
-
function collectCargoManifests(cratesDir
|
|
167
|
-
if(!existsSync(cratesDir)){
|
|
168
|
-
return[];
|
|
180
|
+
function collectCargoManifests(cratesDir: string): string[] {
|
|
181
|
+
if (!existsSync(cratesDir)) {
|
|
182
|
+
return [];
|
|
169
183
|
}
|
|
170
|
-
return readdirSync(cratesDir, { withFileTypes
|
|
171
|
-
.filter((entry) =>entry.isDirectory())
|
|
172
|
-
.map((entry) =>join(cratesDir, entry.name, "Cargo.toml"))
|
|
173
|
-
.filter((path) =>existsSync(path));
|
|
184
|
+
return readdirSync(cratesDir, { withFileTypes: true })
|
|
185
|
+
.filter((entry) => entry.isDirectory())
|
|
186
|
+
.map((entry) => join(cratesDir, entry.name, "Cargo.toml"))
|
|
187
|
+
.filter((path) => existsSync(path));
|
|
174
188
|
}
|
|
175
189
|
|
|
176
|
-
function collectPackageManifests(packagesDir
|
|
177
|
-
if(!existsSync(packagesDir)){
|
|
178
|
-
return[];
|
|
190
|
+
function collectPackageManifests(packagesDir: string): string[] {
|
|
191
|
+
if (!existsSync(packagesDir)) {
|
|
192
|
+
return [];
|
|
179
193
|
}
|
|
180
|
-
return readdirSync(packagesDir, { withFileTypes
|
|
181
|
-
.filter((entry) =>entry.isDirectory() && entry.name!== "node_modules")
|
|
182
|
-
.map((entry) =>join(packagesDir, entry.name, "package.json"))
|
|
183
|
-
.filter((path) =>existsSync(path));
|
|
194
|
+
return readdirSync(packagesDir, { withFileTypes: true })
|
|
195
|
+
.filter((entry) => entry.isDirectory() && entry.name !== "node_modules")
|
|
196
|
+
.map((entry) => join(packagesDir, entry.name, "package.json"))
|
|
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
|
|
201
|
-
if(!
|
|
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 };
|
|
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
|
|
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}`);
|
|
216
|
-
for(const path of report.cargo){
|
|
303
|
+
for (const path of report.cargo) {
|
|
217
304
|
console.log(` cargo: ${path}`);
|
|
218
305
|
}
|
|
219
|
-
for(const path of report.npm){
|
|
306
|
+
for (const path of report.npm) {
|
|
220
307
|
console.log(` npm: ${path}`);
|
|
221
308
|
}
|
|
222
309
|
}
|
|
223
310
|
|
|
224
|
-
function printBumpHelp()
|
|
311
|
+
function printBumpHelp(): void {
|
|
225
312
|
console.log(`nifty bump — align all crate and package versions in the workspace
|
|
226
313
|
|
|
227
314
|
Usage:
|
|
@@ -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
|
}
|