Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 803c05b

Browse files
authored
feat(cz-commitlint): support customizable commit prompt with emojis (#4540)
* feat(cz-commitlint): add emoji to title * docs(cz-commitlint): add emoji to title
1 parent c4d419b commit 803c05b

File tree

7 files changed

+425
-7
lines changed

7 files changed

+425
-7
lines changed

‎@commitlint/cz-commitlint/src/services/getRuleQuestionConfig.test.ts‎

Lines changed: 271 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { describe, test, expect } from "vitest";
21
import { RuleConfigSeverity } from "@commitlint/types";
2+
import { describe, expect, test } from "vitest";
33

44
import { setPromptConfig } from "../store/prompts.js";
55
import { setRules } from "../store/rules.js";
@@ -253,6 +253,276 @@ describe("enum list", () => {
253253
expect(enumList?.[1]).toBe("lint");
254254
expect(enumList?.[2]).toBe("cli");
255255
});
256+
257+
test("should format enum items with emojis and descriptions, ensuring consistent spacing", () => {
258+
const ENUM_RULE_LIST = ["feat", "fix", "chore"];
259+
setRules({
260+
"type-enum": [RuleConfigSeverity.Error, "always", ENUM_RULE_LIST],
261+
} as any);
262+
263+
setPromptConfig({
264+
questions: {
265+
type: {
266+
enum: {
267+
feat: {
268+
description: "Features",
269+
emoji: "✨",
270+
},
271+
fix: {
272+
description: "Bug fixes",
273+
emoji: "🐛",
274+
},
275+
chore: {
276+
description: "Chore",
277+
emoji: "♻️",
278+
},
279+
},
280+
},
281+
},
282+
});
283+
284+
const enumList = getRuleQuestionConfig("type")?.enumList;
285+
expect(enumList).toEqual([
286+
{
287+
name: "✨ feat: Features",
288+
value: "feat",
289+
short: "feat",
290+
},
291+
{
292+
name: "🐛 fix: Bug fixes",
293+
value: "fix",
294+
short: "fix",
295+
},
296+
{
297+
name: "♻️ chore: Chore",
298+
value: "chore",
299+
short: "chore",
300+
},
301+
]);
302+
});
303+
304+
test("should handle custom alignment for emoji and description with extra space", () => {
305+
const ENUM_RULE_LIST = ["feat", "fix", "chore"];
306+
setRules({
307+
"type-enum": [RuleConfigSeverity.Error, "always", ENUM_RULE_LIST],
308+
} as any);
309+
310+
setPromptConfig({
311+
questions: {
312+
type: {
313+
enum: {
314+
feat: {
315+
description: "Features",
316+
emoji: "✨ ",
317+
},
318+
fix: {
319+
description: "Bug fixes",
320+
emoji: "🐛 ",
321+
},
322+
chore: {
323+
description: "Chore",
324+
emoji: "♻️ ",
325+
},
326+
},
327+
},
328+
},
329+
});
330+
331+
const enumList = getRuleQuestionConfig("type")?.enumList;
332+
expect(enumList).toEqual([
333+
{
334+
name: "✨ feat: Features",
335+
value: "feat",
336+
short: "feat",
337+
},
338+
{
339+
name: "🐛 fix: Bug fixes",
340+
value: "fix",
341+
short: "fix",
342+
},
343+
{
344+
name: "♻️ chore: Chore",
345+
value: "chore",
346+
short: "chore",
347+
},
348+
]);
349+
});
350+
351+
test("should handle inconsistent emoji usage by using 4 blank spaces as a prefix", () => {
352+
const ENUM_RULE_LIST = ["feat", "fix", "chore"];
353+
setRules({
354+
"type-enum": [RuleConfigSeverity.Error, "always", ENUM_RULE_LIST],
355+
} as any);
356+
357+
setPromptConfig({
358+
questions: {
359+
type: {
360+
enum: {
361+
feat: {
362+
description: "Features",
363+
emoji: "✨",
364+
},
365+
fix: {
366+
description: "Bug fixes",
367+
},
368+
chore: {
369+
description: "Chore",
370+
},
371+
},
372+
},
373+
},
374+
});
375+
376+
const enumList = getRuleQuestionConfig("type")?.enumList;
377+
expect(enumList).toEqual([
378+
{
379+
name: "✨ feat: Features",
380+
value: "feat",
381+
short: "feat",
382+
},
383+
{
384+
name: " fix: Bug fixes",
385+
value: "fix",
386+
short: "fix",
387+
},
388+
{
389+
name: " chore: Chore",
390+
value: "chore",
391+
short: "chore",
392+
},
393+
]);
394+
});
395+
396+
test("should handle no enums having emojis correctly", () => {
397+
const ENUM_RULE_LIST = ["feat", "fix", "chore"];
398+
setRules({
399+
"type-enum": [RuleConfigSeverity.Error, "always", ENUM_RULE_LIST],
400+
} as any);
401+
402+
setPromptConfig({
403+
questions: {
404+
type: {
405+
enum: {
406+
feat: {
407+
description: "Features",
408+
},
409+
fix: {
410+
description: "Bug fixes",
411+
},
412+
chore: {
413+
description: "Chore",
414+
},
415+
},
416+
},
417+
},
418+
});
419+
420+
const enumList = getRuleQuestionConfig("type")?.enumList;
421+
expect(enumList).toEqual([
422+
{
423+
name: "feat: Features",
424+
value: "feat",
425+
short: "feat",
426+
},
427+
{
428+
name: "fix: Bug fixes",
429+
value: "fix",
430+
short: "fix",
431+
},
432+
{
433+
name: "chore: Chore",
434+
value: "chore",
435+
short: "chore",
436+
},
437+
]);
438+
});
439+
440+
test("should include the emoji in the value when `emojiInHeader` is true", () => {
441+
const ENUM_RULE_LIST = ["feat", "fix"];
442+
setRules({
443+
"type-enum": [RuleConfigSeverity.Error, "always", ENUM_RULE_LIST],
444+
} as any);
445+
446+
setPromptConfig({
447+
questions: {
448+
type: {
449+
emojiInHeader: true,
450+
enum: {
451+
feat: {
452+
description: "Features",
453+
emoji: "✨",
454+
},
455+
fix: {
456+
description: "Bug fixes",
457+
emoji: "🐛",
458+
},
459+
},
460+
},
461+
},
462+
});
463+
464+
const enumList = getRuleQuestionConfig("type")?.enumList;
465+
expect(enumList).toEqual([
466+
{
467+
name: "✨ feat: Features",
468+
value: "✨ feat",
469+
short: "feat",
470+
},
471+
{
472+
name: "🐛 fix: Bug fixes",
473+
value: "🐛 fix",
474+
short: "fix",
475+
},
476+
]);
477+
});
478+
479+
test("should trim empty spaces from emoji in the answer", () => {
480+
const ENUM_RULE_LIST = ["feat", "fix", "chore"];
481+
setRules({
482+
"type-enum": [RuleConfigSeverity.Error, "always", ENUM_RULE_LIST],
483+
} as any);
484+
485+
setPromptConfig({
486+
questions: {
487+
type: {
488+
emojiInHeader: true,
489+
enum: {
490+
feat: {
491+
description: "Features",
492+
emoji: "✨ ",
493+
},
494+
fix: {
495+
description: "Bug fixes",
496+
emoji: "🐛 ",
497+
},
498+
chore: {
499+
description: "Chore",
500+
emoji: "♻️ ",
501+
},
502+
},
503+
},
504+
},
505+
});
506+
507+
const enumList = getRuleQuestionConfig("type")?.enumList;
508+
expect(enumList).toEqual([
509+
{
510+
name: "✨ feat: Features",
511+
value: "✨ feat",
512+
short: "feat",
513+
},
514+
{
515+
name: "🐛 fix: Bug fixes",
516+
value: "🐛 fix",
517+
short: "fix",
518+
},
519+
{
520+
name: "♻️ chore: Chore",
521+
value: "♻️ chore",
522+
short: "chore",
523+
},
524+
]);
525+
});
256526
});
257527

