Skip to content

Commit 4443062

Browse files
authoredFeb 18, 2020
feat: add async promise based rules methods into lint (#976)
* feat(rules): add async promise based rules methods into lint * fix: accomodate async rules in type system * style: apply autoformatting
1 parent 89168b8 commit 4443062

35 files changed

+111
-74
lines changed
 

‎@commitlint/lint/src/lint.test.ts

+20
Original file line numberDiff line numberDiff line change
@@ -283,3 +283,23 @@ test('returns original message with commit header, body and footer, parsing comm
283283

284284
expect(report.input).toBe(expected);
285285
});
286+
287+
test('passes for async rule', async () => {
288+
const report = await lint(
289+
'somehting #1',
290+
{
291+
'async-rule': [2, 'never']
292+
},
293+
{
294+
plugins: {
295+
'example-plugin': {
296+
rules: {
297+
'async-rule': async () => [true, 'all good'] as const
298+
}
299+
}
300+
}
301+
}
302+
);
303+
304+
expect(report.valid).toBe(true);
305+
});

‎@commitlint/lint/src/lint.ts

+12-7
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import {
88
LintOptions,
99
LintRuleOutcome,
1010
Rule,
11-
RuleSeverity
11+
RuleSeverity,
12+
BaseRule,
13+
RuleType
1214
} from '@commitlint/types';
1315

