Skip to content

Commit d0bde42

Browse files
committed
feat: added commitlint plugin
1 parent 02a51f1 commit d0bde42

File tree

5 files changed

+156
-7
lines changed

5 files changed

+156
-7
lines changed

src/index.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1-
export function demo(): boolean {
2-
return true;
3-
}
1+
import { properties, type PluginRuleEntry } from './types.js';
2+
import { makeRule } from './utils.js';
3+
4+
import type { Plugin } from '@commitlint/types';
5+
6+
const ruleEntries: PluginRuleEntry[] = properties.map((property) => {
7+
return [`cspell/${property}`, makeRule(property)];
8+
});
9+
10+
const plugin: Plugin = {
11+
rules: Object.fromEntries(ruleEntries),
12+
};
13+
14+
export default plugin;

src/types.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { Plugin } from '@commitlint/types';
2+
import type { RemoveIndex } from '@webdeveric/utils/types/records';
3+
4+
export type CommitLintRule = Plugin['rules'][string];
5+
6+
export type RuleParameters = Parameters<CommitLintRule>;
7+
8+
export type Commit = RuleParameters[0];
9+
10+
export type CommitBase = RemoveIndex<RuleParameters[0]>;
11+
12+
export const properties = ['body', 'footer', 'header', 'scope', 'subject', 'type'] satisfies (keyof CommitBase)[];
13+
14+
export type CommitProperty = (typeof properties)[number];
15+
16+
export type PluginRuleEntry = [ruleName: `cspell/${CommitProperty}`, ruleFn: CommitLintRule];

src/utils.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { spellCheckDocument, type ValidationIssue } from 'cspell-lib';
2+
3+
import type { CommitProperty, CommitLintRule } from './types.js';
4+
5+
export async function checkSpelling(text: string | null | undefined): Promise<ValidationIssue[]> {
6+
if (!text) {
7+
return [];
8+
}
9+
10+
const results = await spellCheckDocument(
11+
{
12+
text,
13+
uri: '',
14+
languageId: 'plaintext',
15+
locale: 'en',
16+
},
17+
{
18+
generateSuggestions: false,
19+
},
20+
{},
21+
);
22+
23+
return results.issues;
24+
}
25+
26+
export function makeMessage(property: CommitProperty, issues: ValidationIssue[]): string | undefined {
27+
if (issues.length) {
28+
const misspelledWords = issues.map((issue) => issue.text);
29+
30+
return `Spelling error found in ${property}: ${misspelledWords.join(', ')}`;
31+
}
32+
}
33+
34+
export function makeRule(property: CommitProperty): CommitLintRule {
35+
return async (parsed) => {
36+
const issues = await checkSpelling(parsed[property]);
37+
38+
return [!issues.length, makeMessage(property, issues)];
39+
};
40+
}

test/index.test.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import { describe, expect, it } from 'vitest';
22

3-
import { demo } from '../src/index.js';
3+
import plugin from '../src/index.js';
4+
import { properties } from '../src/types.js';
45

5-
describe('demo()', () => {
6-
it('Does a thing', () => {
7-
expect(demo()).toBeTruthy();
6+
describe('default export', () => {
7+
it('Is a Plugin with cspell rules', () => {
8+
expect(Object.keys(plugin.rules)).toEqual(
9+
expect.arrayContaining(properties.map((property) => expect.stringMatching(`cspell/${property}`))),
10+
);
811
});
912
});

test/utils.test.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// cSpell:ignore Speling
2+
import { describe, expect, it } from 'vitest';
3+
4+
import { checkSpelling, makeMessage, makeRule } from '../src/utils.js';
5+
6+
import type { Commit } from '../src/types.js';
7+
8+
describe('checkSpelling()', () => {
9+
it('Returns spelling issues', async () => {
10+
await expect(checkSpelling('Speling')).resolves.toEqual([
11+
expect.objectContaining({
12+
text: 'Speling',
13+
}),
14+
]);
15+
16+
await expect(checkSpelling(null)).resolves.toHaveLength(0);
17+
await expect(checkSpelling(undefined)).resolves.toHaveLength(0);
18+
});
19+
});
20+
21+
describe('makeMessage()', () => {
22+
it('Returns an error message or undefined', () => {
23+
expect(makeMessage('subject', [])).toBeUndefined();
24+
25+
expect(
26+
makeMessage('subject', [
27+
{
28+
isFlagged: false,
29+
isFound: false,
30+
length: 7,
31+
line: {
32+
offset: 0,
33+
text: 'Speling',
34+
},
35+
offset: 0,
36+
text: 'Speling',
37+
},
38+
]),
39+
).toEqual(expect.stringContaining('Speling'));
40+
});
41+
});
42+
43+
describe('makeRule()', () => {
44+
it('Returns a commitlint rule function', async () => {
45+
const ruleFn = makeRule('subject');
46+
47+
expect(ruleFn).instanceOf(Function);
48+
49+
await expect(
50+
ruleFn({
51+
merge: 'merge',
52+
header: 'header',
53+
body: 'body',
54+
footer: 'footer',
55+
revert: null,
56+
scope: 'testing',
57+
subject: 'subject',
58+
notes: [],
59+
references: [],
60+
mentions: [],
61+
} as unknown as Commit),
62+
).resolves.toEqual([true, undefined]);
63+
64+
await expect(
65+
ruleFn({
66+
merge: 'merge',
67+
header: 'header',
68+
body: 'body',
69+
footer: 'footer',
70+
revert: null,
71+
scope: 'testing',
72+
subject: 'Speling',
73+
notes: [],
74+
references: [],
75+
mentions: [],
76+
} as unknown as Commit),
77+
).resolves.toEqual([false, expect.stringContaining('Speling')]);
78+
});
79+
});

0 commit comments

Comments
 (0)