diff --git a/.all-contributorsrc b/.all-contributorsrc index f22a035..ba2c160 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -247,6 +247,17 @@ "contributions": [ "bug" ] + }, + { + "login": "Garbee", + "name": "Jonathan Garbee", + "avatar_url": "https://avatars.githubusercontent.com/u/868301?v=3", + "profile": "http://jonathan.garbee.me", + "contributions": [ + "code", + "doc", + "test" + ] } ] } diff --git a/README.md b/README.md index fb5a037..5435d2c 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![downloads][downloads-badge]][npm-stat] [![MIT License][license-badge]][LICENSE] -[![All Contributors](https://img.shields.io/badge/all_contributors-24-orange.svg?style=flat-square)](#contributors) +[![All Contributors](https://img.shields.io/badge/all_contributors-25-orange.svg?style=flat-square)](#contributors) [![PRs Welcome][prs-badge]][prs] [![Donate][donate-badge]][donate] [![Code of Conduct][coc-badge]][coc] @@ -34,6 +34,12 @@ You can specify options in `.vcmrc` ```js { "types": ["feat", "fix", "docs", "style", "refactor", "perf", "test", "chore", "revert"], // default + "scope": { + required: false, // default, + allowed: ['button', 'card'], // default is '*' for anything, + validate: false, // default, + multiple: false // default + }, "warnOnFail": false, // default "maxSubjectLength": 100, // default "subjectPattern": ".+", // default @@ -50,6 +56,12 @@ or in `package.json` "config": { "validate-commit-msg": { "types": ["feat", "fix", "docs", "style", "refactor", "perf", "test", "chore", "revert"], // default + "scope": { + required: false, // default, + allowed: ['button', 'card'], // default is '*' for anything, + validate: false, // default, + multiple: false // default + }, "warnOnFail": false, // default "maxSubjectLength": 100, // default "subjectPattern": ".+", // default @@ -73,6 +85,28 @@ Or you can specify the name of a module that exports types according to the [conventional-commit-types](https://github.com/adjohnson916/conventional-commit-types) spec, e.g. `"types": "conventional-commit-types"`. +#### scope + +This object defines scope requirements for the commit message. Possible properties are: + +##### required + +A boolean to define whether a scope is required for all commit messages. + +##### allowed + +An array of scopes that are allowed for your commit message. + +You may also define it as `"*"` which is the default to allow any scope names. + +##### validate + +A boolean to define whether or not to validate the scope(s) provided. + +##### multiple + +A boolean to define whether or not to allow multiple scopes. + #### warnOnFail If this is set to `true` errors will be logged to the console, however the commit will still pass. @@ -155,7 +189,7 @@ Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds | :---: | :---: | :---: | :---: | :---: | :---: | :---: | | [
Shawn Erquhart](http://www.professant.com)
[πŸ’»](https://github.com/kentcdodds/validate-commit-msg/commits?author=erquhart) [πŸ“–](https://github.com/kentcdodds/validate-commit-msg/commits?author=erquhart) [⚠️](https://github.com/kentcdodds/validate-commit-msg/commits?author=erquhart) | [
Tushar Mathur](http://tusharm.com)
[πŸ’»](https://github.com/kentcdodds/validate-commit-msg/commits?author=tusharmath) [⚠️](https://github.com/kentcdodds/validate-commit-msg/commits?author=tusharmath) | [
Jason Dreyzehner](https://twitter.com/bitjson)
[πŸ’»](https://github.com/kentcdodds/validate-commit-msg/commits?author=bitjson) [πŸ“–](https://github.com/kentcdodds/validate-commit-msg/commits?author=bitjson) [⚠️](https://github.com/kentcdodds/validate-commit-msg/commits?author=bitjson) | [
Abimbola Idowu](http://twitter.com/hisabimbola)
[πŸ’»](https://github.com/kentcdodds/validate-commit-msg/commits?author=hisabimbola) | [
Gleb Bahmutov](https://glebbahmutov.com/)
[πŸ’»](https://github.com/kentcdodds/validate-commit-msg/commits?author=bahmutov) [⚠️](https://github.com/kentcdodds/validate-commit-msg/commits?author=bahmutov) | [
Dennis](http://dennis.io)
[πŸ’»](https://github.com/kentcdodds/validate-commit-msg/commits?author=ds82) | [
Matt Lewis](https://mattlewis.me/)
[πŸ’»](https://github.com/kentcdodds/validate-commit-msg/commits?author=mattlewis92) | | [
Tom Vincent](https://tlvince.com)
[πŸ’»](https://github.com/kentcdodds/validate-commit-msg/commits?author=tlvince) | [
Anders D. Johnson](https://andrz.me/)
[πŸ’»](https://github.com/kentcdodds/validate-commit-msg/commits?author=adjohnson916) [πŸ“–](https://github.com/kentcdodds/validate-commit-msg/commits?author=adjohnson916) [⚠️](https://github.com/kentcdodds/validate-commit-msg/commits?author=adjohnson916) | [
James Zetlen](jameszetlen.com)
[πŸ’»](https://github.com/kentcdodds/validate-commit-msg/commits?author=zetlen) [⚠️](https://github.com/kentcdodds/validate-commit-msg/commits?author=zetlen) | [
Paul Bienkowski](http://opatut.de)
[πŸ’»](https://github.com/kentcdodds/validate-commit-msg/commits?author=opatut) [⚠️](https://github.com/kentcdodds/validate-commit-msg/commits?author=opatut) | [
Barney Scott](https://github.com/bmds)
[πŸ’»](https://github.com/kentcdodds/validate-commit-msg/commits?author=bmds) [⚠️](https://github.com/kentcdodds/validate-commit-msg/commits?author=bmds) | [
Emmanuel Murillo SΓ‘nchez](https://github.com/Emmurillo)
[πŸ’»](https://github.com/kentcdodds/validate-commit-msg/commits?author=Emmurillo) [⚠️](https://github.com/kentcdodds/validate-commit-msg/commits?author=Emmurillo) | [
Hans Kristian Flaatten](https://starefossen.github.io)
[πŸ’»](https://github.com/kentcdodds/validate-commit-msg/commits?author=Starefossen) [⚠️](https://github.com/kentcdodds/validate-commit-msg/commits?author=Starefossen) | -| [
Bo Lingen](https://github.com/citycide)
[πŸ“–](https://github.com/kentcdodds/validate-commit-msg/commits?author=citycide) | [
Spyros Ioakeimidis](http://www.spyros.io)
[πŸ’»](https://github.com/kentcdodds/validate-commit-msg/commits?author=spirosikmd) [πŸ“–](https://github.com/kentcdodds/validate-commit-msg/commits?author=spirosikmd) [⚠️](https://github.com/kentcdodds/validate-commit-msg/commits?author=spirosikmd) | [
Matt Travi](https://matt.travi.org)
[πŸ›](https://github.com/kentcdodds/validate-commit-msg/issues?q=author%3Atravi) | +| [
Bo Lingen](https://github.com/citycide)
[πŸ“–](https://github.com/kentcdodds/validate-commit-msg/commits?author=citycide) | [
Spyros Ioakeimidis](http://www.spyros.io)
[πŸ’»](https://github.com/kentcdodds/validate-commit-msg/commits?author=spirosikmd) [πŸ“–](https://github.com/kentcdodds/validate-commit-msg/commits?author=spirosikmd) [⚠️](https://github.com/kentcdodds/validate-commit-msg/commits?author=spirosikmd) | [
Matt Travi](https://matt.travi.org)
[πŸ›](https://github.com/kentcdodds/validate-commit-msg/issues?q=author%3Atravi) | [
Jonathan Garbee](http://jonathan.garbee.me)
[πŸ’»](https://github.com/kentcdodds/validate-commit-msg/commits?author=Garbee) [πŸ“–](https://github.com/kentcdodds/validate-commit-msg/commits?author=Garbee) [⚠️](https://github.com/kentcdodds/validate-commit-msg/commits?author=Garbee) | This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome! diff --git a/lib/validateMessage.js b/lib/validateMessage.js index ae28f5c..4da90d7 100644 --- a/lib/validateMessage.js +++ b/lib/validateMessage.js @@ -82,6 +82,8 @@ exports.validateMessage = function validateMessage(raw) { isValid = false; } + isValid = validateScope(isValid, scope); + if (config.autoFix) { subject = lowercaseFirstLetter(subject); } @@ -94,7 +96,6 @@ exports.validateMessage = function validateMessage(raw) { // Some more ideas, do want anything like this ? // - Validate the rest of the message (body, footer, BREAKING CHANGE annotations) - // - allow only specific scopes (eg. fix(docs) should not be allowed ? // - auto add empty line after subject ? // - auto remove empty () ? // - auto correct typos in type ? @@ -128,3 +129,43 @@ function lowercase(string) { function lowercaseFirstLetter(string) { return lowercase(string.charAt(0)) + string.slice(1); } + +function validateScope(isValid, scope) { + config.scope = config.scope || {}; + var validateScopes = config.scope.validate || false; + var multipleScopesAllowed = config.scope.multiple || false; + var allowedScopes = config.scope.allowed || '*'; + var scopeRequired = config.scope.required || false; + var scopes = scope ? scope.split(',') : []; + + function validateIndividualScope(item) { + if (allowedScopes[0].trim() === '*') { + return; + } + if (allowedScopes.indexOf(item) === -1) { + error('"%s" is not an allowed scope ! Valid scope are: %s', item, allowedScopes.join(', ')); + isValid = false; + } + } + + if (validateScopes) { + if (scopeRequired && scopes.length === 0) { + error('a scope is required !'); + isValid = false; + } + if (isValid && multipleScopesAllowed) { + scopes.forEach(validateIndividualScope); + } + if (isValid && !multipleScopesAllowed) { + if (scopes.length > 1) { + error('only one scope can be provided !'); + isValid = false; + } + if (isValid) { + validateIndividualScope(scopes[0]); + } + } + } + + return isValid; +}; diff --git a/test/validateMessage.test.js b/test/validateMessage.test.js index a392574..ce4f14a 100644 --- a/test/validateMessage.test.js +++ b/test/validateMessage.test.js @@ -157,6 +157,81 @@ describe('validate-commit-msg.js', function() { expect(logs).to.deep.equal([msg]); }); + it('should require a scope', function() { + var msg = 'feat: Add new feature'; + + m.config.scope = { + validate: true, + allowed: '*', + required: true + }; + + expect(m.validateMessage(msg)).to.equal(INVALID); + expect(errors[0]).to.equal('INVALID COMMIT MSG: a scope is required !'); + expect(logs).to.deep.equal([msg]); + + m.config.scope = undefined; + }); + + it('should validate scope', function() { + var msg = 'feat(nonexistant): Add new feature'; + + m.config.scope = { + validate: true, + allowed: ['button', 'card'] + }; + + expect(m.validateMessage(msg)).to.equal(INVALID); + expect(errors[0]).to.equal('INVALID COMMIT MSG: "nonexistant" is not an allowed scope ! Valid scope are: ' + m.config.scope.allowed.join(', ')); + expect(logs).to.deep.equal([msg]); + + m.config.scope = undefined; + }); + + it('should only allow a single scope when multiples is off', function() { + var msg = 'feat(button,card): Add new feature'; + + m.config.scope = { + validate: true, + allowed: '*' + }; + + expect(m.validateMessage(msg)).to.equal(INVALID); + expect(errors[0]).to.equal('INVALID COMMIT MSG: only one scope can be provided !'); + expect(logs).to.deep.equal([msg]); + + m.config.scope = undefined; + }); + + it('should catch an invalid scope among many', function() { + var msg = 'feat(button,card,ripple): Add new feature'; + + m.config.scope = { + validate: true, + allowed: ['button', 'card'], + multiple: true + }; + + expect(m.validateMessage(msg)).to.equal(INVALID); + expect(errors[0]).to.equal('INVALID COMMIT MSG: "ripple" is not an allowed scope ! Valid scope are: ' + m.config.scope.allowed.join(', ')); + expect(logs).to.deep.equal([msg]); + + m.config.scope = undefined; + }); + + it('should allow any scope if "*" is defined', function() { + var msg = 'feat(anything): Fixed'; + + m.config.scope = { + validate: true + }; + + expect(m.validateMessage(msg)).to.equal(VALID); + expect(logs).to.deep.equal([]); + + m.config.scope = undefined; + }); + it('should allow empty scope', function() { expect(m.validateMessage('fix: blablabla')).to.equal(VALID); expect(errors).to.deep.equal([]);