1416
export default async function lint(
@@ -35,7 +37,7 @@ export default async function lint(
3537

3638
// Parse the commit message
3739
const parsed = await parse(message, undefined, opts.parserOpts);
38-
const allRules: Map<string, Rule<unknown> | Rule<never>> = new Map(
40+
const allRules: Map<string, BaseRule<never, RuleType>> = new Map(
3941
Object.entries(defaultRules)
4042
);
4143

@@ -130,9 +132,9 @@ export default async function lint(
130132
}
131133

132134
// Validate against all rules
133-
const results = Object.entries(rulesConfig)
135+
const pendingResults = Object.entries(rulesConfig)
134136
.filter(([, [level]]) => level > 0)
135-
.map(entry => {
137+
.map(async entry => {
136138
const [name, config] = entry;
137139
const [level, when, value] = config;
138140

@@ -148,16 +150,19 @@ export default async function lint(
148150
}
149151

150152
const executableRule = rule as Rule<unknown>;
151-
const [valid, message] = executableRule(parsed, when, value);
153+
const [valid, message] = await executableRule(parsed, when, value);
152154

153155
return {
154156
level,
155157
valid,
156158
name,
157159
message
158160
};
159-
})
160-
.filter((result): result is LintRuleOutcome => result !== null);
161+
});
162+
163+
const results = (await Promise.all(pendingResults)).filter(
164+
(result): result is LintRuleOutcome => result !== null
165+
);
161166

162167
const errors = results.filter(result => result.level === 2 && !result.valid);
163168
const warnings = results.filter(

‎@commitlint/rules/src/body-case.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import {case as ensureCase} from '@commitlint/ensure';
22
import message from '@commitlint/message';
3-
import {TargetCaseType, Rule} from '@commitlint/types';
3+
import {TargetCaseType, SyncRule} from '@commitlint/types';
44

5-
export const bodyCase: Rule<TargetCaseType> = (
5+
export const bodyCase: SyncRule<TargetCaseType> = (
66
parsed,
77
when = 'always',
88
value = undefined

‎@commitlint/rules/src/body-empty.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import * as ensure from '@commitlint/ensure';
22
import message from '@commitlint/message';
3-
import {Rule} from '@commitlint/types';
3+
import {SyncRule} from '@commitlint/types';
44

5-
export const bodyEmpty: Rule = (parsed, when = 'always') => {
5+
export const bodyEmpty: SyncRule = (parsed, when = 'always') => {
66
const negated = when === 'never';
77
const notEmpty = ensure.notEmpty(parsed.body || '');
88

‎@commitlint/rules/src/body-leading-blank.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import toLines from '@commitlint/to-lines';
22
import message from '@commitlint/message';
3-
import {Rule} from '@commitlint/types';
3+
import {SyncRule} from '@commitlint/types';
44

5-
export const bodyLeadingBlank: Rule = (parsed, when) => {
5+
export const bodyLeadingBlank: SyncRule = (parsed, when) => {
66
// Flunk if no body is found
77
if (!parsed.body) {
88
return [true];

‎@commitlint/rules/src/body-max-length.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {maxLength} from '@commitlint/ensure';
2-
import {Rule} from '@commitlint/types';
2+
import {SyncRule} from '@commitlint/types';
33

4-
export const bodyMaxLength: Rule<number> = (
4+
export const bodyMaxLength: SyncRule<number> = (
55
parsed,
66
_when = undefined,
77
value = 0

‎@commitlint/rules/src/body-max-line-length.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {maxLineLength} from '@commitlint/ensure';
2-
import {Rule} from '@commitlint/types';
2+
import {SyncRule} from '@commitlint/types';
33

4-
export const bodyMaxLineLength: Rule<number> = (
4+
export const bodyMaxLineLength: SyncRule<number> = (
55
parsed,
66
_when = undefined,
77
value = 0

‎@commitlint/rules/src/body-min-length.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {minLength} from '@commitlint/ensure';
2-
import {Rule} from '@commitlint/types';
2+
import {SyncRule} from '@commitlint/types';
33

4-
export const bodyMinLength: Rule<number> = (
4+
export const bodyMinLength: SyncRule<number> = (
55
parsed,
66
_when = undefined,
77
value = 0

‎@commitlint/rules/src/footer-empty.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import * as ensure from '@commitlint/ensure';
22
import message from '@commitlint/message';
3-
import {Rule} from '@commitlint/types';
3+
import {SyncRule} from '@commitlint/types';
44

5-
export const footerEmpty: Rule = (parsed, when = 'always') => {
5+
export const footerEmpty: SyncRule = (parsed, when = 'always') => {
66
const negated = when === 'never';
77
const notEmpty = ensure.notEmpty(parsed.footer || '');
88

‎@commitlint/rules/src/footer-leading-blank.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import toLines from '@commitlint/to-lines';
22
import message from '@commitlint/message';
3-
import {Rule} from '@commitlint/types';
3+
import {SyncRule} from '@commitlint/types';
44

5-
export const footerLeadingBlank: Rule = (parsed, when = 'always') => {
5+
export const footerLeadingBlank: SyncRule = (parsed, when = 'always') => {
66
// Flunk if no footer is found
77
if (!parsed.footer) {
88
return [true];

‎@commitlint/rules/src/footer-max-length.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {maxLength} from '@commitlint/ensure';
2-
import {Rule} from '@commitlint/types';
2+
import {SyncRule} from '@commitlint/types';
33

4-
export const footerMaxLength: Rule<number> = (
4+
export const footerMaxLength: SyncRule<number> = (
55
parsed,
66
_when = undefined,
77
value = 0

‎@commitlint/rules/src/footer-max-line-length.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {maxLineLength} from '@commitlint/ensure';
2-
import {Rule} from '@commitlint/types';
2+
import {SyncRule} from '@commitlint/types';
33

4-
export const footerMaxLineLength: Rule<number> = (
4+
export const footerMaxLineLength: SyncRule<number> = (
55
parsed,
66
_when = undefined,
77
value = 0

‎@commitlint/rules/src/footer-min-length.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {minLength} from '@commitlint/ensure';
2-
import {Rule} from '@commitlint/types';
2+
import {SyncRule} from '@commitlint/types';
33

4-
export const footerMinLength: Rule<number> = (
4+
export const footerMinLength: SyncRule<number> = (
55
parsed,
66
_when = undefined,
77
value = 0

‎@commitlint/rules/src/header-case.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import {case as ensureCase} from '@commitlint/ensure';
22
import message from '@commitlint/message';
3-
import {TargetCaseType, Rule} from '@commitlint/types';
3+
import {TargetCaseType, SyncRule} from '@commitlint/types';
44

55
const negated = (when?: string) => when === 'never';
66

7-
export const headerCase: Rule<TargetCaseType | TargetCaseType[]> = (
7+
export const headerCase: SyncRule<TargetCaseType | TargetCaseType[]> = (
88
parsed,
99
when = 'always',
1010
value = []

‎@commitlint/rules/src/header-full-stop.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import message from '@commitlint/message';
2-
import {Rule} from '@commitlint/types';
2+
import {SyncRule} from '@commitlint/types';
33

4-
export const headerFullStop: Rule<string> = (
4+
export const headerFullStop: SyncRule<string> = (
55
parsed,
66
when = 'always',
77
value = '.'

‎@commitlint/rules/src/header-max-length.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {maxLength} from '@commitlint/ensure';
2-
import {Rule} from '@commitlint/types';
2+
import {SyncRule} from '@commitlint/types';
33

4-
export const headerMaxLength: Rule<number> = (
4+
export const headerMaxLength: SyncRule<number> = (
55
parsed,
66
_when = undefined,
77
value = 0

‎@commitlint/rules/src/header-min-length.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {minLength} from '@commitlint/ensure';
2-
import {Rule} from '@commitlint/types';
2+
import {SyncRule} from '@commitlint/types';
33

4-
export const headerMinLength: Rule<number> = (
4+
export const headerMinLength: SyncRule<number> = (
55
parsed,
66
_when = undefined,
77
value = 0

‎@commitlint/rules/src/references-empty.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import message from '@commitlint/message';
2-
import {Rule} from '@commitlint/types';
2+
import {SyncRule} from '@commitlint/types';
33

4-
export const referencesEmpty: Rule = (parsed, when = 'never') => {
4+
export const referencesEmpty: SyncRule = (parsed, when = 'never') => {
55
const negated = when === 'always';
66
const notEmpty = parsed.references.length > 0;
77
return [

‎@commitlint/rules/src/scope-case.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import {case as ensureCase} from '@commitlint/ensure';
22
import message from '@commitlint/message';
3-
import {TargetCaseType, Rule} from '@commitlint/types';
3+
import {TargetCaseType, SyncRule} from '@commitlint/types';
44

55
const negated = (when?: string) => when === 'never';
66

7-
export const scopeCase: Rule<TargetCaseType | TargetCaseType[]> = (
7+
export const scopeCase: SyncRule<TargetCaseType | TargetCaseType[]> = (
88
parsed,
99
when = 'always',
1010
value = []

‎@commitlint/rules/src/scope-empty.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import * as ensure from '@commitlint/ensure';
22
import message from '@commitlint/message';
3-
import {Rule} from '@commitlint/types';
3+
import {SyncRule} from '@commitlint/types';
44

5-
export const scopeEmpty: Rule = (parsed, when = 'never') => {
5+
export const scopeEmpty: SyncRule = (parsed, when = 'never') => {
66
const negated = when === 'always';
77
const notEmpty = ensure.notEmpty(parsed.scope || '');
88
return [

‎@commitlint/rules/src/scope-enum.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import * as ensure from '@commitlint/ensure';
22
import message from '@commitlint/message';
3-
import {Rule} from '@commitlint/types';
3+
import {SyncRule} from '@commitlint/types';
44

5-
export const scopeEnum: Rule<string[]> = (
5+
export const scopeEnum: SyncRule<string[]> = (
66
parsed,
77
when = 'always',
88
value = []

‎@commitlint/rules/src/scope-max-length.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {maxLength} from '@commitlint/ensure';
2-
import {Rule} from '@commitlint/types';
2+
import {SyncRule} from '@commitlint/types';
33

4-
export const scopeMaxLength: Rule<number> = (
4+
export const scopeMaxLength: SyncRule<number> = (
55
parsed,
66
_when = undefined,
77
value = 0

‎@commitlint/rules/src/scope-min-length.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {minLength} from '@commitlint/ensure';
2-
import {Rule} from '@commitlint/types';
2+
import {SyncRule} from '@commitlint/types';
33

4-
export const scopeMinLength: Rule<number> = (
4+
export const scopeMinLength: SyncRule<number> = (
55
parsed,
66
_when = undefined,
77
value = 0

‎@commitlint/rules/src/signed-off-by.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import message from '@commitlint/message';
22
import toLines from '@commitlint/to-lines';
3-
import {Rule} from '@commitlint/types';
3+
import {SyncRule} from '@commitlint/types';
44

5-
export const signedOffBy: Rule<string> = (
5+
export const signedOffBy: SyncRule<string> = (
66
parsed,
77
when = 'always',
88
value = ''

‎@commitlint/rules/src/subject-case.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import {case as ensureCase} from '@commitlint/ensure';
22
import message from '@commitlint/message';
3-
import {TargetCaseType, Rule} from '@commitlint/types';
3+
import {TargetCaseType, SyncRule} from '@commitlint/types';
44

55
const negated = (when?: string) => when === 'never';
66

7-
export const subjectCase: Rule<TargetCaseType | TargetCaseType[]> = (
7+
export const subjectCase: SyncRule<TargetCaseType | TargetCaseType[]> = (
88
parsed,
99
when = 'always',
1010
value = []

‎@commitlint/rules/src/subject-empty.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import * as ensure from '@commitlint/ensure';
22
import message from '@commitlint/message';
3-
import {Rule} from '@commitlint/types';
3+
import {SyncRule} from '@commitlint/types';
44

5-
export const subjectEmpty: Rule = (parsed, when = 'always') => {
5+
export const subjectEmpty: SyncRule = (parsed, when = 'always') => {
66
const negated = when === 'never';
77
const notEmpty = ensure.notEmpty(parsed.subject || '');
88

‎@commitlint/rules/src/subject-full-stop.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import message from '@commitlint/message';
2-
import {Rule} from '@commitlint/types';
2+
import {SyncRule} from '@commitlint/types';
33

4-
export const subjectFullStop: Rule<string> = (
4+
export const subjectFullStop: SyncRule<string> = (
55
parsed,
66
when = 'always',
77
value = '.'

‎@commitlint/rules/src/subject-max-length.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {maxLength} from '@commitlint/ensure';
2-
import {Rule} from '@commitlint/types';
2+
import {SyncRule} from '@commitlint/types';
33

4-
export const subjectMaxLength: Rule<number> = (
4+
export const subjectMaxLength: SyncRule<number> = (
55
parsed,
66
_when = undefined,
77
value = 0

‎@commitlint/rules/src/subject-min-length.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {minLength} from '@commitlint/ensure';
2-
import {Rule} from '@commitlint/types';
2+
import {SyncRule} from '@commitlint/types';
33

4-
export const subjectMinLength: Rule<number> = (
4+
export const subjectMinLength: SyncRule<number> = (
55
parsed,
66
_when = undefined,
77
value = 0

‎@commitlint/rules/src/type-case.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import {case as ensureCase} from '@commitlint/ensure';
22
import message from '@commitlint/message';
3-
import {TargetCaseType, Rule} from '@commitlint/types';
3+
import {TargetCaseType, SyncRule} from '@commitlint/types';
44

55
const negated = (when?: string) => when === 'never';
66

7-
export const typeCase: Rule<TargetCaseType | TargetCaseType[]> = (
7+
export const typeCase: SyncRule<TargetCaseType | TargetCaseType[]> = (
88
parsed,
99
when = 'always',
1010
value = []

‎@commitlint/rules/src/type-empty.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import * as ensure from '@commitlint/ensure';
22
import message from '@commitlint/message';
3-
import {Rule} from '@commitlint/types';
3+
import {SyncRule} from '@commitlint/types';
44

5-
export const typeEmpty: Rule = (parsed, when = 'always') => {
5+
export const typeEmpty: SyncRule = (parsed, when = 'always') => {
66
const negated = when === 'never';
77
const notEmpty = ensure.notEmpty(parsed.type || '');
88
return [

‎@commitlint/rules/src/type-enum.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import * as ensure from '@commitlint/ensure';
22
import message from '@commitlint/message';
3-
import {Rule} from '@commitlint/types';
3+
import {SyncRule} from '@commitlint/types';
44

5-
export const typeEnum: Rule<string[]> = (
5+
export const typeEnum: SyncRule<string[]> = (
66
parsed,
77
when = 'always',
88
value = []

‎@commitlint/rules/src/type-max-length.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {maxLength} from '@commitlint/ensure';
2-
import {Rule} from '@commitlint/types';
2+
import {SyncRule} from '@commitlint/types';
33

4-
export const typeMaxLength: Rule<number> = (
4+
export const typeMaxLength: SyncRule<number> = (
55
parsed,
66
_when = undefined,
77
value = 0

‎@commitlint/rules/src/type-min-length.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {minLength} from '@commitlint/ensure';
2-
import {Rule} from '@commitlint/types';
2+
import {SyncRule} from '@commitlint/types';
33

4-
export const typeMinLength: Rule<number> = (
4+
export const typeMinLength: SyncRule<number> = (
55
parsed,
66
_when = undefined,
77
value = 0

‎@commitlint/types/src/rules.ts

+15-3
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,26 @@ export type RuleCondition = 'always' | 'never';
1212
* For example, when `header-full-stop` detects a full stop and is set as "always"; it's true.
1313
* If the `header-full-stop` discovers a full stop but is set to "never"; it's false.
1414
*/
15-
export type RuleOutcome = [boolean, string?];
15+
export type RuleOutcome = Readonly<[boolean, string?]>;
1616

1717
/**
1818
* Rules receive a parsed commit, condition, and possible additional settings through value.
1919
* All rules should provide the most sensible rule condition and value.
2020
*/
21-
export type Rule<Value = never> = (
21+
export type RuleType = 'async' | 'sync' | 'either';
22+
23+
export type BaseRule<Value = never, Type extends RuleType = 'either'> = (
2224
parsed: Commit,
2325
when?: RuleCondition,
2426
value?: Value
25-
) => RuleOutcome;
27+
) => Type extends 'either'
28+
? RuleOutcome | Promise<RuleOutcome>
29+
: Type extends 'async'
30+
? Promise<RuleOutcome>
31+
: Type extends 'sync'
32+
? RuleOutcome
33+
: never;
34+
35+
export type Rule<Value = never> = BaseRule<Value, 'either'>;
36+
export type AsyncRule<Value = never> = BaseRule<Value, 'async'>;
37+
export type SyncRule<Value = never> = BaseRule<Value, 'sync'>;

0 commit comments

Comments
 (0)
Please sign in to comment.