1
1
import util from 'util' ;
2
2
import isIgnored from '@commitlint/is-ignored' ;
3
3
import parse from '@commitlint/parse' ;
4
- import implementations from '@commitlint/rules' ;
4
+ import defaultRules , { Rule } from '@commitlint/rules' ;
5
5
import toPairs from 'lodash/toPairs' ;
6
6
import values from 'lodash/values' ;
7
+ import { buildCommitMesage } from './commit-message' ;
8
+ import { LintRuleConfig , LintOptions , LintRuleOutcome } from './types' ;
9
+ import { Plugin , RuleSeverity } from '@commitlint/load' ;
10
+
11
+ export default async function lint (
12
+ message : string ,
13
+ rawRulesConfig ?: LintRuleConfig ,
14
+ rawOpts ?: LintOptions
15
+ ) {
16
+ const opts = rawOpts
17
+ ? rawOpts
18
+ : { defaultIgnores : undefined , ignores : undefined } ;
19
+ const rulesConfig = rawRulesConfig || { } ;
7
20
8
- const buildCommitMesage = ( { header, body, footer} ) => {
9
- let message = header ;
10
-
11
- message = body ? `${ message } \n\n${ body } ` : message ;
12
- message = footer ? `${ message } \n\n${ footer } ` : message ;
13
-
14
- return message ;
15
- } ;
16
-
17
- export default async ( message , rules = { } , opts = { } ) => {
18
21
// Found a wildcard match, skip
19
22
if (
20
23
isIgnored ( message , { defaults : opts . defaultIgnores , ignores : opts . ignores } )
@@ -29,33 +32,35 @@ export default async (message, rules = {}, opts = {}) => {
29
32
30
33
// Parse the commit message
31
34
const parsed = await parse ( message , undefined , opts . parserOpts ) ;
35
+ const allRules : Map < string , Rule < unknown > | Rule < never > > = new Map (
36
+ Object . entries ( defaultRules )
37
+ ) ;
32
38
33
- const mergedImplementations = Object . assign ( { } , implementations ) ;
34
39
if ( opts . plugins ) {
35
- values ( opts . plugins ) . forEach ( plugin => {
40
+ values ( opts . plugins ) . forEach ( ( plugin : Plugin ) => {
36
41
if ( plugin . rules ) {
37
- Object . keys ( plugin . rules ) . forEach ( ruleKey => {
38
- mergedImplementations [ ruleKey ] = plugin . rules [ ruleKey ] ;
39
- } ) ;
42
+ Object . keys ( plugin . rules ) . forEach ( ruleKey =>
43
+ allRules . set ( ruleKey , plugin . rules [ ruleKey ] )
44
+ ) ;
40
45
}
41
46
} ) ;
42
47
}
43
48
44
49
// Find invalid rules configs
45
- const missing = Object . keys ( rules ) . filter (
46
- name => typeof mergedImplementations [ name ] !== 'function'
50
+ const missing = Object . keys ( rulesConfig ) . filter (
51
+ name => typeof allRules . get ( name ) !== 'function'
47
52
) ;
48
53
49
54
if ( missing . length > 0 ) {
50
- const names = Object . keys ( mergedImplementations ) ;
55
+ const names = [ ... allRules . keys ( ) ] ;
51
56
throw new RangeError (
52
57
`Found invalid rule names: ${ missing . join (
53
58
', '
54
59
) } . Supported rule names are: ${ names . join ( ', ' ) } `
55
60
) ;
56
61
}
57
62
58
- const invalid = toPairs ( rules )
63
+ const invalid = toPairs ( rulesConfig )
59
64
. map ( ( [ name , config ] ) => {
60
65
if ( ! Array . isArray ( config ) ) {
61
66
return new Error (
@@ -65,7 +70,13 @@ export default async (message, rules = {}, opts = {}) => {
65
70
) ;
66
71
}
67
72
68
- const [ level , when ] = config ;
73
+ const [ level ] = config ;
74
+
75
+ if ( level === RuleSeverity . Disabled && config . length === 1 ) {
76
+ return null ;
77
+ }
78
+
79
+ const [ , when ] = config ;
69
80
70
81
if ( typeof level !== 'number' || isNaN ( level ) ) {
71
82
return new Error (
@@ -75,10 +86,6 @@ export default async (message, rules = {}, opts = {}) => {
75
86
) ;
76
87
}
77
88
78
- if ( level === 0 && config . length === 1 ) {
79
- return null ;
80
- }
81
-
82
89
if ( config . length !== 2 && config . length !== 3 ) {
83
90
return new Error (
84
91
`config for rule ${ name } must be 2 or 3 items long, received ${ util . inspect (
@@ -113,18 +120,15 @@ export default async (message, rules = {}, opts = {}) => {
113
120
114
121
return null ;
115
122
} )
116
- . filter ( item => item instanceof Error ) ;
123
+ . filter ( ( item ) : item is Error => item instanceof Error ) ;
117
124
118
125
if ( invalid . length > 0 ) {
119
126
throw new Error ( invalid . map ( i => i . message ) . join ( '\n' ) ) ;
120
127
}
121
128
122
129
// Validate against all rules
123
- const results = toPairs ( rules )
124
- . filter ( entry => {
125
- const [ , [ level ] ] = toPairs ( entry ) ;
126
- return level > 0 ;
127
- } )
130
+ const results = toPairs ( rulesConfig )
131
+ . filter ( ( [ , [ level ] ] ) => level > 0 )
128
132
. map ( entry => {
129
133
const [ name , config ] = entry ;
130
134
const [ level , when , value ] = config ;
@@ -134,9 +138,14 @@ export default async (message, rules = {}, opts = {}) => {
134
138
return null ;
135
139
}
136
140
137
- const rule = mergedImplementations [ name ] ;
141
+ const rule = allRules . get ( name ) ;
142
+
143
+ if ( ! rule ) {
144
+ throw new Error ( `Could not find rule implementation for ${ name } ` ) ;
145
+ }
138
146
139
- const [ valid , message ] = rule ( parsed , when , value ) ;
147
+ const executableRule = rule as Rule < unknown > ;
148
+ const [ valid , message ] = executableRule ( parsed , when , value ) ;
140
149
141
150
return {
142
151
level,
@@ -145,7 +154,7 @@ export default async (message, rules = {}, opts = {}) => {
145
154
message
146
155
} ;
147
156
} )
148
- . filter ( Boolean ) ;
157
+ . filter ( ( result ) : result is LintRuleOutcome => result !== null ) ;
149
158
150
159
const errors = results . filter ( result => result . level === 2 && ! result . valid ) ;
151
160
const warnings = results . filter (
@@ -160,4 +169,4 @@ export default async (message, rules = {}, opts = {}) => {
160
169
warnings,
161
170
input : buildCommitMesage ( parsed )
162
171
} ;
163
- } ;
172
+ }
0 commit comments