@cluerise/tools 4.2.2 → 5.0.1
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/README.md +1 -1
- package/dist/configs/.nvmrc +1 -1
- package/dist/configs/.prettierignore +6 -6
- package/dist/configs/_gitignore +2 -1
- package/dist/configs/eslint.config.js +6 -6
- package/dist/configs/release.config.js +2 -0
- package/dist/scripts/check-heroku-node-version/main.js +43 -14
- package/dist/scripts/create-commit-message/main.js +29 -22
- package/dist/scripts/format-commit-message/main.js +21 -18
- package/dist/scripts/init/main.js +66 -33
- package/dist/scripts/lint/main.js +42 -35
- package/dist/scripts/release/main.js +211 -30
- package/dist/scripts/update-node-versions/main.js +44 -15
- package/package.json +3 -3
package/README.md
CHANGED
package/dist/configs/.nvmrc
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
v24.
|
|
1
|
+
v24.3.0
|
|
@@ -18,6 +18,12 @@ worker-configuration.d.ts
|
|
|
18
18
|
# Dependencies
|
|
19
19
|
node_modules/
|
|
20
20
|
|
|
21
|
+
# .env, .envrc
|
|
22
|
+
.env
|
|
23
|
+
.env.*
|
|
24
|
+
.envrc
|
|
25
|
+
.envrc.*
|
|
26
|
+
|
|
21
27
|
# Git
|
|
22
28
|
.git/
|
|
23
29
|
|
|
@@ -35,12 +41,6 @@ yarn.lock
|
|
|
35
41
|
etc/
|
|
36
42
|
.DS_Store
|
|
37
43
|
|
|
38
|
-
# Sample of .env file
|
|
39
|
-
.env.example
|
|
40
|
-
.env.sample
|
|
41
|
-
.envrc.example
|
|
42
|
-
.envrc.sample
|
|
43
|
-
|
|
44
44
|
# Stryker
|
|
45
45
|
.stryker-tmp/
|
|
46
46
|
|
package/dist/configs/_gitignore
CHANGED
|
@@ -285,6 +285,12 @@ export default defineConfig([
|
|
|
285
285
|
// Dependencies
|
|
286
286
|
'**/node_modules/',
|
|
287
287
|
|
|
288
|
+
// .env, .envrc
|
|
289
|
+
'**/.env',
|
|
290
|
+
'**/.env.*',
|
|
291
|
+
'**/.envrc',
|
|
292
|
+
'**/.envrc.*',
|
|
293
|
+
|
|
288
294
|
// Fonts
|
|
289
295
|
'**/*.ttf',
|
|
290
296
|
'**/*.woff2',
|
|
@@ -322,12 +328,6 @@ export default defineConfig([
|
|
|
322
328
|
'**/etc/',
|
|
323
329
|
'**/.DS_Store',
|
|
324
330
|
|
|
325
|
-
// Sample of .env and .envrc files
|
|
326
|
-
'**/.env.example',
|
|
327
|
-
'**/.env.sample',
|
|
328
|
-
'**/.envrc.example',
|
|
329
|
-
'**/.envrc.sample',
|
|
330
|
-
|
|
331
331
|
// Stryker
|
|
332
332
|
'**/.stryker-tmp/',
|
|
333
333
|
|
|
@@ -37,12 +37,14 @@ export const createReleaseConfig = ({
|
|
|
37
37
|
owner,
|
|
38
38
|
repository,
|
|
39
39
|
path = '',
|
|
40
|
+
tagFormat,
|
|
40
41
|
releaseRules = defaultReleaseRules,
|
|
41
42
|
changelogTypes = defaultChangelogTypes
|
|
42
43
|
}) => ({
|
|
43
44
|
extends: [Path.join(import.meta.dirname, 'release-cwd.config.cjs')],
|
|
44
45
|
branches: ['main'],
|
|
45
46
|
repositoryUrl: getRepositoryUrl(path),
|
|
47
|
+
tagFormat,
|
|
46
48
|
preset: 'conventionalcommits',
|
|
47
49
|
dryRun: true,
|
|
48
50
|
|
|
@@ -9,19 +9,19 @@ class GitProvider {
|
|
|
9
9
|
static #origins = gitProviderOrigins;
|
|
10
10
|
static #names = Object.keys(this.#origins);
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
12
|
+
* Check if the provided name is a valid Git provider name.
|
|
13
13
|
*
|
|
14
|
-
* @param name
|
|
15
|
-
* @returns True if the name is valid, false
|
|
14
|
+
* @param name The name to check.
|
|
15
|
+
* @returns True if the name is valid, otherwise false.
|
|
16
16
|
*/
|
|
17
17
|
static isValidName(name) {
|
|
18
18
|
return this.#names.includes(name);
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
21
|
+
* Get the origin URL for the given Git provider name.
|
|
22
22
|
*
|
|
23
|
-
* @param name
|
|
24
|
-
* @returns The origin URL
|
|
23
|
+
* @param name The Git provider name.
|
|
24
|
+
* @returns The origin URL.
|
|
25
25
|
*/
|
|
26
26
|
static getOrigin(name) {
|
|
27
27
|
return this.#origins[name];
|
|
@@ -48,6 +48,13 @@ class PackageJson {
|
|
|
48
48
|
constructor(data) {
|
|
49
49
|
this.#data = data;
|
|
50
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Load and parse the `package.json` file, returning a `PackageJson` instance.
|
|
53
|
+
*
|
|
54
|
+
* Throws an error if the file is invalid or cannot be parsed.
|
|
55
|
+
*
|
|
56
|
+
* @returns A new `PackageJson` instance with parsed data.
|
|
57
|
+
*/
|
|
51
58
|
static async init() {
|
|
52
59
|
const content = await FileSystem.readFile("package.json", { encoding: "utf8" });
|
|
53
60
|
const data = JsonUtils.parse(content);
|
|
@@ -60,21 +67,41 @@ class PackageJson {
|
|
|
60
67
|
return new PackageJson(data);
|
|
61
68
|
}
|
|
62
69
|
/**
|
|
63
|
-
*
|
|
70
|
+
* Get the package name from `package.json`.
|
|
71
|
+
*
|
|
72
|
+
* @returns The package name.
|
|
73
|
+
*/
|
|
74
|
+
get name() {
|
|
75
|
+
return this.#data.name;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Get the package version from `package.json`.
|
|
79
|
+
*
|
|
80
|
+
* @returns The package version.
|
|
81
|
+
*/
|
|
82
|
+
get version() {
|
|
83
|
+
return this.#data.version;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Get the engines field from `package.json`, if present.
|
|
87
|
+
*
|
|
88
|
+
* @returns The engines object or undefined.
|
|
64
89
|
*/
|
|
65
90
|
get engines() {
|
|
66
91
|
return this.#data.engines;
|
|
67
92
|
}
|
|
68
93
|
/**
|
|
69
|
-
*
|
|
94
|
+
* Set the engines field in `package.json`.
|
|
70
95
|
*
|
|
71
|
-
* @param engines
|
|
96
|
+
* @param engines The engines object to set.
|
|
72
97
|
*/
|
|
73
98
|
set engines(engines) {
|
|
74
99
|
this.#data.engines = engines;
|
|
75
100
|
}
|
|
76
101
|
/**
|
|
77
|
-
*
|
|
102
|
+
* Get the repository field from `package.json`, if present.
|
|
103
|
+
*
|
|
104
|
+
* @returns The repository object or string, or undefined.
|
|
78
105
|
*/
|
|
79
106
|
get repository() {
|
|
80
107
|
return this.#data.repository;
|
|
@@ -111,9 +138,11 @@ class PackageJson {
|
|
|
111
138
|
throw new Error("Unsupported repository URL");
|
|
112
139
|
}
|
|
113
140
|
/**
|
|
114
|
-
*
|
|
141
|
+
* Parse the repository information from `package.json` and return a `GitRepository` object or null.
|
|
142
|
+
*
|
|
143
|
+
* Returns null if no repository is defined or if parsing fails.
|
|
115
144
|
*
|
|
116
|
-
* @returns The parsed GitRepository or null if
|
|
145
|
+
* @returns The parsed `GitRepository`, or null if not available.
|
|
117
146
|
*/
|
|
118
147
|
parseGitRepository() {
|
|
119
148
|
if (!this.repository) {
|
|
@@ -125,7 +154,7 @@ class PackageJson {
|
|
|
125
154
|
return this.#parseGitRepository(this.repository.url);
|
|
126
155
|
}
|
|
127
156
|
/**
|
|
128
|
-
*
|
|
157
|
+
* Save the current `package.json` data to disk, formatting it as prettified JSON.
|
|
129
158
|
*/
|
|
130
159
|
async save() {
|
|
131
160
|
const content = JsonUtils.prettify(this.#data) + "\n";
|
|
@@ -165,7 +194,7 @@ const herokuNodeReleasesSchema = z.object({
|
|
|
165
194
|
class Heroku {
|
|
166
195
|
static #nodeReleasesUrl = "https://raw.githubusercontent.com/heroku/heroku-buildpack-nodejs/latest/inventory/node.toml";
|
|
167
196
|
/**
|
|
168
|
-
*
|
|
197
|
+
* Fetch supported Node.js releases on Heroku.
|
|
169
198
|
*
|
|
170
199
|
* @returns A promise that resolves to an array of HerokuNodeRelease objects.
|
|
171
200
|
*/
|
|
@@ -31,10 +31,12 @@ const runMain = (main2) => {
|
|
|
31
31
|
};
|
|
32
32
|
class StringUtils {
|
|
33
33
|
/**
|
|
34
|
-
*
|
|
34
|
+
* Capitalize the first letter of a given string.
|
|
35
35
|
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
36
|
+
* If the string is empty, it is returned unchanged.
|
|
37
|
+
*
|
|
38
|
+
* @param value The string to capitalize.
|
|
39
|
+
* @returns The string with the first letter capitalized, or the original string if empty.
|
|
38
40
|
*/
|
|
39
41
|
static capitalize(value) {
|
|
40
42
|
const [firstLetter] = value;
|
|
@@ -46,17 +48,21 @@ class StringUtils {
|
|
|
46
48
|
}
|
|
47
49
|
class Git {
|
|
48
50
|
/**
|
|
49
|
-
*
|
|
51
|
+
* Get the name of the current branch in the git repository.
|
|
52
|
+
*
|
|
53
|
+
* Uses `git symbolic-ref --short HEAD` to determine the branch name.
|
|
50
54
|
*
|
|
51
|
-
* @returns The
|
|
55
|
+
* @returns The current branch name.
|
|
52
56
|
*/
|
|
53
57
|
static getBranchName() {
|
|
54
58
|
return ChildProcess.execSync("git symbolic-ref --short HEAD").toString().trim();
|
|
55
59
|
}
|
|
56
60
|
/**
|
|
57
|
-
*
|
|
61
|
+
* Check if the repository is currently in a rebase state.
|
|
58
62
|
*
|
|
59
|
-
*
|
|
63
|
+
* Checks for the presence of a rebase directory in the git directory.
|
|
64
|
+
*
|
|
65
|
+
* @returns True if rebasing, otherwise false.
|
|
60
66
|
*/
|
|
61
67
|
static isRebasing() {
|
|
62
68
|
try {
|
|
@@ -73,7 +79,7 @@ class CommitLinter {
|
|
|
73
79
|
this.#config = config;
|
|
74
80
|
}
|
|
75
81
|
/**
|
|
76
|
-
*
|
|
82
|
+
* Initialize the CommitLinter with the loaded commitlint configuration.
|
|
77
83
|
*
|
|
78
84
|
* @returns A Promise that resolves to an instance of CommitLinter.
|
|
79
85
|
*/
|
|
@@ -82,9 +88,9 @@ class CommitLinter {
|
|
|
82
88
|
return new CommitLinter(config);
|
|
83
89
|
}
|
|
84
90
|
/**
|
|
85
|
-
*
|
|
91
|
+
* Lint commit messages using commitlint.
|
|
86
92
|
*
|
|
87
|
-
* @param args
|
|
93
|
+
* @param args An array of arguments to pass to commitlint.
|
|
88
94
|
* @returns The exit status of the commitlint command.
|
|
89
95
|
*/
|
|
90
96
|
static lint(args) {
|
|
@@ -109,9 +115,9 @@ class CommitLinter {
|
|
|
109
115
|
return this.#isValidEnumValue("scope-enum", scope);
|
|
110
116
|
}
|
|
111
117
|
/**
|
|
112
|
-
*
|
|
118
|
+
* Parse a semantic branch name into its type and scope.
|
|
113
119
|
*
|
|
114
|
-
* @param name
|
|
120
|
+
* @param name The branch name to parse.
|
|
115
121
|
* @returns An object containing the type and scope of the commit message.
|
|
116
122
|
* @throws An error if the branch name is invalid.
|
|
117
123
|
*/
|
|
@@ -128,12 +134,13 @@ class CommitLinter {
|
|
|
128
134
|
};
|
|
129
135
|
}
|
|
130
136
|
/**
|
|
131
|
-
*
|
|
137
|
+
* Get the prefix of a semantic commit message as a string.
|
|
132
138
|
*
|
|
133
139
|
* This prefix consists of the type and scope of the commit message.
|
|
134
140
|
* If the scope is not provided, it will only return the type.
|
|
135
|
-
*
|
|
136
|
-
* @
|
|
141
|
+
*
|
|
142
|
+
* @param prefix An object containing the type and scope of the commit message.
|
|
143
|
+
* @returns The stringified prefix.
|
|
137
144
|
*/
|
|
138
145
|
static stringifySemanticCommitMessagePrefix({ type, scope }) {
|
|
139
146
|
return `${type}${scope ? `(${scope})` : ""}`;
|
|
@@ -145,9 +152,9 @@ class CommitLinter {
|
|
|
145
152
|
return { prefix, content };
|
|
146
153
|
}
|
|
147
154
|
/**
|
|
148
|
-
*
|
|
155
|
+
* Format a semantic commit message by capitalizing the content after the prefix.
|
|
149
156
|
*
|
|
150
|
-
* @param message
|
|
157
|
+
* @param message The commit message to format.
|
|
151
158
|
* @returns The formatted commit message.
|
|
152
159
|
*/
|
|
153
160
|
static formatSemanticCommitMessage(message) {
|
|
@@ -158,20 +165,20 @@ class CommitLinter {
|
|
|
158
165
|
return `${prefix}: ${StringUtils.capitalize(content)}`;
|
|
159
166
|
}
|
|
160
167
|
/**
|
|
161
|
-
*
|
|
168
|
+
* Create a semantic commit message from a prefix and a message.
|
|
162
169
|
*
|
|
163
|
-
* @param prefix
|
|
164
|
-
* @param message
|
|
170
|
+
* @param prefix The prefix of the commit message, containing type and scope.
|
|
171
|
+
* @param message The content of the commit message.
|
|
165
172
|
* @returns The formatted semantic commit message.
|
|
166
173
|
*/
|
|
167
174
|
static createSemanticCommitMessage(prefix, message) {
|
|
168
175
|
const [subject, ...comments] = message.split("\n#");
|
|
169
176
|
let commitMessage = this.stringifySemanticCommitMessagePrefix(prefix) + ": ";
|
|
170
177
|
if (subject) {
|
|
171
|
-
commitMessage += StringUtils.capitalize(subject);
|
|
178
|
+
commitMessage += StringUtils.capitalize(subject.trim());
|
|
172
179
|
}
|
|
173
180
|
if (comments.length > 0) {
|
|
174
|
-
commitMessage += "\n\n#" + comments.join("\n#");
|
|
181
|
+
commitMessage += "\n\n#" + comments.map((comment) => comment.trim()).join("\n#");
|
|
175
182
|
}
|
|
176
183
|
return commitMessage;
|
|
177
184
|
}
|
|
@@ -31,10 +31,12 @@ const runMain = (main2) => {
|
|
|
31
31
|
};
|
|
32
32
|
class StringUtils {
|
|
33
33
|
/**
|
|
34
|
-
*
|
|
34
|
+
* Capitalize the first letter of a given string.
|
|
35
35
|
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
36
|
+
* If the string is empty, it is returned unchanged.
|
|
37
|
+
*
|
|
38
|
+
* @param value The string to capitalize.
|
|
39
|
+
* @returns The string with the first letter capitalized, or the original string if empty.
|
|
38
40
|
*/
|
|
39
41
|
static capitalize(value) {
|
|
40
42
|
const [firstLetter] = value;
|
|
@@ -50,7 +52,7 @@ class CommitLinter {
|
|
|
50
52
|
this.#config = config;
|
|
51
53
|
}
|
|
52
54
|
/**
|
|
53
|
-
*
|
|
55
|
+
* Initialize the CommitLinter with the loaded commitlint configuration.
|
|
54
56
|
*
|
|
55
57
|
* @returns A Promise that resolves to an instance of CommitLinter.
|
|
56
58
|
*/
|
|
@@ -59,9 +61,9 @@ class CommitLinter {
|
|
|
59
61
|
return new CommitLinter(config);
|
|
60
62
|
}
|
|
61
63
|
/**
|
|
62
|
-
*
|
|
64
|
+
* Lint commit messages using commitlint.
|
|
63
65
|
*
|
|
64
|
-
* @param args
|
|
66
|
+
* @param args An array of arguments to pass to commitlint.
|
|
65
67
|
* @returns The exit status of the commitlint command.
|
|
66
68
|
*/
|
|
67
69
|
static lint(args) {
|
|
@@ -86,9 +88,9 @@ class CommitLinter {
|
|
|
86
88
|
return this.#isValidEnumValue("scope-enum", scope);
|
|
87
89
|
}
|
|
88
90
|
/**
|
|
89
|
-
*
|
|
91
|
+
* Parse a semantic branch name into its type and scope.
|
|
90
92
|
*
|
|
91
|
-
* @param name
|
|
93
|
+
* @param name The branch name to parse.
|
|
92
94
|
* @returns An object containing the type and scope of the commit message.
|
|
93
95
|
* @throws An error if the branch name is invalid.
|
|
94
96
|
*/
|
|
@@ -105,12 +107,13 @@ class CommitLinter {
|
|
|
105
107
|
};
|
|
106
108
|
}
|
|
107
109
|
/**
|
|
108
|
-
*
|
|
110
|
+
* Get the prefix of a semantic commit message as a string.
|
|
109
111
|
*
|
|
110
112
|
* This prefix consists of the type and scope of the commit message.
|
|
111
113
|
* If the scope is not provided, it will only return the type.
|
|
112
|
-
*
|
|
113
|
-
* @
|
|
114
|
+
*
|
|
115
|
+
* @param prefix An object containing the type and scope of the commit message.
|
|
116
|
+
* @returns The stringified prefix.
|
|
114
117
|
*/
|
|
115
118
|
static stringifySemanticCommitMessagePrefix({ type, scope }) {
|
|
116
119
|
return `${type}${scope ? `(${scope})` : ""}`;
|
|
@@ -122,9 +125,9 @@ class CommitLinter {
|
|
|
122
125
|
return { prefix, content };
|
|
123
126
|
}
|
|
124
127
|
/**
|
|
125
|
-
*
|
|
128
|
+
* Format a semantic commit message by capitalizing the content after the prefix.
|
|
126
129
|
*
|
|
127
|
-
* @param message
|
|
130
|
+
* @param message The commit message to format.
|
|
128
131
|
* @returns The formatted commit message.
|
|
129
132
|
*/
|
|
130
133
|
static formatSemanticCommitMessage(message) {
|
|
@@ -135,20 +138,20 @@ class CommitLinter {
|
|
|
135
138
|
return `${prefix}: ${StringUtils.capitalize(content)}`;
|
|
136
139
|
}
|
|
137
140
|
/**
|
|
138
|
-
*
|
|
141
|
+
* Create a semantic commit message from a prefix and a message.
|
|
139
142
|
*
|
|
140
|
-
* @param prefix
|
|
141
|
-
* @param message
|
|
143
|
+
* @param prefix The prefix of the commit message, containing type and scope.
|
|
144
|
+
* @param message The content of the commit message.
|
|
142
145
|
* @returns The formatted semantic commit message.
|
|
143
146
|
*/
|
|
144
147
|
static createSemanticCommitMessage(prefix, message) {
|
|
145
148
|
const [subject, ...comments] = message.split("\n#");
|
|
146
149
|
let commitMessage = this.stringifySemanticCommitMessagePrefix(prefix) + ": ";
|
|
147
150
|
if (subject) {
|
|
148
|
-
commitMessage += StringUtils.capitalize(subject);
|
|
151
|
+
commitMessage += StringUtils.capitalize(subject.trim());
|
|
149
152
|
}
|
|
150
153
|
if (comments.length > 0) {
|
|
151
|
-
commitMessage += "\n\n#" + comments.join("\n#");
|
|
154
|
+
commitMessage += "\n\n#" + comments.map((comment) => comment.trim()).join("\n#");
|
|
152
155
|
}
|
|
153
156
|
return commitMessage;
|
|
154
157
|
}
|
|
@@ -11,19 +11,19 @@ import * as Prettier from "prettier";
|
|
|
11
11
|
class CoreConfig {
|
|
12
12
|
static #packageName = "@cluerise/tools";
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
14
|
+
* Determine if the application is running in production mode.
|
|
15
15
|
*
|
|
16
|
-
* This
|
|
16
|
+
* This checks the environment variable `import.meta.env.PROD` to determine the mode.
|
|
17
17
|
*
|
|
18
|
-
* @returns True if in production mode, false
|
|
18
|
+
* @returns True if running in production mode, otherwise false.
|
|
19
19
|
*/
|
|
20
20
|
static get isProd() {
|
|
21
21
|
return true;
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
24
|
+
* Get the path to package configuration files in production.
|
|
25
25
|
*
|
|
26
|
-
*
|
|
26
|
+
* Used to load configuration from the package when running in production.
|
|
27
27
|
*
|
|
28
28
|
* @returns The path to the configuration files in the package.
|
|
29
29
|
*/
|
|
@@ -34,10 +34,9 @@ class CoreConfig {
|
|
|
34
34
|
return this.isProd ? `./node_modules/${this.#packageName}` : ".";
|
|
35
35
|
}
|
|
36
36
|
/**
|
|
37
|
-
*
|
|
37
|
+
* Get the directory path where configuration files are stored.
|
|
38
38
|
*
|
|
39
|
-
* In production
|
|
40
|
-
* In development mode, it points to the root directory.
|
|
39
|
+
* In production, this points to `dist/configs` in the package; in development, it points to the root directory.
|
|
41
40
|
*
|
|
42
41
|
* @returns The path to the configuration directory.
|
|
43
42
|
*/
|
|
@@ -47,13 +46,18 @@ class CoreConfig {
|
|
|
47
46
|
}
|
|
48
47
|
class ConsoleStatusLogger {
|
|
49
48
|
#command;
|
|
49
|
+
/**
|
|
50
|
+
* Create a new logger for a specific command.
|
|
51
|
+
*
|
|
52
|
+
* @param command The command name to log.
|
|
53
|
+
*/
|
|
50
54
|
constructor(command) {
|
|
51
55
|
this.#command = command;
|
|
52
56
|
}
|
|
53
57
|
/**
|
|
54
|
-
*
|
|
58
|
+
* Log the beginning of a command with the provided argument.
|
|
55
59
|
*
|
|
56
|
-
* @param argument
|
|
60
|
+
* @param argument The argument for the command.
|
|
57
61
|
*
|
|
58
62
|
* @example
|
|
59
63
|
* const logger = new ConsoleStatusLogger('Linting');
|
|
@@ -69,10 +73,10 @@ class ConsoleStatusLogger {
|
|
|
69
73
|
return exitCode === 0 ? "OK" : "Failed";
|
|
70
74
|
}
|
|
71
75
|
/**
|
|
72
|
-
*
|
|
76
|
+
* Log the end of a command with the provided argument and exit code.
|
|
73
77
|
*
|
|
74
|
-
* @param argument
|
|
75
|
-
* @param exitCode
|
|
78
|
+
* @param argument The argument for the command.
|
|
79
|
+
* @param exitCode The exit code of the command execution. If null, it indicates completion without an error.
|
|
76
80
|
*
|
|
77
81
|
* @example
|
|
78
82
|
* const logger = new ConsoleStatusLogger('Linting');
|
|
@@ -91,19 +95,19 @@ class GitProvider {
|
|
|
91
95
|
static #origins = gitProviderOrigins;
|
|
92
96
|
static #names = Object.keys(this.#origins);
|
|
93
97
|
/**
|
|
94
|
-
*
|
|
98
|
+
* Check if the provided name is a valid Git provider name.
|
|
95
99
|
*
|
|
96
|
-
* @param name
|
|
97
|
-
* @returns True if the name is valid, false
|
|
100
|
+
* @param name The name to check.
|
|
101
|
+
* @returns True if the name is valid, otherwise false.
|
|
98
102
|
*/
|
|
99
103
|
static isValidName(name) {
|
|
100
104
|
return this.#names.includes(name);
|
|
101
105
|
}
|
|
102
106
|
/**
|
|
103
|
-
*
|
|
107
|
+
* Get the origin URL for the given Git provider name.
|
|
104
108
|
*
|
|
105
|
-
* @param name
|
|
106
|
-
* @returns The origin URL
|
|
109
|
+
* @param name The Git provider name.
|
|
110
|
+
* @returns The origin URL.
|
|
107
111
|
*/
|
|
108
112
|
static getOrigin(name) {
|
|
109
113
|
return this.#origins[name];
|
|
@@ -130,6 +134,13 @@ class PackageJson {
|
|
|
130
134
|
constructor(data) {
|
|
131
135
|
this.#data = data;
|
|
132
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Load and parse the `package.json` file, returning a `PackageJson` instance.
|
|
139
|
+
*
|
|
140
|
+
* Throws an error if the file is invalid or cannot be parsed.
|
|
141
|
+
*
|
|
142
|
+
* @returns A new `PackageJson` instance with parsed data.
|
|
143
|
+
*/
|
|
133
144
|
static async init() {
|
|
134
145
|
const content = await FileSystem.readFile("package.json", { encoding: "utf8" });
|
|
135
146
|
const data = JsonUtils.parse(content);
|
|
@@ -142,21 +153,41 @@ class PackageJson {
|
|
|
142
153
|
return new PackageJson(data);
|
|
143
154
|
}
|
|
144
155
|
/**
|
|
145
|
-
*
|
|
156
|
+
* Get the package name from `package.json`.
|
|
157
|
+
*
|
|
158
|
+
* @returns The package name.
|
|
159
|
+
*/
|
|
160
|
+
get name() {
|
|
161
|
+
return this.#data.name;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Get the package version from `package.json`.
|
|
165
|
+
*
|
|
166
|
+
* @returns The package version.
|
|
167
|
+
*/
|
|
168
|
+
get version() {
|
|
169
|
+
return this.#data.version;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Get the engines field from `package.json`, if present.
|
|
173
|
+
*
|
|
174
|
+
* @returns The engines object or undefined.
|
|
146
175
|
*/
|
|
147
176
|
get engines() {
|
|
148
177
|
return this.#data.engines;
|
|
149
178
|
}
|
|
150
179
|
/**
|
|
151
|
-
*
|
|
180
|
+
* Set the engines field in `package.json`.
|
|
152
181
|
*
|
|
153
|
-
* @param engines
|
|
182
|
+
* @param engines The engines object to set.
|
|
154
183
|
*/
|
|
155
184
|
set engines(engines) {
|
|
156
185
|
this.#data.engines = engines;
|
|
157
186
|
}
|
|
158
187
|
/**
|
|
159
|
-
*
|
|
188
|
+
* Get the repository field from `package.json`, if present.
|
|
189
|
+
*
|
|
190
|
+
* @returns The repository object or string, or undefined.
|
|
160
191
|
*/
|
|
161
192
|
get repository() {
|
|
162
193
|
return this.#data.repository;
|
|
@@ -193,9 +224,11 @@ class PackageJson {
|
|
|
193
224
|
throw new Error("Unsupported repository URL");
|
|
194
225
|
}
|
|
195
226
|
/**
|
|
196
|
-
*
|
|
227
|
+
* Parse the repository information from `package.json` and return a `GitRepository` object or null.
|
|
228
|
+
*
|
|
229
|
+
* Returns null if no repository is defined or if parsing fails.
|
|
197
230
|
*
|
|
198
|
-
* @returns The parsed GitRepository or null if
|
|
231
|
+
* @returns The parsed `GitRepository`, or null if not available.
|
|
199
232
|
*/
|
|
200
233
|
parseGitRepository() {
|
|
201
234
|
if (!this.repository) {
|
|
@@ -207,7 +240,7 @@ class PackageJson {
|
|
|
207
240
|
return this.#parseGitRepository(this.repository.url);
|
|
208
241
|
}
|
|
209
242
|
/**
|
|
210
|
-
*
|
|
243
|
+
* Save the current `package.json` data to disk, formatting it as prettified JSON.
|
|
211
244
|
*/
|
|
212
245
|
async save() {
|
|
213
246
|
const content = JsonUtils.prettify(this.#data) + "\n";
|
|
@@ -272,7 +305,7 @@ class ToolInitializer {
|
|
|
272
305
|
this.#configPackage = configPackage;
|
|
273
306
|
}
|
|
274
307
|
/**
|
|
275
|
-
*
|
|
308
|
+
* Get the names of all available tools.
|
|
276
309
|
*
|
|
277
310
|
* @returns An array of tool names.
|
|
278
311
|
*/
|
|
@@ -380,9 +413,9 @@ export default createReleaseConfig(${JsonUtils.prettify(configParams)});
|
|
|
380
413
|
await FileUtils.createFile(settingsPath, settingsContent);
|
|
381
414
|
}
|
|
382
415
|
/**
|
|
383
|
-
*
|
|
416
|
+
* Initialize the specified tool.
|
|
384
417
|
*
|
|
385
|
-
* @param toolName
|
|
418
|
+
* @param toolName The name of the tool to initialize.
|
|
386
419
|
* @returns A promise that resolves when the tool is initialized.
|
|
387
420
|
*/
|
|
388
421
|
async initTool(toolName) {
|
|
@@ -433,9 +466,9 @@ class FileLinter {
|
|
|
433
466
|
}
|
|
434
467
|
}
|
|
435
468
|
/**
|
|
436
|
-
*
|
|
469
|
+
* Lint files using `lint-staged` with the provided arguments.
|
|
437
470
|
*
|
|
438
|
-
* @param args
|
|
471
|
+
* @param args An array of arguments to pass to `lint-staged`.
|
|
439
472
|
* @returns The exit status of the `lint-staged` command.
|
|
440
473
|
*/
|
|
441
474
|
static lintStaged(args) {
|
|
@@ -528,9 +561,9 @@ class FileLinter {
|
|
|
528
561
|
return results;
|
|
529
562
|
}
|
|
530
563
|
/**
|
|
531
|
-
*
|
|
564
|
+
* Lint files matching the provided patterns using ESLint and Prettier.
|
|
532
565
|
*
|
|
533
|
-
* @param patterns
|
|
566
|
+
* @param patterns An array of glob patterns to match files against. Defaults to ['**'] which matches all files.
|
|
534
567
|
* @returns A promise that resolves to a LintFilesResult indicating the success or failure of the linting process.
|
|
535
568
|
*/
|
|
536
569
|
async lint(patterns = ["**"]) {
|
|
@@ -10,19 +10,19 @@ import * as Prettier from "prettier";
|
|
|
10
10
|
class CoreConfig {
|
|
11
11
|
static #packageName = "@cluerise/tools";
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
13
|
+
* Determine if the application is running in production mode.
|
|
14
14
|
*
|
|
15
|
-
* This
|
|
15
|
+
* This checks the environment variable `import.meta.env.PROD` to determine the mode.
|
|
16
16
|
*
|
|
17
|
-
* @returns True if in production mode, false
|
|
17
|
+
* @returns True if running in production mode, otherwise false.
|
|
18
18
|
*/
|
|
19
19
|
static get isProd() {
|
|
20
20
|
return true;
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
23
|
+
* Get the path to package configuration files in production.
|
|
24
24
|
*
|
|
25
|
-
*
|
|
25
|
+
* Used to load configuration from the package when running in production.
|
|
26
26
|
*
|
|
27
27
|
* @returns The path to the configuration files in the package.
|
|
28
28
|
*/
|
|
@@ -33,10 +33,9 @@ class CoreConfig {
|
|
|
33
33
|
return this.isProd ? `./node_modules/${this.#packageName}` : ".";
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
36
|
+
* Get the directory path where configuration files are stored.
|
|
37
37
|
*
|
|
38
|
-
* In production
|
|
39
|
-
* In development mode, it points to the root directory.
|
|
38
|
+
* In production, this points to `dist/configs` in the package; in development, it points to the root directory.
|
|
40
39
|
*
|
|
41
40
|
* @returns The path to the configuration directory.
|
|
42
41
|
*/
|
|
@@ -46,13 +45,18 @@ class CoreConfig {
|
|
|
46
45
|
}
|
|
47
46
|
class ConsoleStatusLogger {
|
|
48
47
|
#command;
|
|
48
|
+
/**
|
|
49
|
+
* Create a new logger for a specific command.
|
|
50
|
+
*
|
|
51
|
+
* @param command The command name to log.
|
|
52
|
+
*/
|
|
49
53
|
constructor(command) {
|
|
50
54
|
this.#command = command;
|
|
51
55
|
}
|
|
52
56
|
/**
|
|
53
|
-
*
|
|
57
|
+
* Log the beginning of a command with the provided argument.
|
|
54
58
|
*
|
|
55
|
-
* @param argument
|
|
59
|
+
* @param argument The argument for the command.
|
|
56
60
|
*
|
|
57
61
|
* @example
|
|
58
62
|
* const logger = new ConsoleStatusLogger('Linting');
|
|
@@ -68,10 +72,10 @@ class ConsoleStatusLogger {
|
|
|
68
72
|
return exitCode === 0 ? "OK" : "Failed";
|
|
69
73
|
}
|
|
70
74
|
/**
|
|
71
|
-
*
|
|
75
|
+
* Log the end of a command with the provided argument and exit code.
|
|
72
76
|
*
|
|
73
|
-
* @param argument
|
|
74
|
-
* @param exitCode
|
|
77
|
+
* @param argument The argument for the command.
|
|
78
|
+
* @param exitCode The exit code of the command execution. If null, it indicates completion without an error.
|
|
75
79
|
*
|
|
76
80
|
* @example
|
|
77
81
|
* const logger = new ConsoleStatusLogger('Linting');
|
|
@@ -109,10 +113,12 @@ const runMain = (main2) => {
|
|
|
109
113
|
};
|
|
110
114
|
class StringUtils {
|
|
111
115
|
/**
|
|
112
|
-
*
|
|
116
|
+
* Capitalize the first letter of a given string.
|
|
113
117
|
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
118
|
+
* If the string is empty, it is returned unchanged.
|
|
119
|
+
*
|
|
120
|
+
* @param value The string to capitalize.
|
|
121
|
+
* @returns The string with the first letter capitalized, or the original string if empty.
|
|
116
122
|
*/
|
|
117
123
|
static capitalize(value) {
|
|
118
124
|
const [firstLetter] = value;
|
|
@@ -128,7 +134,7 @@ class CommitLinter {
|
|
|
128
134
|
this.#config = config;
|
|
129
135
|
}
|
|
130
136
|
/**
|
|
131
|
-
*
|
|
137
|
+
* Initialize the CommitLinter with the loaded commitlint configuration.
|
|
132
138
|
*
|
|
133
139
|
* @returns A Promise that resolves to an instance of CommitLinter.
|
|
134
140
|
*/
|
|
@@ -137,9 +143,9 @@ class CommitLinter {
|
|
|
137
143
|
return new CommitLinter(config);
|
|
138
144
|
}
|
|
139
145
|
/**
|
|
140
|
-
*
|
|
146
|
+
* Lint commit messages using commitlint.
|
|
141
147
|
*
|
|
142
|
-
* @param args
|
|
148
|
+
* @param args An array of arguments to pass to commitlint.
|
|
143
149
|
* @returns The exit status of the commitlint command.
|
|
144
150
|
*/
|
|
145
151
|
static lint(args) {
|
|
@@ -164,9 +170,9 @@ class CommitLinter {
|
|
|
164
170
|
return this.#isValidEnumValue("scope-enum", scope);
|
|
165
171
|
}
|
|
166
172
|
/**
|
|
167
|
-
*
|
|
173
|
+
* Parse a semantic branch name into its type and scope.
|
|
168
174
|
*
|
|
169
|
-
* @param name
|
|
175
|
+
* @param name The branch name to parse.
|
|
170
176
|
* @returns An object containing the type and scope of the commit message.
|
|
171
177
|
* @throws An error if the branch name is invalid.
|
|
172
178
|
*/
|
|
@@ -183,12 +189,13 @@ class CommitLinter {
|
|
|
183
189
|
};
|
|
184
190
|
}
|
|
185
191
|
/**
|
|
186
|
-
*
|
|
192
|
+
* Get the prefix of a semantic commit message as a string.
|
|
187
193
|
*
|
|
188
194
|
* This prefix consists of the type and scope of the commit message.
|
|
189
195
|
* If the scope is not provided, it will only return the type.
|
|
190
|
-
*
|
|
191
|
-
* @
|
|
196
|
+
*
|
|
197
|
+
* @param prefix An object containing the type and scope of the commit message.
|
|
198
|
+
* @returns The stringified prefix.
|
|
192
199
|
*/
|
|
193
200
|
static stringifySemanticCommitMessagePrefix({ type, scope }) {
|
|
194
201
|
return `${type}${scope ? `(${scope})` : ""}`;
|
|
@@ -200,9 +207,9 @@ class CommitLinter {
|
|
|
200
207
|
return { prefix, content };
|
|
201
208
|
}
|
|
202
209
|
/**
|
|
203
|
-
*
|
|
210
|
+
* Format a semantic commit message by capitalizing the content after the prefix.
|
|
204
211
|
*
|
|
205
|
-
* @param message
|
|
212
|
+
* @param message The commit message to format.
|
|
206
213
|
* @returns The formatted commit message.
|
|
207
214
|
*/
|
|
208
215
|
static formatSemanticCommitMessage(message) {
|
|
@@ -213,20 +220,20 @@ class CommitLinter {
|
|
|
213
220
|
return `${prefix}: ${StringUtils.capitalize(content)}`;
|
|
214
221
|
}
|
|
215
222
|
/**
|
|
216
|
-
*
|
|
223
|
+
* Create a semantic commit message from a prefix and a message.
|
|
217
224
|
*
|
|
218
|
-
* @param prefix
|
|
219
|
-
* @param message
|
|
225
|
+
* @param prefix The prefix of the commit message, containing type and scope.
|
|
226
|
+
* @param message The content of the commit message.
|
|
220
227
|
* @returns The formatted semantic commit message.
|
|
221
228
|
*/
|
|
222
229
|
static createSemanticCommitMessage(prefix, message) {
|
|
223
230
|
const [subject, ...comments] = message.split("\n#");
|
|
224
231
|
let commitMessage = this.stringifySemanticCommitMessagePrefix(prefix) + ": ";
|
|
225
232
|
if (subject) {
|
|
226
|
-
commitMessage += StringUtils.capitalize(subject);
|
|
233
|
+
commitMessage += StringUtils.capitalize(subject.trim());
|
|
227
234
|
}
|
|
228
235
|
if (comments.length > 0) {
|
|
229
|
-
commitMessage += "\n\n#" + comments.join("\n#");
|
|
236
|
+
commitMessage += "\n\n#" + comments.map((comment) => comment.trim()).join("\n#");
|
|
230
237
|
}
|
|
231
238
|
return commitMessage;
|
|
232
239
|
}
|
|
@@ -248,9 +255,9 @@ class FileLinter {
|
|
|
248
255
|
}
|
|
249
256
|
}
|
|
250
257
|
/**
|
|
251
|
-
*
|
|
258
|
+
* Lint files using `lint-staged` with the provided arguments.
|
|
252
259
|
*
|
|
253
|
-
* @param args
|
|
260
|
+
* @param args An array of arguments to pass to `lint-staged`.
|
|
254
261
|
* @returns The exit status of the `lint-staged` command.
|
|
255
262
|
*/
|
|
256
263
|
static lintStaged(args) {
|
|
@@ -343,9 +350,9 @@ class FileLinter {
|
|
|
343
350
|
return results;
|
|
344
351
|
}
|
|
345
352
|
/**
|
|
346
|
-
*
|
|
353
|
+
* Lint files matching the provided patterns using ESLint and Prettier.
|
|
347
354
|
*
|
|
348
|
-
* @param patterns
|
|
355
|
+
* @param patterns An array of glob patterns to match files against. Defaults to ['**'] which matches all files.
|
|
349
356
|
* @returns A promise that resolves to a LintFilesResult indicating the success or failure of the linting process.
|
|
350
357
|
*/
|
|
351
358
|
async lint(patterns = ["**"]) {
|
|
@@ -9,6 +9,31 @@ import "@commitlint/load";
|
|
|
9
9
|
import { ESLint } from "eslint";
|
|
10
10
|
import { glob } from "glob";
|
|
11
11
|
import * as Prettier from "prettier";
|
|
12
|
+
const gitProviderOrigins = {
|
|
13
|
+
github: "https://github.com"
|
|
14
|
+
};
|
|
15
|
+
class GitProvider {
|
|
16
|
+
static #origins = gitProviderOrigins;
|
|
17
|
+
static #names = Object.keys(this.#origins);
|
|
18
|
+
/**
|
|
19
|
+
* Check if the provided name is a valid Git provider name.
|
|
20
|
+
*
|
|
21
|
+
* @param name The name to check.
|
|
22
|
+
* @returns True if the name is valid, otherwise false.
|
|
23
|
+
*/
|
|
24
|
+
static isValidName(name) {
|
|
25
|
+
return this.#names.includes(name);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Get the origin URL for the given Git provider name.
|
|
29
|
+
*
|
|
30
|
+
* @param name The Git provider name.
|
|
31
|
+
* @returns The origin URL.
|
|
32
|
+
*/
|
|
33
|
+
static getOrigin(name) {
|
|
34
|
+
return this.#origins[name];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
12
37
|
const enginesSchema = z.object({
|
|
13
38
|
node: z.string()
|
|
14
39
|
});
|
|
@@ -18,13 +43,131 @@ const repositoryObjectSchema = z.object({
|
|
|
18
43
|
directory: z.ostring()
|
|
19
44
|
});
|
|
20
45
|
const repositorySchema = z.union([z.string(), repositoryObjectSchema]);
|
|
21
|
-
z.object({
|
|
46
|
+
const packageJsonDataSchema = z.object({
|
|
22
47
|
name: z.string(),
|
|
23
48
|
version: z.string(),
|
|
24
49
|
description: z.string(),
|
|
25
50
|
engines: enginesSchema.optional(),
|
|
26
51
|
repository: repositorySchema.optional()
|
|
27
52
|
});
|
|
53
|
+
class PackageJson {
|
|
54
|
+
#data;
|
|
55
|
+
constructor(data) {
|
|
56
|
+
this.#data = data;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Load and parse the `package.json` file, returning a `PackageJson` instance.
|
|
60
|
+
*
|
|
61
|
+
* Throws an error if the file is invalid or cannot be parsed.
|
|
62
|
+
*
|
|
63
|
+
* @returns A new `PackageJson` instance with parsed data.
|
|
64
|
+
*/
|
|
65
|
+
static async init() {
|
|
66
|
+
const content = await FileSystem.readFile("package.json", { encoding: "utf8" });
|
|
67
|
+
const data = JsonUtils.parse(content);
|
|
68
|
+
const parseResult = packageJsonDataSchema.safeParse(data);
|
|
69
|
+
if (!parseResult.success) {
|
|
70
|
+
throw new Error("Invalid package.json", {
|
|
71
|
+
cause: parseResult.error
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
return new PackageJson(data);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get the package name from `package.json`.
|
|
78
|
+
*
|
|
79
|
+
* @returns The package name.
|
|
80
|
+
*/
|
|
81
|
+
get name() {
|
|
82
|
+
return this.#data.name;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Get the package version from `package.json`.
|
|
86
|
+
*
|
|
87
|
+
* @returns The package version.
|
|
88
|
+
*/
|
|
89
|
+
get version() {
|
|
90
|
+
return this.#data.version;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get the engines field from `package.json`, if present.
|
|
94
|
+
*
|
|
95
|
+
* @returns The engines object or undefined.
|
|
96
|
+
*/
|
|
97
|
+
get engines() {
|
|
98
|
+
return this.#data.engines;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Set the engines field in `package.json`.
|
|
102
|
+
*
|
|
103
|
+
* @param engines The engines object to set.
|
|
104
|
+
*/
|
|
105
|
+
set engines(engines) {
|
|
106
|
+
this.#data.engines = engines;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Get the repository field from `package.json`, if present.
|
|
110
|
+
*
|
|
111
|
+
* @returns The repository object or string, or undefined.
|
|
112
|
+
*/
|
|
113
|
+
get repository() {
|
|
114
|
+
return this.#data.repository;
|
|
115
|
+
}
|
|
116
|
+
#parseGitRepository(urlString) {
|
|
117
|
+
if (!urlString) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
const urlValue = urlString.includes(":") ? urlString : `https://${urlString}`;
|
|
121
|
+
const url = new URL(urlValue);
|
|
122
|
+
const scheme = url.protocol.slice(0, -1);
|
|
123
|
+
if (GitProvider.isValidName(scheme)) {
|
|
124
|
+
const [owner, repositoryName] = url.pathname.split("/");
|
|
125
|
+
if (!owner || !repositoryName) {
|
|
126
|
+
throw new Error("Unknown owner or repositoryName");
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
origin: GitProvider.getOrigin(scheme),
|
|
130
|
+
owner,
|
|
131
|
+
repositoryName
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
if (scheme === "https") {
|
|
135
|
+
const [, owner, repositoryName] = url.pathname.split("/");
|
|
136
|
+
if (!owner || !repositoryName) {
|
|
137
|
+
throw new Error("Unknown owner or repositoryName");
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
origin: url.origin,
|
|
141
|
+
owner,
|
|
142
|
+
repositoryName: repositoryName.endsWith(".git") ? repositoryName.slice(0, -4) : repositoryName
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
throw new Error("Unsupported repository URL");
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Parse the repository information from `package.json` and return a `GitRepository` object or null.
|
|
149
|
+
*
|
|
150
|
+
* Returns null if no repository is defined or if parsing fails.
|
|
151
|
+
*
|
|
152
|
+
* @returns The parsed `GitRepository`, or null if not available.
|
|
153
|
+
*/
|
|
154
|
+
parseGitRepository() {
|
|
155
|
+
if (!this.repository) {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
if (typeof this.repository === "string") {
|
|
159
|
+
return this.#parseGitRepository(this.repository);
|
|
160
|
+
}
|
|
161
|
+
return this.#parseGitRepository(this.repository.url);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Save the current `package.json` data to disk, formatting it as prettified JSON.
|
|
165
|
+
*/
|
|
166
|
+
async save() {
|
|
167
|
+
const content = JsonUtils.prettify(this.#data) + "\n";
|
|
168
|
+
await FileSystem.writeFile("package.json", content, { encoding: "utf8" });
|
|
169
|
+
}
|
|
170
|
+
}
|
|
28
171
|
const runMain = (main2) => {
|
|
29
172
|
Promise.resolve().then(() => main2(process.argv.slice(2))).then((exitCode) => {
|
|
30
173
|
process.exit(exitCode);
|
|
@@ -33,6 +176,17 @@ const runMain = (main2) => {
|
|
|
33
176
|
process.exit(1);
|
|
34
177
|
});
|
|
35
178
|
};
|
|
179
|
+
class JsonUtils {
|
|
180
|
+
static stringify(value) {
|
|
181
|
+
return JSON.stringify(value);
|
|
182
|
+
}
|
|
183
|
+
static prettify(value) {
|
|
184
|
+
return JSON.stringify(value, null, 2);
|
|
185
|
+
}
|
|
186
|
+
static parse(value) {
|
|
187
|
+
return JSON.parse(value);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
36
190
|
class FileLinter {
|
|
37
191
|
#options;
|
|
38
192
|
#eslint;
|
|
@@ -50,9 +204,9 @@ class FileLinter {
|
|
|
50
204
|
}
|
|
51
205
|
}
|
|
52
206
|
/**
|
|
53
|
-
*
|
|
207
|
+
* Lint files using `lint-staged` with the provided arguments.
|
|
54
208
|
*
|
|
55
|
-
* @param args
|
|
209
|
+
* @param args An array of arguments to pass to `lint-staged`.
|
|
56
210
|
* @returns The exit status of the `lint-staged` command.
|
|
57
211
|
*/
|
|
58
212
|
static lintStaged(args) {
|
|
@@ -145,9 +299,9 @@ class FileLinter {
|
|
|
145
299
|
return results;
|
|
146
300
|
}
|
|
147
301
|
/**
|
|
148
|
-
*
|
|
302
|
+
* Lint files matching the provided patterns using ESLint and Prettier.
|
|
149
303
|
*
|
|
150
|
-
* @param patterns
|
|
304
|
+
* @param patterns An array of glob patterns to match files against. Defaults to ['**'] which matches all files.
|
|
151
305
|
* @returns A promise that resolves to a LintFilesResult indicating the success or failure of the linting process.
|
|
152
306
|
*/
|
|
153
307
|
async lint(patterns = ["**"]) {
|
|
@@ -175,13 +329,9 @@ class Releaser {
|
|
|
175
329
|
this.#changelogPath = changelogPath;
|
|
176
330
|
}
|
|
177
331
|
/**
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
* The configuration is loaded from a specified file path, and the changelog path is set.
|
|
332
|
+
* Initialize a new instance of the Releaser class with the provided configuration.
|
|
181
333
|
*
|
|
182
|
-
* @param options
|
|
183
|
-
* @param options.configPath - The path to the semantic-release configuration file. Defaults to 'release.config.js'.
|
|
184
|
-
* @param options.changelogPath - The path to the changelog file. Defaults to 'CHANGELOG.md'.
|
|
334
|
+
* @param options Options for initializing the releaser, including paths for the config and changelog.
|
|
185
335
|
* @returns A new instance of Releaser.
|
|
186
336
|
*/
|
|
187
337
|
static async init({
|
|
@@ -193,7 +343,7 @@ class Releaser {
|
|
|
193
343
|
return new Releaser(config, changelogPath);
|
|
194
344
|
}
|
|
195
345
|
/**
|
|
196
|
-
*
|
|
346
|
+
* Create a new release based on the semantic-release configuration.
|
|
197
347
|
*
|
|
198
348
|
* It updates the changelog with the new release notes and runs linting on the changelog file.
|
|
199
349
|
*
|
|
@@ -212,15 +362,38 @@ class Releaser {
|
|
|
212
362
|
const linter = new FileLinter({ fix: true });
|
|
213
363
|
await linter.lint([this.#changelogPath]);
|
|
214
364
|
ChildProcess.execFileSync("pnpm", ["version", nextRelease.version, "--no-git-tag-version"]);
|
|
215
|
-
|
|
365
|
+
const packageJson = await PackageJson.init();
|
|
366
|
+
return {
|
|
367
|
+
name: packageJson.name,
|
|
368
|
+
tag: nextRelease.gitTag,
|
|
369
|
+
type: nextRelease.type,
|
|
370
|
+
versions: {
|
|
371
|
+
from: lastRelease.version,
|
|
372
|
+
to: nextRelease.version
|
|
373
|
+
}
|
|
374
|
+
};
|
|
216
375
|
}
|
|
217
376
|
/**
|
|
218
|
-
*
|
|
377
|
+
* Get the latest release information.
|
|
219
378
|
*
|
|
220
|
-
* @returns The
|
|
379
|
+
* @returns The latest release information including name, version, tag, and changelog.
|
|
221
380
|
* @throws If no release is found or if the header is not found in the changelog.
|
|
222
381
|
*/
|
|
223
|
-
async
|
|
382
|
+
async getLatestReleaseInfo() {
|
|
383
|
+
const packageJson = await PackageJson.init();
|
|
384
|
+
const tagFormat = this.#config.tagFormat ?? "v${version}";
|
|
385
|
+
const tag = tagFormat.replace("${version}", packageJson.version);
|
|
386
|
+
const latestBranch = tagFormat.replace("v${version}", "latest");
|
|
387
|
+
const changelog = await this.#getLatestReleaseChangelog();
|
|
388
|
+
return {
|
|
389
|
+
name: packageJson.name,
|
|
390
|
+
version: packageJson.version,
|
|
391
|
+
tag,
|
|
392
|
+
latestBranch,
|
|
393
|
+
changelog
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
async #getLatestReleaseChangelog() {
|
|
224
397
|
const contentBuffer = await FileSystem.readFile(this.#changelogPath);
|
|
225
398
|
const content = contentBuffer.toString();
|
|
226
399
|
const [, latestReleaseChangelog] = content.split(Releaser.#releaseSeparator);
|
|
@@ -258,7 +431,7 @@ class Releaser {
|
|
|
258
431
|
${lastReleaseTitle}`);
|
|
259
432
|
}
|
|
260
433
|
}
|
|
261
|
-
const commandNameArgSchema = z.union([z.literal("create"), z.literal("
|
|
434
|
+
const commandNameArgSchema = z.union([z.literal("create"), z.literal("info")]);
|
|
262
435
|
const parseReleaseArgs = ([commandArg]) => {
|
|
263
436
|
const command = commandNameArgSchema.parse(commandArg);
|
|
264
437
|
return {
|
|
@@ -269,23 +442,31 @@ const main = async (args) => {
|
|
|
269
442
|
try {
|
|
270
443
|
const { command } = parseReleaseArgs(args);
|
|
271
444
|
const releaser = await Releaser.init();
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
445
|
+
switch (command) {
|
|
446
|
+
case "create": {
|
|
447
|
+
const release = await releaser.createRelease();
|
|
448
|
+
if (!release) {
|
|
449
|
+
return 0;
|
|
450
|
+
}
|
|
451
|
+
if (process.env.GITHUB_ACTIONS === "true") {
|
|
452
|
+
ActionsCore.setOutput("release", release);
|
|
453
|
+
ActionsCore.setOutput(`release.${release.name}`, release);
|
|
454
|
+
} else {
|
|
455
|
+
console.log(JsonUtils.prettify(release));
|
|
456
|
+
}
|
|
275
457
|
return 0;
|
|
276
458
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
459
|
+
case "info": {
|
|
460
|
+
const info = await releaser.getLatestReleaseInfo();
|
|
461
|
+
if (process.env.GITHUB_ACTIONS === "true") {
|
|
462
|
+
ActionsCore.setOutput("info", info);
|
|
463
|
+
ActionsCore.setOutput(`info.${info.name}`, info);
|
|
464
|
+
} else {
|
|
465
|
+
console.log(JsonUtils.prettify(info));
|
|
466
|
+
}
|
|
467
|
+
return 0;
|
|
286
468
|
}
|
|
287
469
|
}
|
|
288
|
-
return 0;
|
|
289
470
|
} catch (error) {
|
|
290
471
|
if (error instanceof ZodError) {
|
|
291
472
|
const firstIssue = error.issues[0];
|
|
@@ -9,19 +9,19 @@ class GitProvider {
|
|
|
9
9
|
static #origins = gitProviderOrigins;
|
|
10
10
|
static #names = Object.keys(this.#origins);
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
12
|
+
* Check if the provided name is a valid Git provider name.
|
|
13
13
|
*
|
|
14
|
-
* @param name
|
|
15
|
-
* @returns True if the name is valid, false
|
|
14
|
+
* @param name The name to check.
|
|
15
|
+
* @returns True if the name is valid, otherwise false.
|
|
16
16
|
*/
|
|
17
17
|
static isValidName(name) {
|
|
18
18
|
return this.#names.includes(name);
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
21
|
+
* Get the origin URL for the given Git provider name.
|
|
22
22
|
*
|
|
23
|
-
* @param name
|
|
24
|
-
* @returns The origin URL
|
|
23
|
+
* @param name The Git provider name.
|
|
24
|
+
* @returns The origin URL.
|
|
25
25
|
*/
|
|
26
26
|
static getOrigin(name) {
|
|
27
27
|
return this.#origins[name];
|
|
@@ -48,6 +48,13 @@ class PackageJson {
|
|
|
48
48
|
constructor(data) {
|
|
49
49
|
this.#data = data;
|
|
50
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Load and parse the `package.json` file, returning a `PackageJson` instance.
|
|
53
|
+
*
|
|
54
|
+
* Throws an error if the file is invalid or cannot be parsed.
|
|
55
|
+
*
|
|
56
|
+
* @returns A new `PackageJson` instance with parsed data.
|
|
57
|
+
*/
|
|
51
58
|
static async init() {
|
|
52
59
|
const content = await FileSystem.readFile("package.json", { encoding: "utf8" });
|
|
53
60
|
const data = JsonUtils.parse(content);
|
|
@@ -60,21 +67,41 @@ class PackageJson {
|
|
|
60
67
|
return new PackageJson(data);
|
|
61
68
|
}
|
|
62
69
|
/**
|
|
63
|
-
*
|
|
70
|
+
* Get the package name from `package.json`.
|
|
71
|
+
*
|
|
72
|
+
* @returns The package name.
|
|
73
|
+
*/
|
|
74
|
+
get name() {
|
|
75
|
+
return this.#data.name;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Get the package version from `package.json`.
|
|
79
|
+
*
|
|
80
|
+
* @returns The package version.
|
|
81
|
+
*/
|
|
82
|
+
get version() {
|
|
83
|
+
return this.#data.version;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Get the engines field from `package.json`, if present.
|
|
87
|
+
*
|
|
88
|
+
* @returns The engines object or undefined.
|
|
64
89
|
*/
|
|
65
90
|
get engines() {
|
|
66
91
|
return this.#data.engines;
|
|
67
92
|
}
|
|
68
93
|
/**
|
|
69
|
-
*
|
|
94
|
+
* Set the engines field in `package.json`.
|
|
70
95
|
*
|
|
71
|
-
* @param engines
|
|
96
|
+
* @param engines The engines object to set.
|
|
72
97
|
*/
|
|
73
98
|
set engines(engines) {
|
|
74
99
|
this.#data.engines = engines;
|
|
75
100
|
}
|
|
76
101
|
/**
|
|
77
|
-
*
|
|
102
|
+
* Get the repository field from `package.json`, if present.
|
|
103
|
+
*
|
|
104
|
+
* @returns The repository object or string, or undefined.
|
|
78
105
|
*/
|
|
79
106
|
get repository() {
|
|
80
107
|
return this.#data.repository;
|
|
@@ -111,9 +138,11 @@ class PackageJson {
|
|
|
111
138
|
throw new Error("Unsupported repository URL");
|
|
112
139
|
}
|
|
113
140
|
/**
|
|
114
|
-
*
|
|
141
|
+
* Parse the repository information from `package.json` and return a `GitRepository` object or null.
|
|
142
|
+
*
|
|
143
|
+
* Returns null if no repository is defined or if parsing fails.
|
|
115
144
|
*
|
|
116
|
-
* @returns The parsed GitRepository or null if
|
|
145
|
+
* @returns The parsed `GitRepository`, or null if not available.
|
|
117
146
|
*/
|
|
118
147
|
parseGitRepository() {
|
|
119
148
|
if (!this.repository) {
|
|
@@ -125,7 +154,7 @@ class PackageJson {
|
|
|
125
154
|
return this.#parseGitRepository(this.repository.url);
|
|
126
155
|
}
|
|
127
156
|
/**
|
|
128
|
-
*
|
|
157
|
+
* Save the current `package.json` data to disk, formatting it as prettified JSON.
|
|
129
158
|
*/
|
|
130
159
|
async save() {
|
|
131
160
|
const content = JsonUtils.prettify(this.#data) + "\n";
|
|
@@ -176,7 +205,7 @@ const nodeJsReleaseSchema = z.object({
|
|
|
176
205
|
class NodeJs {
|
|
177
206
|
static #releasesUrl = "https://nodejs.org/dist/index.json";
|
|
178
207
|
/**
|
|
179
|
-
*
|
|
208
|
+
* Fetch all Node.js releases.
|
|
180
209
|
*
|
|
181
210
|
* @returns A promise that resolves to an array of NodeJsRelease objects.
|
|
182
211
|
*/
|
|
@@ -186,7 +215,7 @@ class NodeJs {
|
|
|
186
215
|
return z.array(nodeJsReleaseSchema).parse(data);
|
|
187
216
|
}
|
|
188
217
|
/**
|
|
189
|
-
*
|
|
218
|
+
* Fetch the latest Node.js release.
|
|
190
219
|
*
|
|
191
220
|
* @returns A promise that resolves to the latest NodeJsRelease object or null if no releases are found.
|
|
192
221
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cluerise/tools",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.1",
|
|
4
4
|
"description": "Tools for maintaining TypeScript projects.",
|
|
5
5
|
"author": "Branislav Holý <brano@holy.am>",
|
|
6
6
|
"repository": "github:cluerise/tools",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"conventional-changelog-conventionalcommits": "9.0.0",
|
|
32
32
|
"eslint": "9.29.0",
|
|
33
33
|
"eslint-config-prettier": "10.1.5",
|
|
34
|
-
"eslint-import-resolver-typescript": "4.4.
|
|
34
|
+
"eslint-import-resolver-typescript": "4.4.4",
|
|
35
35
|
"eslint-plugin-import": "2.32.0",
|
|
36
36
|
"eslint-plugin-prettier": "5.5.1",
|
|
37
37
|
"eslint-plugin-simple-import-sort": "12.1.1",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"globals": "16.2.0",
|
|
42
42
|
"lint-staged": "16.1.2",
|
|
43
43
|
"prettier": "3.6.1",
|
|
44
|
-
"prettier-plugin-sh": "0.
|
|
44
|
+
"prettier-plugin-sh": "0.18.0",
|
|
45
45
|
"semantic-release": "24.2.5",
|
|
46
46
|
"semver": "7.7.2",
|
|
47
47
|
"smol-toml": "1.3.4",
|