Skip to content
This repository was archived by the owner on Feb 5, 2018. It is now read-only.

Commit e05c627

Browse files
committed
feat(lib): Add support for limiting allowed scopes
1 parent 04facc4 commit e05c627

File tree

3 files changed

+130
-1
lines changed

3 files changed

+130
-1
lines changed

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ You can specify options in `.vcmrc`
3434
```js
3535
{
3636
"types": ["feat", "fix", "docs", "style", "refactor", "perf", "test", "chore", "revert"], // default
37+
"scopes": {
38+
required: false, // default,
39+
allowed: ['button', 'card'], // default is '*' for anything,
40+
validate: false, // default,
41+
multiple: false // default
42+
},
3743
"warnOnFail": false, // default
3844
"maxSubjectLength": 100, // default
3945
"subjectPattern": ".+", // default
@@ -50,6 +56,12 @@ or in `package.json`
5056
"config": {
5157
"validate-commit-msg": {
5258
"types": ["feat", "fix", "docs", "style", "refactor", "perf", "test", "chore", "revert"], // default
59+
"scopes": {
60+
required: false, // default,
61+
allowed: ['button', 'card'], // default is '*' for anything,
62+
validate: false, // default,
63+
multiple: false // default
64+
},
5365
"warnOnFail": false, // default
5466
"maxSubjectLength": 100, // default
5567
"subjectPattern": ".+", // default
@@ -73,6 +85,28 @@ Or you can specify the name of a module that exports types according to the
7385
[conventional-commit-types](https://github.com/adjohnson916/conventional-commit-types)
7486
spec, e.g. `"types": "conventional-commit-types"`.
7587

88+
#### scopes
89+
90+
This object defines scope requirements for the commit message. Possible properties are:
91+
92+
##### required
93+
94+
A boolean to define whether a scope is required for all commit messages.
95+
96+
##### allowed
97+
98+
An array of scopes that are allowed for your commit message.
99+
100+
You may also define it as `"*"` which is the default to allow any scope names.
101+
102+
##### validate
103+
104+
A boolean to define whether or not to validate the scope(s) provided.
105+
106+
##### multiple
107+
108+
A boolean to define whether or not to allow multiple scopes.
109+
76110
#### warnOnFail
77111

78112
If this is set to `true` errors will be logged to the console, however the commit will still pass.

lib/validateMessage.js

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,21 @@ exports.validateMessage = function validateMessage(raw) {
6363
var type = match[3];
6464
var scope = match[4];
6565
var subject = match[5];
66+
config.scopes = config.scopes || {};
67+
var validateScopes = config.scopes.validate || false;
68+
var multipleScopesAllowed = config.scopes.multiple || false;
69+
var allowedScopes = config.scopes.allowed || '*';
70+
var scopeRequired = config.scopes.required || false;
71+
var scopes = scope ? scope.split(',') : [];
72+
var validateScope = (item) => {
73+
if (allowedScopes[0] === '*') {
74+
return;
75+
}
76+
if (allowedScopes.indexOf(item) === -1) {
77+
error('"%s" is not an allowed scope ! Valid scopes are: %s', item, allowedScopes.join(', '));
78+
isValid = false;
79+
}
80+
};
6681

6782
var SUBJECT_PATTERN = new RegExp(config.subjectPattern || '.+');
6883
var SUBJECT_PATTERN_ERROR_MSG = config.subjectPatternErrorMsg || 'subject does not match subject pattern!';
@@ -82,6 +97,25 @@ exports.validateMessage = function validateMessage(raw) {
8297
isValid = false;
8398
}
8499

100+
if (validateScopes) {
101+
if (scopeRequired && scopes.length === 0) {
102+
error('a scope is required !');
103+
isValid = false;
104+
}
105+
if (isValid && multipleScopesAllowed) {
106+
scopes.forEach(validateScope);
107+
}
108+
if (isValid && !multipleScopesAllowed) {
109+
if (scopes.length > 1) {
110+
error('only one scope can be provided !');
111+
isValid = false;
112+
}
113+
if (isValid) {
114+
validateScope(scopes[0]);
115+
}
116+
}
117+
}
118+
85119
if (config.autoFix) {
86120
subject = lowercaseFirstLetter(subject);
87121
}
@@ -94,7 +128,6 @@ exports.validateMessage = function validateMessage(raw) {
94128

95129
// Some more ideas, do want anything like this ?
96130
// - Validate the rest of the message (body, footer, BREAKING CHANGE annotations)
97-
// - allow only specific scopes (eg. fix(docs) should not be allowed ?
98131
// - auto add empty line after subject ?
99132
// - auto remove empty () ?
100133
// - auto correct typos in type ?

test/validateMessage.test.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,68 @@ describe('validate-commit-msg.js', function() {
157157
expect(logs).to.deep.equal([msg]);
158158
});
159159

160+
it('should require a scope', function() {
161+
var msg = 'feat: Add new feature';
162+
163+
m.config.scopes = {
164+
validate: true,
165+
allowed: '*',
166+
required: true
167+
};
168+
169+
expect(m.validateMessage(msg)).to.equal(INVALID);
170+
expect(errors[0]).to.equal('INVALID COMMIT MSG: a scope is required !');
171+
expect(logs).to.deep.equal([msg]);
172+
173+
m.config.scopes = undefined;
174+
});
175+
176+
it('should validate scope', function() {
177+
var msg = 'feat(nonexistant): Add new feature';
178+
179+
m.config.scopes = {
180+
validate: true,
181+
allowed: ['button', 'card']
182+
};
183+
184+
expect(m.validateMessage(msg)).to.equal(INVALID);
185+
expect(errors[0]).to.equal('INVALID COMMIT MSG: "nonexistant" is not an allowed scope ! Valid scopes are: ' + m.config.scopes.allowed.join(', '));
186+
expect(logs).to.deep.equal([msg]);
187+
188+
m.config.scopes = undefined;
189+
});
190+
191+
it('should only allow a single scope when multiples is off', function() {
192+
var msg = 'feat(button,card): Add new feature';
193+
194+
m.config.scopes = {
195+
validate: true,
196+
allowed: '*'
197+
};
198+
199+
expect(m.validateMessage(msg)).to.equal(INVALID);
200+
expect(errors[0]).to.equal('INVALID COMMIT MSG: only one scope can be provided !');
201+
expect(logs).to.deep.equal([msg]);
202+
203+
m.config.scopes = undefined;
204+
});
205+
206+
it('should catch an invalid scope among many', function() {
207+
var msg = 'feat(button,card,ripple): Add new feature';
208+
209+
m.config.scopes = {
210+
validate: true,
211+
allowed: ['button', 'card'],
212+
multiple: true
213+
};
214+
215+
expect(m.validateMessage(msg)).to.equal(INVALID);
216+
expect(errors[0]).to.equal('INVALID COMMIT MSG: "ripple" is not an allowed scope ! Valid scopes are: ' + m.config.scopes.allowed.join(', '));
217+
expect(logs).to.deep.equal([msg]);
218+
219+
m.config.scopes = undefined;
220+
});
221+
160222
it('should allow empty scope', function() {
161223
expect(m.validateMessage('fix: blablabla')).to.equal(VALID);
162224
expect(errors).to.deep.equal([]);

0 commit comments

Comments
 (0)