258528
test("should return correct question config", () => {

‎@commitlint/cz-commitlint/src/services/getRuleQuestionConfig.ts‎

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,23 +35,41 @@ export default function (rulePrefix: RuleField): QuestionConfig | null {
3535

3636
if (enumRuleList) {
3737
const enumDescriptions = questionSettings?.["enum"];
38+
const emojiInHeader = questionSettings?.emojiInHeader;
3839

3940
if (enumDescriptions) {
4041
const enumNames = Object.keys(enumDescriptions);
4142
const longest = Math.max(
4243
...enumRuleList.map((enumName) => enumName.length),
4344
);
44-
// TODO emoji + title
45+
const firstHasEmoji =
46+
(enumDescriptions[enumNames[0]]?.emoji?.length ?? 0) > 0;
47+
const hasConsistentEmojiUsage = !enumRuleList.some(
48+
(enumName) =>
49+
(enumDescriptions[enumName]?.emoji?.length ?? 0) > 0 !==
50+
firstHasEmoji,
51+
);
4552
enumList = enumRuleList
4653
.sort((a, b) => enumNames.indexOf(a) - enumNames.indexOf(b))
4754
.map((enumName) => {
4855
const enumDescription = enumDescriptions[enumName]?.description;
4956
if (enumDescription) {
50-
return {
51-
name: `${enumName}:`.padEnd(longest + 4) + enumDescription,
52-
value: enumName,
53-
short: enumName,
54-
};
57+
const emoji = enumDescriptions[enumName]?.emoji;
58+
59+
const emojiPrefix = emoji
60+
? `${emoji} `
61+
: hasConsistentEmojiUsage
62+
? ""
63+
: " ";
64+
65+
const paddedName = `${enumName}:`.padEnd(longest + 4);
66+
67+
const name = `${emojiPrefix}${paddedName}${enumDescription}`;
68+
69+
const value =
70+
emojiInHeader && emoji ? `${emoji.trim()} ${enumName}` : enumName;
71+
72+
return { name, value, short: enumName };
5573
} else {
5674
return enumName;
5775
}

‎@commitlint/types/src/prompt.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export type PromptConfig = {
3434
emoji?: string;
3535
};
3636
};
37+
emojiInHeader?: boolean;
3738
}
3839
>
3940
>;

‎docs/public/assets/cz-commitlint.png‎

-25.9 KB
Loading[フレーム]
9.79 KB
Loading[フレーム]

‎docs/public/assets/vs-code-emoji.png‎

26.1 KB
Loading[フレーム]

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /