Skip to content

Commit c6fd8d4

Browse files
committed
style: validate rule configuration
1 parent 61d021d commit c6fd8d4

File tree

2 files changed

+134
-2
lines changed

2 files changed

+134
-2
lines changed

@commitlint/core/src/lint.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import util from 'util';
12
import isIgnored from '@commitlint/is-ignored';
23
import parse from '@commitlint/parse';
34
import implementations from '@commitlint/rules';
@@ -30,6 +31,66 @@ export default async (message, rules = {}, opts = {}) => {
3031
);
3132
}
3233

34+
const invalid = entries(rules)
35+
.map(([name, config]) => {
36+
if (!Array.isArray(config)) {
37+
return new Error(
38+
`config for rule ${name} must be array, received ${util.inspect(
39+
config
40+
)} of type ${typeof config}`
41+
);
42+
}
43+
44+
if (config.length !== 2 && config.length !== 3) {
45+
return new Error(
46+
`config for rule ${name} must be 2 or 3 items long, received ${util.inspect(
47+
config
48+
)} of length ${config.length}`
49+
);
50+
}
51+
52+
const [level, when] = config;
53+
54+
if (typeof level !== 'number' || isNaN(level)) {
55+
return new Error(
56+
`level for rule ${name} must be number, received ${util.inspect(
57+
level
58+
)} of type ${typeof level}`
59+
);
60+
}
61+
62+
if (level < 0 || level > 2) {
63+
return new RangeError(
64+
`level for rule ${name} must be between 0 and 2, received ${util.inspect(
65+
level
66+
)}`
67+
);
68+
}
69+
70+
if (typeof when !== 'string') {
71+
return new Error(
72+
`condition for rule ${name} must be string, received ${util.inspect(
73+
when
74+
)} of type ${typeof when}`
75+
);
76+
}
77+
78+
if (when !== 'never' && when !== 'always') {
79+
return new Error(
80+
`condition for rule ${name} must be "always" or "never", received ${util.inspect(
81+
when
82+
)}`
83+
);
84+
}
85+
86+
return null;
87+
})
88+
.filter(item => item instanceof Error);
89+
90+
if (invalid.length > 0) {
91+
throw new Error(invalid.map(i => i.message).join('\n'));
92+
}
93+
3394
// Validate against all rules
3495
const results = entries(rules)
3596
.filter(entry => {

@commitlint/core/src/lint.test.js

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,81 @@ test('positive on stub message and opts', async t => {
5353
t.true(actual.valid);
5454
});
5555

56-
test('should throw for invalid rule names', async t => {
56+
test('throws for invalid rule names', async t => {
5757
const error = await t.throws(
5858
lint('foo', {foo: [2, 'always'], bar: [1, 'never']})
5959
);
6060

61-
t.is(error.message.indexOf('Found invalid rule names: foo, bar'), 0);
61+
t.is(error.message.indexOf('Found missing rule names: foo, bar'), 0);
62+
});
63+
64+
test('throws for invalid rule config', async t => {
65+
const error = await t.throws(
66+
lint('type(scope): foo', {
67+
'type-enum': 1,
68+
'scope-enum': {0: 2, 1: 'never', 2: ['foo'], length: 3}
69+
})
70+
);
71+
72+
t.true(error.message.indexOf('type-enum must be array') > -1);
73+
t.true(error.message.indexOf('scope-enum must be array') > -1);
74+
});
75+
76+
test('throws for rule with invalid length', async t => {
77+
const error = await t.throws(
78+
lint('type(scope): foo', {'type-enum': [], 'scope-enum': [1, 2, 3, 4]})
79+
);
80+
81+
t.true(error.message.indexOf('type-enum must be 2 or 3 items long') > -1);
82+
t.true(error.message.indexOf('scope-enum must be 2 or 3 items long') > -1);
83+
});
84+
85+
test('throws for rule with invalid level', async t => {
86+
const error = await t.throws(
87+
lint('type(scope): foo', {
88+
'type-enum': ['2', 'always'],
89+
'header-max-length': [{}, 'always']
90+
})
91+
);
92+
93+
t.true(error.message.indexOf('rule type-enum must be number') > -1);
94+
t.true(error.message.indexOf('rule type-enum must be number') > -1);
95+
});
96+
97+
test('throws for rule with out of range level', async t => {
98+
const error = await t.throws(
99+
lint('type(scope): foo', {
100+
'type-enum': [-1, 'always'],
101+
'header-max-length': [3, 'always']
102+
})
103+
);
104+
105+
t.true(error.message.indexOf('rule type-enum must be between 0 and 2') > -1);
106+
t.true(error.message.indexOf('rule type-enum must be between 0 and 2') > -1);
107+
});
108+
109+
test('throws for rule with invalid condition', async t => {
110+
const error = await t.throws(
111+
lint('type(scope): foo', {
112+
'type-enum': [1, 2],
113+
'header-max-length': [1, {}]
114+
})
115+
);
116+
117+
t.true(error.message.indexOf('type-enum must be string') > -1);
118+
t.true(error.message.indexOf('header-max-length must be string') > -1);
119+
});
120+
121+
test('throws for rule with out of range condition', async t => {
122+
const error = await t.throws(
123+
lint('type(scope): foo', {
124+
'type-enum': [1, 'foo'],
125+
'header-max-length': [1, 'bar']
126+
})
127+
);
128+
129+
t.true(error.message.indexOf('type-enum must be "always" or "never"') > -1);
130+
t.true(
131+
error.message.indexOf('header-max-length must be "always" or "never"') > -1
132+
);
62133
});

0 commit comments

Comments
 (0)