@kcconfigs/commitlint 0.0.1 → 0.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -0
- package/dist/index.cjs +216 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +42 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +210 -0
- package/dist/index.js.map +1 -0
- package/dist/lib-26NWB_Cl.cjs +142 -0
- package/dist/lib-26NWB_Cl.cjs.map +1 -0
- package/dist/lib-CzUh7oEE.cjs +31 -0
- package/dist/lib-CzUh7oEE.cjs.map +1 -0
- package/dist/lib-D5qzG1z4.js +25 -0
- package/dist/lib-D5qzG1z4.js.map +1 -0
- package/dist/lib-DPex9ONF.js +142 -0
- package/dist/lib-DPex9ONF.js.map +1 -0
- package/package.json +58 -1
- package/src/apis/projects.test.ts +344 -0
- package/src/apis/projects.ts +52 -0
- package/src/apis/scopes.test.ts +126 -0
- package/src/apis/scopes.ts +56 -0
- package/src/apis/types.test.ts +88 -0
- package/src/apis/types.ts +103 -0
- package/src/index.test.ts +497 -0
- package/src/index.ts +82 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
export interface TypeEnum {
|
|
2
|
+
description?: string;
|
|
3
|
+
title?: string;
|
|
4
|
+
emoji?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export type TypeObject = Record<string, TypeEnum>;
|
|
8
|
+
|
|
9
|
+
export type TypeMode = "standard" | "minimal" | string[] | TypeObject;
|
|
10
|
+
|
|
11
|
+
const STANDARD_TYPES = {
|
|
12
|
+
feat: {
|
|
13
|
+
description: "A new feature",
|
|
14
|
+
title: "Features",
|
|
15
|
+
emoji: "✨",
|
|
16
|
+
},
|
|
17
|
+
perf: {
|
|
18
|
+
description: "A code change that improves performance",
|
|
19
|
+
title: "Performance Improvements",
|
|
20
|
+
emoji: "🚀",
|
|
21
|
+
},
|
|
22
|
+
fix: {
|
|
23
|
+
description: "A bug fix",
|
|
24
|
+
title: "Bugfixes",
|
|
25
|
+
emoji: "🐛",
|
|
26
|
+
},
|
|
27
|
+
docs: {
|
|
28
|
+
description: "Documentation only changes",
|
|
29
|
+
title: "Documentation",
|
|
30
|
+
emoji: "📚",
|
|
31
|
+
},
|
|
32
|
+
test: {
|
|
33
|
+
description: "Adding missing tests or correcting existing tests",
|
|
34
|
+
title: "Tests",
|
|
35
|
+
emoji: "🚨",
|
|
36
|
+
},
|
|
37
|
+
style: {
|
|
38
|
+
description:
|
|
39
|
+
"Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)",
|
|
40
|
+
title: "Styles",
|
|
41
|
+
emoji: "💎",
|
|
42
|
+
},
|
|
43
|
+
build: {
|
|
44
|
+
description:
|
|
45
|
+
"Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)",
|
|
46
|
+
title: "Builds",
|
|
47
|
+
emoji: "🛠",
|
|
48
|
+
},
|
|
49
|
+
refactor: {
|
|
50
|
+
description: "A code change that neither fixes a bug nor adds a feature",
|
|
51
|
+
title: "Code Refactoring",
|
|
52
|
+
emoji: "📦",
|
|
53
|
+
},
|
|
54
|
+
ci: {
|
|
55
|
+
description:
|
|
56
|
+
"Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)",
|
|
57
|
+
title: "Continuous Integrations",
|
|
58
|
+
emoji: "⚙️",
|
|
59
|
+
},
|
|
60
|
+
chore: {
|
|
61
|
+
description: "Other changes that don't modify src or test files",
|
|
62
|
+
title: "Miscellaneous Chores",
|
|
63
|
+
emoji: "♻️",
|
|
64
|
+
},
|
|
65
|
+
revert: {
|
|
66
|
+
description: "Reverts a previous commit",
|
|
67
|
+
title: "Reverts",
|
|
68
|
+
emoji: "🗑",
|
|
69
|
+
},
|
|
70
|
+
} satisfies TypeObject;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Retrieves commit types based on the specified mode.
|
|
74
|
+
*
|
|
75
|
+
* Returns a predefined list of commit types for standard or kc modes,
|
|
76
|
+
* or a custom list if an array or object is provided.
|
|
77
|
+
*
|
|
78
|
+
* @param mode - The commit type mode: "standard", "kc", or a custom object
|
|
79
|
+
* @returns Map of type object
|
|
80
|
+
*/
|
|
81
|
+
export const getTypes = (mode: TypeMode): TypeObject => {
|
|
82
|
+
if (Array.isArray(mode)) {
|
|
83
|
+
return mode.reduce((acc, type) => {
|
|
84
|
+
acc[type] = {};
|
|
85
|
+
return acc;
|
|
86
|
+
}, {} as TypeObject);
|
|
87
|
+
}
|
|
88
|
+
if (typeof mode !== "string") {
|
|
89
|
+
return mode;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
switch (mode) {
|
|
93
|
+
case "standard":
|
|
94
|
+
return STANDARD_TYPES;
|
|
95
|
+
case "minimal":
|
|
96
|
+
return {
|
|
97
|
+
feat: STANDARD_TYPES.feat,
|
|
98
|
+
perf: STANDARD_TYPES.perf,
|
|
99
|
+
fix: STANDARD_TYPES.fix,
|
|
100
|
+
chore: STANDARD_TYPES.chore,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
};
|
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
/** biome-ignore-all lint/style/noNonNullAssertion: testing known structures */
|
|
2
|
+
|
|
3
|
+
import { cwd } from "node:process";
|
|
4
|
+
import { vol } from "@kcconfigs/vitest/mocks";
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
|
6
|
+
import { defineConfig, Severity } from ".";
|
|
7
|
+
|
|
8
|
+
describe("Main API", () => {
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
vol.reset();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
describe("exports", () => {
|
|
14
|
+
test("should export Severity enum", () => {
|
|
15
|
+
expect(Severity).toBeDefined();
|
|
16
|
+
expect(Severity.Error).toBe(2);
|
|
17
|
+
expect(Severity.Warning).toBe(1);
|
|
18
|
+
expect(Severity.Disabled).toBe(0);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe(defineConfig.name, () => {
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
vi.mocked(cwd).mockReturnValue("/mock/workspace");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("should return config with default parameters", async () => {
|
|
28
|
+
vol.fromJSON(
|
|
29
|
+
{
|
|
30
|
+
"./package.json": JSON.stringify({
|
|
31
|
+
name: "test-workspace",
|
|
32
|
+
workspaces: ["packages/*"],
|
|
33
|
+
}),
|
|
34
|
+
"./packages/pkg-a/package.json": JSON.stringify({
|
|
35
|
+
name: "@scope/pkg-a",
|
|
36
|
+
}),
|
|
37
|
+
},
|
|
38
|
+
cwd(),
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const config = await defineConfig();
|
|
42
|
+
|
|
43
|
+
expect(config).toBeDefined();
|
|
44
|
+
expect(config.helpUrl).toBe("use 'pnpm commit' to create commit instead");
|
|
45
|
+
|
|
46
|
+
expect(config.rules).toBeDefined();
|
|
47
|
+
expect(config.prompt?.questions?.type).toBeDefined();
|
|
48
|
+
expect(config.prompt?.questions?.scope).toBeDefined();
|
|
49
|
+
expect(config.prompt?.questions?.subject).toBeDefined();
|
|
50
|
+
expect(config.prompt?.questions?.body).toBeDefined();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("should configure type-enum rule with standard types", async () => {
|
|
54
|
+
vol.fromJSON(
|
|
55
|
+
{
|
|
56
|
+
"./package.json": JSON.stringify({
|
|
57
|
+
name: "test-workspace",
|
|
58
|
+
}),
|
|
59
|
+
},
|
|
60
|
+
cwd(),
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const config = await defineConfig();
|
|
64
|
+
|
|
65
|
+
expect(config.rules?.["type-enum"]).toBeDefined();
|
|
66
|
+
const [severity, condition, types] = config.rules!["type-enum"] as [
|
|
67
|
+
number,
|
|
68
|
+
string,
|
|
69
|
+
string[],
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
expect(severity).toBe(Severity.Error);
|
|
73
|
+
expect(condition).toBe("always");
|
|
74
|
+
expect(types.sort()).toEqual(
|
|
75
|
+
[
|
|
76
|
+
"feat",
|
|
77
|
+
"perf",
|
|
78
|
+
"fix",
|
|
79
|
+
"docs",
|
|
80
|
+
"test",
|
|
81
|
+
"style",
|
|
82
|
+
"build",
|
|
83
|
+
"refactor",
|
|
84
|
+
"ci",
|
|
85
|
+
"chore",
|
|
86
|
+
"revert",
|
|
87
|
+
].sort(),
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("should configure type-enum rule with minimal types", async () => {
|
|
92
|
+
vol.fromJSON(
|
|
93
|
+
{
|
|
94
|
+
"./package.json": JSON.stringify({
|
|
95
|
+
name: "test-workspace",
|
|
96
|
+
}),
|
|
97
|
+
},
|
|
98
|
+
cwd(),
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const config = await defineConfig({ types: "minimal" });
|
|
102
|
+
|
|
103
|
+
const [, , types] = config.rules!["type-enum"] as [
|
|
104
|
+
number,
|
|
105
|
+
string,
|
|
106
|
+
string[],
|
|
107
|
+
];
|
|
108
|
+
|
|
109
|
+
expect(types.sort()).toEqual(["feat", "perf", "fix", "chore"].sort());
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("should configure type-enum rule with custom types array", async () => {
|
|
113
|
+
vol.fromJSON(
|
|
114
|
+
{
|
|
115
|
+
"./package.json": JSON.stringify({
|
|
116
|
+
name: "test-workspace",
|
|
117
|
+
}),
|
|
118
|
+
},
|
|
119
|
+
cwd(),
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
const config = await defineConfig({
|
|
123
|
+
types: ["custom1", "custom2", "custom3"],
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const [, , types] = config.rules!["type-enum"] as [
|
|
127
|
+
number,
|
|
128
|
+
string,
|
|
129
|
+
string[],
|
|
130
|
+
];
|
|
131
|
+
|
|
132
|
+
expect(types.sort()).toEqual(["custom1", "custom2", "custom3"].sort());
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("should configure type-enum rule with custom types object", async () => {
|
|
136
|
+
vol.fromJSON(
|
|
137
|
+
{
|
|
138
|
+
"./package.json": JSON.stringify({
|
|
139
|
+
name: "test-workspace",
|
|
140
|
+
}),
|
|
141
|
+
},
|
|
142
|
+
cwd(),
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
const customTypes = {
|
|
146
|
+
type1: { description: "Type 1", emoji: "🎨" },
|
|
147
|
+
type2: { description: "Type 2" },
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const config = await defineConfig({ types: customTypes });
|
|
151
|
+
|
|
152
|
+
const [, , types] = config.rules!["type-enum"] as [
|
|
153
|
+
number,
|
|
154
|
+
string,
|
|
155
|
+
string[],
|
|
156
|
+
];
|
|
157
|
+
|
|
158
|
+
expect(types.sort()).toEqual(["type1", "type2"].sort());
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("should configure scope-enum with auto-detected scopes", async () => {
|
|
162
|
+
vol.fromJSON(
|
|
163
|
+
{
|
|
164
|
+
"./pnpm-workspace.yaml": "packages:\n - 'packages/*'",
|
|
165
|
+
"./packages/pkg-a/package.json": JSON.stringify({
|
|
166
|
+
name: "@scope/pkg-a",
|
|
167
|
+
}),
|
|
168
|
+
"./packages/pkg-b/package.json": JSON.stringify({
|
|
169
|
+
name: "@scope/pkg-b",
|
|
170
|
+
}),
|
|
171
|
+
},
|
|
172
|
+
cwd(),
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
const config = await defineConfig();
|
|
176
|
+
|
|
177
|
+
expect(config.rules?.["scope-enum"]).toBeDefined();
|
|
178
|
+
|
|
179
|
+
// Test scope-enum rule function
|
|
180
|
+
const scopeRule = config.rules!["scope-enum"] as () => [
|
|
181
|
+
number,
|
|
182
|
+
string,
|
|
183
|
+
string[],
|
|
184
|
+
];
|
|
185
|
+
const [severity, condition, scopes] = scopeRule();
|
|
186
|
+
|
|
187
|
+
expect(severity).toBe(Severity.Error);
|
|
188
|
+
expect(condition).toBe("always");
|
|
189
|
+
expect(scopes.sort()).toEqual(["scope/pkg-a", "scope/pkg-b"].sort());
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test("should configure scope-enum with custom scopes", async () => {
|
|
193
|
+
vol.fromJSON(
|
|
194
|
+
{
|
|
195
|
+
"./package.json": JSON.stringify({
|
|
196
|
+
name: "test-workspace",
|
|
197
|
+
}),
|
|
198
|
+
},
|
|
199
|
+
cwd(),
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
const config = await defineConfig({
|
|
203
|
+
autoScopes: false,
|
|
204
|
+
scopes: ["custom1", "custom2"],
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
const scopeRule = config.rules!["scope-enum"] as () => [
|
|
208
|
+
number,
|
|
209
|
+
string,
|
|
210
|
+
string[],
|
|
211
|
+
];
|
|
212
|
+
const [, , scopes] = scopeRule();
|
|
213
|
+
|
|
214
|
+
expect(scopes.sort()).toEqual(["custom1", "custom2"].sort());
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
test("should merge auto-detected and custom scopes", async () => {
|
|
218
|
+
vol.fromJSON(
|
|
219
|
+
{
|
|
220
|
+
"./pnpm-workspace.yaml": "packages:\n - 'packages/*'",
|
|
221
|
+
"./packages/pkg-a/package.json": JSON.stringify({
|
|
222
|
+
name: "@scope/pkg-a",
|
|
223
|
+
}),
|
|
224
|
+
},
|
|
225
|
+
cwd(),
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
const config = await defineConfig({
|
|
229
|
+
autoScopes: true,
|
|
230
|
+
scopes: ["custom1", "custom2"],
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
const scopeRule = config.rules!["scope-enum"] as () => [
|
|
234
|
+
number,
|
|
235
|
+
string,
|
|
236
|
+
string[],
|
|
237
|
+
];
|
|
238
|
+
const [, , scopes] = scopeRule();
|
|
239
|
+
|
|
240
|
+
expect(scopes.sort()).toEqual(
|
|
241
|
+
["custom1", "custom2", "scope/pkg-a"].sort(),
|
|
242
|
+
);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
test("should use default scopes when autoScopes is false and no custom scopes", async () => {
|
|
246
|
+
vol.fromJSON(
|
|
247
|
+
{
|
|
248
|
+
"./package.json": JSON.stringify({
|
|
249
|
+
name: "test-workspace",
|
|
250
|
+
}),
|
|
251
|
+
},
|
|
252
|
+
cwd(),
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
const config = await defineConfig({ autoScopes: false });
|
|
256
|
+
|
|
257
|
+
const scopeRule = config.rules!["scope-enum"] as () => [
|
|
258
|
+
number,
|
|
259
|
+
string,
|
|
260
|
+
string[],
|
|
261
|
+
];
|
|
262
|
+
const [, , scopes] = scopeRule();
|
|
263
|
+
|
|
264
|
+
expect(scopes.sort()).toEqual(
|
|
265
|
+
["core", "config", "script", "deps", "deps-dev"].sort(),
|
|
266
|
+
);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
test("should configure subject-max-length rule", async () => {
|
|
270
|
+
vol.fromJSON(
|
|
271
|
+
{
|
|
272
|
+
"./package.json": JSON.stringify({
|
|
273
|
+
name: "test-workspace",
|
|
274
|
+
}),
|
|
275
|
+
},
|
|
276
|
+
cwd(),
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
const config = await defineConfig();
|
|
280
|
+
|
|
281
|
+
expect(config.rules?.["subject-max-length"]).toEqual([
|
|
282
|
+
Severity.Warning,
|
|
283
|
+
"always",
|
|
284
|
+
80,
|
|
285
|
+
]);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
test("should configure body-max-line-length rule", async () => {
|
|
289
|
+
vol.fromJSON(
|
|
290
|
+
{
|
|
291
|
+
"./package.json": JSON.stringify({
|
|
292
|
+
name: "test-workspace",
|
|
293
|
+
}),
|
|
294
|
+
},
|
|
295
|
+
cwd(),
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
const config = await defineConfig();
|
|
299
|
+
|
|
300
|
+
expect(config.rules?.["body-max-line-length"]).toEqual([
|
|
301
|
+
Severity.Warning,
|
|
302
|
+
"always",
|
|
303
|
+
300,
|
|
304
|
+
]);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
test("should configure prompt with type enum", async () => {
|
|
308
|
+
vol.fromJSON(
|
|
309
|
+
{
|
|
310
|
+
"./package.json": JSON.stringify({
|
|
311
|
+
name: "test-workspace",
|
|
312
|
+
}),
|
|
313
|
+
},
|
|
314
|
+
cwd(),
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
const config = await defineConfig();
|
|
318
|
+
|
|
319
|
+
expect(config.prompt).toBeDefined();
|
|
320
|
+
expect(config.prompt?.questions).toBeDefined();
|
|
321
|
+
expect(config.prompt?.questions?.type).toBeDefined();
|
|
322
|
+
expect(config.prompt?.questions?.type?.enum).toBeDefined();
|
|
323
|
+
|
|
324
|
+
const typeEnum = config.prompt!.questions!.type!.enum;
|
|
325
|
+
expect(typeEnum).toBeDefined();
|
|
326
|
+
if (typeEnum) {
|
|
327
|
+
expect(Object.keys(typeEnum).sort()).toEqual(
|
|
328
|
+
[
|
|
329
|
+
"feat",
|
|
330
|
+
"perf",
|
|
331
|
+
"fix",
|
|
332
|
+
"docs",
|
|
333
|
+
"test",
|
|
334
|
+
"style",
|
|
335
|
+
"build",
|
|
336
|
+
"refactor",
|
|
337
|
+
"ci",
|
|
338
|
+
"chore",
|
|
339
|
+
"revert",
|
|
340
|
+
].sort(),
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
test("should configure prompt with scope enum", async () => {
|
|
346
|
+
vol.fromJSON(
|
|
347
|
+
{
|
|
348
|
+
"./pnpm-workspace.yaml": "packages:\n - 'packages/*'",
|
|
349
|
+
"./packages/pkg-a/package.json": JSON.stringify({
|
|
350
|
+
name: "@scope/pkg-a",
|
|
351
|
+
}),
|
|
352
|
+
},
|
|
353
|
+
cwd(),
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
const config = await defineConfig();
|
|
357
|
+
|
|
358
|
+
expect(config.prompt?.questions?.scope).toBeDefined();
|
|
359
|
+
expect(config.prompt?.questions?.scope?.enum).toBeDefined();
|
|
360
|
+
|
|
361
|
+
const scopeEnum = config.prompt!.questions!.scope!.enum;
|
|
362
|
+
expect(scopeEnum).toBeDefined();
|
|
363
|
+
if (scopeEnum) {
|
|
364
|
+
expect(Object.keys(scopeEnum).sort()).toEqual(["scope/pkg-a"].sort());
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
test("should disable multiple scopes in prompt settings", async () => {
|
|
369
|
+
vol.fromJSON(
|
|
370
|
+
{
|
|
371
|
+
"./package.json": JSON.stringify({
|
|
372
|
+
name: "test-workspace",
|
|
373
|
+
}),
|
|
374
|
+
},
|
|
375
|
+
cwd(),
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
const config = await defineConfig();
|
|
379
|
+
|
|
380
|
+
expect(config.prompt?.settings).toBeDefined();
|
|
381
|
+
expect(config.prompt?.settings?.enableMultipleScopes).toBe(false);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
test("should preserve other rules from config-conventional", async () => {
|
|
385
|
+
vol.fromJSON(
|
|
386
|
+
{
|
|
387
|
+
"./package.json": JSON.stringify({
|
|
388
|
+
name: "test-workspace",
|
|
389
|
+
}),
|
|
390
|
+
},
|
|
391
|
+
cwd(),
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
const config = await defineConfig();
|
|
395
|
+
|
|
396
|
+
// Rules from @commitlint/config-conventional should be preserved
|
|
397
|
+
expect(config.rules).toBeDefined();
|
|
398
|
+
expect(Object.keys(config.rules!).length).toBeGreaterThan(4);
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
test("should preserve other prompt questions from config-conventional", async () => {
|
|
402
|
+
vol.fromJSON(
|
|
403
|
+
{
|
|
404
|
+
"./package.json": JSON.stringify({
|
|
405
|
+
name: "test-workspace",
|
|
406
|
+
}),
|
|
407
|
+
},
|
|
408
|
+
cwd(),
|
|
409
|
+
);
|
|
410
|
+
|
|
411
|
+
const config = await defineConfig();
|
|
412
|
+
|
|
413
|
+
expect(config.prompt?.questions).toBeDefined();
|
|
414
|
+
// Should have type and scope plus other questions
|
|
415
|
+
expect(Object.keys(config.prompt!.questions!).length).toBeGreaterThan(2);
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
test("should work with empty workspace (npm detection)", async () => {
|
|
419
|
+
vol.fromJSON(
|
|
420
|
+
{
|
|
421
|
+
"./package.json": JSON.stringify({
|
|
422
|
+
name: "test-workspace",
|
|
423
|
+
}),
|
|
424
|
+
},
|
|
425
|
+
cwd(),
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
const config = await defineConfig();
|
|
429
|
+
|
|
430
|
+
expect(config).toBeDefined();
|
|
431
|
+
expect(config.rules?.["type-enum"]).toBeDefined();
|
|
432
|
+
expect(config.rules?.["scope-enum"]).toBeDefined();
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
test("should work with bun workspace", async () => {
|
|
436
|
+
vol.fromJSON(
|
|
437
|
+
{
|
|
438
|
+
"./bun.lock": "",
|
|
439
|
+
"./package.json": JSON.stringify({
|
|
440
|
+
name: "bun-workspace",
|
|
441
|
+
workspaces: ["packages/*"],
|
|
442
|
+
}),
|
|
443
|
+
"./packages/pkg-a/package.json": JSON.stringify({
|
|
444
|
+
name: "@scope/pkg-a",
|
|
445
|
+
}),
|
|
446
|
+
},
|
|
447
|
+
cwd(),
|
|
448
|
+
);
|
|
449
|
+
|
|
450
|
+
const config = await defineConfig();
|
|
451
|
+
|
|
452
|
+
const scopeRule = config.rules!["scope-enum"] as () => [
|
|
453
|
+
number,
|
|
454
|
+
string,
|
|
455
|
+
string[],
|
|
456
|
+
];
|
|
457
|
+
const [, , scopes] = scopeRule();
|
|
458
|
+
|
|
459
|
+
expect(scopes).toContain("scope/pkg-a");
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
test("should handle all parameters together", async () => {
|
|
463
|
+
vol.fromJSON(
|
|
464
|
+
{
|
|
465
|
+
"./pnpm-workspace.yaml": "packages:\n - 'packages/*'",
|
|
466
|
+
"./packages/pkg-a/package.json": JSON.stringify({
|
|
467
|
+
name: "@scope/pkg-a",
|
|
468
|
+
}),
|
|
469
|
+
},
|
|
470
|
+
cwd(),
|
|
471
|
+
);
|
|
472
|
+
|
|
473
|
+
const config = await defineConfig({
|
|
474
|
+
types: "minimal",
|
|
475
|
+
autoScopes: true,
|
|
476
|
+
scopes: ["custom"],
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
// Check types
|
|
480
|
+
const [, , types] = config.rules!["type-enum"] as [
|
|
481
|
+
number,
|
|
482
|
+
string,
|
|
483
|
+
string[],
|
|
484
|
+
];
|
|
485
|
+
expect(types.sort()).toEqual(["feat", "perf", "fix", "chore"].sort());
|
|
486
|
+
|
|
487
|
+
// Check scopes
|
|
488
|
+
const scopeRule = config.rules!["scope-enum"] as () => [
|
|
489
|
+
number,
|
|
490
|
+
string,
|
|
491
|
+
string[],
|
|
492
|
+
];
|
|
493
|
+
const [, , scopes] = scopeRule();
|
|
494
|
+
expect(scopes.sort()).toEqual(["custom", "scope/pkg-a"].sort());
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
});
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RuleConfigSeverity as Severity,
|
|
3
|
+
type UserConfig,
|
|
4
|
+
} from "@commitlint/types";
|
|
5
|
+
|
|
6
|
+
import { getScopes } from "./apis/scopes";
|
|
7
|
+
import { getTypes, type TypeMode } from "./apis/types";
|
|
8
|
+
|
|
9
|
+
export interface DefineConfigParams {
|
|
10
|
+
/**
|
|
11
|
+
* Commit types configuration
|
|
12
|
+
* - `"standard"`: Use standard conventional commit types
|
|
13
|
+
* - `string[]`: Use custom types
|
|
14
|
+
* @default "standard"
|
|
15
|
+
*/
|
|
16
|
+
types?: TypeMode;
|
|
17
|
+
/**
|
|
18
|
+
* Automatically gather scopes from pnpm workspaces
|
|
19
|
+
* @default true
|
|
20
|
+
*/
|
|
21
|
+
autoScopes?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Provide additional scopes; or override if `autoScopes` is false
|
|
24
|
+
*/
|
|
25
|
+
scopes?: string[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Defines a commitlint configuration with automatic workspace scope detection and customizable commit types.
|
|
30
|
+
*
|
|
31
|
+
* @param params - Configuration options
|
|
32
|
+
* @returns A commitlint configuration object
|
|
33
|
+
*/
|
|
34
|
+
export const defineConfig = async (
|
|
35
|
+
params?: DefineConfigParams,
|
|
36
|
+
): Promise<UserConfig> => {
|
|
37
|
+
const {
|
|
38
|
+
default: {
|
|
39
|
+
parserPreset,
|
|
40
|
+
prompt: {
|
|
41
|
+
questions: { type, scope, ...questions },
|
|
42
|
+
...prompt
|
|
43
|
+
},
|
|
44
|
+
rules,
|
|
45
|
+
},
|
|
46
|
+
} = await import("@commitlint/config-conventional");
|
|
47
|
+
|
|
48
|
+
const types = getTypes(params?.types ?? "standard");
|
|
49
|
+
const scopes = await getScopes(params?.autoScopes ?? true, params?.scopes);
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
helpUrl: "use 'pnpm commit' to create commit instead",
|
|
53
|
+
parserPreset,
|
|
54
|
+
rules: {
|
|
55
|
+
...rules,
|
|
56
|
+
"type-enum": [Severity.Error, "always", Object.keys(types)],
|
|
57
|
+
"scope-enum": () => [Severity.Error, "always", scopes],
|
|
58
|
+
"subject-max-length": [Severity.Warning, "always", 80],
|
|
59
|
+
"body-max-line-length": [Severity.Warning, "always", 300],
|
|
60
|
+
},
|
|
61
|
+
prompt: {
|
|
62
|
+
...prompt,
|
|
63
|
+
questions: {
|
|
64
|
+
...questions,
|
|
65
|
+
type: {
|
|
66
|
+
...type,
|
|
67
|
+
enum: types,
|
|
68
|
+
},
|
|
69
|
+
scope: {
|
|
70
|
+
...scope,
|
|
71
|
+
enum: Object.fromEntries(scopes.map((s) => [s, {}])),
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
settings: {
|
|
75
|
+
enableMultipleScopes: false,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export { Severity };
|
|
82
|
+
export type { UserConfig };
|