diff --git a/.gitignore b/.gitignore
index 5e37fcfe85..56f9832b18 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,41 +1,9 @@
-# Logs
-logs
-*.log
npm-debug.log*
-
-# Runtime data
-pids
-*.pid
-*.seed
-
-# Directory for instrumented libs generated by jscoverage/JSCover
-lib-cov
-
-# Coverage directory used by tools like istanbul
-coverage
-
-# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
-.grunt
-
-# node-waf configuration
-.lock-wscript
-
-# Compiled binary addons (http://nodejs.org/api/addons.html)
-build/Release
-
-# Dependency directory
+lerna-debug.log
node_modules
-
-# Optional npm cache directory
.npm
-
-# Optional REPL history
-.node_repl_history
-
-# jsonlint-cli cache
-.tmp
-
-# transpiled artifacts
-distribution
-
.nyc_output
+.dockerignore
+.*.dockerfile
+@commitlint/**/lib
+@commitlint/**/package.json.lerna_backup
diff --git a/.travis.yml b/.travis.yml
index 8606782c5f..7d69d741ba 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,12 +1,5 @@
language: node_js
node_js:
- - '7'
+ - '8'
- '6'
- '4'
-before_install:
- - git fetch --unshallow
-before_script:
- - npm run build
-script:
- - npm run travis:lint:commits
- - npm test
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000000..abf8296a48
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,4 @@
+// Place your settings in this file to overwrite default and user settings.
+{
+ "xo.enable": true
+}
diff --git a/@commitlint/cli/CHANGELOG.md b/@commitlint/cli/CHANGELOG.md
new file mode 100644
index 0000000000..eac9f3ab4d
--- /dev/null
+++ b/@commitlint/cli/CHANGELOG.md
@@ -0,0 +1,30 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+
+
+## 3.0.1 (2017-07-11)
+
+
+
+
+# 3.0.0 (2017-07-10)
+
+
+### Bug Fixes
+
+* **cli:** remove destructuring for node 4 support ([94437e8](https://github.com/marionebl/commitlint/commit/94437e8))
+* ensure node4 compat ([a5e658a](https://github.com/marionebl/commitlint/commit/a5e658a))
+
+
+
+
+
+# 3.0.0 (2017-07-10)
+
+
+### Bug Fixes
+
+* **cli:** remove destructuring for node 4 support ([94437e8](https://github.com/marionebl/commitlint/commit/94437e8))
+* ensure node4 compat ([a5e658a](https://github.com/marionebl/commitlint/commit/a5e658a))
diff --git a/@commitlint/cli/cli.js b/@commitlint/cli/cli.js
new file mode 100755
index 0000000000..919ef5c40b
--- /dev/null
+++ b/@commitlint/cli/cli.js
@@ -0,0 +1,118 @@
+#!/usr/bin/env node
+require('babel-polyfill'); // eslint-disable-line import/no-unassigned-import
+
+// npm modules
+const core = require('@commitlint/core');
+const chalk = require('chalk');
+const meow = require('meow');
+const pick = require('lodash').pick;
+const stdin = require('get-stdin');
+
+const pkg = require('./package');
+const help = require('./help');
+
+/**
+ * Behavioural rules
+ */
+const rules = {
+ fromStdin: (input, flags) => input.length === 0 &&
+ typeof flags.from !== 'string' &&
+ typeof flags.to !== 'string' &&
+ !flags.edit
+};
+
+const configuration = {
+ string: ['from', 'to', 'extends'],
+ boolean: ['edit', 'help', 'version', 'quiet', 'color'],
+ alias: {
+ c: 'color',
+ e: 'edit',
+ f: 'from',
+ t: 'to',
+ q: 'quiet',
+ h: 'help',
+ v: 'version',
+ x: 'extends'
+ },
+ description: {
+ color: 'toggle colored output',
+ edit: 'read last commit message found in ./git/COMMIT_EDITMSG',
+ extends: 'array of shareable configurations to extend',
+ from: 'lower end of the commit range to lint; applies if edit=false',
+ to: 'upper end of the commit range to lint; applies if edit=false',
+ quiet: 'toggle console output'
+ },
+ default: {
+ color: true,
+ edit: false,
+ from: null,
+ to: null,
+ quiet: false
+ },
+ unknown(arg) {
+ throw new Error(`unknown flags: ${arg}`);
+ }
+};
+
+const cli = meow({
+ help: `[input] reads from stdin if --edit, --from and --to are omitted\n${help(configuration)}`,
+ description: `${pkg.name}@${pkg.version} - ${pkg.description}`
+}, configuration);
+
+const load = seed => core.load(seed);
+
+function main(options) {
+ const raw = options.input;
+ const flags = options.flags;
+ const fromStdin = rules.fromStdin(raw, flags);
+
+ const range = pick(flags, 'edit', 'from', 'to');
+ const input = fromStdin ? stdin() : core.read(range);
+ const fmt = new chalk.constructor({enabled: flags.color});
+
+ return input
+ .then(raw => Array.isArray(raw) ? raw : [raw])
+ .then(messages => Promise.all(messages.map(commit => {
+ return load(getSeed(flags))
+ .then(opts => core.lint(commit, opts.rules))
+ .then(report => {
+ const formatted = core.format(report, {color: flags.color});
+
+ if (!flags.quiet) {
+ console.log(`${fmt.grey('⧗')} input: ${fmt.bold(commit.split('\n')[0])}`);
+ console.log(formatted.join('\n'));
+ }
+
+ if (report.errors.length > 0) {
+ const error = new Error(formatted[formatted.length - 1]);
+ error.type = pkg.name;
+ throw error;
+ }
+ return console.log('');
+ });
+ })
+ ));
+}
+
+function getSeed(seed) {
+ const e = Array.isArray(seed.extends) ? seed.extends : [seed.extends];
+ const n = e.filter(i => typeof i === 'string');
+ return n.length > 0 ? {extends: n} : {};
+}
+
+// Start the engine
+main(cli)
+ .catch(err =>
+ setTimeout(() => {
+ if (err.type === pkg.name) {
+ process.exit(1);
+ }
+ throw err;
+ })
+ );
+
+// Catch unhandled rejections globally
+process.on('unhandledRejection', (reason, promise) => {
+ console.log('Unhandled Rejection at: Promise ', promise, ' reason: ', reason);
+ throw reason;
+});
diff --git a/@commitlint/cli/cli.test.js b/@commitlint/cli/cli.test.js
new file mode 100644
index 0000000000..24573769b4
--- /dev/null
+++ b/@commitlint/cli/cli.test.js
@@ -0,0 +1,71 @@
+import path from 'path';
+import test from 'ava';
+import execa from 'execa';
+import stream from 'string-to-stream';
+
+const here = path.join.bind(null, __dirname);
+
+const SIMPLE = here('fixtures/simple');
+const EXTENDS_ROOT = here('fixtures/extends-root');
+const EMPTY = here('fixtures/empty');
+
+const cli = (input = '', args = [], opts = {}) => {
+ const c = execa(here('cli.js'), args, {
+ capture: ['stdout'],
+ cwd: opts.cwd
+ });
+ stream(input).pipe(c.stdin);
+ return c;
+};
+
+test('should throw when called without [input]', t => {
+ t.throws(cli(), /Expected a raw commit/);
+});
+
+test('should reprint input from stdin', async t => {
+ const actual = await cli('foo: bar', [], {cwd: EMPTY});
+ t.true(actual.stdout.includes('foo: bar'));
+});
+
+test('should produce no success output with --quiet flag', async t => {
+ const actual = await cli('foo: bar', ['--quiet'], {cwd: EMPTY});
+ t.is(actual.stdout, '');
+ t.is(actual.stderr, '');
+});
+
+test('should produce no success output with -q flag', async t => {
+ const actual = await cli('foo: bar', ['-q'], {cwd: EMPTY});
+ t.is(actual.stdout, '');
+ t.is(actual.stderr, '');
+});
+
+test('should succeed for input from stdin without rules', async t => {
+ const actual = await cli('foo: bar', [], {cwd: EMPTY});
+ t.is(actual.code, 0);
+});
+
+test('should fail for input from stdin with rule from rc', async t => {
+ const actual = await t.throws(cli('foo: bar', [], {cwd: SIMPLE}));
+ t.true(actual.stdout.includes('scope must not be one of [foo]'));
+ t.is(actual.code, 1);
+});
+
+test('should fail for input from stdin with rule from js', async t => {
+ const actual = await t.throws(cli('foo: bar', ['--extends', './extended'], {cwd: EXTENDS_ROOT}));
+ t.true(actual.stdout.includes('scope must not be one of [foo]'));
+ t.is(actual.code, 1);
+});
+
+test('should produce no error output with --quiet flag', async t => {
+ const actual = await t.throws(cli('foo: bar', ['--quiet'], {cwd: SIMPLE}));
+ t.is(actual.stdout, '');
+ t.is(actual.stderr, '');
+ t.is(actual.code, 1);
+});
+
+test('should produce no error output with -q flag', async t => {
+ const actual = await t.throws(cli('foo: bar', ['-q'], {cwd: SIMPLE}));
+ t.is(actual.stdout, '');
+ t.is(actual.stderr, '');
+ t.is(actual.code, 1);
+});
diff --git a/@commitlint/cli/fixtures/empty/commitlint.config.js b/@commitlint/cli/fixtures/empty/commitlint.config.js
new file mode 100644
index 0000000000..f053ebf797
--- /dev/null
+++ b/@commitlint/cli/fixtures/empty/commitlint.config.js
@@ -0,0 +1 @@
+module.exports = {};
diff --git a/@commitlint/cli/fixtures/extends-root/extended.js b/@commitlint/cli/fixtures/extends-root/extended.js
new file mode 100644
index 0000000000..b6775fef5b
--- /dev/null
+++ b/@commitlint/cli/fixtures/extends-root/extended.js
@@ -0,0 +1,5 @@
+module.exports = {
+ rules: {
+ 'type-enum': [2, 'never', ['foo']]
+ }
+};
diff --git a/@commitlint/cli/fixtures/simple/commitlint.config.js b/@commitlint/cli/fixtures/simple/commitlint.config.js
new file mode 100644
index 0000000000..b6775fef5b
--- /dev/null
+++ b/@commitlint/cli/fixtures/simple/commitlint.config.js
@@ -0,0 +1,5 @@
+module.exports = {
+ rules: {
+ 'type-enum': [2, 'never', ['foo']]
+ }
+};
diff --git a/source/help.js b/@commitlint/cli/help.js
similarity index 80%
rename from source/help.js
rename to @commitlint/cli/help.js
index 6f5478c2c3..dbe2471527 100644
--- a/source/help.js
+++ b/@commitlint/cli/help.js
@@ -1,7 +1,8 @@
-export default configuration => {
+module.exports = configuration => {
const lines = Object.entries(configuration.description)
.map(entry => {
- const [name, desc] = entry;
+ const name = entry[0];
+ const desc = entry[1];
const alias = Object.entries(configuration.alias)
.find(entry => entry[1] === name)
.map(entry => entry[0])[0];
@@ -11,14 +12,16 @@ export default configuration => {
const longest = lines
.map(line => {
- const [flags] = line;
+ const flags = line[0];
return flags.reduce((sum, flag) => sum + flag.length, 0);
})
.sort(Number)[0];
return lines
.map(line => {
- const [flags, desc, defaults] = line;
+ const flags = line[0];
+ const desc = line[1];
+ const defaults = line[2];
const fs = flags.map(flag => flag.length > 1 ? `--${flag}` : `-${flag}`);
const ds = defaults ? `, defaults to: ${defaults}` : '';
const length = flags.reduce((sum, flag) => sum + flag.length, 0);
diff --git a/@commitlint/cli/package.json b/@commitlint/cli/package.json
new file mode 100644
index 0000000000..340ceaecdd
--- /dev/null
+++ b/@commitlint/cli/package.json
@@ -0,0 +1,58 @@
+{
+ "name": "@commitlint/cli",
+ "version": "3.0.1",
+ "description": "Lint your commit messages",
+ "bin": {
+ "commitlint": "cli.js"
+ },
+ "scripts": {
+ "build": "exit 0",
+ "clean": "exit 0",
+ "pretest": "dep-check",
+ "test": "ava",
+ "prepublish": "npm test"
+ },
+ "ava": {
+ "files": [
+ "cli.test.js"
+ ]
+ },
+ "xo": false,
+ "engines": {
+ "node": ">=4"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/marionebl/commitlint.git"
+ },
+ "bugs": {
+ "url": "https://github.com/marionebl/commitlint/issues"
+ },
+ "homepage": "https://github.com/marionebl/commitlint#readme",
+ "keywords": [
+ "conventional-changelog",
+ "commitlint",
+ "cli"
+ ],
+ "author": {
+ "name": "Mario Nebl",
+ "email": "hello@herebecode.com"
+ },
+ "license": "MIT",
+ "devDependencies": {
+ "@commitlint/utils": "^3.0.0",
+ "ava": "^0.18.2",
+ "dependency-check": "^2.9.1",
+ "execa": "^0.7.0",
+ "string-to-stream": "^1.1.0",
+ "xo": "^0.18.2"
+ },
+ "dependencies": {
+ "@commitlint/core": "^3.0.1",
+ "babel-polyfill": "^6.23.0",
+ "chalk": "^2.0.1",
+ "get-stdin": "^5.0.1",
+ "lodash": "^4.17.4",
+ "meow": "^3.7.0"
+ }
+}
diff --git a/@commitlint/cli/readme.md b/@commitlint/cli/readme.md
new file mode 100644
index 0000000000..a0014014f8
--- /dev/null
+++ b/@commitlint/cli/readme.md
@@ -0,0 +1,12 @@
+> Lint commit messages
+
+# @commitlint/cli
+
+## Getting started
+
+```shell
+npm install --save-dev @commitlint/cli @commitlint/config-angular
+echo "module.exports = {extends: ['@commitlint/config-angular']};" > .commitlint.config.js
+```
+
+Consult [docs/cli](../../docs/cli) for comprehensive documentation.
diff --git a/@commitlint/commitlint-config-angular/CHANGELOG.md b/@commitlint/commitlint-config-angular/CHANGELOG.md
new file mode 100644
index 0000000000..503de2dde0
--- /dev/null
+++ b/@commitlint/commitlint-config-angular/CHANGELOG.md
@@ -0,0 +1,28 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+
+
+## 3.0.1 (2017-07-11)
+
+
+
+
+# 3.0.0 (2017-07-10)
+
+
+### Features
+
+* alias config packages ([67ba5c6](https://github.com/marionebl/commitlint/commit/67ba5c6))
+
+
+
+
+
+# 3.0.0 (2017-07-10)
+
+
+### Features
+
+* alias config packages ([67ba5c6](https://github.com/marionebl/commitlint/commit/67ba5c6))
diff --git a/@commitlint/commitlint-config-angular/index.js b/@commitlint/commitlint-config-angular/index.js
new file mode 100644
index 0000000000..350c9f08f2
--- /dev/null
+++ b/@commitlint/commitlint-config-angular/index.js
@@ -0,0 +1 @@
+module.exports = require('@commitlint/config-angular');
diff --git a/@commitlint/commitlint-config-angular/package.json b/@commitlint/commitlint-config-angular/package.json
new file mode 100644
index 0000000000..4dba67f7f7
--- /dev/null
+++ b/@commitlint/commitlint-config-angular/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "commitlint-config-angular",
+ "version": "3.0.1",
+ "description": "Shareable commitlint config enforcing the angular commit convention",
+ "scripts": {
+ "test": "exit 0",
+ "clean": "exit 0"
+ },
+ "xo": false,
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/marionebl/commitlint.git"
+ },
+ "keywords": [
+ "conventional-changelog",
+ "commitlint",
+ "commitlint-config",
+ "angular"
+ ],
+ "author": "Mario Nebl ",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/marionebl/commitlint/issues"
+ },
+ "homepage": "https://github.com/marionebl/commitlint#readme",
+ "dependencies": {
+ "@commitlint/config-angular": "^3.0.0"
+ }
+}
diff --git a/@commitlint/commitlint-config-lerna-scopes/CHANGELOG.md b/@commitlint/commitlint-config-lerna-scopes/CHANGELOG.md
new file mode 100644
index 0000000000..503de2dde0
--- /dev/null
+++ b/@commitlint/commitlint-config-lerna-scopes/CHANGELOG.md
@@ -0,0 +1,28 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+
+
+## 3.0.1 (2017-07-11)
+
+
+
+
+# 3.0.0 (2017-07-10)
+
+
+### Features
+
+* alias config packages ([67ba5c6](https://github.com/marionebl/commitlint/commit/67ba5c6))
+
+
+
+
+
+# 3.0.0 (2017-07-10)
+
+
+### Features
+
+* alias config packages ([67ba5c6](https://github.com/marionebl/commitlint/commit/67ba5c6))
diff --git a/@commitlint/commitlint-config-lerna-scopes/index.js b/@commitlint/commitlint-config-lerna-scopes/index.js
new file mode 100644
index 0000000000..ff8392cc1b
--- /dev/null
+++ b/@commitlint/commitlint-config-lerna-scopes/index.js
@@ -0,0 +1 @@
+module.exports = require('@commitlint/config-lerna-scopes');
diff --git a/@commitlint/commitlint-config-lerna-scopes/package.json b/@commitlint/commitlint-config-lerna-scopes/package.json
new file mode 100644
index 0000000000..4b529c3434
--- /dev/null
+++ b/@commitlint/commitlint-config-lerna-scopes/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "commitlint-config-lerna-scopes",
+ "version": "3.0.1",
+ "description": "Shareable commitlint config enforcing lerna package names as scopes",
+ "scripts": {
+ "test": "exit 0",
+ "clean": "exit 0"
+ },
+ "xo": false,
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/marionebl/commitlint.git"
+ },
+ "keywords": [
+ "conventional-changelog",
+ "commitlint",
+ "commitlint-config",
+ "angular"
+ ],
+ "author": "Mario Nebl ",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/marionebl/commitlint/issues"
+ },
+ "homepage": "https://github.com/marionebl/commitlint#readme",
+ "dependencies": {
+ "@commitlint/config-lerna-scopes": "^3.0.0"
+ }
+}
diff --git a/@commitlint/commitlint-config-patternplate/CHANGELOG.md b/@commitlint/commitlint-config-patternplate/CHANGELOG.md
new file mode 100644
index 0000000000..503de2dde0
--- /dev/null
+++ b/@commitlint/commitlint-config-patternplate/CHANGELOG.md
@@ -0,0 +1,28 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+
+
+## 3.0.1 (2017-07-11)
+
+
+
+
+# 3.0.0 (2017-07-10)
+
+
+### Features
+
+* alias config packages ([67ba5c6](https://github.com/marionebl/commitlint/commit/67ba5c6))
+
+
+
+
+
+# 3.0.0 (2017-07-10)
+
+
+### Features
+
+* alias config packages ([67ba5c6](https://github.com/marionebl/commitlint/commit/67ba5c6))
diff --git a/@commitlint/commitlint-config-patternplate/index.js b/@commitlint/commitlint-config-patternplate/index.js
new file mode 100644
index 0000000000..609d2cc13d
--- /dev/null
+++ b/@commitlint/commitlint-config-patternplate/index.js
@@ -0,0 +1 @@
+module.exports = require('@commitlint/config-patternplate');
diff --git a/@commitlint/commitlint-config-patternplate/package.json b/@commitlint/commitlint-config-patternplate/package.json
new file mode 100644
index 0000000000..413f162e01
--- /dev/null
+++ b/@commitlint/commitlint-config-patternplate/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "commitlint-config-patternplate",
+ "version": "3.0.1",
+ "description": "Lint your commits, patternplate-style",
+ "scripts": {
+ "test": "exit 0",
+ "clean": "exit 0"
+ },
+ "xo": false,
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/marionebl/commitlint.git"
+ },
+ "keywords": [
+ "conventional-changelog",
+ "commitlint",
+ "commitlint-config",
+ "angular"
+ ],
+ "author": "Mario Nebl ",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/marionebl/commitlint/issues"
+ },
+ "homepage": "https://github.com/marionebl/commitlint#readme",
+ "dependencies": {
+ "@commitlint/config-patternplate": "^3.0.0"
+ }
+}
diff --git a/@commitlint/config-angular/CHANGELOG.md b/@commitlint/config-angular/CHANGELOG.md
new file mode 100644
index 0000000000..f30746b685
--- /dev/null
+++ b/@commitlint/config-angular/CHANGELOG.md
@@ -0,0 +1,7 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+
+
+# 3.0.0 (2017-07-10)
diff --git a/@commitlint/config-angular/index.js b/@commitlint/config-angular/index.js
new file mode 100644
index 0000000000..24dd31573c
--- /dev/null
+++ b/@commitlint/config-angular/index.js
@@ -0,0 +1,64 @@
+module.exports = {
+ rules: {
+ 'body-leading-blank': [1,
+ 'always'
+ ],
+ 'body-tense': [1,
+ 'always',
+ ['present-imperative']
+ ],
+ 'footer-leading-blank': [1,
+ 'always'
+ ],
+ 'footer-tense': [1,
+ 'always',
+ ['present-imperative']
+ ],
+ 'header-max-length': [2,
+ 'always',
+ 72
+ ],
+ lang: [1,
+ 'always',
+ 'eng'
+ ],
+ 'scope-case': [2,
+ 'always',
+ 'lowerCase'
+ ],
+ 'subject-empty': [2,
+ 'never'
+ ],
+ 'subject-full-stop': [2,
+ 'never',
+ '.'
+ ],
+ 'subject-tense': [1,
+ 'always',
+ ['present-imperative']
+ ],
+ 'type-case': [2,
+ 'always',
+ 'lowerCase'
+ ],
+ 'type-empty': [2,
+ 'never'
+ ],
+ 'type-enum': [2,
+ 'always',
+ [
+ 'build',
+ 'chore',
+ 'ci',
+ 'docs',
+ 'feat',
+ 'fix',
+ 'perf',
+ 'refactor',
+ 'revert',
+ 'style',
+ 'test'
+ ]
+ ]
+ }
+};
diff --git a/@commitlint/config-angular/package.json b/@commitlint/config-angular/package.json
new file mode 100644
index 0000000000..4befe4b1d3
--- /dev/null
+++ b/@commitlint/config-angular/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "@commitlint/config-angular",
+ "version": "3.0.0",
+ "description": "Shareable commitlint config enforcing the angular commit convention",
+ "scripts": {
+ "pretest": "dep-check",
+ "test": "exit 0",
+ "clean": "exit 0"
+ },
+ "xo": false,
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/marionebl/commitlint.git"
+ },
+ "keywords": [
+ "conventional-changelog",
+ "commitlint",
+ "commitlint-config",
+ "angular"
+ ],
+ "author": "Mario Nebl ",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/marionebl/commitlint/issues"
+ },
+ "homepage": "https://github.com/marionebl/commitlint#readme",
+ "devDependencies": {
+ "@commitlint/utils": "^3.0.0"
+ }
+}
diff --git a/@commitlint/config-angular/readme.md b/@commitlint/config-angular/readme.md
new file mode 100644
index 0000000000..964e1df158
--- /dev/null
+++ b/@commitlint/config-angular/readme.md
@@ -0,0 +1,91 @@
+> Lint your commits, angular-style
+
+# @commitlint/config-angular
+Shareable `commitlint` config enforcing the angular commit convention.
+
+## Getting started
+
+```sh
+npm install --save-dev @commitlint/config-angular @commitlint/cli
+echo "module.exports = {extends: ['@commitlint/config-angular']};" > .commitlint.config.js
+```
+
+## Rules
+### Problems
+
+The following rules are considered problems for `@commitlint/config-angular` and will yield a non-zero exit code when not met.
+
+#### type-enum
+* **condition**: `type` is found in value
+* **rule**: `always`
+* **value**
+
+ ```js
+ [
+ 'build',
+ 'chore',
+ 'ci',
+ 'docs',
+ 'feat',
+ 'fix',
+ 'perf',
+ 'refactor',
+ 'revert',
+ 'style',
+ 'test'
+ ]
+ ```
+
+#### type-case
+* **description**: `type` is in case `value`
+* **rule**: `always`
+* **value**
+ ```js
+ 'lowerCase'
+ ```
+
+#### type-empty
+* **condition**: `type` is empty
+* **rule**: `never`
+
+#### scope-case
+* **condition**: `scope` is in case `value`
+* **rule**: `always`
+```js
+ 'lowerCase'
+```
+
+#### subject-empty
+* **condition**: `subject` is empty
+* **rule**: `never`
+
+#### subject-full-stop
+* **condition**: `subject` ends with `value`
+* **rule**: `never`
+* **value**
+```js
+ '.'
+```
+
+#### header-max-length
+* **condition**: `header` has `value` or less characters
+* **rule**: `always`
+* **value**
+```js
+ 72
+```
+
+### Warnings
+The following rules are considered warnings for `@commitlint/config-angular` and will print warning messages when not met.
+
+#### body-leading-blank
+* **condition**: Body begins with blank line
+* **rule**: `always`
+
+#### lang
+* **condition**: `subject` is of language `value`
+* **rule**: `always`
+* **value**
+```js
+ eng
+```
diff --git a/@commitlint/config-lerna-scopes/CHANGELOG.md b/@commitlint/config-lerna-scopes/CHANGELOG.md
new file mode 100644
index 0000000000..d219463af0
--- /dev/null
+++ b/@commitlint/config-lerna-scopes/CHANGELOG.md
@@ -0,0 +1,12 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+
+
+# 3.0.0 (2017-07-10)
+
+
+### Features
+
+* **config-lerna-scopes:** support non-standard lerna repos ([903df4b](https://github.com/marionebl/commitlint/commit/903df4b))
diff --git a/@commitlint/config-lerna-scopes/index.js b/@commitlint/config-lerna-scopes/index.js
new file mode 100644
index 0000000000..f08e564506
--- /dev/null
+++ b/@commitlint/config-lerna-scopes/index.js
@@ -0,0 +1,15 @@
+const Repository = require('lerna/lib/Repository');
+
+module.exports = {
+ utils: {getPackages},
+ rules: {
+ 'scope-enum': () => [2, 'always', getPackages()]
+ }
+};
+
+function getPackages() {
+ const repo = new Repository(process.cwd());
+ return repo.packages
+ .map(pkg => pkg.name)
+ .map(name => name.charAt(0) === '@' ? name.split('/')[1] : name);
+}
diff --git a/@commitlint/config-lerna-scopes/package.json b/@commitlint/config-lerna-scopes/package.json
new file mode 100644
index 0000000000..d60e4c24d1
--- /dev/null
+++ b/@commitlint/config-lerna-scopes/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "@commitlint/config-lerna-scopes",
+ "version": "3.0.0",
+ "description": "Shareable commitlint config enforcing lerna package names as scopes",
+ "scripts": {
+ "pretest": "dep-check",
+ "test": "exit 0",
+ "clean": "exit 0"
+ },
+ "xo": false,
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/marionebl/commitlint.git"
+ },
+ "keywords": [
+ "conventional-changelog",
+ "commitlint",
+ "commitlint-config",
+ "lerna"
+ ],
+ "author": "Mario Nebl ",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/marionebl/commitlint/issues"
+ },
+ "homepage": "https://github.com/marionebl/commitlint#readme",
+ "dependencies": {
+ "lerna": "^2.0.0"
+ },
+ "devDependencies": {
+ "@commitlint/utils": "^3.0.0"
+ }
+}
diff --git a/@commitlint/config-lerna-scopes/readme.md b/@commitlint/config-lerna-scopes/readme.md
new file mode 100644
index 0000000000..7bf094a9cb
--- /dev/null
+++ b/@commitlint/config-lerna-scopes/readme.md
@@ -0,0 +1,39 @@
+> Lint your commits, angular-style
+
+# @commitlint/config-lerna-scopes
+Shareable `commitlint` config enforcing lerna package names as scopes.
+
+## Getting started
+```sh
+npm install --save-dev @commitlint/config-lerna-scopes @commitlint/cli
+echo "module.exports = {extends: ['@commitlint/config-lerna-scopes']};" > .commitlint.config.js
+```
+
+## Examples
+
+```
+❯ cat .commitlintrc
+{
+ "extends": ["lerna-scopes"]
+}
+
+❯ tree packages
+
+packages
+├── api
+├── app
+└── web
+
+❯ echo "chore(api): fix something in api's build" | commitlint
+⧗ input: chore(api): fix something in api's build
+✔ found 0 problems, 0 warnings
+
+❯ echo "chore(foo): this won't pass" | commitlint
+⧗ input: chore(foo): this won't pass
+✖ scope must be one of [api, app, web] [scope-enum]
+✖ found 1 problems, 0 warnings
+
+❯ echo "chore: do some general maintenance" | commitlint
+⧗ input: chore: do some general maintenance
+✔ found 0 problems, 0 warnings
+```
diff --git a/@commitlint/config-patternplate/CHANGELOG.md b/@commitlint/config-patternplate/CHANGELOG.md
new file mode 100644
index 0000000000..f30746b685
--- /dev/null
+++ b/@commitlint/config-patternplate/CHANGELOG.md
@@ -0,0 +1,7 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+
+
+# 3.0.0 (2017-07-10)
diff --git a/@commitlint/config-patternplate/index.js b/@commitlint/config-patternplate/index.js
new file mode 100644
index 0000000000..916a27dbea
--- /dev/null
+++ b/@commitlint/config-patternplate/index.js
@@ -0,0 +1,25 @@
+const path = require('path');
+const globby = require('globby');
+const merge = require('lodash').merge;
+
+function pathToId(root, filePath) {
+ const relativePath = path.relative(root, filePath);
+ return path.dirname(relativePath).split(path.sep).join('/');
+}
+
+function getPatternIDs() {
+ const root = path.resolve(process.cwd(), './patterns');
+ const glob = path.resolve(root, '**/pattern.json');
+ return globby(glob)
+ .then(results => results.map(result => pathToId(root, result)));
+}
+
+module.exports = merge(
+ require('@commitlint/config-angular'),
+ {
+ rules: {
+ 'scope-enum': () => getPatternIDs()
+ .then(ids => [2, 'always', ids.concat(['system'])])
+ }
+ }
+);
diff --git a/@commitlint/config-patternplate/package.json b/@commitlint/config-patternplate/package.json
new file mode 100644
index 0000000000..ce856ba8d5
--- /dev/null
+++ b/@commitlint/config-patternplate/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "@commitlint/config-patternplate",
+ "version": "3.0.0",
+ "description": "Lint your commits, patternplate-style",
+ "scripts": {
+ "pretest": "dep-check",
+ "test": "exit 0",
+ "clean": "exit 0"
+ },
+ "xo": false,
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/marionebl/commitlint.git"
+ },
+ "keywords": [
+ "conventional-changelog",
+ "commitlint",
+ "commitlint-config",
+ "patternplate"
+ ],
+ "author": "Mario Nebl ",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/marionebl/commitlint/issues"
+ },
+ "homepage": "https://github.com/marionebl/commitlint#readme",
+ "dependencies": {
+ "@commitlint/config-angular": "^3.0.0",
+ "globby": "^4.0.0",
+ "lodash": "^4.5.1"
+ },
+ "devDependencies": {
+ "@commitlint/utils": "^3.0.0"
+ }
+}
diff --git a/@commitlint/config-patternplate/readme.md b/@commitlint/config-patternplate/readme.md
new file mode 100644
index 0000000000..5dc3e19b43
--- /dev/null
+++ b/@commitlint/config-patternplate/readme.md
@@ -0,0 +1,21 @@
+> Lint your commits, patternplate-style
+
+# @commitlint/config-patternplate
+Shareable `commitlint` config enforcing the patternplate commit convention.
+
+## Getting started
+```sh
+npm install --save-dev @commitlint/config-patternplate @commitlint/cli
+echo "module.exports = {extends: ['@commitlint/config-patternplate']};" > .commitlint.config.js
+```
+
+## Rules
+`@commitlint/config-patternplate` extends the [shareable angular config](../config-angular#rules). Additionally these rules apply:
+
+### Problems
+The following rules are considered problems for `@commitlint/config-patterplate` and will yield a non-zero exit code when not met.
+
+#### scope-enum
+* **description**: `scope` is found in `value`
+* **rule**: `always`
+* **value**: determined based on pattern tree. `system` and all pattern ids present in `patterns` are allowed
diff --git a/@commitlint/core/CHANGELOG.md b/@commitlint/core/CHANGELOG.md
new file mode 100644
index 0000000000..28d27e2647
--- /dev/null
+++ b/@commitlint/core/CHANGELOG.md
@@ -0,0 +1,53 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+
+
+## 3.0.1 (2017-07-11)
+
+
+### Bug Fixes
+
+* use conventional-changelog-angular again ([2bcc941](https://github.com/marionebl/commitlint/commit/2bcc941))
+
+
+
+
+# 3.0.0 (2017-07-10)
+
+
+### Bug Fixes
+
+* enable recursive relative extends ([e715d86](https://github.com/marionebl/commitlint/commit/e715d86))
+* ensure node4 compat ([a5e658a](https://github.com/marionebl/commitlint/commit/a5e658a))
+* **core:** ignore version commits with leading whitespace ([ead20b6](https://github.com/marionebl/commitlint/commit/ead20b6))
+* **core:** resolve extends relative to config file ([2257a80](https://github.com/marionebl/commitlint/commit/2257a80))
+
+
+### Features
+
+* **core:** do not prefix relative extends ([dfb661f](https://github.com/marionebl/commitlint/commit/dfb661f))
+* **core:** readd support for .conventional-changelog-lintrc ([030298e](https://github.com/marionebl/commitlint/commit/030298e))
+* **core:** support conventional-changelog-lint-config-* ([3092ce5](https://github.com/marionebl/commitlint/commit/3092ce5))
+
+
+
+
+
+# 3.0.0 (2017-07-10)
+
+
+### Bug Fixes
+
+* enable recursive relative extends ([e715d86](https://github.com/marionebl/commitlint/commit/e715d86))
+* ensure node4 compat ([a5e658a](https://github.com/marionebl/commitlint/commit/a5e658a))
+* **core:** ignore version commits with leading whitespace ([ead20b6](https://github.com/marionebl/commitlint/commit/ead20b6))
+* **core:** resolve extends relative to config file ([2257a80](https://github.com/marionebl/commitlint/commit/2257a80))
+
+
+### Features
+
+* **core:** do not prefix relative extends ([dfb661f](https://github.com/marionebl/commitlint/commit/dfb661f))
+* **core:** readd support for .conventional-changelog-lintrc ([030298e](https://github.com/marionebl/commitlint/commit/030298e))
+* **core:** support conventional-changelog-lint-config-* ([3092ce5](https://github.com/marionebl/commitlint/commit/3092ce5))
diff --git a/fixtures/empty-file/.conventional-changelog-lintrc b/@commitlint/core/fixtures/empty-file/commitlint.config.js
similarity index 100%
rename from fixtures/empty-file/.conventional-changelog-lintrc
rename to @commitlint/core/fixtures/empty-file/commitlint.config.js
diff --git a/@commitlint/core/fixtures/empty-object-file/commitlint.config.js b/@commitlint/core/fixtures/empty-object-file/commitlint.config.js
new file mode 100644
index 0000000000..f053ebf797
--- /dev/null
+++ b/@commitlint/core/fixtures/empty-object-file/commitlint.config.js
@@ -0,0 +1 @@
+module.exports = {};
diff --git a/@commitlint/core/fixtures/extends-empty/commitlint.config.js b/@commitlint/core/fixtures/extends-empty/commitlint.config.js
new file mode 100644
index 0000000000..8461bcc039
--- /dev/null
+++ b/@commitlint/core/fixtures/extends-empty/commitlint.config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ extends: []
+};
diff --git a/@commitlint/core/fixtures/extends-invalid/commitlint.config.js b/@commitlint/core/fixtures/extends-invalid/commitlint.config.js
new file mode 100644
index 0000000000..547e81f5c3
--- /dev/null
+++ b/@commitlint/core/fixtures/extends-invalid/commitlint.config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ extends: ['____foooooo']
+};
diff --git a/@commitlint/core/fixtures/legacy/.conventional-changelog-lintrc b/@commitlint/core/fixtures/legacy/.conventional-changelog-lintrc
new file mode 100644
index 0000000000..39911211f1
--- /dev/null
+++ b/@commitlint/core/fixtures/legacy/.conventional-changelog-lintrc
@@ -0,0 +1,5 @@
+{
+ "rules": {
+ "legacy": true
+ }
+}
diff --git a/@commitlint/core/fixtures/overridden-type-enums/commitlint.config.js b/@commitlint/core/fixtures/overridden-type-enums/commitlint.config.js
new file mode 100644
index 0000000000..99dbaeda8a
--- /dev/null
+++ b/@commitlint/core/fixtures/overridden-type-enums/commitlint.config.js
@@ -0,0 +1,6 @@
+module.exports = {
+ extends: ['./extended'],
+ rules: {
+ 'type-enum': [2, 'always', ['a', 'b', 'c', 'd']]
+ }
+};
diff --git a/@commitlint/core/fixtures/overridden-type-enums/extended.js b/@commitlint/core/fixtures/overridden-type-enums/extended.js
new file mode 100644
index 0000000000..87103567fd
--- /dev/null
+++ b/@commitlint/core/fixtures/overridden-type-enums/extended.js
@@ -0,0 +1,20 @@
+module.exports = {
+ rules: {
+ 'type-enum': [2,
+ 'always',
+ [
+ 'build',
+ 'chore',
+ 'ci',
+ 'docs',
+ 'feat',
+ 'fix',
+ 'perf',
+ 'refactor',
+ 'revert',
+ 'style',
+ 'test'
+ ]
+ ]
+ }
+};
diff --git a/@commitlint/core/fixtures/overriden-legacy/.conventional-changelog-lintrc b/@commitlint/core/fixtures/overriden-legacy/.conventional-changelog-lintrc
new file mode 100644
index 0000000000..39911211f1
--- /dev/null
+++ b/@commitlint/core/fixtures/overriden-legacy/.conventional-changelog-lintrc
@@ -0,0 +1,5 @@
+{
+ "rules": {
+ "legacy": true
+ }
+}
diff --git a/@commitlint/core/fixtures/overriden-legacy/commitlint.config.js b/@commitlint/core/fixtures/overriden-legacy/commitlint.config.js
new file mode 100644
index 0000000000..0625ee6360
--- /dev/null
+++ b/@commitlint/core/fixtures/overriden-legacy/commitlint.config.js
@@ -0,0 +1,5 @@
+module.exports = {
+ rules: {
+ legacy: false
+ }
+};
diff --git a/@commitlint/core/fixtures/recursive-extends/commitlint.config.js b/@commitlint/core/fixtures/recursive-extends/commitlint.config.js
new file mode 100644
index 0000000000..7564fdc432
--- /dev/null
+++ b/@commitlint/core/fixtures/recursive-extends/commitlint.config.js
@@ -0,0 +1,6 @@
+module.exports = {
+ extends: ['./first-extended'],
+ rules: {
+ zero: 0
+ }
+};
diff --git a/@commitlint/core/fixtures/recursive-extends/first-extended/index.js b/@commitlint/core/fixtures/recursive-extends/first-extended/index.js
new file mode 100644
index 0000000000..4317428ad1
--- /dev/null
+++ b/@commitlint/core/fixtures/recursive-extends/first-extended/index.js
@@ -0,0 +1,6 @@
+module.exports = {
+ extends: ['./second-extended'],
+ rules: {
+ one: 1
+ }
+};
diff --git a/@commitlint/core/fixtures/recursive-extends/first-extended/second-extended/index.js b/@commitlint/core/fixtures/recursive-extends/first-extended/second-extended/index.js
new file mode 100644
index 0000000000..d199d354da
--- /dev/null
+++ b/@commitlint/core/fixtures/recursive-extends/first-extended/second-extended/index.js
@@ -0,0 +1,5 @@
+module.exports = {
+ rules: {
+ two: 2
+ }
+};
diff --git a/@commitlint/core/fixtures/trash-extend/commitlint.config.js b/@commitlint/core/fixtures/trash-extend/commitlint.config.js
new file mode 100644
index 0000000000..3b41dace2b
--- /dev/null
+++ b/@commitlint/core/fixtures/trash-extend/commitlint.config.js
@@ -0,0 +1,7 @@
+module.exports = {
+ extends: ['./one'],
+ zero: '0',
+ rules: {
+ zero: 0
+ }
+};
diff --git a/@commitlint/core/fixtures/trash-extend/one.js b/@commitlint/core/fixtures/trash-extend/one.js
new file mode 100644
index 0000000000..60f3a3530d
--- /dev/null
+++ b/@commitlint/core/fixtures/trash-extend/one.js
@@ -0,0 +1,6 @@
+module.exports = {
+ one: 1,
+ rules: {
+ one: 1
+ }
+};
diff --git a/@commitlint/core/fixtures/trash-file/commitlint.config.js b/@commitlint/core/fixtures/trash-file/commitlint.config.js
new file mode 100644
index 0000000000..a7a8e43bd8
--- /dev/null
+++ b/@commitlint/core/fixtures/trash-file/commitlint.config.js
@@ -0,0 +1,8 @@
+module.exports = {
+ foo: 'bar',
+ baz: 'bar',
+ rules: {
+ foo: 'bar',
+ baz: 'bar'
+ }
+};
diff --git a/@commitlint/core/package.json b/@commitlint/core/package.json
new file mode 100644
index 0000000000..03ead9dfa3
--- /dev/null
+++ b/@commitlint/core/package.json
@@ -0,0 +1,149 @@
+{
+ "name": "@commitlint/core",
+ "version": "3.0.1",
+ "description": "Lint your commit messages",
+ "main": "lib/index.js",
+ "scripts": {
+ "pretest": "dep-check",
+ "test": "ava -c 4",
+ "build": "cross-env NODE_ENV=production babel src --out-dir lib",
+ "clean": "rimraf lib",
+ "prepublish": "npm run build"
+ },
+ "ava": {
+ "files": [
+ "src/**/*.test.js",
+ "!lib/**/*"
+ ],
+ "source": [
+ "src/**/*.js",
+ "!lib/**/*"
+ ],
+ "babel": "inherit",
+ "require": [
+ "babel-register",
+ "babel-polyfill"
+ ]
+ },
+ "babel": {
+ "env": {
+ "development": {
+ "sourceMaps": "inline",
+ "plugins": [
+ "add-module-exports",
+ "istanbul",
+ [
+ "transform-runtime",
+ {
+ "polyfill": false,
+ "regenerator": true
+ }
+ ]
+ ]
+ },
+ "production": {
+ "ignore": [
+ "**/*.test.js"
+ ]
+ }
+ },
+ "presets": [
+ [
+ "env",
+ {
+ "targets": {
+ "node": 4
+ }
+ }
+ ],
+ "stage-0"
+ ],
+ "plugins": [
+ "add-module-exports",
+ [
+ "transform-runtime",
+ {
+ "polyfill": false,
+ "regenerator": true
+ }
+ ]
+ ]
+ },
+ "xo": false,
+ "nyc": {
+ "all": true,
+ "sourceMap": false,
+ "instrument": false,
+ "include": [
+ "source/**/*.js"
+ ]
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/marionebl/commitlint.git"
+ },
+ "bugs": {
+ "url": "https://github.com/marionebl/commitlint/issues"
+ },
+ "homepage": "https://github.com/marionebl/commitlint#readme",
+ "keywords": [
+ "conventional-changelog",
+ "commitlint",
+ "library",
+ "core"
+ ],
+ "author": {
+ "name": "Mario Nebl",
+ "email": "hello@herebecode.com"
+ },
+ "license": "MIT",
+ "devDependencies": {
+ "@commitlint/utils": "^3.0.0",
+ "ansi-styles": "3.1.0",
+ "ava": "0.18.2",
+ "babel-cli": "^6.18.0",
+ "babel-plugin-add-module-exports": "0.2.1",
+ "babel-plugin-istanbul": "4.1.3",
+ "babel-plugin-transform-runtime": "6.23.0",
+ "babel-polyfill": "^6.20.0",
+ "babel-preset-env": "^1.2.1",
+ "babel-preset-stage-0": "^6.16.0",
+ "babel-register": "6.24.1",
+ "conventional-changelog-cli": "1.2.0",
+ "conventional-recommended-bump": "0.3.0",
+ "cross-env": "^5.0.1",
+ "cz-conventional-changelog-lint": "0.1.3",
+ "denodeify": "1.2.1",
+ "dependency-check": "2.7.0",
+ "eslint-plugin-flow-check": "1.1.1",
+ "execa": "0.6.3",
+ "globby": "6.1.0",
+ "has-ansi": "3.0.0",
+ "import-from": "2.1.0",
+ "nyc": "10.3.2",
+ "path-exists": "3.0.0",
+ "resolve-from": "3.0.0",
+ "rimraf": "2.6.1",
+ "xo": "0.18.2"
+ },
+ "dependencies": {
+ "babel-runtime": "^6.23.0",
+ "chalk": "^2.0.1",
+ "conventional-changelog-angular": "^1.3.3",
+ "conventional-commits-parser": "^1.3.0",
+ "franc": "^2.0.0",
+ "git-raw-commits": "^1.1.2",
+ "git-toplevel": "^1.1.1",
+ "import-from": "^2.1.0",
+ "lodash": "^4.17.4",
+ "mz": "^2.6.0",
+ "path-exists": "^3.0.0",
+ "pos": "^0.4.2",
+ "rc": "^1.1.7",
+ "resolve-from": "^3.0.0",
+ "semver": "^5.3.0"
+ }
+}
diff --git a/@commitlint/core/readme.md b/@commitlint/core/readme.md
new file mode 100644
index 0000000000..0dca919199
--- /dev/null
+++ b/@commitlint/core/readme.md
@@ -0,0 +1,11 @@
+> Lint commit messages
+
+# @commitlint/core
+
+## Getting started
+
+```shell
+npm install --save-dev @commitlint/core
+```
+
+Consult [docs/api](../../docs/api) for comprehensive documentation.
diff --git a/source/library/format.js b/@commitlint/core/src/format.js
similarity index 93%
rename from source/library/format.js
rename to @commitlint/core/src/format.js
index c8da5c6a7c..781e8b15ea 100644
--- a/source/library/format.js
+++ b/@commitlint/core/src/format.js
@@ -12,7 +12,7 @@ export default function format(report = {}, options = {}) {
const sign = signs[problem.level] || '';
const color = colors[problem.level] || 'white';
const decoration = enabled ? chalk[color](sign) : sign;
- const name = chalk.grey(`[${problem.name}]`);
+ const name = enabled ? chalk.grey(`[${problem.name}]`) : `[${problem.name}]`;
return `${decoration} ${problem.message} ${name}`;
});
diff --git a/source/library/format.test.js b/@commitlint/core/src/format.test.js
similarity index 100%
rename from source/library/format.test.js
rename to @commitlint/core/src/format.test.js
diff --git a/@commitlint/core/src/index.js b/@commitlint/core/src/index.js
new file mode 100644
index 0000000000..fa9b907951
--- /dev/null
+++ b/@commitlint/core/src/index.js
@@ -0,0 +1,6 @@
+import format from './format';
+import lint from './lint';
+import load from './load';
+import read from './read';
+
+export {format, load, read, lint};
diff --git a/@commitlint/core/src/index.test.js b/@commitlint/core/src/index.test.js
new file mode 100644
index 0000000000..98b83f2cc7
--- /dev/null
+++ b/@commitlint/core/src/index.test.js
@@ -0,0 +1,21 @@
+import test from 'ava';
+
+test('exports format method', t => {
+ const {format} = require('.');
+ t.is(typeof format, 'function');
+});
+
+test('exports lint method', t => {
+ const {lint} = require('.');
+ t.is(typeof lint, 'function');
+});
+
+test('exports load method', t => {
+ const {load} = require('.');
+ t.is(typeof load, 'function');
+});
+
+test('exports read method', t => {
+ const {read} = require('.');
+ t.is(typeof read, 'function');
+});
diff --git a/source/library/ensure-case.js b/@commitlint/core/src/library/ensure-case.js
similarity index 100%
rename from source/library/ensure-case.js
rename to @commitlint/core/src/library/ensure-case.js
diff --git a/source/library/ensure-case.test.js b/@commitlint/core/src/library/ensure-case.test.js
similarity index 100%
rename from source/library/ensure-case.test.js
rename to @commitlint/core/src/library/ensure-case.test.js
diff --git a/source/library/ensure-enum.js b/@commitlint/core/src/library/ensure-enum.js
similarity index 100%
rename from source/library/ensure-enum.js
rename to @commitlint/core/src/library/ensure-enum.js
diff --git a/source/library/ensure-enum.test.js b/@commitlint/core/src/library/ensure-enum.test.js
similarity index 100%
rename from source/library/ensure-enum.test.js
rename to @commitlint/core/src/library/ensure-enum.test.js
diff --git a/source/library/ensure-language.js b/@commitlint/core/src/library/ensure-language.js
similarity index 100%
rename from source/library/ensure-language.js
rename to @commitlint/core/src/library/ensure-language.js
diff --git a/source/library/ensure-language.test.js b/@commitlint/core/src/library/ensure-language.test.js
similarity index 100%
rename from source/library/ensure-language.test.js
rename to @commitlint/core/src/library/ensure-language.test.js
diff --git a/source/library/ensure-max-length.js b/@commitlint/core/src/library/ensure-max-length.js
similarity index 100%
rename from source/library/ensure-max-length.js
rename to @commitlint/core/src/library/ensure-max-length.js
diff --git a/source/library/ensure-max-length.test.js b/@commitlint/core/src/library/ensure-max-length.test.js
similarity index 100%
rename from source/library/ensure-max-length.test.js
rename to @commitlint/core/src/library/ensure-max-length.test.js
diff --git a/source/library/ensure-min-length.js b/@commitlint/core/src/library/ensure-min-length.js
similarity index 100%
rename from source/library/ensure-min-length.js
rename to @commitlint/core/src/library/ensure-min-length.js
diff --git a/source/library/ensure-min-length.test.js b/@commitlint/core/src/library/ensure-min-length.test.js
similarity index 100%
rename from source/library/ensure-min-length.test.js
rename to @commitlint/core/src/library/ensure-min-length.test.js
diff --git a/source/library/ensure-not-empty.js b/@commitlint/core/src/library/ensure-not-empty.js
similarity index 100%
rename from source/library/ensure-not-empty.js
rename to @commitlint/core/src/library/ensure-not-empty.js
diff --git a/source/library/ensure-not-empty.test.js b/@commitlint/core/src/library/ensure-not-empty.test.js
similarity index 100%
rename from source/library/ensure-not-empty.test.js
rename to @commitlint/core/src/library/ensure-not-empty.test.js
diff --git a/source/library/ensure-tense.js b/@commitlint/core/src/library/ensure-tense.js
similarity index 95%
rename from source/library/ensure-tense.js
rename to @commitlint/core/src/library/ensure-tense.js
index 872d832a2c..158f5f247d 100644
--- a/source/library/ensure-tense.js
+++ b/@commitlint/core/src/library/ensure-tense.js
@@ -1,4 +1,5 @@
import {Lexer, Tagger} from 'pos';
+import {entries} from 'lodash';
const lexer = new Lexer();
const tagger = new Tagger();
@@ -49,7 +50,7 @@ export default (input, allowed, options = {}) => {
.filter(Boolean)
.map(verb => {
const [lemma, tag] = verb;
- const tense = Object.entries(tenses)
+ const tense = entries(tenses)
.filter(item => {
const [, tags] = item;
return tags.indexOf(tag) > -1;
diff --git a/source/library/ensure-tense.test.js b/@commitlint/core/src/library/ensure-tense.test.js
similarity index 100%
rename from source/library/ensure-tense.test.js
rename to @commitlint/core/src/library/ensure-tense.test.js
diff --git a/source/library/execute-rule.js b/@commitlint/core/src/library/execute-rule.js
similarity index 100%
rename from source/library/execute-rule.js
rename to @commitlint/core/src/library/execute-rule.js
diff --git a/source/library/execute-rule.test.js b/@commitlint/core/src/library/execute-rule.test.js
similarity index 100%
rename from source/library/execute-rule.test.js
rename to @commitlint/core/src/library/execute-rule.test.js
diff --git a/source/library/is-ignored.js b/@commitlint/core/src/library/is-ignored.js
similarity index 91%
rename from source/library/is-ignored.js
rename to @commitlint/core/src/library/is-ignored.js
index 97df674b59..df6f4d88d0 100644
--- a/source/library/is-ignored.js
+++ b/@commitlint/core/src/library/is-ignored.js
@@ -4,7 +4,7 @@ const WILDCARDS = [
c => c.match(/^(Merge pull request)|(Merge (.*?) into (.*?)|(Merge branch (.*?))$)/),
c => c.match(/^(R|r)evert (.*)/),
c => c.match(/^(fixup|squash)!/),
- c => semver.valid(c)
+ c => semver.valid(c.trim())
];
export default function isIgnored(commit = '') {
diff --git a/source/library/is-ignored.test.js b/@commitlint/core/src/library/is-ignored.test.js
similarity index 96%
rename from source/library/is-ignored.test.js
rename to @commitlint/core/src/library/is-ignored.test.js
index 132bb0263a..13fb2e1bab 100644
--- a/source/library/is-ignored.test.js
+++ b/@commitlint/core/src/library/is-ignored.test.js
@@ -39,6 +39,8 @@ test('should return true for npm version commits', t => {
t.true(isIgnored(`0.0.1-some-crazy-tag.0`));
t.true(isIgnored(`0.0.1-some-crazy-tag.999`));
t.true(isIgnored(`0.0.1-1e69d54`));
+ t.true(isIgnored(`v0.0.1`));
+ t.true(isIgnored(` v3.0.0`));
});
test('should return true fixup commits', t => {
diff --git a/@commitlint/core/src/library/parse.js b/@commitlint/core/src/library/parse.js
new file mode 100644
index 0000000000..c86262b582
--- /dev/null
+++ b/@commitlint/core/src/library/parse.js
@@ -0,0 +1,17 @@
+import {sync} from 'conventional-commits-parser';
+
+export default parse;
+
+async function parse(message, parser = sync) {
+ // Prevent conventional-changelog-angular from spamming startup
+ // TODO: Remove when https://github.com/conventional-changelog/conventional-changelog/pull/206 lands
+ const _error = console.error;
+ console.error = () => {};
+ const opts = require('conventional-changelog-angular');
+ console.error = _error;
+
+ const {parserOpts} = await opts;
+ const parsed = parser(message, parserOpts);
+ parsed.raw = message;
+ return parsed;
+}
diff --git a/@commitlint/core/src/library/parse.test.js b/@commitlint/core/src/library/parse.test.js
new file mode 100644
index 0000000000..03382ce84d
--- /dev/null
+++ b/@commitlint/core/src/library/parse.test.js
@@ -0,0 +1,79 @@
+import test from 'ava';
+import parse from './parse';
+
+test('throws when called without params', t => {
+ t.throws(parse(), /Expected a raw commit/);
+});
+
+test('throws when called with empty message', t => {
+ t.throws(parse(''), /Expected a raw commit/);
+});
+
+test('returns object with raw message', async t => {
+ const message = 'type(scope): subject';
+ const actual = await parse(message);
+ t.is(actual.raw, message);
+});
+
+test('calls parser with message and passed options', t => {
+ const message = 'message';
+
+ parse(message, m => {
+ t.is(message, m);
+ return {};
+ });
+});
+
+test('passes object up from parser function', async t => {
+ const message = 'message';
+ const result = {};
+ const actual = await parse(message, () => result);
+ t.is(actual, result);
+});
+
+test('returns object with expected keys', async t => {
+ const message = 'message';
+ const actual = await parse(message);
+ const expected = {
+ body: null,
+ footer: null,
+ header: 'message',
+ mentions: [],
+ merge: null,
+ notes: [],
+ raw: 'message',
+ references: [],
+ revert: null,
+ scope: null,
+ subject: null,
+ type: null
+ };
+ t.deepEqual(actual, expected);
+});
+
+test('uses angular grammar', async t => {
+ const message = 'type(scope): subject';
+ const actual = await parse(message);
+ const expected = {
+ body: null,
+ footer: null,
+ header: 'type(scope): subject',
+ mentions: [],
+ merge: null,
+ notes: [],
+ raw: 'type(scope): subject',
+ references: [],
+ revert: null,
+ scope: 'scope',
+ subject: 'subject',
+ type: 'type'
+ };
+ t.deepEqual(actual, expected);
+});
+
+test('supports scopes with /', async t => {
+ const message = 'type(some/scope): subject';
+ const actual = await parse(message);
+ t.is(actual.scope, 'some/scope');
+ t.is(actual.subject, 'subject');
+});
diff --git a/@commitlint/core/src/library/resolve-extends.js b/@commitlint/core/src/library/resolve-extends.js
new file mode 100644
index 0000000000..a97cc3879e
--- /dev/null
+++ b/@commitlint/core/src/library/resolve-extends.js
@@ -0,0 +1,62 @@
+import path from 'path';
+import from from 'resolve-from';
+import {merge, omit} from 'lodash';
+
+// Resolve extend configs
+export default function resolveExtends(config = {}, context = {}) {
+ const {extends: e} = config;
+ const extended = loadExtends(config, context)
+ .reduceRight((r, c) => merge(r, omit(c, 'extends')), e ? {extends: e} : {});
+
+ // Remove deprecation warning in version 3
+ if (typeof config === 'object' && 'wildcards' in config) {
+ console.warn(`'wildcards' found in top-level configuration ignored. Remove them from your config to silence this warning.`);
+ }
+
+ return merge({}, extended, config);
+}
+
+// (any, string, string, Function) => any[];
+function loadExtends(config = {}, context = {}) {
+ return (config.extends || []).reduce((configs, raw) => {
+ const load = context.require || require;
+ const resolved = resolveConfig(raw, context);
+ const c = load(resolved);
+
+ // Remove deprecation warning in version 3
+ if (typeof c === 'object' && 'wildcards' in c) {
+ console.warn(`'wildcards' found in '${raw}' ignored. To silence this warning raise an issue at 'npm repo ${raw}' to remove the wildcards.`);
+ }
+
+ const ctx = merge({}, context, {
+ cwd: path.dirname(resolved)
+ });
+
+ return [...configs, c, ...loadExtends(c, ctx)];
+ }, []);
+}
+
+function getId(raw = '', prefix = '') {
+ const first = raw.charAt(0);
+ const scoped = first === '@';
+ const relative = first === '.';
+ return (scoped || relative) ? raw : [prefix, raw].filter(String).join('-');
+}
+
+function resolveConfig(raw, context = {}) {
+ const resolve = context.resolve || resolveId;
+ const id = getId(raw, context.prefix);
+
+ try {
+ return resolve(id, context);
+ } catch (err) {
+ const legacy = getId(raw, 'conventional-changelog-lint-config');
+ const resolved = resolve(legacy, context);
+ console.warn(`Resolving ${raw} to legacy config ${legacy}. To silence this warning raise an issue at 'npm repo ${legacy}' to rename to ${id}.`);
+ return resolved;
+ }
+}
+
+function resolveId(id, context = {}) {
+ return from(context.cwd || process.cwd(), id);
+}
diff --git a/@commitlint/core/src/library/resolve-extends.test.js b/@commitlint/core/src/library/resolve-extends.test.js
new file mode 100644
index 0000000000..1ca4005062
--- /dev/null
+++ b/@commitlint/core/src/library/resolve-extends.test.js
@@ -0,0 +1,200 @@
+import test from 'ava';
+import resolveExtends from './resolve-extends';
+
+const id = id => id;
+
+test('returns empty object when called without params', t => {
+ const actual = resolveExtends();
+ t.deepEqual(actual, {});
+});
+
+test('returns an equivalent object as passed in', t => {
+ const expected = {foo: 'bar'};
+ const actual = resolveExtends(expected);
+ t.deepEqual(actual, expected);
+});
+
+test('uses empty prefix by default', t => {
+ const input = {extends: ['extender-name']};
+
+ resolveExtends(input, {
+ resolve: id,
+ require(id) {
+ t.is(id, 'extender-name');
+ }
+ });
+});
+
+test('uses prefix as configured', t => {
+ const input = {extends: ['extender-name']};
+
+ resolveExtends(input, {
+ prefix: 'prefix',
+ resolve: id,
+ require(id) {
+ t.is(id, 'prefix-extender-name');
+ }
+ });
+});
+
+test('ignores prefix for scoped extends', t => {
+ const input = {extends: ['@scope/extender-name']};
+
+ resolveExtends(input, {
+ prefix: 'prefix',
+ resolve: id,
+ require(id) {
+ t.is(id, '@scope/extender-name');
+ }
+ });
+});
+
+test('ignores prefix for relative extends', t => {
+ const input = {extends: ['./extender']};
+
+ resolveExtends(input, {
+ prefix: 'prefix',
+ resolve: id,
+ require(id) {
+ t.is(id, './extender');
+ }
+ });
+});
+
+test('propagates return value of require function', t => {
+ const input = {extends: ['extender-name']};
+ const propagated = {foo: 'bar'};
+
+ const actual = resolveExtends(input, {
+ resolve: id,
+ require() {
+ return propagated;
+ }
+ });
+
+ t.is(actual.foo, 'bar');
+});
+
+test('resolves extends recursively', t => {
+ const input = {extends: ['extender-name']};
+ const actual = [];
+
+ resolveExtends(input, {
+ resolve: id,
+ require(id) {
+ actual.push(id);
+ if (id === 'extender-name') {
+ return {extends: ['recursive-extender-name']};
+ }
+ if (id === 'recursive-extender-name') {
+ return {foo: 'bar'};
+ }
+ }
+ });
+
+ t.deepEqual(actual, ['extender-name', 'recursive-extender-name']);
+});
+
+test('uses prefix key recursively', t => {
+ const input = {extends: ['extender-name']};
+ const actual = [];
+
+ resolveExtends(input, {
+ prefix: 'prefix',
+ resolve: id,
+ require(id) {
+ actual.push(id);
+ if (id === 'prefix-extender-name') {
+ return {extends: ['recursive-extender-name']};
+ }
+ if (id === 'prefix-recursive-extender-name') {
+ return {foo: 'bar'};
+ }
+ }
+ });
+
+ t.deepEqual(actual, ['prefix-extender-name', 'prefix-recursive-extender-name']);
+});
+
+test('propagates contents recursively', t => {
+ const input = {extends: ['extender-name']};
+
+ const actual = resolveExtends(input, {
+ resolve: id,
+ require(id) {
+ if (id === 'extender-name') {
+ return {extends: ['recursive-extender-name'], foo: 'bar'};
+ }
+ if (id === 'recursive-extender-name') {
+ return {baz: 'bar'};
+ }
+ }
+ });
+
+ const expected = {
+ extends: ['extender-name'],
+ foo: 'bar',
+ baz: 'bar'
+ };
+
+ t.deepEqual(actual, expected);
+});
+
+test('extending contents should take precedence', t => {
+ const input = {extends: ['extender-name'], zero: 'root'};
+
+ const actual = resolveExtends(input, {
+ resolve: id,
+ require(id) {
+ if (id === 'extender-name') {
+ return {extends: ['recursive-extender-name'], zero: id, one: id};
+ }
+ if (id === 'recursive-extender-name') {
+ return {extends: ['second-recursive-extender-name'], zero: id, one: id, two: id};
+ }
+ if (id === 'second-recursive-extender-name') {
+ return {zero: id, one: id, two: id, three: id};
+ }
+ }
+ });
+
+ const expected = {
+ extends: ['extender-name'],
+ zero: 'root',
+ one: 'extender-name',
+ two: 'recursive-extender-name',
+ three: 'second-recursive-extender-name'
+ };
+
+ t.deepEqual(actual, expected);
+});
+
+test('should fall back to conventional-changelog-lint-config prefix', t => {
+ const input = {extends: ['extender-name']};
+
+ const actual = resolveExtends(input, {
+ prefix: 'prefix',
+ resolve(id) {
+ if (id === 'conventional-changelog-lint-config-extender-name') {
+ return 'conventional-changelog-lint-config-extender-name';
+ }
+ throw new Error(`Could not find module "*${id}"`);
+ },
+ require(id) {
+ if (id === 'conventional-changelog-lint-config-extender-name') {
+ return {
+ rules: {
+ fallback: true
+ }
+ };
+ }
+ }
+ });
+
+ t.deepEqual(actual, {
+ extends: ['extender-name'],
+ rules: {
+ fallback: true
+ }
+ });
+});
diff --git a/source/index.js b/@commitlint/core/src/lint.js
similarity index 64%
rename from source/index.js
rename to @commitlint/core/src/lint.js
index a4af1a0e7f..e29c394893 100644
--- a/source/index.js
+++ b/@commitlint/core/src/lint.js
@@ -1,16 +1,9 @@
-import ruleFunctions from './rules';
-import format from './library/format';
-import getConfiguration from './library/get-configuration';
-import getMessages from './library/get-messages';
-import getPreset from './library/get-preset';
+import {entries} from 'lodash';
import isIgnored from './library/is-ignored';
import parse from './library/parse';
+import implementations from './rules';
-export {format, getConfiguration, getMessages, getPreset};
-
-export default async (message, options = {}) => {
- const {configuration} = options;
-
+export default async (message, rules = {}) => {
// Found a wildcard match, skip
if (isIgnored(message)) {
return {
@@ -21,10 +14,10 @@ export default async (message, options = {}) => {
}
// Parse the commit message
- const parsed = parse(message);
+ const parsed = await parse(message);
// Validate against all rules
- const results = Object.entries(configuration.rules)
+ const results = entries(rules)
.filter(entry => {
const [, [level]] = entry;
return level > 0;
@@ -38,7 +31,7 @@ export default async (message, options = {}) => {
return null;
}
- const rule = ruleFunctions[name];
+ const rule = implementations[name];
const [valid, message] = rule(parsed, when, value);
return {
diff --git a/@commitlint/core/src/lint.test.js b/@commitlint/core/src/lint.test.js
new file mode 100644
index 0000000000..750e8cf739
--- /dev/null
+++ b/@commitlint/core/src/lint.test.js
@@ -0,0 +1,36 @@
+import test from 'ava';
+import lint from './lint';
+
+test('throws without params', t => {
+ t.throws(lint());
+});
+
+test('throws with empty message', t => {
+ t.throws(lint(''));
+});
+
+test('positive on stub message and no rule', async t => {
+ const actual = await lint('foo: bar');
+ t.true(actual.valid);
+});
+
+test('positive on stub message and adhered rule', async t => {
+ const actual = await lint('foo: bar', {
+ 'type-enum': [2, 'always', ['foo']]
+ });
+ t.true(actual.valid);
+});
+
+test('negative on stub message and broken rule', async t => {
+ const actual = await lint('foo: bar', {
+ 'type-enum': [2, 'never', ['foo']]
+ });
+ t.false(actual.valid);
+});
+
+test('positive on ignored message and broken rule', async t => {
+ const actual = await lint('Revert "some bogus commit"', {
+ 'type-empty': [2, 'never']
+ });
+ t.true(actual.valid);
+});
diff --git a/@commitlint/core/src/load.js b/@commitlint/core/src/load.js
new file mode 100644
index 0000000000..7c7668e43b
--- /dev/null
+++ b/@commitlint/core/src/load.js
@@ -0,0 +1,88 @@
+import path from 'path';
+import importFrom from 'import-from';
+import {entries, merge, mergeWith, pick} from 'lodash';
+import rc from 'rc';
+import resolveFrom from 'resolve-from';
+
+import resolveExtends from './library/resolve-extends';
+import executeRule from './library/execute-rule';
+
+const w = (a, b) => Array.isArray(b) ? b : undefined;
+const valid = input => pick(input, 'extends', 'rules');
+
+export default async (seed = {}) => {
+ // Obtain config from .rc files
+ const raw = file();
+
+ // Merge passed config with file based options
+ const config = valid(merge(raw, seed));
+ const opts = merge({extends: [], rules: {}}, pick(config, 'extends'));
+
+ // Resolve extends key
+ const extended = resolveExtends(opts, {
+ prefix: 'commitlint-config',
+ cwd: raw.config ? path.dirname(raw.config) : process.cwd()
+ });
+
+ const preset = valid(mergeWith({}, extended, config, w));
+
+ // Execute rule config functions if needed
+ const executed = await Promise.all(['rules']
+ .map(key => {
+ return [key, preset[key]];
+ })
+ .map(async item => {
+ const [key, value] = item;
+ const executedValue = await Promise.all(
+ entries(value || {})
+ .map(entry => executeRule(entry))
+ );
+ return [key, executedValue.reduce((registry, item) => {
+ const [key, value] = item;
+ return {
+ ...registry,
+ [key]: value
+ };
+ }, {})];
+ }));
+
+ // Merge executed config keys into preset
+ return executed.reduce((registry, item) => {
+ const [key, value] = item;
+ return {
+ ...registry,
+ [key]: value
+ };
+ }, preset);
+};
+
+function file() {
+ const legacy = rc('conventional-changelog-lint');
+ const legacyFound = typeof legacy.config === 'string';
+
+ const found = resolveable('./commitlint.config');
+ const raw = found ? importFrom(process.cwd(), './commitlint.config') : {};
+
+ if (legacyFound && !found) {
+ console.warn(`Using legacy ${path.relative(process.cwd(), legacy.config)}. Rename to commitlint.config.js to silence this warning.`);
+ }
+
+ if (legacyFound && found) {
+ console.warn(`Ignored legacy ${path.relative(process.cwd(), legacy.config)} as commitlint.config.js superseeds it. Remove .conventional-changelog-lintrc to silence this warning.`);
+ }
+
+ if (found) {
+ return raw;
+ }
+
+ return legacy;
+}
+
+function resolveable(id) {
+ try {
+ resolveFrom(process.cwd(), id);
+ return true;
+ } catch (err) {
+ return false;
+ }
+}
diff --git a/@commitlint/core/src/load.test.js b/@commitlint/core/src/load.test.js
new file mode 100644
index 0000000000..5b955a99f3
--- /dev/null
+++ b/@commitlint/core/src/load.test.js
@@ -0,0 +1,104 @@
+import path from 'path';
+import test from 'ava';
+
+import load from './load';
+
+const cwd = process.cwd();
+
+test.afterEach.always(t => {
+ t.context.back();
+});
+
+test('extends-empty should have no rules', async t => {
+ t.context.back = chdir('fixtures/extends-empty');
+ const actual = await load();
+ t.deepEqual(actual.rules, {});
+});
+
+test('uses seed as configured', async t => {
+ t.context.back = chdir('fixtures/extends-empty');
+ const actual = await load({rules: {foo: 'bar'}});
+ t.is(actual.rules.foo, 'bar');
+});
+
+test('invalid extend should throw', t => {
+ t.context.back = chdir('fixtures/extends-invalid');
+ t.throws(load());
+});
+
+test('empty file should have no rules', async t => {
+ t.context.back = chdir('fixtures/empty-object-file');
+ const actual = await load();
+ t.deepEqual(actual.rules, {});
+});
+
+test('empty file should extend nothing', async t => {
+ t.context.back = chdir('fixtures/empty-file');
+ const actual = await load();
+ t.deepEqual(actual.extends, []);
+});
+
+test('recursive extends', async t => {
+ t.context.back = chdir('fixtures/recursive-extends');
+ const actual = await load();
+ t.deepEqual(actual, {
+ extends: ['./first-extended'],
+ rules: {
+ zero: 0,
+ one: 1,
+ two: 2
+ }
+ });
+});
+
+test('ignores unknow keys', async t => {
+ t.context.back = chdir('fixtures/trash-file');
+ const actual = await load();
+ t.deepEqual(actual, {
+ extends: [],
+ rules: {
+ foo: 'bar',
+ baz: 'bar'
+ }
+ });
+});
+
+test('ignores unknow keys recursively', async t => {
+ t.context.back = chdir('fixtures/trash-extend');
+ const actual = await load();
+ t.deepEqual(actual, {
+ extends: ['./one'],
+ rules: {
+ zero: 0,
+ one: 1
+ }
+ });
+});
+
+test('supports legacy .conventional-changelog-lintrc', async t => {
+ t.context.back = chdir('fixtures/legacy');
+ const actual = await load();
+ t.deepEqual(actual, {
+ extends: [],
+ rules: {
+ legacy: true
+ }
+ });
+});
+
+test('commitlint.config.js overrides .conventional-changelog-lintrc', async t => {
+ t.context.back = chdir('fixtures/overriden-legacy');
+ const actual = await load();
+ t.deepEqual(actual, {
+ extends: [],
+ rules: {
+ legacy: false
+ }
+ });
+});
+
+function chdir(target) {
+ const to = path.resolve(cwd, target.split('/').join(path.sep));
+ process.chdir(to);
+ return () => process.chdir(cwd);
+}
diff --git a/source/library/get-messages.js b/@commitlint/core/src/read.js
similarity index 97%
rename from source/library/get-messages.js
rename to @commitlint/core/src/read.js
index c9557aa666..3d284a631c 100644
--- a/source/library/get-messages.js
+++ b/@commitlint/core/src/read.js
@@ -29,7 +29,7 @@ async function getCommitMessages(settings) {
}
// Get commit messages from history
-// Object => Promise>
+// Object => Promise
function getHistoryCommits(options) {
return new Promise((resolve, reject) => {
const data = [];
diff --git a/source/library/get-messages.test.js b/@commitlint/core/src/read.test.js
similarity index 89%
rename from source/library/get-messages.test.js
rename to @commitlint/core/src/read.test.js
index 4be06cca50..e05f097fa0 100644
--- a/source/library/get-messages.test.js
+++ b/@commitlint/core/src/read.test.js
@@ -9,8 +9,8 @@ import {mkdir, writeFile} from 'mz/fs';
import exists from 'path-exists';
import rimraf from 'rimraf';
-import pkg from '../../package';
-import getMessages from './get-messages';
+import pkg from '../package';
+import read from './read';
const rm = denodeify(rimraf);
@@ -32,7 +32,7 @@ test.serial('get edit commit message from git root', async t => {
await execa('git', ['add', '.']);
await execa('git', ['commit', '-m', 'alpha']);
const expected = ['alpha\n\n'];
- const actual = await getMessages({edit: true});
+ const actual = await read({edit: true});
t.deepEqual(actual, expected);
});
@@ -44,7 +44,7 @@ test.serial('get history commit messages', async t => {
await execa('git', ['commit', '-m', 'remove alpha']);
const expected = ['remove alpha\n\n', 'alpha\n\n'];
- const actual = await getMessages({});
+ const actual = await read({});
t.deepEqual(actual, expected);
});
@@ -56,11 +56,11 @@ test.serial('get edit commit message from git subdirectory', async t => {
await execa('git', ['commit', '-m', 'beta']);
const expected = ['beta\n\n'];
- const actual = await getMessages({edit: true});
+ const actual = await read({edit: true});
t.deepEqual(actual, expected);
});
-test.serial('get history commit messages from shallow clone', async t => {
+test.serial.failing('get history commit messages from shallow clone', async t => {
const [repo] = t.context.repos;
await writeFile('alpha.txt', 'alpha');
@@ -70,7 +70,7 @@ test.serial('get history commit messages from shallow clone', async t => {
const clone = await cloneRepository(pkg.repository.url, repo, '--depth', '1');
t.context.repos = [...t.context.repos, clone];
- const err = await t.throws(getMessages({from: 'master'}));
+ const err = await t.throws(read({from: 'master'}));
t.true(err.message.indexOf('Could not get git history from shallow clone') > -1);
});
diff --git a/source/rules/body-case.js b/@commitlint/core/src/rules/body-case.js
similarity index 100%
rename from source/rules/body-case.js
rename to @commitlint/core/src/rules/body-case.js
diff --git a/@commitlint/core/src/rules/body-case.test.js b/@commitlint/core/src/rules/body-case.test.js
new file mode 100644
index 0000000000..d4b4f920f9
--- /dev/null
+++ b/@commitlint/core/src/rules/body-case.test.js
@@ -0,0 +1,89 @@
+import test from 'ava';
+import parse from '../library/parse';
+import bodyCase from './body-case';
+
+const messages = {
+ empty: 'chore: subject',
+ lowercase: 'chore: subject\nbody',
+ mixedcase: 'chore: subject\nBody',
+ uppercase: 'chore: subject\nBODY'
+};
+
+const parsed = {
+ empty: parse(messages.empty),
+ lowercase: parse(messages.lowercase),
+ mixedcase: parse(messages.mixedcase),
+ uppercase: parse(messages.uppercase)
+};
+
+test('with empty body should succeed for "never lowercase"', async t => {
+ const [actual] = bodyCase(await parsed.empty, 'never', 'lowercase');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with empty body should succeed for "always lowercase"', async t => {
+ const [actual] = bodyCase(await parsed.empty, 'always', 'lowercase');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with empty body should succeed for "never uppercase"', async t => {
+ const [actual] = bodyCase(await parsed.empty, 'never', 'uppercase');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with empty body should succeed for "always uppercase"', async t => {
+ const [actual] = bodyCase(await parsed.empty, 'always', 'uppercase');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with lowercase body should fail for "never lowercase"', async t => {
+ const [actual] = bodyCase(await parsed.lowercase, 'never', 'lowercase');
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('with lowercase body should succeed for "always lowercase"', async t => {
+ const [actual] = bodyCase(await parsed.lowercase, 'always', 'lowercase');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with mixedcase body should succeed for "never lowercase"', async t => {
+ const [actual] = bodyCase(await parsed.mixedcase, 'never', 'lowercase');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with mixedcase body should fail for "always lowercase"', async t => {
+ const [actual] = bodyCase(await parsed.mixedcase, 'always', 'lowercase');
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('with mixedcase body should succeed for "never uppercase"', async t => {
+ const [actual] = bodyCase(await parsed.mixedcase, 'never', 'uppercase');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with mixedcase body should fail for "always uppercase"', async t => {
+ const [actual] = bodyCase(await parsed.mixedcase, 'always', 'uppercase');
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('with uppercase body should fail for "never uppercase"', async t => {
+ const [actual] = bodyCase(await parsed.uppercase, 'never', 'uppercase');
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('with lowercase body should succeed for "always uppercase"', async t => {
+ const [actual] = bodyCase(await parsed.uppercase, 'always', 'uppercase');
+ const expected = true;
+ t.is(actual, expected);
+});
diff --git a/source/rules/body-empty.js b/@commitlint/core/src/rules/body-empty.js
similarity index 100%
rename from source/rules/body-empty.js
rename to @commitlint/core/src/rules/body-empty.js
diff --git a/@commitlint/core/src/rules/body-empty.test.js b/@commitlint/core/src/rules/body-empty.test.js
new file mode 100644
index 0000000000..e3b34ff1e9
--- /dev/null
+++ b/@commitlint/core/src/rules/body-empty.test.js
@@ -0,0 +1,49 @@
+import test from 'ava';
+import parse from '../library/parse';
+import bodyEmpty from './body-empty';
+
+const messages = {
+ empty: 'chore: subject',
+ filled: 'chore: subject\nbody'
+};
+
+const parsed = {
+ empty: parse(messages.empty),
+ filled: parse(messages.filled)
+};
+
+test('with empty body should succeed for empty keyword', async t => {
+ const [actual] = bodyEmpty(await parsed.empty);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with empty body should fail for "never"', async t => {
+ const [actual] = bodyEmpty(await parsed.empty, 'never');
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('with empty body should succeed for "always"', async t => {
+ const [actual] = bodyEmpty(await parsed.empty, 'always');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with body should fail for empty keyword', async t => {
+ const [actual] = bodyEmpty(await parsed.filled);
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('with body should succeed for "never"', async t => {
+ const [actual] = bodyEmpty(await parsed.filled, 'never');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with body should fail for "always"', async t => {
+ const [actual] = bodyEmpty(await parsed.filled, 'always');
+ const expected = false;
+ t.is(actual, expected);
+});
diff --git a/source/rules/body-leading-blank.js b/@commitlint/core/src/rules/body-leading-blank.js
similarity index 100%
rename from source/rules/body-leading-blank.js
rename to @commitlint/core/src/rules/body-leading-blank.js
diff --git a/source/rules/body-leading-blank.test.js b/@commitlint/core/src/rules/body-leading-blank.test.js
similarity index 55%
rename from source/rules/body-leading-blank.test.js
rename to @commitlint/core/src/rules/body-leading-blank.test.js
index 273888999c..233452eb11 100644
--- a/source/rules/body-leading-blank.test.js
+++ b/@commitlint/core/src/rules/body-leading-blank.test.js
@@ -14,56 +14,56 @@ const parsed = {
with: parse(messages.with)
};
-test('with simple message should succeed for empty keyword', t => {
- const [actual] = bodyLeadingBlank(parsed.simple);
+test('with simple message should succeed for empty keyword', async t => {
+ const [actual] = bodyLeadingBlank(await parsed.simple);
const expected = true;
t.is(actual, expected);
});
-test('with simple message should succeed for "never"', t => {
- const [actual] = bodyLeadingBlank(parsed.simple, 'never');
+test('with simple message should succeed for "never"', async t => {
+ const [actual] = bodyLeadingBlank(await parsed.simple, 'never');
const expected = true;
t.is(actual, expected);
});
-test('with simple message should succeed for "always"', t => {
- const [actual] = bodyLeadingBlank(parsed.simple, 'always');
+test('with simple message should succeed for "always"', async t => {
+ const [actual] = bodyLeadingBlank(await parsed.simple, 'always');
const expected = true;
t.is(actual, expected);
});
-test('without blank line before body should fail for empty keyword', t => {
- const [actual] = bodyLeadingBlank(parsed.without);
+test('without blank line before body should fail for empty keyword', async t => {
+ const [actual] = bodyLeadingBlank(await parsed.without);
const expected = false;
t.is(actual, expected);
});
-test('without blank line before body should succeed for "never"', t => {
- const [actual] = bodyLeadingBlank(parsed.without, 'never');
+test('without blank line before body should succeed for "never"', async t => {
+ const [actual] = bodyLeadingBlank(await parsed.without, 'never');
const expected = true;
t.is(actual, expected);
});
-test('without blank line before body should fail for "always"', t => {
- const [actual] = bodyLeadingBlank(parsed.without, 'always');
+test('without blank line before body should fail for "always"', async t => {
+ const [actual] = bodyLeadingBlank(await parsed.without, 'always');
const expected = false;
t.is(actual, expected);
});
-test('with blank line before body should succeed for empty keyword', t => {
- const [actual] = bodyLeadingBlank(parsed.with);
+test('with blank line before body should succeed for empty keyword', async t => {
+ const [actual] = bodyLeadingBlank(await parsed.with);
const expected = true;
t.is(actual, expected);
});
-test('with blank line before body should fail for "never"', t => {
- const [actual] = bodyLeadingBlank(parsed.with, 'never');
+test('with blank line before body should fail for "never"', async t => {
+ const [actual] = bodyLeadingBlank(await parsed.with, 'never');
const expected = false;
t.is(actual, expected);
});
-test('with blank line before body should succeed for "always"', t => {
- const [actual] = bodyLeadingBlank(parsed.with, 'always');
+test('with blank line before body should succeed for "always"', async t => {
+ const [actual] = bodyLeadingBlank(await parsed.with, 'always');
const expected = true;
t.is(actual, expected);
});
diff --git a/source/rules/body-max-length.js b/@commitlint/core/src/rules/body-max-length.js
similarity index 100%
rename from source/rules/body-max-length.js
rename to @commitlint/core/src/rules/body-max-length.js
diff --git a/source/rules/body-max-length.test.js b/@commitlint/core/src/rules/body-max-length.test.js
similarity index 64%
rename from source/rules/body-max-length.test.js
rename to @commitlint/core/src/rules/body-max-length.test.js
index 1d5f319356..3b19e73fbb 100644
--- a/source/rules/body-max-length.test.js
+++ b/@commitlint/core/src/rules/body-max-length.test.js
@@ -19,20 +19,20 @@ const parsed = {
long: parse(messages.long)
};
-test('with empty should succeed', t => {
- const [actual] = check(parsed.empty, '', value);
+test('with empty should succeed', async t => {
+ const [actual] = check(await parsed.empty, '', value);
const expected = true;
t.is(actual, expected);
});
-test('with short should succeed', t => {
- const [actual] = check(parsed.short, '', value);
+test('with short should succeed', async t => {
+ const [actual] = check(await parsed.short, '', value);
const expected = true;
t.is(actual, expected);
});
-test('with long should fail', t => {
- const [actual] = check(parsed.long, '', value);
+test('with long should fail', async t => {
+ const [actual] = check(await parsed.long, '', value);
const expected = false;
t.is(actual, expected);
});
diff --git a/source/rules/body-min-length.js b/@commitlint/core/src/rules/body-min-length.js
similarity index 100%
rename from source/rules/body-min-length.js
rename to @commitlint/core/src/rules/body-min-length.js
diff --git a/source/rules/body-min-length.test.js b/@commitlint/core/src/rules/body-min-length.test.js
similarity index 64%
rename from source/rules/body-min-length.test.js
rename to @commitlint/core/src/rules/body-min-length.test.js
index 63cc7a7b90..a85b996e68 100644
--- a/source/rules/body-min-length.test.js
+++ b/@commitlint/core/src/rules/body-min-length.test.js
@@ -19,20 +19,20 @@ const parsed = {
long: parse(messages.long)
};
-test('with simple should succeed', t => {
- const [actual] = check(parsed.simple, '', value);
+test('with simple should succeed', async t => {
+ const [actual] = check(await parsed.simple, '', value);
const expected = true;
t.is(actual, expected);
});
-test('with short should fail', t => {
- const [actual] = check(parsed.short, '', value);
+test('with short should fail', async t => {
+ const [actual] = check(await parsed.short, '', value);
const expected = false;
t.is(actual, expected);
});
-test('with long should succeed', t => {
- const [actual] = check(parsed.long, '', value);
+test('with long should succeed', async t => {
+ const [actual] = check(await parsed.long, '', value);
const expected = true;
t.is(actual, expected);
});
diff --git a/source/rules/body-tense.js b/@commitlint/core/src/rules/body-tense.js
similarity index 100%
rename from source/rules/body-tense.js
rename to @commitlint/core/src/rules/body-tense.js
diff --git a/@commitlint/core/src/rules/body-tense.test.js b/@commitlint/core/src/rules/body-tense.test.js
new file mode 100644
index 0000000000..922e5e3cee
--- /dev/null
+++ b/@commitlint/core/src/rules/body-tense.test.js
@@ -0,0 +1,114 @@
+import test from 'ava';
+import parse from '../library/parse';
+import footerTense from './body-tense';
+
+const messages = {
+ empty: 'chore: \n',
+ presentImperative: `chore: \nwe implement things`,
+ presentParticiple: `chore: \nimplementing things`,
+ presentThirdPerson: `chore: \nimplements things`,
+ past: `chore: \nwe did implement things`,
+ mixed: `chore: \nimplement, implementing, implements, implemented`
+};
+
+const parsed = {
+ empty: parse(messages.empty),
+ presentImperative: parse(messages.presentImperative),
+ presentParticiple: parse(messages.presentParticiple),
+ presentThirdPerson: parse(messages.presentImperative),
+ past: parse(messages.past),
+ mixed: parse(messages.mixed)
+};
+
+test('empty succeeds', async t => {
+ const [actual] = footerTense(await parsed.empty, '', ['present-imperative']);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('present succeeds "always present-imperative"', async t => {
+ const [actual] = footerTense(await parsed.presentImperative, 'always', ['present-imperative']);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('present fails "never present-imperative"', async t => {
+ const [actual] = footerTense(await parsed.presentImperative, 'never', ['present-imperative']);
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('present succeeds "always present-participle"', async t => {
+ const [actual] = footerTense(await parsed.presentParticiple, 'always', ['present-participle']);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('present fails "never present-participle"', async t => {
+ const [actual] = footerTense(await parsed.presentParticiple, 'never', ['present-participle']);
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('present succeeds "always present-third-person"', async t => {
+ const [actual] = footerTense(await parsed.presentThirdPerson, 'always', ['present-third-person']);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('present fails "never present-third-person"', async t => {
+ const [actual] = footerTense(await parsed.presentThirdPerson, 'never', ['present-third-person']);
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('past should succedd "always past-tense"', async t => {
+ const [actual] = footerTense(await parsed.past, 'always', ['past-tense']);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('past fails "never past-tense"', async t => {
+ const [actual] = footerTense(await parsed.past, 'never', ['past-tense']);
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('mixed fails "always present-third-person"', async t => {
+ const [actual] = footerTense(await parsed.mixed, 'always', ['present-third-person']);
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('mixed fails "always present-imperative"', async t => {
+ const [actual] = footerTense(await parsed.mixed, 'always', ['present-imperative']);
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('present fails "always present-participle"', async t => {
+ const [actual] = footerTense(await parsed.mixed, 'always', ['present-participle']);
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('mixed fails "always past-tense"', async t => {
+ const [actual] = footerTense(await parsed.mixed, 'always', ['past-tense']);
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('mixed succeeds "always present-third-person, present-imperative, present-participle, past-tense"', async t => {
+ const [actual] = footerTense(await parsed.mixed, 'always', ['present-third-person', 'present-imperative', 'present-participle', 'past-tense']);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('mixed succeeds "never allowed: present-third-person" and matching ignored: implements', async t => {
+ const [actual] = footerTense(await parsed.mixed, 'never', {
+ allowed: ['present-third-person'],
+ ignored: ['implements']
+ });
+ const expected = true;
+ t.is(actual, expected);
+});
diff --git a/source/rules/footer-empty.js b/@commitlint/core/src/rules/footer-empty.js
similarity index 100%
rename from source/rules/footer-empty.js
rename to @commitlint/core/src/rules/footer-empty.js
diff --git a/@commitlint/core/src/rules/footer-empty.test.js b/@commitlint/core/src/rules/footer-empty.test.js
new file mode 100644
index 0000000000..f5e074c5a9
--- /dev/null
+++ b/@commitlint/core/src/rules/footer-empty.test.js
@@ -0,0 +1,69 @@
+import test from 'ava';
+import parse from '../library/parse';
+import footerEmpty from './footer-empty';
+
+const messages = {
+ simple: 'chore: subject',
+ empty: 'chore: subject\nbody',
+ filled: 'chore: subject\nBREAKING CHANGE: something important'
+};
+
+const parsed = {
+ simple: parse(messages.simple),
+ empty: parse(messages.empty),
+ filled: parse(messages.filled)
+};
+
+test('with simple message should succeed for empty keyword', async t => {
+ const [actual] = footerEmpty(await parsed.simple);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with simple message should fail for "never"', async t => {
+ const [actual] = footerEmpty(await parsed.simple, 'never');
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('with simple message should succeed for "always"', async t => {
+ const [actual] = footerEmpty(await parsed.simple, 'always');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with empty footer should succeed for empty keyword', async t => {
+ const [actual] = footerEmpty(await parsed.empty);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with empty footer should fail for "never"', async t => {
+ const [actual] = footerEmpty(await parsed.empty, 'never');
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('with empty footer should succeed for "always"', async t => {
+ const [actual] = footerEmpty(await parsed.empty, 'always');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with footer should fail for empty keyword', async t => {
+ const [actual] = footerEmpty(await parsed.filled);
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('with footer should succeed for "never"', async t => {
+ const [actual] = footerEmpty(await parsed.filled, 'never');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with footer should fail for "always"', async t => {
+ const [actual] = footerEmpty(await parsed.filled, 'always');
+ const expected = false;
+ t.is(actual, expected);
+});
diff --git a/source/rules/footer-leading-blank.js b/@commitlint/core/src/rules/footer-leading-blank.js
similarity index 100%
rename from source/rules/footer-leading-blank.js
rename to @commitlint/core/src/rules/footer-leading-blank.js
diff --git a/source/rules/footer-leading-blank.test.js b/@commitlint/core/src/rules/footer-leading-blank.test.js
similarity index 51%
rename from source/rules/footer-leading-blank.test.js
rename to @commitlint/core/src/rules/footer-leading-blank.test.js
index 6fa76fb81b..e14211cc4e 100644
--- a/source/rules/footer-leading-blank.test.js
+++ b/@commitlint/core/src/rules/footer-leading-blank.test.js
@@ -20,110 +20,110 @@ const parsed = {
withMulitLine: parse(messages.withMulitLine)
};
-test('with simple message should succeed for empty keyword', t => {
- const [actual] = footerLeadingBlank(parsed.simple);
+test('with simple message should succeed for empty keyword', async t => {
+ const [actual] = footerLeadingBlank(await parsed.simple);
const expected = true;
t.is(actual, expected);
});
-test('with simple message should succeed for "never"', t => {
- const [actual] = footerLeadingBlank(parsed.simple, 'never');
+test('with simple message should succeed for "never"', async t => {
+ const [actual] = footerLeadingBlank(await parsed.simple, 'never');
const expected = true;
t.is(actual, expected);
});
-test('with simple message should succeed for "always"', t => {
- const [actual] = footerLeadingBlank(parsed.simple, 'always');
+test('with simple message should succeed for "always"', async t => {
+ const [actual] = footerLeadingBlank(await parsed.simple, 'always');
const expected = true;
t.is(actual, expected);
});
-test('with body message should succeed for empty keyword', t => {
- const [actual] = footerLeadingBlank(parsed.body);
+test('with body message should succeed for empty keyword', async t => {
+ const [actual] = footerLeadingBlank(await parsed.body);
const expected = true;
t.is(actual, expected);
});
-test('with body message should succeed for "never"', t => {
- const [actual] = footerLeadingBlank(parsed.body, 'never');
+test('with body message should succeed for "never"', async t => {
+ const [actual] = footerLeadingBlank(await parsed.body, 'never');
const expected = true;
t.is(actual, expected);
});
-test('with body message should succeed for "always"', t => {
- const [actual] = footerLeadingBlank(parsed.body, 'always');
+test('with body message should succeed for "always"', async t => {
+ const [actual] = footerLeadingBlank(await parsed.body, 'always');
const expected = true;
t.is(actual, expected);
});
-test('with trailing message should succeed for empty keyword', t => {
- const [actual] = footerLeadingBlank(parsed.trailing);
+test('with trailing message should succeed for empty keyword', async t => {
+ const [actual] = footerLeadingBlank(await parsed.trailing);
const expected = true;
t.is(actual, expected);
});
-test('with trailing message should succeed for "never"', t => {
- const [actual] = footerLeadingBlank(parsed.trailing, 'never');
+test('with trailing message should succeed for "never"', async t => {
+ const [actual] = footerLeadingBlank(await parsed.trailing, 'never');
const expected = true;
t.is(actual, expected);
});
-test('with trailing message should succeed for "always"', t => {
- const [actual] = footerLeadingBlank(parsed.trailing, 'always');
+test('with trailing message should succeed for "always"', async t => {
+ const [actual] = footerLeadingBlank(await parsed.trailing, 'always');
const expected = true;
t.is(actual, expected);
});
-test('without blank line before footer should fail for empty keyword', t => {
- const [actual] = footerLeadingBlank(parsed.without);
+test('without blank line before footer should fail for empty keyword', async t => {
+ const [actual] = footerLeadingBlank(await parsed.without);
const expected = false;
t.is(actual, expected);
});
-test('without blank line before footer should succeed for "never"', t => {
- const [actual] = footerLeadingBlank(parsed.without, 'never');
+test('without blank line before footer should succeed for "never"', async t => {
+ const [actual] = footerLeadingBlank(await parsed.without, 'never');
const expected = true;
t.is(actual, expected);
});
-test('without blank line before footer should fail for "always"', t => {
- const [actual] = footerLeadingBlank(parsed.without, 'always');
+test('without blank line before footer should fail for "always"', async t => {
+ const [actual] = footerLeadingBlank(await parsed.without, 'always');
const expected = false;
t.is(actual, expected);
});
-test('with blank line before footer should succeed for empty keyword', t => {
- const [actual] = footerLeadingBlank(parsed.with);
+test('with blank line before footer should succeed for empty keyword', async t => {
+ const [actual] = footerLeadingBlank(await parsed.with);
const expected = true;
t.is(actual, expected);
});
-test('with blank line before footer should fail for "never"', t => {
- const [actual] = footerLeadingBlank(parsed.with, 'never');
+test('with blank line before footer should fail for "never"', async t => {
+ const [actual] = footerLeadingBlank(await parsed.with, 'never');
const expected = false;
t.is(actual, expected);
});
-test('with blank line before footer should succeed for "always"', t => {
- const [actual] = footerLeadingBlank(parsed.with, 'always');
+test('with blank line before footer should succeed for "always"', async t => {
+ const [actual] = footerLeadingBlank(await parsed.with, 'always');
const expected = true;
t.is(actual, expected);
});
-test('with blank line before footer and multiline body should succeed for empty keyword', t => {
- const [actual] = footerLeadingBlank(parsed.withMulitLine);
+test('with blank line before footer and multiline body should succeed for empty keyword', async t => {
+ const [actual] = footerLeadingBlank(await parsed.withMulitLine);
const expected = true;
t.is(actual, expected);
});
-test('with blank line before footer and multiline body should fail for "never"', t => {
- const [actual] = footerLeadingBlank(parsed.withMulitLine, 'never');
+test('with blank line before footer and multiline body should fail for "never"', async t => {
+ const [actual] = footerLeadingBlank(await parsed.withMulitLine, 'never');
const expected = false;
t.is(actual, expected);
});
-test('with blank line before footer and multiline body should succeed for "always"', t => {
- const [actual] = footerLeadingBlank(parsed.withMulitLine, 'always');
+test('with blank line before footer and multiline body should succeed for "always"', async t => {
+ const [actual] = footerLeadingBlank(await parsed.withMulitLine, 'always');
const expected = true;
t.is(actual, expected);
});
diff --git a/source/rules/footer-max-length.js b/@commitlint/core/src/rules/footer-max-length.js
similarity index 100%
rename from source/rules/footer-max-length.js
rename to @commitlint/core/src/rules/footer-max-length.js
diff --git a/source/rules/footer-max-length.test.js b/@commitlint/core/src/rules/footer-max-length.test.js
similarity index 63%
rename from source/rules/footer-max-length.test.js
rename to @commitlint/core/src/rules/footer-max-length.test.js
index 13af836f40..703820fab6 100644
--- a/source/rules/footer-max-length.test.js
+++ b/@commitlint/core/src/rules/footer-max-length.test.js
@@ -21,26 +21,26 @@ const parsed = {
long: parse(messages.long)
};
-test('with simple should succeed', t => {
- const [actual] = check(parsed.simple, '', value);
+test('with simple should succeed', async t => {
+ const [actual] = check(await parsed.simple, '', value);
const expected = true;
t.is(actual, expected);
});
-test('with empty should succeed', t => {
- const [actual] = check(parsed.empty, '', value);
+test('with empty should succeed', async t => {
+ const [actual] = check(await parsed.empty, '', value);
const expected = true;
t.is(actual, expected);
});
-test('with short should succeed', t => {
- const [actual] = check(parsed.short, '', value);
+test('with short should succeed', async t => {
+ const [actual] = check(await parsed.short, '', value);
const expected = true;
t.is(actual, expected);
});
-test('with long should fail', t => {
- const [actual] = check(parsed.long, '', value);
+test('with long should fail', async t => {
+ const [actual] = check(await parsed.long, '', value);
const expected = false;
t.is(actual, expected);
});
diff --git a/source/rules/footer-min-length.js b/@commitlint/core/src/rules/footer-min-length.js
similarity index 100%
rename from source/rules/footer-min-length.js
rename to @commitlint/core/src/rules/footer-min-length.js
diff --git a/source/rules/footer-min-length.test.js b/@commitlint/core/src/rules/footer-min-length.test.js
similarity index 63%
rename from source/rules/footer-min-length.test.js
rename to @commitlint/core/src/rules/footer-min-length.test.js
index 5d20465f2c..4092318949 100644
--- a/source/rules/footer-min-length.test.js
+++ b/@commitlint/core/src/rules/footer-min-length.test.js
@@ -21,26 +21,26 @@ const parsed = {
long: parse(messages.long)
};
-test('with simple should succeed', t => {
- const [actual] = check(parsed.simple, '', value);
+test('with simple should succeed', async t => {
+ const [actual] = check(await parsed.simple, '', value);
const expected = true;
t.is(actual, expected);
});
-test('with empty should succeed', t => {
- const [actual] = check(parsed.empty, '', value);
+test('with empty should succeed', async t => {
+ const [actual] = check(await parsed.empty, '', value);
const expected = true;
t.is(actual, expected);
});
-test('with short should fail', t => {
- const [actual] = check(parsed.short, '', value);
+test('with short should fail', async t => {
+ const [actual] = check(await parsed.short, '', value);
const expected = false;
t.is(actual, expected);
});
-test('with long should succeed', t => {
- const [actual] = check(parsed.long, '', value);
+test('with long should succeed', async t => {
+ const [actual] = check(await parsed.long, '', value);
const expected = true;
t.is(actual, expected);
});
diff --git a/source/rules/footer-tense.js b/@commitlint/core/src/rules/footer-tense.js
similarity index 100%
rename from source/rules/footer-tense.js
rename to @commitlint/core/src/rules/footer-tense.js
diff --git a/source/rules/footer-tense.test.js b/@commitlint/core/src/rules/footer-tense.test.js
similarity index 57%
rename from source/rules/footer-tense.test.js
rename to @commitlint/core/src/rules/footer-tense.test.js
index a96bb8198d..51020ec09a 100644
--- a/source/rules/footer-tense.test.js
+++ b/@commitlint/core/src/rules/footer-tense.test.js
@@ -20,92 +20,92 @@ const parsed = {
mixed: parse(messages.mixed)
};
-test('with empty footer should succeed', t => {
- const [actual] = footerTense(parsed.empty, '', ['present-imperative']);
+test('with empty footer should succeed', async t => {
+ const [actual] = footerTense(await parsed.empty, '', ['present-imperative']);
const expected = true;
t.is(actual, expected);
});
-test('with present footer should succeed for "always present-imperative"', t => {
- const [actual] = footerTense(parsed.presentImperative, 'always', ['present-imperative']);
+test('with present footer should succeed for "always present-imperative"', async t => {
+ const [actual] = footerTense(await parsed.presentImperative, 'always', ['present-imperative']);
const expected = true;
t.is(actual, expected);
});
-test('with present footer should fail for "never present-imperative"', t => {
- const [actual] = footerTense(parsed.presentImperative, 'never', ['present-imperative']);
+test('with present footer should fail for "never present-imperative"', async t => {
+ const [actual] = footerTense(await parsed.presentImperative, 'never', ['present-imperative']);
const expected = false;
t.is(actual, expected);
});
-test('with present footer should succeed for "always present-participle"', t => {
- const [actual] = footerTense(parsed.presentParticiple, 'always', ['present-participle']);
+test('with present footer should succeed for "always present-participle"', async t => {
+ const [actual] = footerTense(await parsed.presentParticiple, 'always', ['present-participle']);
const expected = true;
t.is(actual, expected);
});
-test('with present footer should fail for "never present-participle"', t => {
- const [actual] = footerTense(parsed.presentParticiple, 'never', ['present-participle']);
+test('with present footer should fail for "never present-participle"', async t => {
+ const [actual] = footerTense(await parsed.presentParticiple, 'never', ['present-participle']);
const expected = false;
t.is(actual, expected);
});
-test('with present footer should succeed for "always present-third-person"', t => {
- const [actual] = footerTense(parsed.presentThirdPerson, 'always', ['present-third-person']);
+test('with present footer should succeed for "always present-third-person"', async t => {
+ const [actual] = footerTense(await parsed.presentThirdPerson, 'always', ['present-third-person']);
const expected = true;
t.is(actual, expected);
});
-test('with present footer should fail for "never present-third-person"', t => {
- const [actual] = footerTense(parsed.presentThirdPerson, 'never', ['present-third-person']);
+test('with present footer should fail for "never present-third-person"', async t => {
+ const [actual] = footerTense(await parsed.presentThirdPerson, 'never', ['present-third-person']);
const expected = false;
t.is(actual, expected);
});
-test('with past footer should succedd for "always past-tense"', t => {
- const [actual] = footerTense(parsed.past, 'always', ['past-tense']);
+test('with past footer should succedd for "always past-tense"', async t => {
+ const [actual] = footerTense(await parsed.past, 'always', ['past-tense']);
const expected = true;
t.is(actual, expected);
});
-test('with past footer should fail for "never past-tense"', t => {
- const [actual] = footerTense(parsed.past, 'never', ['past-tense']);
+test('with past footer should fail for "never past-tense"', async t => {
+ const [actual] = footerTense(await parsed.past, 'never', ['past-tense']);
const expected = false;
t.is(actual, expected);
});
-test('with mixed footer should fail for "always present-third-person"', t => {
- const [actual] = footerTense(parsed.mixed, 'always', ['present-third-person']);
+test('with mixed footer should fail for "always present-third-person"', async t => {
+ const [actual] = footerTense(await parsed.mixed, 'always', ['present-third-person']);
const expected = false;
t.is(actual, expected);
});
-test('with mixed footer should fail for "always present-imperative"', t => {
- const [actual] = footerTense(parsed.mixed, 'always', ['present-imperative']);
+test('with mixed footer should fail for "always present-imperative"', async t => {
+ const [actual] = footerTense(await parsed.mixed, 'always', ['present-imperative']);
const expected = false;
t.is(actual, expected);
});
-test('with present footer should fail for "always present-participle"', t => {
- const [actual] = footerTense(parsed.mixed, 'always', ['present-participle']);
+test('with present footer should fail for "always present-participle"', async t => {
+ const [actual] = footerTense(await parsed.mixed, 'always', ['present-participle']);
const expected = false;
t.is(actual, expected);
});
-test('with mixed footer should fail for "always past-tense"', t => {
- const [actual] = footerTense(parsed.mixed, 'always', ['past-tense']);
+test('with mixed footer should fail for "always past-tense"', async t => {
+ const [actual] = footerTense(await parsed.mixed, 'always', ['past-tense']);
const expected = false;
t.is(actual, expected);
});
-test('with mixed footer should succeed for "always present-third-person, present-imperative, present-participle, past-tense"', t => {
- const [actual] = footerTense(parsed.mixed, 'always', ['present-third-person', 'present-imperative', 'present-participle', 'past-tense']);
+test('with mixed footer should succeed for "always present-third-person, present-imperative, present-participle, past-tense"', async t => {
+ const [actual] = footerTense(await parsed.mixed, 'always', ['present-third-person', 'present-imperative', 'present-participle', 'past-tense']);
const expected = true;
t.is(actual, expected);
});
-test('with mixed footer should succeed for "never allowed: present-third-person" and matching ignored: implements', t => {
- const [actual] = footerTense(parsed.mixed, 'never', {
+test('with mixed footer should succeed for "never allowed: present-third-person" and matching ignored: implements', async t => {
+ const [actual] = footerTense(await parsed.mixed, 'never', {
allowed: ['present-third-person'],
ignored: ['implements']
});
diff --git a/source/rules/header-max-length.js b/@commitlint/core/src/rules/header-max-length.js
similarity index 100%
rename from source/rules/header-max-length.js
rename to @commitlint/core/src/rules/header-max-length.js
diff --git a/source/rules/header-max-length.test.js b/@commitlint/core/src/rules/header-max-length.test.js
similarity index 67%
rename from source/rules/header-max-length.test.js
rename to @commitlint/core/src/rules/header-max-length.test.js
index 856d228290..08e3f4a2e8 100644
--- a/source/rules/header-max-length.test.js
+++ b/@commitlint/core/src/rules/header-max-length.test.js
@@ -17,14 +17,14 @@ const parsed = {
long: parse(messages.long)
};
-test('with short should succeed', t => {
- const [actual] = check(parsed.short, '', value);
+test('with short should succeed', async t => {
+ const [actual] = check(await parsed.short, '', value);
const expected = true;
t.is(actual, expected);
});
-test('with long should fail', t => {
- const [actual] = check(parsed.long, '', value);
+test('with long should fail', async t => {
+ const [actual] = check(await parsed.long, '', value);
const expected = false;
t.is(actual, expected);
});
diff --git a/source/rules/header-min-length.js b/@commitlint/core/src/rules/header-min-length.js
similarity index 100%
rename from source/rules/header-min-length.js
rename to @commitlint/core/src/rules/header-min-length.js
diff --git a/source/rules/header-min-length.test.js b/@commitlint/core/src/rules/header-min-length.test.js
similarity index 68%
rename from source/rules/header-min-length.test.js
rename to @commitlint/core/src/rules/header-min-length.test.js
index e63777991b..c10d8d5995 100644
--- a/source/rules/header-min-length.test.js
+++ b/@commitlint/core/src/rules/header-min-length.test.js
@@ -17,14 +17,14 @@ const parsed = {
long: parse(messages.long)
};
-test('with short should fail', t => {
- const [actual] = check(parsed.short, '', value);
+test('with short should fail', async t => {
+ const [actual] = check(await parsed.short, '', value);
const expected = false;
t.is(actual, expected);
});
-test('with long should succeed', t => {
- const [actual] = check(parsed.long, '', value);
+test('with long should succeed', async t => {
+ const [actual] = check(await parsed.long, '', value);
const expected = true;
t.is(actual, expected);
});
diff --git a/source/rules/index.js b/@commitlint/core/src/rules/index.js
similarity index 100%
rename from source/rules/index.js
rename to @commitlint/core/src/rules/index.js
diff --git a/source/rules/index.test.js b/@commitlint/core/src/rules/index.test.js
similarity index 92%
rename from source/rules/index.test.js
rename to @commitlint/core/src/rules/index.test.js
index 0cac33dbe9..6d8ca6a007 100644
--- a/source/rules/index.test.js
+++ b/@commitlint/core/src/rules/index.test.js
@@ -1,6 +1,7 @@
import path from 'path';
import test from 'ava';
import globby from 'globby';
+import {values} from 'lodash';
import rules from '.';
test('exports all rules', async t => {
@@ -10,7 +11,7 @@ test('exports all rules', async t => {
});
test('rules export functions', t => {
- const actual = Object.values(rules);
+ const actual = values(rules);
t.true(actual.every(rule => typeof rule === 'function'));
});
diff --git a/source/rules/lang.js b/@commitlint/core/src/rules/lang.js
similarity index 100%
rename from source/rules/lang.js
rename to @commitlint/core/src/rules/lang.js
diff --git a/@commitlint/core/src/rules/lang.test.js b/@commitlint/core/src/rules/lang.test.js
new file mode 100644
index 0000000000..3e45b7811d
--- /dev/null
+++ b/@commitlint/core/src/rules/lang.test.js
@@ -0,0 +1,75 @@
+import test from 'ava';
+import parse from '../library/parse';
+import check from './lang';
+
+const messages = {
+ empty: '(): \n',
+ eng: '(): this is a serious subject',
+ deu: '(): Dies ist ein ernstes Subjekt'
+};
+
+const parsed = {
+ empty: parse(messages.empty),
+ eng: parse(messages.eng),
+ deu: parse(messages.deu)
+};
+
+test('empty succeeds', async t => {
+ const [actual] = check(await parsed.eng, '', 'eng');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('english against "eng" succeeds', async t => {
+ const [actual] = check(await parsed.eng, '', 'eng');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('english against "always eng" succeeds', async t => {
+ const [actual] = check(await parsed.eng, 'always', 'eng');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('english against "never eng" fails', async t => {
+ const [actual] = check(await parsed.eng, 'never', 'eng');
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('english against "deu" fails', async t => {
+ const [actual] = check(await parsed.eng, '', 'deu+');
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('english against "always deu" fails', async t => {
+ const [actual] = check(await parsed.eng, 'always', 'deu');
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('english against "never deu" succeeds', async t => {
+ const [actual] = check(await parsed.eng, 'never', 'deu');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('german against "deu" succeeds', async t => {
+ const [actual] = check(await parsed.deu, '', 'deu');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('german against "always deu" succeeds', async t => {
+ const [actual] = check(await parsed.deu, 'always', 'deu');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('german against "never deu" fails', async t => {
+ const [actual] = check(await parsed.deu, 'never', 'deu');
+ const expected = false;
+ t.is(actual, expected);
+});
diff --git a/source/rules/scope-case.js b/@commitlint/core/src/rules/scope-case.js
similarity index 100%
rename from source/rules/scope-case.js
rename to @commitlint/core/src/rules/scope-case.js
diff --git a/source/rules/scope-case.test.js b/@commitlint/core/src/rules/scope-case.test.js
similarity index 50%
rename from source/rules/scope-case.test.js
rename to @commitlint/core/src/rules/scope-case.test.js
index 3f643a7833..c9adb7f482 100644
--- a/source/rules/scope-case.test.js
+++ b/@commitlint/core/src/rules/scope-case.test.js
@@ -16,74 +16,74 @@ const parsed = {
uppercase: parse(messages.uppercase)
};
-test('with empty scope should succeed for "never lowercase"', t => {
- const [actual] = scopeCase(parsed.empty, 'never', 'lowercase');
+test('with empty scope should succeed for "never lowercase"', async t => {
+ const [actual] = scopeCase(await parsed.empty, 'never', 'lowercase');
const expected = true;
t.is(actual, expected);
});
-test('with empty scope should succeed for "always lowercase"', t => {
- const [actual] = scopeCase(parsed.empty, 'always', 'lowercase');
+test('with empty scope should succeed for "always lowercase"', async t => {
+ const [actual] = scopeCase(await parsed.empty, 'always', 'lowercase');
const expected = true;
t.is(actual, expected);
});
-test('with empty scope should succeed for "never uppercase"', t => {
- const [actual] = scopeCase(parsed.empty, 'never', 'uppercase');
+test('with empty scope should succeed for "never uppercase"', async t => {
+ const [actual] = scopeCase(await parsed.empty, 'never', 'uppercase');
const expected = true;
t.is(actual, expected);
});
-test('with empty scope should succeed for "always uppercase"', t => {
- const [actual] = scopeCase(parsed.empty, 'always', 'uppercase');
+test('with empty scope should succeed for "always uppercase"', async t => {
+ const [actual] = scopeCase(await parsed.empty, 'always', 'uppercase');
const expected = true;
t.is(actual, expected);
});
-test('with lowercase scope should fail for "never lowercase"', t => {
- const [actual] = scopeCase(parsed.lowercase, 'never', 'lowercase');
+test('with lowercase scope should fail for "never lowercase"', async t => {
+ const [actual] = scopeCase(await parsed.lowercase, 'never', 'lowercase');
const expected = false;
t.is(actual, expected);
});
-test('with lowercase scope should succeed for "always lowercase"', t => {
- const [actual] = scopeCase(parsed.lowercase, 'always', 'lowercase');
+test('with lowercase scope should succeed for "always lowercase"', async t => {
+ const [actual] = scopeCase(await parsed.lowercase, 'always', 'lowercase');
const expected = true;
t.is(actual, expected);
});
-test('with mixedcase scope should succeed for "never lowercase"', t => {
- const [actual] = scopeCase(parsed.mixedcase, 'never', 'lowercase');
+test('with mixedcase scope should succeed for "never lowercase"', async t => {
+ const [actual] = scopeCase(await parsed.mixedcase, 'never', 'lowercase');
const expected = true;
t.is(actual, expected);
});
-test('with mixedcase scope should fail for "always lowercase"', t => {
- const [actual] = scopeCase(parsed.mixedcase, 'always', 'lowercase');
+test('with mixedcase scope should fail for "always lowercase"', async t => {
+ const [actual] = scopeCase(await parsed.mixedcase, 'always', 'lowercase');
const expected = false;
t.is(actual, expected);
});
-test('with mixedcase scope should succeed for "never uppercase"', t => {
- const [actual] = scopeCase(parsed.mixedcase, 'never', 'uppercase');
+test('with mixedcase scope should succeed for "never uppercase"', async t => {
+ const [actual] = scopeCase(await parsed.mixedcase, 'never', 'uppercase');
const expected = true;
t.is(actual, expected);
});
-test('with mixedcase scope should fail for "always uppercase"', t => {
- const [actual] = scopeCase(parsed.mixedcase, 'always', 'uppercase');
+test('with mixedcase scope should fail for "always uppercase"', async t => {
+ const [actual] = scopeCase(await parsed.mixedcase, 'always', 'uppercase');
const expected = false;
t.is(actual, expected);
});
-test('with uppercase scope should fail for "never uppercase"', t => {
- const [actual] = scopeCase(parsed.uppercase, 'never', 'uppercase');
+test('with uppercase scope should fail for "never uppercase"', async t => {
+ const [actual] = scopeCase(await parsed.uppercase, 'never', 'uppercase');
const expected = false;
t.is(actual, expected);
});
-test('with lowercase scope should succeed for "always uppercase"', t => {
- const [actual] = scopeCase(parsed.uppercase, 'always', 'uppercase');
+test('with lowercase scope should succeed for "always uppercase"', async t => {
+ const [actual] = scopeCase(await parsed.uppercase, 'always', 'uppercase');
const expected = true;
t.is(actual, expected);
});
diff --git a/source/rules/scope-empty.js b/@commitlint/core/src/rules/scope-empty.js
similarity index 100%
rename from source/rules/scope-empty.js
rename to @commitlint/core/src/rules/scope-empty.js
diff --git a/@commitlint/core/src/rules/scope-empty.test.js b/@commitlint/core/src/rules/scope-empty.test.js
new file mode 100644
index 0000000000..4cf59ca657
--- /dev/null
+++ b/@commitlint/core/src/rules/scope-empty.test.js
@@ -0,0 +1,69 @@
+import test from 'ava';
+import parse from '../library/parse';
+import scopeEmpty from './scope-empty';
+
+const messages = {
+ plain: 'foo(bar): baz',
+ superfluous: 'foo(): baz',
+ empty: 'foo: baz'
+};
+
+const parsed = {
+ plain: parse(messages.plain),
+ superfluous: parse(messages.superfluous),
+ empty: parse(messages.empty)
+};
+
+test('with plain message it should succeed for empty keyword', async t => {
+ const [actual] = scopeEmpty(await parsed.plain);
+ const expected = true;
+ t.deepEqual(actual, expected);
+});
+
+test('with plain message it should succeed for "never"', async t => {
+ const [actual] = scopeEmpty(await parsed.plain, 'never');
+ const expected = true;
+ t.deepEqual(actual, expected);
+});
+
+test('with plain message it should fail for "always"', async t => {
+ const [actual] = scopeEmpty(await parsed.plain, 'always');
+ const expected = false;
+ t.deepEqual(actual, expected);
+});
+
+test('with superfluous message it should fail for empty keyword', async t => {
+ const [actual] = scopeEmpty(await parsed.superfluous);
+ const expected = false;
+ t.deepEqual(actual, expected);
+});
+
+test('with superfluous message it should fail for "never"', async t => {
+ const [actual] = scopeEmpty(await parsed.superfluous, 'never');
+ const expected = false;
+ t.deepEqual(actual, expected);
+});
+
+test('with superfluous message it should fail for "always"', async t => {
+ const [actual] = scopeEmpty(await parsed.superfluous, 'always');
+ const expected = true;
+ t.deepEqual(actual, expected);
+});
+
+test('with empty message it should fail for empty keyword', async t => {
+ const [actual] = scopeEmpty(await parsed.empty);
+ const expected = false;
+ t.deepEqual(actual, expected);
+});
+
+test('with empty message it should fail for "never"', async t => {
+ const [actual] = scopeEmpty(await parsed.empty, 'never');
+ const expected = false;
+ t.deepEqual(actual, expected);
+});
+
+test('with empty message it should fail for "always"', async t => {
+ const [actual] = scopeEmpty(await parsed.empty, 'always');
+ const expected = true;
+ t.deepEqual(actual, expected);
+});
diff --git a/source/rules/scope-enum.js b/@commitlint/core/src/rules/scope-enum.js
similarity index 100%
rename from source/rules/scope-enum.js
rename to @commitlint/core/src/rules/scope-enum.js
diff --git a/source/rules/scope-enum.test.js b/@commitlint/core/src/rules/scope-enum.test.js
similarity index 57%
rename from source/rules/scope-enum.test.js
rename to @commitlint/core/src/rules/scope-enum.test.js
index c470bb8f6e..78a09fad8a 100644
--- a/source/rules/scope-enum.test.js
+++ b/@commitlint/core/src/rules/scope-enum.test.js
@@ -14,80 +14,80 @@ const parsed = {
empty: parse(messages.empty)
};
-test('scope-enum with plain message and always should succeed empty enum', t => {
- const [actual] = scopeEnum(parsed.plain, 'always', []);
+test('scope-enum with plain message and always should succeed empty enum', async t => {
+ const [actual] = scopeEnum(await parsed.plain, 'always', []);
const expected = true;
t.deepEqual(actual, expected);
});
-test('scope-enum with plain message and never should error empty enum', t => {
- const [actual] = scopeEnum(parsed.plain, 'never', []);
+test('scope-enum with plain message and never should error empty enum', async t => {
+ const [actual] = scopeEnum(await parsed.plain, 'never', []);
const expected = false;
t.deepEqual(actual, expected);
});
-test('with plain message should succeed correct enum', t => {
- const [actual] = scopeEnum(parsed.plain, 'always', ['bar']);
+test('with plain message should succeed correct enum', async t => {
+ const [actual] = scopeEnum(await parsed.plain, 'always', ['bar']);
const expected = true;
t.deepEqual(actual, expected);
});
-test('scope-enum with plain message should error false enum', t => {
- const [actual] = scopeEnum(parsed.plain, 'always', ['foo']);
+test('scope-enum with plain message should error false enum', async t => {
+ const [actual] = scopeEnum(await parsed.plain, 'always', ['foo']);
const expected = false;
t.deepEqual(actual, expected);
});
-test('scope-enum with plain message should error forbidden enum', t => {
- const [actual] = scopeEnum(parsed.plain, 'never', ['bar']);
+test('scope-enum with plain message should error forbidden enum', async t => {
+ const [actual] = scopeEnum(await parsed.plain, 'never', ['bar']);
const expected = false;
t.deepEqual(actual, expected);
});
-test('scope-enum with plain message should succeed forbidden enum', t => {
- const [actual] = scopeEnum(parsed.plain, 'never', ['foo']);
+test('scope-enum with plain message should succeed forbidden enum', async t => {
+ const [actual] = scopeEnum(await parsed.plain, 'never', ['foo']);
const expected = true;
t.deepEqual(actual, expected);
});
-test('scope-enum with superfluous scope should succeed enum', t => {
- const [actual] = scopeEnum(parsed.superfluous, 'always', ['bar']);
+test('scope-enum with superfluous scope should succeed enum', async t => {
+ const [actual] = scopeEnum(await parsed.superfluous, 'always', ['bar']);
const expected = true;
t.deepEqual(actual, expected);
});
-test('scope-enum with superfluous scope and "never" should succeed', t => {
- const [actual] = scopeEnum(parsed.superfluous, 'never', ['bar']);
+test('scope-enum with superfluous scope and "never" should succeed', async t => {
+ const [actual] = scopeEnum(await parsed.superfluous, 'never', ['bar']);
const expected = true;
t.deepEqual(actual, expected);
});
-test('scope-enum with superfluous scope and always should succeed empty enum', t => {
- const [actual] = scopeEnum(parsed.superfluous, 'always', []);
+test('scope-enum with superfluous scope and always should succeed empty enum', async t => {
+ const [actual] = scopeEnum(await parsed.superfluous, 'always', []);
const expected = true;
t.deepEqual(actual, expected);
});
-test('scope-enum with superfluous scope and never should succeed empty enum', t => {
- const [actual] = scopeEnum(parsed.superfluous, 'never', []);
+test('scope-enum with superfluous scope and never should succeed empty enum', async t => {
+ const [actual] = scopeEnum(await parsed.superfluous, 'never', []);
const expected = true;
t.deepEqual(actual, expected);
});
-test('scope-enum with empty scope and always should succeed empty enum', t => {
- const [actual] = scopeEnum(parsed.superfluous, 'always', []);
+test('scope-enum with empty scope and always should succeed empty enum', async t => {
+ const [actual] = scopeEnum(await parsed.superfluous, 'always', []);
const expected = true;
t.deepEqual(actual, expected);
});
-test('scope-enum with empty scope and always should succeed filled enum', t => {
- const [actual] = scopeEnum(parsed.superfluous, 'always', ['foo']);
+test('scope-enum with empty scope and always should succeed filled enum', async t => {
+ const [actual] = scopeEnum(await parsed.superfluous, 'always', ['foo']);
const expected = true;
t.deepEqual(actual, expected);
});
-test('scope-enum with empty scope and never should succeed empty enum', t => {
- const [actual] = scopeEnum(parsed.superfluous, 'never', []);
+test('scope-enum with empty scope and never should succeed empty enum', async t => {
+ const [actual] = scopeEnum(await parsed.superfluous, 'never', []);
const expected = true;
t.deepEqual(actual, expected);
});
diff --git a/source/rules/scope-max-length.js b/@commitlint/core/src/rules/scope-max-length.js
similarity index 100%
rename from source/rules/scope-max-length.js
rename to @commitlint/core/src/rules/scope-max-length.js
diff --git a/source/rules/scope-max-length.test.js b/@commitlint/core/src/rules/scope-max-length.test.js
similarity index 64%
rename from source/rules/scope-max-length.test.js
rename to @commitlint/core/src/rules/scope-max-length.test.js
index 95222957a0..b2227c46db 100644
--- a/source/rules/scope-max-length.test.js
+++ b/@commitlint/core/src/rules/scope-max-length.test.js
@@ -19,20 +19,20 @@ const parsed = {
long: parse(messages.long)
};
-test('with empty should succeed', t => {
- const [actual] = check(parsed.empty, '', value);
+test('with empty should succeed', async t => {
+ const [actual] = check(await parsed.empty, '', value);
const expected = true;
t.is(actual, expected);
});
-test('with short should succeed', t => {
- const [actual] = check(parsed.short, '', value);
+test('with short should succeed', async t => {
+ const [actual] = check(await parsed.short, '', value);
const expected = true;
t.is(actual, expected);
});
-test('with long should fail', t => {
- const [actual] = check(parsed.long, '', value);
+test('with long should fail', async t => {
+ const [actual] = check(await parsed.long, '', value);
const expected = false;
t.is(actual, expected);
});
diff --git a/source/rules/scope-min-length.js b/@commitlint/core/src/rules/scope-min-length.js
similarity index 100%
rename from source/rules/scope-min-length.js
rename to @commitlint/core/src/rules/scope-min-length.js
diff --git a/source/rules/scope-min-length.test.js b/@commitlint/core/src/rules/scope-min-length.test.js
similarity index 64%
rename from source/rules/scope-min-length.test.js
rename to @commitlint/core/src/rules/scope-min-length.test.js
index c30c9d0399..f0090cf062 100644
--- a/source/rules/scope-min-length.test.js
+++ b/@commitlint/core/src/rules/scope-min-length.test.js
@@ -19,20 +19,20 @@ const parsed = {
long: parse(messages.long)
};
-test('with empty should succeed', t => {
- const [actual] = check(parsed.empty, '', value);
+test('with empty should succeed', async t => {
+ const [actual] = check(await parsed.empty, '', value);
const expected = true;
t.is(actual, expected);
});
-test('with short should fail', t => {
- const [actual] = check(parsed.short, '', value);
+test('with short should fail', async t => {
+ const [actual] = check(await parsed.short, '', value);
const expected = false;
t.is(actual, expected);
});
-test('with long should succeed', t => {
- const [actual] = check(parsed.long, '', value);
+test('with long should succeed', async t => {
+ const [actual] = check(await parsed.long, '', value);
const expected = true;
t.is(actual, expected);
});
diff --git a/source/rules/subject-case.js b/@commitlint/core/src/rules/subject-case.js
similarity index 100%
rename from source/rules/subject-case.js
rename to @commitlint/core/src/rules/subject-case.js
diff --git a/source/rules/subject-case.test.js b/@commitlint/core/src/rules/subject-case.test.js
similarity index 62%
rename from source/rules/subject-case.test.js
rename to @commitlint/core/src/rules/subject-case.test.js
index 20da9dbe49..de0d5e71fe 100644
--- a/source/rules/subject-case.test.js
+++ b/@commitlint/core/src/rules/subject-case.test.js
@@ -16,74 +16,74 @@ const parsed = {
uppercase: parse(messages.uppercase)
};
-test('with empty subject should succeed for "never lowercase"', t => {
- const [actual] = subjectCase(parsed.empty, 'never', 'lowercase');
+test('with empty subject should succeed for "never lowercase"', async t => {
+ const [actual] = subjectCase(await parsed.empty, 'never', 'lowercase');
const expected = true;
t.is(actual, expected);
});
-test('with empty subject should succeed for "always lowercase"', t => {
- const [actual] = subjectCase(parsed.empty, 'always', 'lowercase');
+test('with empty subject should succeed for "always lowercase"', async t => {
+ const [actual] = subjectCase(await parsed.empty, 'always', 'lowercase');
const expected = true;
t.is(actual, expected);
});
-test('with empty subject should succeed for "never uppercase"', t => {
- const [actual] = subjectCase(parsed.empty, 'never', 'uppercase');
+test('with empty subject should succeed for "never uppercase"', async t => {
+ const [actual] = subjectCase(await parsed.empty, 'never', 'uppercase');
const expected = true;
t.is(actual, expected);
});
-test('with empty subject should succeed for "always uppercase"', t => {
- const [actual] = subjectCase(parsed.empty, 'always', 'uppercase');
+test('with empty subject should succeed for "always uppercase"', async t => {
+ const [actual] = subjectCase(await parsed.empty, 'always', 'uppercase');
const expected = true;
t.is(actual, expected);
});
-test('with lowercase subject should fail for "never lowercase"', t => {
- const [actual] = subjectCase(parsed.lowercase, 'never', 'lowercase');
+test('with lowercase subject should fail for "never lowercase"', async t => {
+ const [actual] = subjectCase(await parsed.lowercase, 'never', 'lowercase');
const expected = false;
t.is(actual, expected);
});
-test('with lowercase subject should succeed for "always lowercase"', t => {
- const [actual] = subjectCase(parsed.lowercase, 'always', 'lowercase');
+test('with lowercase subject should succeed for "always lowercase"', async t => {
+ const [actual] = subjectCase(await parsed.lowercase, 'always', 'lowercase');
const expected = true;
t.is(actual, expected);
});
-test('with mixedcase subject should succeed for "never lowercase"', t => {
- const [actual] = subjectCase(parsed.mixedcase, 'never', 'lowercase');
+test('with mixedcase subject should succeed for "never lowercase"', async t => {
+ const [actual] = subjectCase(await parsed.mixedcase, 'never', 'lowercase');
const expected = true;
t.is(actual, expected);
});
-test('with mixedcase subject should fail for "always lowercase"', t => {
- const [actual] = subjectCase(parsed.mixedcase, 'always', 'lowercase');
+test('with mixedcase subject should fail for "always lowercase"', async t => {
+ const [actual] = subjectCase(await parsed.mixedcase, 'always', 'lowercase');
const expected = false;
t.is(actual, expected);
});
-test('with mixedcase subject should succeed for "never uppercase"', t => {
- const [actual] = subjectCase(parsed.mixedcase, 'never', 'uppercase');
+test('with mixedcase subject should succeed for "never uppercase"', async t => {
+ const [actual] = subjectCase(await parsed.mixedcase, 'never', 'uppercase');
const expected = true;
t.is(actual, expected);
});
-test('with mixedcase subject should fail for "always uppercase"', t => {
- const [actual] = subjectCase(parsed.mixedcase, 'always', 'uppercase');
+test('with mixedcase subject should fail for "always uppercase"', async t => {
+ const [actual] = subjectCase(await parsed.mixedcase, 'always', 'uppercase');
const expected = false;
t.is(actual, expected);
});
-test('with uppercase subject should fail for "never uppercase"', t => {
- const [actual] = subjectCase(parsed.uppercase, 'never', 'uppercase');
+test('with uppercase subject should fail for "never uppercase"', async t => {
+ const [actual] = subjectCase(await parsed.uppercase, 'never', 'uppercase');
const expected = false;
t.is(actual, expected);
});
-test('with lowercase subject should succeed for "always uppercase"', t => {
- const [actual] = subjectCase(parsed.uppercase, 'always', 'uppercase');
+test('with lowercase subject should succeed for "always uppercase"', async t => {
+ const [actual] = subjectCase(await parsed.uppercase, 'always', 'uppercase');
const expected = true;
t.is(actual, expected);
});
diff --git a/source/rules/subject-empty.js b/@commitlint/core/src/rules/subject-empty.js
similarity index 100%
rename from source/rules/subject-empty.js
rename to @commitlint/core/src/rules/subject-empty.js
diff --git a/@commitlint/core/src/rules/subject-empty.test.js b/@commitlint/core/src/rules/subject-empty.test.js
new file mode 100644
index 0000000000..e0482d855a
--- /dev/null
+++ b/@commitlint/core/src/rules/subject-empty.test.js
@@ -0,0 +1,49 @@
+import test from 'ava';
+import parse from '../library/parse';
+import subjectEmpty from './subject-empty';
+
+const messages = {
+ empty: 'chore: \nbody',
+ filled: 'chore: subject\nbody'
+};
+
+const parsed = {
+ empty: parse(messages.empty),
+ filled: parse(messages.filled)
+};
+
+test('without subject should succeed for empty keyword', async t => {
+ const [actual] = subjectEmpty(await parsed.empty);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('without subject should fail for "never"', async t => {
+ const [actual] = subjectEmpty(await parsed.empty, 'never');
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('without subject should succeed for "always"', async t => {
+ const [actual] = subjectEmpty(await parsed.empty, 'always');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with subject fail for empty keyword', async t => {
+ const [actual] = subjectEmpty(await parsed.filled);
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('with subject succeed for "never"', async t => {
+ const [actual] = subjectEmpty(await parsed.filled, 'never');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with subject fail for "always"', async t => {
+ const [actual] = subjectEmpty(await parsed.filled, 'always');
+ const expected = false;
+ t.is(actual, expected);
+});
diff --git a/source/rules/subject-full-stop.js b/@commitlint/core/src/rules/subject-full-stop.js
similarity index 100%
rename from source/rules/subject-full-stop.js
rename to @commitlint/core/src/rules/subject-full-stop.js
diff --git a/@commitlint/core/src/rules/subject-full-stop.test.js b/@commitlint/core/src/rules/subject-full-stop.test.js
new file mode 100644
index 0000000000..7a3d4413cf
--- /dev/null
+++ b/@commitlint/core/src/rules/subject-full-stop.test.js
@@ -0,0 +1,51 @@
+import test from 'ava';
+import parse from '../library/parse';
+import check from './subject-full-stop';
+
+const messages = {
+ empty: 'chore:\n',
+ with: `chore: subject.\n`,
+ without: `chore: subject\n`
+};
+
+const parsed = {
+ empty: parse(messages.empty),
+ with: parse(messages.with),
+ without: parse(messages.without)
+};
+
+test('empty against "always" should succeed', async t => {
+ const [actual] = check(await parsed.empty, 'always', '.');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('empty against "never ." should succeed', async t => {
+ const [actual] = check(await parsed.empty, 'never', '.');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with against "always ." should succeed', async t => {
+ const [actual] = check(await parsed.with, 'always', '.');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with against "never ." should fail', async t => {
+ const [actual] = check(await parsed.with, 'never', '.');
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('without against "always ." should fail', async t => {
+ const [actual] = check(await parsed.without, 'always', '.');
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('without against "never ." should succeed', async t => {
+ const [actual] = check(await parsed.without, 'never', '.');
+ const expected = true;
+ t.is(actual, expected);
+});
diff --git a/source/rules/subject-leading-capital.js b/@commitlint/core/src/rules/subject-leading-capital.js
similarity index 100%
rename from source/rules/subject-leading-capital.js
rename to @commitlint/core/src/rules/subject-leading-capital.js
diff --git a/@commitlint/core/src/rules/subject-leading-capital.test.js b/@commitlint/core/src/rules/subject-leading-capital.test.js
new file mode 100644
index 0000000000..eb920ce345
--- /dev/null
+++ b/@commitlint/core/src/rules/subject-leading-capital.test.js
@@ -0,0 +1,69 @@
+import test from 'ava';
+import parse from '../library/parse';
+import check from './subject-leading-capital';
+
+const messages = {
+ empty: 'chore:\n',
+ with: `chore: Subject\n`,
+ without: `chore: subject\n`
+};
+
+const parsed = {
+ empty: parse(messages.empty),
+ with: parse(messages.with),
+ without: parse(messages.without)
+};
+
+test('empty should succeed', async t => {
+ const [actual] = check(await parsed.empty);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('empty against "always" should succeed', async t => {
+ const [actual] = check(await parsed.empty, 'always', 'uppercase');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('empty against "never" should succeed', async t => {
+ const [actual] = check(await parsed.empty, 'never', 'uppercase');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with should succeed', async t => {
+ const [actual] = check(await parsed.with);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with against "always" should succeed', async t => {
+ const [actual] = check(await parsed.with, 'always', 'uppercase');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with against "never" should fail', async t => {
+ const [actual] = check(await parsed.with, 'never', 'uppercase');
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('without should fail', async t => {
+ const [actual] = check(await parsed.without, 'always', 'uppercase');
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('without against "always" should fail', async t => {
+ const [actual] = check(await parsed.without, 'always', 'uppercase');
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('without against "never" should succeed', async t => {
+ const [actual] = check(await parsed.without, 'never', 'uppercase');
+ const expected = true;
+ t.is(actual, expected);
+});
diff --git a/source/rules/subject-max-length.js b/@commitlint/core/src/rules/subject-max-length.js
similarity index 100%
rename from source/rules/subject-max-length.js
rename to @commitlint/core/src/rules/subject-max-length.js
diff --git a/source/rules/subject-max-length.test.js b/@commitlint/core/src/rules/subject-max-length.test.js
similarity index 64%
rename from source/rules/subject-max-length.test.js
rename to @commitlint/core/src/rules/subject-max-length.test.js
index 7a50873d70..432ce73de0 100644
--- a/source/rules/subject-max-length.test.js
+++ b/@commitlint/core/src/rules/subject-max-length.test.js
@@ -19,20 +19,20 @@ const parsed = {
long: parse(messages.long)
};
-test('with empty should succeed', t => {
- const [actual] = check(parsed.empty, '', value);
+test('with empty should succeed', async t => {
+ const [actual] = check(await parsed.empty, '', value);
const expected = true;
t.is(actual, expected);
});
-test('with short should succeed', t => {
- const [actual] = check(parsed.short, '', value);
+test('with short should succeed', async t => {
+ const [actual] = check(await parsed.short, '', value);
const expected = true;
t.is(actual, expected);
});
-test('with long should fail', t => {
- const [actual] = check(parsed.long, '', value);
+test('with long should fail', async t => {
+ const [actual] = check(await parsed.long, '', value);
const expected = false;
t.is(actual, expected);
});
diff --git a/source/rules/subject-min-length.js b/@commitlint/core/src/rules/subject-min-length.js
similarity index 100%
rename from source/rules/subject-min-length.js
rename to @commitlint/core/src/rules/subject-min-length.js
diff --git a/source/rules/subject-min-length.test.js b/@commitlint/core/src/rules/subject-min-length.test.js
similarity index 64%
rename from source/rules/subject-min-length.test.js
rename to @commitlint/core/src/rules/subject-min-length.test.js
index b77ea43a24..b1df88d5ce 100644
--- a/source/rules/subject-min-length.test.js
+++ b/@commitlint/core/src/rules/subject-min-length.test.js
@@ -19,20 +19,20 @@ const parsed = {
long: parse(messages.long)
};
-test('with empty should succeed', t => {
- const [actual] = check(parsed.empty, '', value);
+test('with empty should succeed', async t => {
+ const [actual] = check(await parsed.empty, '', value);
const expected = true;
t.is(actual, expected);
});
-test('with short should fail', t => {
- const [actual] = check(parsed.short, '', value);
+test('with short should fail', async t => {
+ const [actual] = check(await parsed.short, '', value);
const expected = false;
t.is(actual, expected);
});
-test('with long should succeed', t => {
- const [actual] = check(parsed.long, '', value);
+test('with long should succeed', async t => {
+ const [actual] = check(await parsed.long, '', value);
const expected = true;
t.is(actual, expected);
});
diff --git a/source/rules/subject-tense.js b/@commitlint/core/src/rules/subject-tense.js
similarity index 100%
rename from source/rules/subject-tense.js
rename to @commitlint/core/src/rules/subject-tense.js
diff --git a/@commitlint/core/src/rules/subject-tense.test.js b/@commitlint/core/src/rules/subject-tense.test.js
new file mode 100644
index 0000000000..4946fec824
--- /dev/null
+++ b/@commitlint/core/src/rules/subject-tense.test.js
@@ -0,0 +1,114 @@
+import test from 'ava';
+import parse from '../library/parse';
+import footerTense from './subject-tense';
+
+const messages = {
+ empty: 'chore: \n',
+ presentImperative: `chore: we implement things`,
+ presentParticiple: `chore: implementing things`,
+ presentThirdPerson: `chore: implements things`,
+ past: `chore: we did implement things`,
+ mixed: `chore: implement, implementing, implements, implemented`
+};
+
+const parsed = {
+ empty: parse(messages.empty),
+ presentImperative: parse(messages.presentImperative),
+ presentParticiple: parse(messages.presentParticiple),
+ presentThirdPerson: parse(messages.presentImperative),
+ past: parse(messages.past),
+ mixed: parse(messages.mixed)
+};
+
+test('empty succeeds', async t => {
+ const [actual] = footerTense(await parsed.empty, '', ['present-imperative']);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('present succeeds "always present-imperative"', async t => {
+ const [actual] = footerTense(await parsed.presentImperative, 'always', ['present-imperative']);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('present fails "never present-imperative"', async t => {
+ const [actual] = footerTense(await parsed.presentImperative, 'never', ['present-imperative']);
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('present succeeds "always present-participle"', async t => {
+ const [actual] = footerTense(await parsed.presentParticiple, 'always', ['present-participle']);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('present fails "never present-participle"', async t => {
+ const [actual] = footerTense(await parsed.presentParticiple, 'never', ['present-participle']);
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('present succeeds "always present-third-person"', async t => {
+ const [actual] = footerTense(await parsed.presentThirdPerson, 'always', ['present-third-person']);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('present fails "never present-third-person"', async t => {
+ const [actual] = footerTense(await parsed.presentThirdPerson, 'never', ['present-third-person']);
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('past should succedd "always past-tense"', async t => {
+ const [actual] = footerTense(await parsed.past, 'always', ['past-tense']);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('past fails "never past-tense"', async t => {
+ const [actual] = footerTense(await parsed.past, 'never', ['past-tense']);
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('mixed fails "always present-third-person"', async t => {
+ const [actual] = footerTense(await parsed.mixed, 'always', ['present-third-person']);
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('mixed fails "always present-imperative"', async t => {
+ const [actual] = footerTense(await parsed.mixed, 'always', ['present-imperative']);
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('present fails "always present-participle"', async t => {
+ const [actual] = footerTense(await parsed.mixed, 'always', ['present-participle']);
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('mixed fails "always past-tense"', async t => {
+ const [actual] = footerTense(await parsed.mixed, 'always', ['past-tense']);
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('mixed succeeds "always present-third-person, present-imperative, present-participle, past-tense"', async t => {
+ const [actual] = footerTense(await parsed.mixed, 'always', ['present-third-person', 'present-imperative', 'present-participle', 'past-tense']);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('mixed succeeds "never allowed: present-third-person" and matching ignored: implements', async t => {
+ const [actual] = footerTense(await parsed.mixed, 'never', {
+ allowed: ['present-third-person'],
+ ignored: ['implements']
+ });
+ const expected = true;
+ t.is(actual, expected);
+});
diff --git a/source/rules/type-case.js b/@commitlint/core/src/rules/type-case.js
similarity index 100%
rename from source/rules/type-case.js
rename to @commitlint/core/src/rules/type-case.js
diff --git a/@commitlint/core/src/rules/type-case.test.js b/@commitlint/core/src/rules/type-case.test.js
new file mode 100644
index 0000000000..1729f5686a
--- /dev/null
+++ b/@commitlint/core/src/rules/type-case.test.js
@@ -0,0 +1,89 @@
+import test from 'ava';
+import parse from '../library/parse';
+import typeCase from './type-case';
+
+const messages = {
+ empty: '(scope): subject',
+ lowercase: 'type: subject',
+ mixedcase: 'tYpE: subject',
+ uppercase: 'TYPE: subject'
+};
+
+const parsed = {
+ empty: parse(messages.empty),
+ lowercase: parse(messages.lowercase),
+ mixedcase: parse(messages.mixedcase),
+ uppercase: parse(messages.uppercase)
+};
+
+test('with empty type should succeed for "never lowercase"', async t => {
+ const [actual] = typeCase(await parsed.empty, 'never', 'lowercase');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with empty type should succeed for "always lowercase"', async t => {
+ const [actual] = typeCase(await parsed.empty, 'always', 'lowercase');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with empty type should succeed for "never uppercase"', async t => {
+ const [actual] = typeCase(await parsed.empty, 'never', 'uppercase');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with empty type should succeed for "always uppercase"', async t => {
+ const [actual] = typeCase(await parsed.empty, 'always', 'uppercase');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with lowercase type should fail for "never lowercase"', async t => {
+ const [actual] = typeCase(await parsed.lowercase, 'never', 'lowercase');
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('with lowercase type should succeed for "always lowercase"', async t => {
+ const [actual] = typeCase(await parsed.lowercase, 'always', 'lowercase');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with mixedcase type should succeed for "never lowercase"', async t => {
+ const [actual] = typeCase(await parsed.mixedcase, 'never', 'lowercase');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with mixedcase type should fail for "always lowercase"', async t => {
+ const [actual] = typeCase(await parsed.mixedcase, 'always', 'lowercase');
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('with mixedcase type should succeed for "never uppercase"', async t => {
+ const [actual] = typeCase(await parsed.mixedcase, 'never', 'uppercase');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with mixedcase type should fail for "always uppercase"', async t => {
+ const [actual] = typeCase(await parsed.mixedcase, 'always', 'uppercase');
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('with uppercase type should fail for "never uppercase"', async t => {
+ const [actual] = typeCase(await parsed.uppercase, 'never', 'uppercase');
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('with lowercase type should succeed for "always uppercase"', async t => {
+ const [actual] = typeCase(await parsed.uppercase, 'always', 'uppercase');
+ const expected = true;
+ t.is(actual, expected);
+});
diff --git a/source/rules/type-empty.js b/@commitlint/core/src/rules/type-empty.js
similarity index 100%
rename from source/rules/type-empty.js
rename to @commitlint/core/src/rules/type-empty.js
diff --git a/@commitlint/core/src/rules/type-empty.test.js b/@commitlint/core/src/rules/type-empty.test.js
new file mode 100644
index 0000000000..76c8c6860e
--- /dev/null
+++ b/@commitlint/core/src/rules/type-empty.test.js
@@ -0,0 +1,49 @@
+import test from 'ava';
+import parse from '../library/parse';
+import typeEmpty from './type-empty';
+
+const messages = {
+ empty: '(scope):',
+ filled: 'type: subject'
+};
+
+const parsed = {
+ empty: parse(messages.empty),
+ filled: parse(messages.filled)
+};
+
+test('without type should succeed for empty keyword', async t => {
+ const [actual] = typeEmpty(await parsed.empty);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('without type should fail for "never"', async t => {
+ const [actual] = typeEmpty(await parsed.empty, 'never');
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('without type should succeed for "always"', async t => {
+ const [actual] = typeEmpty(await parsed.empty, 'always');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with type fail for empty keyword', async t => {
+ const [actual] = typeEmpty(await parsed.filled);
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('with type succeed for "never"', async t => {
+ const [actual] = typeEmpty(await parsed.filled, 'never');
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('with type fail for "always"', async t => {
+ const [actual] = typeEmpty(await parsed.filled, 'always');
+ const expected = false;
+ t.is(actual, expected);
+});
diff --git a/source/rules/type-enum.js b/@commitlint/core/src/rules/type-enum.js
similarity index 100%
rename from source/rules/type-enum.js
rename to @commitlint/core/src/rules/type-enum.js
diff --git a/@commitlint/core/src/rules/type-enum.test.js b/@commitlint/core/src/rules/type-enum.test.js
new file mode 100644
index 0000000000..698e7a0794
--- /dev/null
+++ b/@commitlint/core/src/rules/type-enum.test.js
@@ -0,0 +1,123 @@
+import test from 'ava';
+import parse from '../library/parse';
+import check from './type-enum';
+
+const messages = {
+ empty: '(): \n',
+ a: 'a(): \n',
+ b: 'b(): \n'
+};
+
+const parsed = {
+ empty: parse(messages.empty),
+ a: parse(messages.a),
+ b: parse(messages.b)
+};
+
+test('empty succeeds', async t => {
+ const [actual] = check(await parsed.empty);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('empty on "a" succeeds', async t => {
+ const [actual] = check(await parsed.empty, '', ['a']);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('empty on "always a" succeeds', async t => {
+ const [actual] = check(await parsed.empty, 'always', ['a']);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('empty on "never a" succeeds', async t => {
+ const [actual] = check(await parsed.empty, 'never', ['a']);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('empty on "always a, b" succeeds', async t => {
+ const [actual] = check(await parsed.empty, 'always', ['a', 'b']);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('empty on "never a, b" succeeds', async t => {
+ const [actual] = check(await parsed.empty, 'neber', ['a', 'b']);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('a on "a" succeeds', async t => {
+ const [actual] = check(await parsed.a, '', ['a']);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('a on "always a" succeeds', async t => {
+ const [actual] = check(await parsed.a, 'always', ['a']);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('a on "never a" fails', async t => {
+ const [actual] = check(await parsed.a, 'never', ['a']);
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('b on "b" succeeds', async t => {
+ const [actual] = check(await parsed.b, '', ['b']);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('b on "always b" succeeds', async t => {
+ const [actual] = check(await parsed.b, 'always', ['b']);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('b on "never b" fails', async t => {
+ const [actual] = check(await parsed.b, 'never', ['b']);
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('a on "a, b" succeeds', async t => {
+ const [actual] = check(await parsed.a, '', ['a', 'b']);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('a on "always a, b" succeeds', async t => {
+ const [actual] = check(await parsed.a, 'always', ['a', 'b']);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('a on "never a, b" fails', async t => {
+ const [actual] = check(await parsed.a, 'never', ['a', 'b']);
+ const expected = false;
+ t.is(actual, expected);
+});
+
+test('b on "a, b" succeeds', async t => {
+ const [actual] = check(await parsed.b, '', ['a', 'b']);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('b on "always a, b" succeeds', async t => {
+ const [actual] = check(await parsed.b, 'always', ['a', 'b']);
+ const expected = true;
+ t.is(actual, expected);
+});
+
+test('b on "never a, b" fails', async t => {
+ const [actual] = check(await parsed.b, 'never', ['a', 'b']);
+ const expected = false;
+ t.is(actual, expected);
+});
diff --git a/source/rules/type-max-length.js b/@commitlint/core/src/rules/type-max-length.js
similarity index 100%
rename from source/rules/type-max-length.js
rename to @commitlint/core/src/rules/type-max-length.js
diff --git a/source/rules/type-max-length.test.js b/@commitlint/core/src/rules/type-max-length.test.js
similarity index 63%
rename from source/rules/type-max-length.test.js
rename to @commitlint/core/src/rules/type-max-length.test.js
index 1be87bb227..d3f3e7bfb6 100644
--- a/source/rules/type-max-length.test.js
+++ b/@commitlint/core/src/rules/type-max-length.test.js
@@ -19,20 +19,20 @@ const parsed = {
long: parse(messages.long)
};
-test('with empty should succeed', t => {
- const [actual] = check(parsed.empty, '', value);
+test('with empty should succeed', async t => {
+ const [actual] = check(await parsed.empty, '', value);
const expected = true;
t.is(actual, expected);
});
-test('with short should succeed', t => {
- const [actual] = check(parsed.short, '', value);
+test('with short should succeed', async t => {
+ const [actual] = check(await parsed.short, '', value);
const expected = true;
t.is(actual, expected);
});
-test('with long should fail', t => {
- const [actual] = check(parsed.long, '', value);
+test('with long should fail', async t => {
+ const [actual] = check(await parsed.long, '', value);
const expected = false;
t.is(actual, expected);
});
diff --git a/source/rules/type-min-length.js b/@commitlint/core/src/rules/type-min-length.js
similarity index 100%
rename from source/rules/type-min-length.js
rename to @commitlint/core/src/rules/type-min-length.js
diff --git a/source/rules/type-min-length.test.js b/@commitlint/core/src/rules/type-min-length.test.js
similarity index 63%
rename from source/rules/type-min-length.test.js
rename to @commitlint/core/src/rules/type-min-length.test.js
index 4f3cd59636..2371df2324 100644
--- a/source/rules/type-min-length.test.js
+++ b/@commitlint/core/src/rules/type-min-length.test.js
@@ -19,20 +19,20 @@ const parsed = {
long: parse(messages.long)
};
-test('with empty should succeed', t => {
- const [actual] = check(parsed.empty, '', value);
+test('with empty should succeed', async t => {
+ const [actual] = check(await parsed.empty, '', value);
const expected = true;
t.is(actual, expected);
});
-test('with short should fail', t => {
- const [actual] = check(parsed.short, '', value);
+test('with short should fail', async t => {
+ const [actual] = check(await parsed.short, '', value);
const expected = false;
t.is(actual, expected);
});
-test('with long should succeed', t => {
- const [actual] = check(parsed.long, '', value);
+test('with long should succeed', async t => {
+ const [actual] = check(await parsed.long, '', value);
const expected = true;
t.is(actual, expected);
});
diff --git a/@commitlint/example-prompt/CHANGELOG.md b/@commitlint/example-prompt/CHANGELOG.md
new file mode 100644
index 0000000000..d9d2d4caa7
--- /dev/null
+++ b/@commitlint/example-prompt/CHANGELOG.md
@@ -0,0 +1,18 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+
+
+## 3.0.1 (2017-07-11)
+
+
+
+
+# 3.0.0 (2017-07-10)
+
+
+
+
+
+# 3.0.0 (2017-07-10)
diff --git a/@commitlint/example-prompt/package.json b/@commitlint/example-prompt/package.json
new file mode 100644
index 0000000000..59bf2a3077
--- /dev/null
+++ b/@commitlint/example-prompt/package.json
@@ -0,0 +1,22 @@
+{
+ "name": "@commitlint/example-prompt",
+ "private": true,
+ "version": "3.0.1",
+ "description": "Example for prompt guide",
+ "scripts": {
+ "commit": "commit"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/marionebl/commitlint.git"
+ },
+ "author": "Mario Nebl ",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/marionebl/commitlint/issues"
+ },
+ "homepage": "https://github.com/marionebl/commitlint#readme",
+ "devDependencies": {
+ "@commitlint/prompt-cli": "^3.0.1"
+ }
+}
diff --git a/@commitlint/prompt-cli/CHANGELOG.md b/@commitlint/prompt-cli/CHANGELOG.md
new file mode 100644
index 0000000000..6ba43407f3
--- /dev/null
+++ b/@commitlint/prompt-cli/CHANGELOG.md
@@ -0,0 +1,28 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+
+
+## 3.0.1 (2017-07-11)
+
+
+
+
+# 3.0.0 (2017-07-10)
+
+
+### Features
+
+* **prompt-cli:** add standalone prompt interface ([b0239d2](https://github.com/marionebl/commitlint/commit/b0239d2))
+
+
+
+
+
+# 3.0.0 (2017-07-10)
+
+
+### Features
+
+* **prompt-cli:** add standalone prompt interface ([b0239d2](https://github.com/marionebl/commitlint/commit/b0239d2))
diff --git a/@commitlint/prompt-cli/cli.js b/@commitlint/prompt-cli/cli.js
new file mode 100755
index 0000000000..0936d26df3
--- /dev/null
+++ b/@commitlint/prompt-cli/cli.js
@@ -0,0 +1,29 @@
+#!/usr/bin/env node
+const execa = require('execa');
+const meow = require('meow');
+const prompter = require('@commitlint/prompt').prompter;
+
+const HELP = `
+ Usage
+ $ commit
+`;
+
+const _ = undefined;
+const prompt = () => prompter(_, commit);
+
+main(meow(HELP))
+ .catch(err => {
+ setTimeout(() => {
+ throw err;
+ });
+ });
+
+function main() {
+ return prompt();
+}
+
+function commit(message) {
+ const c = execa('git', ['commit', '-m', message]);
+ c.stdout.pipe(process.stdout);
+ c.stderr.pipe(process.stderr);
+}
diff --git a/@commitlint/prompt-cli/package.json b/@commitlint/prompt-cli/package.json
new file mode 100644
index 0000000000..3594503ef4
--- /dev/null
+++ b/@commitlint/prompt-cli/package.json
@@ -0,0 +1,36 @@
+{
+ "name": "@commitlint/prompt-cli",
+ "version": "3.0.1",
+ "description": "commit prompt using .commitlintrc",
+ "bin": {
+ "commit": "./cli.js"
+ },
+ "scripts": {
+ "clean": "rimraf lib",
+ "commit": "$npm_package_bin_commit",
+ "pretest": "dep-check"
+ },
+ "xo": false,
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/marionebl/commitlint.git"
+ },
+ "keywords": [
+ "commitlint",
+ "prompt"
+ ],
+ "author": "Mario Nebl ",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/marionebl/commitlint/issues"
+ },
+ "homepage": "https://github.com/marionebl/commitlint#readme",
+ "devDependencies": {
+ "@commitlint/utils": "^3.0.0"
+ },
+ "dependencies": {
+ "@commitlint/prompt": "^3.0.1",
+ "execa": "^0.7.0",
+ "meow": "^3.7.0"
+ }
+}
diff --git a/@commitlint/prompt-cli/readme.md b/@commitlint/prompt-cli/readme.md
new file mode 100644
index 0000000000..31e79b2b1f
--- /dev/null
+++ b/@commitlint/prompt-cli/readme.md
@@ -0,0 +1,15 @@
+> commit prompt using .commitlintrc
+
+# @commitlint/prompt-cli
+
+## Getting started
+
+```bash
+npm install --g @commitlint/prompt-cli @commitlint/config-angular
+echo "module.exports = {extends: ['@commitlint/config-angular']};" > .commitlint.config.js
+```
+
+```bash
+git add .
+commit
+```
diff --git a/@commitlint/prompt/CHANGELOG.md b/@commitlint/prompt/CHANGELOG.md
new file mode 100644
index 0000000000..d9d2d4caa7
--- /dev/null
+++ b/@commitlint/prompt/CHANGELOG.md
@@ -0,0 +1,18 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+
+
+## 3.0.1 (2017-07-11)
+
+
+
+
+# 3.0.0 (2017-07-10)
+
+
+
+
+
+# 3.0.0 (2017-07-10)
diff --git a/@commitlint/prompt/package.json b/@commitlint/prompt/package.json
new file mode 100644
index 0000000000..f30d429132
--- /dev/null
+++ b/@commitlint/prompt/package.json
@@ -0,0 +1,114 @@
+{
+ "name": "@commitlint/prompt",
+ "version": "3.0.1",
+ "description": "commitizen prompt using .commitlintrc",
+ "main": "./lib/index.js",
+ "scripts": {
+ "build": "cross-env NODE_ENV=production babel src --out-dir lib",
+ "clean": "rimraf lib",
+ "commit": "git-cz",
+ "pretest": "dep-check",
+ "test": "ava",
+ "prepublish": "npm run build"
+ },
+ "ava": {
+ "babel": "inherit",
+ "require": [
+ "babel-register"
+ ],
+ "files": [
+ "src/**/*.test.js"
+ ],
+ "sources": [
+ "src/**/*.js"
+ ]
+ },
+ "babel": {
+ "env": {
+ "development": {
+ "sourceMaps": "inline",
+ "plugins": [
+ "add-module-exports",
+ [
+ "transform-runtime",
+ {
+ "polyfill": false,
+ "regenerator": true
+ }
+ ]
+ ]
+ },
+ "production": {
+ "ignore": [
+ "**/*.test.js"
+ ]
+ }
+ },
+ "presets": [
+ [
+ "env",
+ {
+ "targets": {
+ "node": 4
+ }
+ }
+ ],
+ "stage-0"
+ ],
+ "plugins": [
+ "add-module-exports",
+ [
+ "transform-runtime",
+ {
+ "polyfill": false,
+ "regenerator": true
+ }
+ ]
+ ]
+ },
+ "config": {
+ "commitizen": {
+ "path": "./@commitlint/prompt"
+ }
+ },
+ "xo": false,
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/marionebl/commitlint.git"
+ },
+ "keywords": [
+ "conventional-changelog",
+ "commitlint",
+ "prompt",
+ "cz",
+ "commitizen"
+ ],
+ "author": "Mario Nebl ",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/marionebl/commitlint/issues"
+ },
+ "homepage": "https://github.com/marionebl/commitlint#readme",
+ "devDependencies": {
+ "@commitlint/utils": "^3.0.0",
+ "ava": "^0.20.0",
+ "babel-cli": "^6.24.1",
+ "babel-plugin-add-module-exports": "^0.2.1",
+ "babel-plugin-transform-runtime": "^6.23.0",
+ "babel-preset-env": "^1.6.0",
+ "babel-preset-stage-0": "^6.24.1",
+ "babel-register": "^6.24.1",
+ "commitizen": "^2.9.6",
+ "cross-env": "^5.0.1",
+ "throat": "^4.1.0"
+ },
+ "dependencies": {
+ "@commitlint/core": "^3.0.1",
+ "babel-polyfill": "^6.23.0",
+ "babel-runtime": "^6.23.0",
+ "chalk": "^1.1.1",
+ "lodash": "^4.17.4",
+ "throat": "^4.1.0",
+ "vorpal": "^1.10.0"
+ }
+}
diff --git a/@commitlint/prompt/readme.md b/@commitlint/prompt/readme.md
new file mode 100644
index 0000000000..b10fd2665f
--- /dev/null
+++ b/@commitlint/prompt/readme.md
@@ -0,0 +1,29 @@
+> commitizen adapter using .commitlintrc
+
+# @commitlint/prompt
+
+## Getting started
+
+```bash
+npm install --save @commitlint/prompt @commitlint/config-angular commitizen
+echo "module.exports = {extends: ['@commitlint/config-angular']};" > .commitlint.config.js
+```
+
+In package.json
+```
+{
+ "scripts": {
+ "commit": "git-cz"
+ },
+ "config": {
+ "commitizen": {
+ "path": "@commitlint/prompt"
+ }
+ }
+}
+```
+
+```bash
+git add .
+npm run commit
+```
diff --git a/@commitlint/prompt/src/index.js b/@commitlint/prompt/src/index.js
new file mode 100644
index 0000000000..78d5ab2a17
--- /dev/null
+++ b/@commitlint/prompt/src/index.js
@@ -0,0 +1,14 @@
+import 'babel-polyfill'; // eslint-disable-line import/no-unassigned-import
+import vorpal from 'vorpal';
+import input from './input';
+
+/**
+ * Entry point for commitizen
+ * @param {object} _ inquirer instance passed by commitizen, unused
+ * @param {function} commit callback to execute with complete commit message
+ * @return {string} genersated commit message
+ */
+export const prompter = async (_, commit) => { // eslint-disable-line import/prefer-default-export
+ const message = await input(vorpal);
+ commit(message);
+};
diff --git a/@commitlint/prompt/src/input.js b/@commitlint/prompt/src/input.js
new file mode 100644
index 0000000000..d4779d9169
--- /dev/null
+++ b/@commitlint/prompt/src/input.js
@@ -0,0 +1,94 @@
+import {load} from '@commitlint/core';
+import throat from 'throat';
+
+import format from './library/format';
+import getHasName from './library/get-has-name';
+import getPrompt from './library/get-prompt';
+import settings from './settings';
+
+export default input;
+
+/* eslint-disable valid-jsdoc */
+/**
+ * Get user input by interactive prompt based on
+ * conventional-changelog-lint rules.
+ * @param {function} prompter
+ * @return {Promise} commit message
+ */
+async function input(prompter) {
+ const results = {
+ type: null,
+ scope: null,
+ subject: null,
+ body: null,
+ footer: null
+ };
+
+ const {rules} = await load();
+
+ await Promise.all(['type', 'scope', 'subject', 'body', 'footer']
+ .map(throat(1, async input => {
+ const inputRules = getRules(input, rules);
+ const inputSettings = settings[input];
+
+ const isHeader = ['type', 'scope', 'subject'].indexOf(input) > -1;
+
+ const headerLengthRule = getRules('header', rules)
+ .filter(getHasName('max-length'))[0];
+
+ if (isHeader && headerLengthRule) {
+ const [, [severity, applicable, length]] = headerLengthRule;
+ if (severity > 0 && applicable === 'always') {
+ inputSettings.header = {
+ length
+ };
+ }
+ }
+
+ results[input] = await getPrompt(input, { // eslint-disable-line no-await-in-loop
+ rules: inputRules,
+ settings: inputSettings,
+ results,
+ prompter
+ });
+ })))
+ .catch(err => {
+ console.error(err);
+ return '';
+ });
+
+ // Return the results
+ return format(results);
+}
+
+/**
+ * Get prefix for a given rule id
+ * @param {string} id of the rule
+ * @return {string} prefix of the rule
+ */
+function getRulePrefix(id) {
+ const fragments = id.split('-');
+ const [prefix] = fragments;
+ return fragments.length > 1 ?
+ prefix :
+ null;
+}
+
+/**
+ * Get a predecate matching rule definitions with a given prefix
+ * @param {[type]} name [description]
+ * @return {[type]} [description]
+ */
+function getHasPrefix(name) {
+ return rule => getRulePrefix(rule[0]) === name;
+}
+
+/**
+ * Get rules for a given prefix
+ * @param {string} prefix to search in rule names
+ * @param {object} rules rules to search in
+ * @return {object} rules matching the prefix search
+ */
+function getRules(prefix, rules) {
+ return Object.entries(rules).filter(getHasPrefix(prefix));
+}
diff --git a/@commitlint/prompt/src/library/enum-rule-is-active.js b/@commitlint/prompt/src/library/enum-rule-is-active.js
new file mode 100644
index 0000000000..b20318ed21
--- /dev/null
+++ b/@commitlint/prompt/src/library/enum-rule-is-active.js
@@ -0,0 +1,14 @@
+import ruleIsApplicable from './rule-is-applicable';
+import ruleIsActive from './rule-is-active';
+
+/**
+ * [enumRuleIsActive description]
+ * @param {[type]} rule [description]
+ * @return {[type]} [description]
+ */
+export default function enumRuleIsActive(rule) {
+ const [, [, , value]] = rule;
+ return ruleIsActive(rule) &&
+ ruleIsApplicable(rule) &&
+ value.length > 0;
+}
diff --git a/@commitlint/prompt/src/library/format.js b/@commitlint/prompt/src/library/format.js
new file mode 100644
index 0000000000..19d3d5b48f
--- /dev/null
+++ b/@commitlint/prompt/src/library/format.js
@@ -0,0 +1,33 @@
+import chalk from 'chalk';
+import {entries} from 'lodash';
+
+export default format;
+
+/**
+ * Get formatted commit message
+ * @param {object} input object containing structured results
+ * @param {boolean} debug show debug information in commit message
+ * @return {string} formatted debug message
+ */
+function format(input, debug = false) {
+ const results = debug ?
+ entries(input)
+ .reduce((registry, item) => {
+ const [name, value] = item;
+ return {
+ ...registry,
+ [name]: value === null ?
+ chalk.grey(`<${name}>`) :
+ chalk.bold(value)
+ };
+ }, {}) :
+ input;
+
+ // Return formatted string
+ const {type, scope, subject, body, footer} = results;
+ return [
+ `${type}${scope ? '(' : ''}${scope}${scope ? ')' : ''}${type || scope ? ':' : ''} ${subject}`,
+ body,
+ footer
+ ].filter(Boolean).join('\n');
+}
diff --git a/@commitlint/prompt/src/library/get-forced-case-fn.js b/@commitlint/prompt/src/library/get-forced-case-fn.js
new file mode 100644
index 0000000000..b5be3a239a
--- /dev/null
+++ b/@commitlint/prompt/src/library/get-forced-case-fn.js
@@ -0,0 +1,26 @@
+import getForcedCase from './get-forced-case';
+
+/**
+ * Get forced case for rule
+ * @param {object} rule to parse
+ * @return {fn} transform function applying the enforced case
+ */
+export default function getForcedCaseFn(rule) {
+ const noop = input => input;
+ const lowerCase = input => String.prototype.toLowerCase.call(input);
+ const upperCase = input => String.prototype.toUpperCase.call(input);
+
+ if (!rule) {
+ return noop;
+ }
+
+ const forcedCase = getForcedCase(rule);
+
+ if (forcedCase === null) {
+ return noop;
+ }
+
+ return forcedCase === 'lowerCase' ?
+ lowerCase :
+ upperCase;
+}
diff --git a/@commitlint/prompt/src/library/get-forced-case.js b/@commitlint/prompt/src/library/get-forced-case.js
new file mode 100644
index 0000000000..8927a9f4ac
--- /dev/null
+++ b/@commitlint/prompt/src/library/get-forced-case.js
@@ -0,0 +1,27 @@
+/**
+ * Get forced case for rule
+ * @param {object} rule to parse
+ * @return {string|null} transform function applying the enforced case
+ */
+export default function getForcedCase(rule) {
+ if (!rule) {
+ return null;
+ }
+
+ const [, [severity, applicable, value]] = rule;
+ const negated = applicable === 'never';
+
+ if (severity === 0) {
+ return null;
+ }
+
+ if (negated) {
+ return value === 'lowerCase' ?
+ 'upperCase' :
+ 'lowerCase';
+ }
+
+ return value === 'lowerCase' ?
+ 'lowerCase' :
+ 'upperCase';
+}
diff --git a/@commitlint/prompt/src/library/get-forced-case.js-fn.js b/@commitlint/prompt/src/library/get-forced-case.js-fn.js
new file mode 100644
index 0000000000..b5be3a239a
--- /dev/null
+++ b/@commitlint/prompt/src/library/get-forced-case.js-fn.js
@@ -0,0 +1,26 @@
+import getForcedCase from './get-forced-case';
+
+/**
+ * Get forced case for rule
+ * @param {object} rule to parse
+ * @return {fn} transform function applying the enforced case
+ */
+export default function getForcedCaseFn(rule) {
+ const noop = input => input;
+ const lowerCase = input => String.prototype.toLowerCase.call(input);
+ const upperCase = input => String.prototype.toUpperCase.call(input);
+
+ if (!rule) {
+ return noop;
+ }
+
+ const forcedCase = getForcedCase(rule);
+
+ if (forcedCase === null) {
+ return noop;
+ }
+
+ return forcedCase === 'lowerCase' ?
+ lowerCase :
+ upperCase;
+}
diff --git a/@commitlint/prompt/src/library/get-forced-leading-fn.js b/@commitlint/prompt/src/library/get-forced-leading-fn.js
new file mode 100644
index 0000000000..8b304282ef
--- /dev/null
+++ b/@commitlint/prompt/src/library/get-forced-leading-fn.js
@@ -0,0 +1,54 @@
+/**
+ * Get forced leading for rule
+ * @param {object} rule to parse
+ * @return {fn} transform function applying the leading
+ */
+export default function getForcedLeadingFn(rule) {
+ const noop = input => input;
+ const remove = input => {
+ const fragments = input.split('\n');
+ return fragments[0] === '' ?
+ fragments.slice(1).join('\n') :
+ input;
+ };
+ const lead = input => {
+ const fragments = input.split('\n');
+ return fragments[0] === '' ?
+ input :
+ ['', ...fragments].join('\n');
+ };
+
+ if (!rule) {
+ return noop;
+ }
+
+ const leading = getForcedLeading(rule);
+
+ if (leading === null) {
+ return noop;
+ }
+
+ return leading ?
+ lead :
+ remove;
+}
+
+/**
+ * Get forced leading for rule
+ * @param {object} rule to parse
+ * @return {boolean|null} transform function applying the leading
+ */
+function getForcedLeading(rule) {
+ if (!rule) {
+ return null;
+ }
+
+ const [, [severity, applicable]] = rule;
+ const negated = applicable === 'never';
+
+ if (severity === 0) {
+ return null;
+ }
+
+ return !negated;
+}
diff --git a/@commitlint/prompt/src/library/get-has-name.js b/@commitlint/prompt/src/library/get-has-name.js
new file mode 100644
index 0000000000..1f7df8f102
--- /dev/null
+++ b/@commitlint/prompt/src/library/get-has-name.js
@@ -0,0 +1,10 @@
+import getRuleName from './get-rule-name';
+
+/**
+ * Get a predecate matching rule definitions with a given name
+ * @param {[type]} name [description]
+ * @return {[type]} [description]
+ */
+export default function getHasName(name) {
+ return rule => getRuleName(rule[0]) === name;
+}
diff --git a/@commitlint/prompt/src/library/get-prompt.js b/@commitlint/prompt/src/library/get-prompt.js
new file mode 100644
index 0000000000..3f5c4f843e
--- /dev/null
+++ b/@commitlint/prompt/src/library/get-prompt.js
@@ -0,0 +1,244 @@
+import chalk from 'chalk';
+
+import enumRuleIsActive from './enum-rule-is-active';
+import format from './format';
+import getForcedCase from './get-forced-case';
+import getForcedCaseFn from './get-forced-case-fn';
+import getForcedLeadingFn from './get-forced-leading-fn';
+import getHasName from './get-has-name';
+import meta from './meta';
+
+export default getPrompt;
+
+/**
+ * Get a cli prompt based on rule configuration
+ * @param {string} type type of the data to gather
+ * @param {object} context rules to parse
+ * @return {object} prompt instance
+ */
+function getPrompt(type, context = {}) {
+ const {rules = [], settings = {}, results = {}, prompter} = context;
+
+ if (typeof prompter !== 'function') {
+ throw new TypeError('Missing prompter function in getPrompt context');
+ }
+
+ const prompt = prompter();
+
+ if (typeof prompt.removeAllListeners !== 'function') {
+ throw new TypeError('getPrompt: prompt.removeAllListeners is not a function');
+ }
+
+ if (typeof prompt.command !== 'function') {
+ throw new TypeError('getPrompt: prompt.command is not a function');
+ }
+
+ if (typeof prompt.catch !== 'function') {
+ throw new TypeError('getPrompt: prompt.catch is not a function');
+ }
+
+ if (typeof prompt.addListener !== 'function') {
+ throw new TypeError('getPrompt: prompt.addListener is not a function');
+ }
+
+ if (typeof prompt.log !== 'function') {
+ throw new TypeError('getPrompt: prompt.log is not a function');
+ }
+
+ if (typeof prompt.delimiter !== 'function') {
+ throw new TypeError('getPrompt: prompt.delimiter is not a function');
+ }
+
+ if (typeof prompt.show !== 'function') {
+ throw new TypeError('getPrompt: prompt.show is not a function');
+ }
+
+ const enumRule = rules
+ .filter(getHasName('enum'))
+ .filter(enumRuleIsActive)[0];
+
+ const emptyRule = rules
+ .filter(getHasName('empty'))[0];
+
+ const mustBeEmpty = emptyRule ?
+ emptyRule[1][0] > 0 &&
+ emptyRule[1][1] === 'always' :
+ false;
+
+ const mayNotBeEmpty = emptyRule ?
+ emptyRule[1][0] > 0 &&
+ emptyRule[1][1] === 'never' :
+ false;
+
+ const mayBeEmpty = !mayNotBeEmpty;
+
+ if (mustBeEmpty) {
+ prompt.removeAllListeners('keypress');
+ prompt.removeAllListeners('client_prompt_submit');
+ prompt.ui.redraw.done();
+ return Promise.resolve();
+ }
+
+ const caseRule = rules
+ .filter(getHasName('case'))[0];
+
+ const forcedCase = getForcedCase(caseRule);
+ const forceCaseFn = getForcedCaseFn(caseRule);
+
+ const leadingBlankRule = rules
+ .filter(getHasName('leading-blank'))[0];
+
+ const forceLeadingBlankFn = getForcedLeadingFn(leadingBlankRule);
+
+ const maxLenghtRule = rules
+ .filter(getHasName('max-length'))[0];
+
+ const hasMaxLength = maxLenghtRule && maxLenghtRule[1][0] > 0;
+
+ const inputMaxLength = hasMaxLength ?
+ maxLenghtRule[1][1] :
+ Infinity;
+
+ const headerLength = settings.header ?
+ settings.header.length :
+ Infinity;
+
+ const remainingHeaderLength = headerLength ?
+ headerLength - [
+ results.type,
+ results.scope,
+ results.scope ? '()' : '',
+ results.type && results.scope ? ':' : '',
+ results.subject
+ ].join('').length :
+ Infinity;
+
+ const maxLength = Math.min(inputMaxLength, remainingHeaderLength);
+
+ return new Promise(resolve => {
+ // Add the defined enums as sub commands if applicable
+ if (enumRule) {
+ const [, [, , enums]] = enumRule;
+
+ enums.forEach(enumerable => {
+ const enumSettings = (settings.enumerables || {})[enumerable] || {};
+ prompt
+ .command(enumerable)
+ .description(enumSettings.description || '')
+ .action(() => {
+ prompt.removeAllListeners();
+ prompt.ui.redraw.done();
+ return resolve(forceLeadingBlankFn(forceCaseFn(enumerable)));
+ });
+ });
+ } else {
+ prompt
+ .catch('[text...]')
+ .action(parameters => {
+ const {text = ''} = parameters;
+ prompt.removeAllListeners();
+ prompt.ui.redraw.done();
+ return resolve(forceLeadingBlankFn(forceCaseFn(text.join(' '))));
+ });
+ }
+
+ if (mayBeEmpty) {
+ // Add an easy exit command
+ prompt
+ .command(':skip')
+ .description('Skip the input if possible.')
+ .action(() => {
+ prompt.removeAllListeners();
+ prompt.ui.redraw.done();
+ resolve('');
+ });
+ }
+
+ // Handle empty input
+ const onSubmit = input => {
+ if (input.length > 0) {
+ return;
+ }
+
+ // Show help if enum is defined and input may not be empty
+ if (mayNotBeEmpty) {
+ prompt.ui.log(chalk.yellow(`⚠ ${chalk.bold(type)} may not be empty.`));
+ }
+
+ if (mayBeEmpty) {
+ prompt.ui.log(chalk.blue(`ℹ Enter ${chalk.bold(':skip')} to omit ${chalk.bold(type)}.`));
+ }
+
+ if (enumRule) {
+ prompt.exec('help');
+ }
+ };
+
+ const drawRemaining = length => {
+ if (length < Infinity) {
+ const colors = [
+ {
+ threshold: 5,
+ color: 'red'
+ },
+ {
+ threshold: 10,
+ color: 'yellow'
+ },
+ {
+ threshold: Infinity,
+ color: 'grey'
+ }
+ ];
+
+ const color = colors
+ .filter(item => {
+ return item.threshold >= length;
+ })
+ .map(item => item.color)[0];
+
+ prompt.ui.redraw(chalk[color](`${length} characters left`));
+ }
+ };
+
+ const onKey = event => {
+ const sanitized = forceCaseFn(event.value);
+ const cropped = sanitized.slice(0, maxLength);
+
+ // We **could** do live editing, but there are some quirks to solve
+ /* const live = merge({}, results, {
+ [type]: cropped
+ });
+ prompt.ui.redraw(`\n\n${format(live, true)}\n\n`); */
+
+ if (maxLength) {
+ drawRemaining(maxLength - cropped.length);
+ }
+ prompt.ui.input(cropped);
+ };
+
+ prompt.addListener('keypress', onKey);
+ prompt.addListener('client_prompt_submit', onSubmit);
+
+ prompt.log(`\n\nPlease enter a ${chalk.bold(type)}: ${meta({
+ optional: !mayNotBeEmpty,
+ required: mayNotBeEmpty,
+ 'tab-completion': typeof enumRule !== 'undefined',
+ header: typeof settings.header !== 'undefined',
+ case: forcedCase,
+ 'multi-line': settings.multiline
+ })}`);
+
+ if (settings.description) {
+ prompt.log(chalk.grey(`${settings.description}\n`));
+ }
+
+ prompt.log(`\n\n${format(results, true)}\n\n`);
+
+ drawRemaining(maxLength);
+
+ prompt
+ .delimiter(`❯ ${type}:`)
+ .show();
+ });
+}
diff --git a/@commitlint/prompt/src/library/get-prompt.test.js b/@commitlint/prompt/src/library/get-prompt.test.js
new file mode 100644
index 0000000000..fab4ec110e
--- /dev/null
+++ b/@commitlint/prompt/src/library/get-prompt.test.js
@@ -0,0 +1,84 @@
+import test from 'ava';
+import getPrompt from './get-prompt';
+
+test('throws without params', t => {
+ t.throws(() => getPrompt(), /Missing prompter function/);
+});
+
+test('throws with incompatible prompter', t => {
+ t.throws(() => getPrompt('type', {
+ prompter() {
+ return {};
+ }
+ }), /prompt.removeAllListeners/);
+});
+
+test('returns input unaltered wihtout rules', async t => {
+ const message = await getPrompt('type', {
+ prompter: stub('foobar')
+ });
+
+ t.is(message, 'foobar');
+});
+
+function stub(input = '') {
+ return stubPrompter;
+
+ function stubPrompter() {
+ const called = [];
+ const actions = [];
+
+ const instance = {
+ action(...args) {
+ actions.push(args[0]);
+ called.push([instance.action, args]);
+ },
+ addListener(...args) {
+ called.push([instance.addListener, args]);
+ },
+ catch(...args) {
+ called.push([instance.catch, args]);
+ return instance;
+ },
+ command(...args) {
+ called.push([instance.command, args]);
+ return instance;
+ },
+ description(...args) {
+ called.push([instance.description, args]);
+ return instance;
+ },
+ delimiter(...args) {
+ called.push([instance.delimiter, args]);
+ return instance;
+ },
+ log(...args) {
+ called.push([instance.log, args]);
+ return instance;
+ },
+ removeAllListeners(...args) {
+ called.push([instance.removeAllListeners, args]);
+ },
+ show(...args) {
+ called.push([instance.show, args]);
+ return instance;
+ },
+ ui: {
+ redraw: {
+ done(...args) {
+ called.push([instance.ui.redraw.done, args]);
+ }
+ }
+ },
+ called
+ };
+
+ setTimeout(() => {
+ actions[0]({
+ text: Array.isArray(input) ? input : [input]
+ });
+ });
+
+ return instance;
+ }
+}
diff --git a/@commitlint/prompt/src/library/get-rule-name.js b/@commitlint/prompt/src/library/get-rule-name.js
new file mode 100644
index 0000000000..d644bedaeb
--- /dev/null
+++ b/@commitlint/prompt/src/library/get-rule-name.js
@@ -0,0 +1,11 @@
+/**
+ * Get name for a given rule id
+ * @param {string} id of the rule
+ * @return {[type]} name of the rule
+ */
+export default function getRuleName(id) {
+ const fragments = id.split('-');
+ return fragments.length > 1 ?
+ fragments.slice(1).join('-') :
+ fragments[0];
+}
diff --git a/@commitlint/prompt/src/library/meta.js b/@commitlint/prompt/src/library/meta.js
new file mode 100644
index 0000000000..71c73ffe02
--- /dev/null
+++ b/@commitlint/prompt/src/library/meta.js
@@ -0,0 +1,19 @@
+import chalk from 'chalk';
+import {entries} from 'lodash';
+
+/**
+ * Get formatted meta hints for configuration
+ * @param {object} settings dictionary to parse
+ * @return {string} formatted meta information
+ */
+export default function meta(settings) {
+ return chalk.grey(entries(settings)
+ .filter(item => item[1])
+ .map(item => {
+ const [name, value] = item;
+ return typeof value === 'boolean' ?
+ `[${name}]` :
+ `[${name}=${value}]`;
+ })
+ .join(' '));
+}
diff --git a/@commitlint/prompt/src/library/rule-is-active.js b/@commitlint/prompt/src/library/rule-is-active.js
new file mode 100644
index 0000000000..aae811891a
--- /dev/null
+++ b/@commitlint/prompt/src/library/rule-is-active.js
@@ -0,0 +1,9 @@
+/**
+ * Check if a rule definition is active
+ * @param {object} rule to check
+ * @return {boolean} if the rule definition is active
+ */
+export default function ruleIsActive(rule) {
+ const [, [severity]] = rule;
+ return severity > 0;
+}
diff --git a/@commitlint/prompt/src/library/rule-is-applicable.js b/@commitlint/prompt/src/library/rule-is-applicable.js
new file mode 100644
index 0000000000..ec94d60bcc
--- /dev/null
+++ b/@commitlint/prompt/src/library/rule-is-applicable.js
@@ -0,0 +1,9 @@
+/**
+ * Check if a rule definition is applicable
+ * @param {object} rule to check
+ * @return {boolean} if the rule definition is appliable
+ */
+export default function ruleIsApplicable(rule) {
+ const [, [, applicable]] = rule;
+ return applicable === 'always';
+}
diff --git a/@commitlint/prompt/src/settings.js b/@commitlint/prompt/src/settings.js
new file mode 100644
index 0000000000..387d7f88fc
--- /dev/null
+++ b/@commitlint/prompt/src/settings.js
@@ -0,0 +1,45 @@
+export default {
+ type: {
+ description: ' holds information about the goal of a change.',
+ enumerables: {
+ feat: {
+ description: 'Adds a new feature.'
+ },
+ fix: {
+ description: 'Solves a bug.'
+ },
+ docs: {
+ description: 'Adds or alters documentation.'
+ },
+ style: {
+ description: 'Improves formatting, white-space.'
+ },
+ refactor: {
+ description: 'Rewrites code without feature, performance or bug changes.'
+ },
+ perf: {
+ description: 'Improves performance.'
+ },
+ test: {
+ description: 'Adds or modifies tests.'
+ },
+ chore: {
+ description: 'Change build process, tooling or dependencies.'
+ }
+ }
+ },
+ scope: {
+ description: ' marks which sub-component of the project is affected'
+ },
+ subject: {
+ description: ' is a short, high-level description of the change'
+ },
+ body: {
+ description: ' holds additional information about the change',
+ multline: true
+ },
+ footer: {
+ description: '
+# commitlint
-Yeay! You want to contribute to conventional-changelog-lint. That's amazing!
+Yeay! You want to contribute to commitlint. That's amazing!
To smoothen everyone's experience involved with the project please take note of the following guidelines and rules.
## Found an Issue?
-Thank you for reporting any issues you find. We do our best to test and make conventional-changelog-lint as solid as possible, but any reported issue is a real help.
+Thank you for reporting any issues you find. We do our best to test and make commitlint as solid as possible, but any reported issue is a real help.
-> conventional-changelog-lint issues
+> commitlint issues
Please follow these guidelines when reporting issues:
* Provide a title in the format of ` when `
* Tag your issue with the tag `bug`
* Provide a short summary of what you are trying to do
* Provide the log of the encountered error if applicable
-* Provide the exact version of conventional-changelog-lint. Check `npm ls conventional-changelog-lint` when in doubt
+* Provide the exact version of commitlint. Check `npm ls @commitlint/cli` when in doubt
* Be awesome and consider contributing a [pull request](#want-to-contribute)
## Want to contribute?
-You consider contributing changes to conventional-changelog-lint – we dig that!
+You consider contributing changes to commitlint – we dig that!
Please consider these guidelines when filing a pull request:
-> conventional-changelog-lint pull requests
+> commitlint pull requests
* Follow the [Coding Rules](#coding-rules)
* Follow the [Commit Rules](#commit-rules)
@@ -38,28 +36,22 @@ Please consider these guidelines when filing a pull request:
* What you removed
## Coding Rules
-To keep the code base of conventional-changelog-lint neat and tidy the following rules apply to every change
+To keep the code base of commitlint neat and tidy the following rules apply to every change
> Coding standards
-* [Happiness](/sindresorhus/xo) enforced via eslint
+* [Happiness](/sindresorhus/xo) enforced
* Favor micro library over swiss army knives (rimraf, ncp vs. fs-extra)
-* Coverage never drops below 90%
-* No change may lower coverage by more than 5%
* Be awesome
## Commit Rules
-To help everyone with understanding the commit history of conventional-changelog-lint the following commit rules are enforced.
-To make your life easier conventional-changelog-lint is commitizen-friendly and provides the npm run-script `commit`.
+To help everyone with understanding the commit history of commitlint the following commit rules are enforced.
+To make your life easier commitlint is commitizen-friendly and provides the npm run-script `commit`.
> Commit standards
-* [conventional-changelog](/commitizen/cz-conventional-changelog)
+* [conventional-changelog](./@commitlint/prompt)
* husky commit message hook available
* present tense
* maximum of 100 characters
* message format of `$type($scope): $message`
-
-
----
-Copyright 2016 by [Mario Nebl](https://github.com/marionebl) and [contributors](./graphs/contributors). Released under the [MIT license]('./license.md').
diff --git a/conventional-changelog-lint.gif b/conventional-changelog-lint.gif
deleted file mode 100644
index a5382aaae6..0000000000
Binary files a/conventional-changelog-lint.gif and /dev/null differ
diff --git a/docs/.nojekyll b/docs/.nojekyll
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/docs/_sidebar.md b/docs/_sidebar.md
new file mode 100644
index 0000000000..8141935f57
--- /dev/null
+++ b/docs/_sidebar.md
@@ -0,0 +1,16 @@
+* **Guides**
+ * [Local setup](guides-local-setup.md)
+ * [CI setup](guides-ci-setup.md)
+ * [Use prompt](guides-use-prompt.md)
+ * [Upgrade commitlint](guides-upgrade.md)
+
+* **Concepts**
+ * [Commit conventions](concepts-commit-conventions)
+ * [Shareable configuration](concepts-shareable-config.md)
+
+* **Reference**
+ * [CLI](reference-cli.md)
+ * [Rules](reference-rules.md)
+ * [API](reference-api.md)
+
+* [**Changelog**](changelog.md)
diff --git a/docs/changelog.md b/docs/changelog.md
new file mode 120000
index 0000000000..7b91ffb658
--- /dev/null
+++ b/docs/changelog.md
@@ -0,0 +1 @@
+../changelog.md
\ No newline at end of file
diff --git a/docs/concepts-commit-conventions.md b/docs/concepts-commit-conventions.md
new file mode 100644
index 0000000000..8f166dbaab
--- /dev/null
+++ b/docs/concepts-commit-conventions.md
@@ -0,0 +1,17 @@
+# Concept: Commit conventions
+
+Commit conventions allow your team to add more semantic meaning to your git history. This e.g. includes `type`, `scope` or `breaking changes`.
+
+With this additional information tools can derive useful human-readable information for releases of your project. Some examples are
+
+* Automated, rich changelogs
+* Aumomatic version bumps
+* Filter fo test harnesses to run
+
+The most common commit conventions follow this pattern:
+
+```
+type(scope?): subject
+body?
+footer?
+```
diff --git a/docs/concepts-shareable-config.md b/docs/concepts-shareable-config.md
new file mode 100644
index 0000000000..9e3fe91f6e
--- /dev/null
+++ b/docs/concepts-shareable-config.md
@@ -0,0 +1,41 @@
+# Concept: Shareable configuration
+
+Most commonly shareable configuration is delivered as npm package exporting
+an object containing `.rules` as default. To use shared configuration you specify it as item in the `.extends` array:
+
+```js
+// commitlint.config.js
+module.exports = {
+ extends: ['example'] // => @commitlint-config-example
+};
+```
+
+This causes `commitlint` to pick up `commitlint-config-example`. Make it available by installing it.
+
+```
+npm install --save-dev commitlint-config-example
+```
+
+The rules found in `commitlint-config-example` are merged with the rules in `commitlint.config.js`, if any.
+
+This works recursively, enabling shareable configuration to extend on an indefinite chain of other shareable configurations.
+
+## Special cases
+
+Scoped npm packages are not prefixed.
+
+```js
+// .commitlint.config.js
+module.exports = {
+ extends: ['@commitlint/config-angular'] // => @commitlint/config-angular
+};
+```
+
+The same is true for relative imports
+
+```js
+// .commitlint
+module.expors = {
+ extends: ['./example'] // => ./example.js
+}
+```
diff --git a/docs/guides-ci-setup.md b/docs/guides-ci-setup.md
new file mode 100644
index 0000000000..68205402ac
--- /dev/null
+++ b/docs/guides-ci-setup.md
@@ -0,0 +1,125 @@
+# Guide: CI Setup
+
+Enforce commit conventions with confidence by linting on your CI servers with `commitlint`.
+
+ We'll use TravisCI for this guide but the principles are valid for any CI server.
+
+## Install
+
+```sh
+# Create a git repository if needed
+git init
+
+# Create a package.json if needed
+npm init
+
+# Install and configure if needed
+npm install --save-dev @commitlint-{cli,angular}
+echo "module.exports = {extends: ['@commitlint/config-angular']};"
+```
+
+## First test run with Travis
+
+Add a `.travis.yml` to your project root
+
+```yml
+# .travis.yml
+language: node_js
+before_install: git fetch --unshallow
+script:
+ - ./node_modules/.bin/commitlint --from=HEAD~1
+ - npm test
+```
+
+Make sure Travis is connected to your git repository.
+Trigger a build by pushing to your repository.
+
+```
+git add .
+git commit -m "add travis stuff"
+```
+
+We expect this build to fail:
+
+```
+...
+./node_modules/.bin/commitlint --from=HEAD~1
+⧗ input: add travis stuff
+✖ message may not be empty [subject-empty]
+✖ type may not be empty [type-empty]
+✖ found 2 problems, 0 warnings
+```
+
+## Linting relevant commits
+
+What we did so far works but is not very useful as it simply lints the last commit in history.
+Let's change that by using environment information provided by TravisCI.
+
+Every build exposes the commit that triggered the build via `TRAVIS_COMMIT`.
+
+```yml
+# .travis.yml
+language: node_js
+before_install: git fetch --unshallow
+script:
+ - ./node_modules/.bin/commitlint --from=$TRAVIS_COMMIT
+ - npm test
+```
+
+That's a bit better, but we are not handling branches at all yet. Travis provides the branch we are on via `TRAVIS_BRANCH`.
+
+```yml
+# .travis.yml
+language: node_js
+before_install: git fetch --unshallow
+script:
+ - ./node_modules/.bin/commitlint --from="$TRAVIS_BRANCH" --to="$TRAVIS_COMMIT"
+ - ./node_modules/.bin/commitlint --from=$TRAVIS_COMMIT
+ - npm test
+```
+
+Nice. This handles direct commits and PR originating from the same repository. Let's add forks to the mix.
+
+## The full scripts
+
+We'll have to differentiate between forks and same-repo PRs on our own and move the linting to a dedicated script.
+
+```yml
+# .travis.yml
+language: node_js
+before_install: git fetch --unshallow
+script:
+ - /bin/bash lint-commits.sh"
+ - ./node_modules/.bin/commitlint --from=$TRAVIS_COMMIT
+ - npm test
+```
+
+```sh
+# lint-commits.sh
+#!/bin/bash
+set -e
+set -u
+
+if [[ $TRAVIS_PULL_REQUEST_SLUG != "" && $TRAVIS_PULL_REQUEST_SLUG != $TRAVIS_REPO_SLUG ]]; then
+ # This is a Pull Request from a different slug, hence a forked repository
+ git remote add "$TRAVIS_PULL_REQUEST_SLUG" "https://github.com/$TRAVIS_PULL_REQUEST_SLUG.git"
+ git fetch "$TRAVIS_PULL_REQUEST_SLUG"
+
+ # Use the fetched remote pointing to the source clone for comparison
+ TO="$TRAVIS_PULL_REQUEST_SLUG/$TRAVIS_PULL_REQUEST_BRANCH"
+else
+ # This is a Pull Request from the same remote, no clone repository
+ TO=$TRAVIS_COMMIT
+fi
+
+# Lint all commits in the PR
+# - Covers fork pull requests (when TO=slug/branch)
+# - Covers branch pull requests (when TO=branch)
+./node_modules/.bin/commitlint --from="$TRAVIS_BRANCH" --to="$TO"
+
+# Always lint the triggerig commit
+# - Covers direct commits
+./node_modules/.bin/commitlint --from="$TRAVIS_COMMIT"
+```
+
+?> Help yourself adopting a commit convention by using an interactive commit prompt. Learn how to use `@commitlint/prompt-cli` in the [Use prompt guide](guides-use-prompt.md)
diff --git a/docs/guides-local-setup.md b/docs/guides-local-setup.md
new file mode 100644
index 0000000000..2968517bd7
--- /dev/null
+++ b/docs/guides-local-setup.md
@@ -0,0 +1,62 @@
+# Guide: Local setup
+
+Get high commit message quality and short feedback cycles by linting commit messages right when they are authored.
+
+This guide demonstrates how to achieve this via git hooks.
+
+## Install commitlint
+
+Install `commitlint` and a `commitlint-config-*` of your choice as devDependency and
+configure `commitlint` to use it.
+
+```sh
+# Create a package.json if needed
+npm init
+
+# Install and configure if needed
+npm install --save-dev @commitlint-{cli,angular}
+echo "module.exports = {extends: ['@commitlint/config-angular']};"
+```
+
+## Install husky
+
+Install `husky` as devDependency, a handy git hook helper available on npm.
+
+```sh
+npm install --save-dev husky
+```
+
+This allows us to add [git hooks](https://github.com/typicode/husky/blob/master/HOOKS.md#hooks) directly into our `package.json` scripts.
+
+```json
+{
+ "scripts": {
+ "commitmsg": "commitlint -e"
+ }
+}
+```
+
+Using `commitmsg` gives us exactly what we want: It is executed everytime a new commit is created. Invoking `commitlint` with the `-e|--edit` flag causes it to correctly read the commit message from `.git/COMMIT_MSG`.
+
+## Test
+
+You can test the hook by simple commiting. You should see something like this if everything works.
+
+```
+git commit -m "foo: this will fail"
+husky > npm run -s commitmsg
+
+⧗ input: foo: this will fail
+✖ scope must be one of [build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test] [type-enum]
+✖ found 1 problems, 0 warnings
+
+husky > commit-msg hook failed (add --no-verify to bypass)
+
+git commit -m "chore: lint on commitmsg"
+husky > npm run -s commitmsg
+
+⧗ input: chore: lint on commitmsg
+✔ found 0 problems, 0 warnings
+```
+
+?> Local linting is fine for fast feedback but can easily be tinkered with. To ensure all commits are linted you'll want to check commits on an automated CI Server to. Learn how to in the [CI Setup guide](guides-ci-setup.md).
diff --git a/docs/guides-publish-config.md b/docs/guides-publish-config.md
new file mode 100644
index 0000000000..158e01fd03
--- /dev/null
+++ b/docs/guides-publish-config.md
@@ -0,0 +1 @@
+# Guide: Publish a config
diff --git a/docs/guides-upgrade.md b/docs/guides-upgrade.md
new file mode 100644
index 0000000000..ae880dd165
--- /dev/null
+++ b/docs/guides-upgrade.md
@@ -0,0 +1,53 @@
+# Upgrade Guides
+
+## Version 1 to 2
+
+```sh
+npm install --save-dev conventional-changelog-lint@latest
+```
+
+### Breaking changes
+
+#### CLI
+
+* None
+
+#### Config
+
+* **wildcards** config is ignored - as of version `2.0.0` the former `.wildcards` configuration is ignored entirely. If your `.conventional-changelog-lintrc`, `commitlint.config.js` or an extended shareable configuration has a `.wildcards` key a warning will be issued.
+
+#### API
+
+* None
+
+## Version 2 to 3
+
+```sh
+npm remove --save-dev conventional-changelog-lint
+npm install --save commitlint
+mv .conventional-changelog-lintrc .commitlintrc
+```
+
+* Rename all calls to `conventional-changelog-lint` to `commitlint`
+
+### Breaking changes
+
+#### CLI
+
+* `conventional-changelog-lint` command now is called `commitlint`
+* `commitlint` command now is installed via `@commitlint/cli`
+* `.conventional-changelog-lintrc` now is called `commitlint.config.js`
+* `commitlint` does not search upwards in the directory structure for config
+* `--preset | -p` flag was removed. The `angular` preset is used always.
+
+#### Config
+
+* `.preset` key is removed. The `angular` preset is used always.
+
+#### API
+
+* `getConfiguration(name, settings, seed)` changed to `load(seed)`
+* `getMessages(range)` changed to `read(range)`
+* `getPreset(name, require)` removed
+* `format(report, options)` now only respects `.color` on `options`
+* `lint(message, options)` changed to `lint(message, rules)`
diff --git a/docs/guides-use-prompt.md b/docs/guides-use-prompt.md
new file mode 100644
index 0000000000..1da99aa35a
--- /dev/null
+++ b/docs/guides-use-prompt.md
@@ -0,0 +1,36 @@
+# Guide: Use prompt
+
+`@commitlint/prompt-cli` helps with fast authoring of commit messages and ensures they adhere to the commit convention configured in `commitlint.config.js`.
+
+## Install
+
+```sh
+# Create a git repository if needed
+git init
+
+# Create a package.json if needed
+npm init
+
+# Install and configure if needed
+npm install --save-dev @commitlint-{cli,angular,prompt-cli}
+echo "module.exports = {extends: ['@commitlint/config-angular']};"
+```
+
+## Provide a shortcut
+
+To make prompt-cli easy to use, add a npm run-script to your `package.json`
+
+```json
+{
+ "scripts": {
+ "commit": "commit"
+ }
+}
+```
+
+Test the prompt by executing
+
+```sh
+git add .
+npm run commit
+```
diff --git a/docs/index.html b/docs/index.html
new file mode 100644
index 0000000000..b1b7a0da45
--- /dev/null
+++ b/docs/index.html
@@ -0,0 +1,21 @@
+
+
+
+
+ commitlint - Lint commit messages
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/readme.md b/docs/readme.md
new file mode 100644
index 0000000000..fbbc7a1e38
--- /dev/null
+++ b/docs/readme.md
@@ -0,0 +1,40 @@
+> Lint commit messages
+
+`commitlint` helps your team adhereing to a commit convention. By supporting npm-installed configurations it makes sharing of commit conventions easy.
+
+# Getting started
+
+## Install
+
+```sh
+npm install -g @commitlint/cli @commitlint/config-angular
+```
+
+## Configure
+
+```
+echo "module.exports = {extends: [@commitlint/config-angular']}" > .commitlint.config.js
+```
+
+## Test
+
+```
+# Lint from stdin
+echo 'foo: bar' | commitlint
+⧗ input: foo: bar
+✖ scope must be one of [build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test] [type-enum]
+✖ found 1 problems, 0 warnings
+
+# Lint last commit from history
+commitlint --from=HEAD~1
+```
+
+
+?> To get the most out of `commitlint` you'll want to autmate its use in your project lifecycle. See our [Local setup guide](./guides-local-setup?id=guide-local-setup) for next steps.
+
+## Documentation
+
+* **Guides** - Common use cases explained in a step-by-step pace
+* **Concepts** - Overarching topics important to unterstand the use of `commitlint`
+* **Reference** - Mostly technical documentation
+* **Changelog**
diff --git a/docs/reference-api.md b/docs/reference-api.md
new file mode 100644
index 0000000000..ec625b94be
--- /dev/null
+++ b/docs/reference-api.md
@@ -0,0 +1,267 @@
+# API
+
+## Install
+
+```
+npm install --save @commitlint/core
+```
+
+## Methods
+
+### format
+> Format commitlint report data to a human-readable format
+
+* **Signature**
+
+```ts
+type Problem = {
+ /*
+ * Level of the problem hint | warning | error
+ */
+ level: 0 | 1 | 2;
+ /*
+ * Name of the problem to annotate the message with
+ */
+ name: string;
+ /*
+ * Message to print
+ */
+ message: string;
+}
+
+type Report = {
+ errors: Problem[];
+ warnings: Problem[];
+}
+
+type formatOptions = {
+ /*
+ * Color the output
+ */
+ color: boolean = true;
+}
+
+format(report?: Report = {}, options?: formatOptions = {}) => string[];
+```
+
+* **Example**
+
+```js
+const {format} = require('@commitlint/core');
+
+format(); // => [ '\u001b[1m\u001b[32m✔\u001b[39m found 0 problems, 0 warnings\u001b[22m' ]
+
+format({
+ warnings: [
+ {
+ level: 0,
+ name: 'some-hint',
+ message: 'This will not show up as it has level 0'
+ },
+ {
+ level: 1,
+ name: 'some-warning',
+ message: 'This will show up yellow as it has level 1'
+ }
+ ],
+ errors: [
+ {
+ level: 2,
+ name: 'some-error',
+ message: 'This will show up red as it has level 2'
+ }
+ ]
+}, {
+ color: false
+});
+/* => [
+ '✖ This will show up red as it has level 2 [some-error]',
+ ' This will not show up as it has level 0 [some-hint]',
+ '⚠ This will show up yellow as it has level 1 [some-warning]',
+ '✖ found 1 problems, 2 warnings'
+] */
+```
+
+### load
+
+> load all relevant shared configuration
+
+* **Signature**
+
+```ts
+type RuleLevel = 0 | 1 | 2;
+type RuleCondition = 'always' | 'never';
+type RuleOption = any;
+type PrimitiveRule = [RuleLevel, RuleCondition, RuleOption?];
+type AsyncRule = Promise;
+type FunctionRule = () => PrimitiveRule;
+type AsyncFunctionRule () => Promise;
+type Rule = PrimitiveRule | FunctionRule | AsyncFunctionRule;
+
+type Config = {
+ extends: string[];
+ rules: {[ruleName: string]: Rule};
+}
+
+load(seed: Config = {}) => Promise;
+```
+
+* **Example**
+
+```js
+const {load} = require('@commitlint/core');
+
+load({
+ rules: {
+ 'body-leading-blank': [2, 'always']
+ }
+})
+.then(config => console.log(config));
+// => { extends: [], rules: { 'body-leading-blank': [ 2, 'always' ] } }
+
+load({extends: ['./package']})
+.then(config => console.log(config));
+// => { extends: [], rules: {} }
+```
+
+### read
+
+> Read commit messages from as specified range
+
+* **Signature**
+
+```ts
+type Range = {
+ from: string;
+ to: string;
+ edit?: boolean;
+};
+
+read(range: Range) => Promise
+```
+
+* **Example**
+
+```js
+// git commit -m "I did something"
+const {read} = require('@commitlint/core');
+
+read({edit: true})
+ .then(messages => console.log(messages));
+// => ['I did something\n\n']
+
+read({from: 'HEAD~2'})
+ .then(messages => console.log(messages));
+// => ['I did something\n\n', 'Initial commit\n\n']
+
+read({from: 'HEAD~2', to: 'HEAD~1'})
+ .then(messages => console.log(messages));
+// => ['Initial commit\n\n']
+```
+
+### lint
+
+* **Signature**
+
+```ts
+type RuleLevel = 0 | 1 | 2;
+type RuleCondition = 'always' | 'never';
+type RuleOption = any;
+type PrimitiveRule = [RuleLevel, RuleCondition, RuleOption?];
+type AsyncRule = Promise;
+type FunctionRule = () => PrimitiveRule;
+type AsyncFunctionRule () => Promise;
+type Rule = PrimitiveRule | FunctionRule | AsyncFunctionRule;
+
+type Problem = {
+ level: number;
+ valid: boolean;
+ name: string;
+ message: string;
+}
+
+type Report = {
+ valid: boolean;
+ errors: Problem[];
+ warnings: Problem[];
+}
+
+lint(message: string, rules: {[ruleName: string]: Rule}) => Promise;
+```
+
+* **Basic Example**
+
+```js
+const {lint} = require('@commitlint/core');
+
+lint('foo: bar')
+ .then(report => console.log(report));
+ // => { valid: true, errors: [], warnings: [] }
+
+lint('foo: bar', {'type-enum': [1, 'always', ['foo']]})
+ .then(report => console.log(report));
+ // => { valid: true, errors: [], warnings: [] }
+
+lint('foo: bar', {'type-enum': [1, 'always', ['bar']]})
+ .then(report => console.log(report));
+ /* =>
+ { valid: true,
+ errors: [],
+ warnings:
+ [ { level: 1,
+ valid: false,
+ name: 'type-enum',
+ message: 'scope must be one of [bar]' } ] }
+ */
+```
+
+* **Load configuration**
+
+```js
+const {lint, load} = require('@commitlint/core');
+
+const CONFIG = {
+ extends: ['./@commitlint/config-angular']
+};
+
+load(CONFIG)
+ .then(opts =>lint('foo: bar', opts.rules))
+ .then(report => console.log(report));
+ /* =>
+ { valid: false,
+ errors:
+ [ { level: 2,
+ valid: false,
+ name: 'type-enum',
+ message: 'scope must be one of [build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test]' } ],
+ warnings: [] }
+ */
+```
+
+* **Read git history**
+
+```js
+const {lint, read} = require('@commitlint/core');
+
+const RULES = {
+ 'type-enum': [2, 'always', ['foo']]
+};
+
+const check = commit => lint(commit, RULES);
+
+read({to: 'HEAD', from: 'HEAD~2'})
+ .then(commits => Promise.all(commits.map(check)));
+```
+
+* **Simplfied last-commit checker**
+
+```js
+const {lint, load, read} = require('@commitlint/core');
+
+Promise.all([load(), read({from: 'HEAD~1'})])
+ .then(tasks => {
+ const [{rules}, [commit]] = tasks;
+ return lint(commit, rules);
+ })
+ .then(report => console.log(JSON.stringify(result.valid)));
+```
diff --git a/docs/reference-cli.md b/docs/reference-cli.md
new file mode 100644
index 0000000000..90f4f026aa
--- /dev/null
+++ b/docs/reference-cli.md
@@ -0,0 +1,22 @@
+# CLI
+
+## Installation
+
+```shell
+npm install --save-dev @commitlint/cli
+```
+
+## Usage
+
+```shell
+❯ commitlint --help
+ commitlint - Lint commit messages
+
+ [input] reads from stdin if --edit, --from, --to are omitted
+ --color,-c toggle formatted output, defaults to: true
+ --edit,-e read last commit message found in ./git/COMMIT_EDITMSG
+ --extends,-x array of shareable configurations to extend
+ --from,-f lower end of the commit range to lint; applies if edit=false
+ --to,-t upper end of the commit range to lint; applies if edit=false
+ --quiet,-q toggle console output
+```
diff --git a/documentation/rules.md b/docs/reference-rules.md
similarity index 99%
rename from documentation/rules.md
rename to docs/reference-rules.md
index a5451532f2..ea007782b1 100644
--- a/documentation/rules.md
+++ b/docs/reference-rules.md
@@ -1,4 +1,5 @@
# Rules
+
Rules are made up by a name and a configuration array. The configuration array contains:
* **Level** `[0..2]`: `0` disables the rule. For `1` it will be considered a warning for `2` an error.
* **Applicable** `always|never`: `never` inverts the rule.
diff --git a/documentation/shareable-config.md b/documentation/shareable-config.md
deleted file mode 100644
index 65cb670b3d..0000000000
--- a/documentation/shareable-config.md
+++ /dev/null
@@ -1,12 +0,0 @@
-# Shareable configuration
-
-```js
- "extends": ["angular"]
-```
-
-Every array item found in `extends` is resolved to `conventional-changelog-lint-config-${name}`. The default main export found there is merged into the configuration with increasing precedence. This works recursively, enabling shareable configuration to extend on an indefinite chain of other shareable configurations.
-
-See the [angular](/marionebl/conventional-changelog-lint-config-angular) shareable configuration as example. This configuration is the default used by `conventional-changelog-lint`.
-
----
-Copyright 2016 by [Mario Nebl](https://github.com/marionebl) and [contributors](./graphs/contributors). Released under the [MIT license]('../license.md').
diff --git a/fixtures/empty-object-file/.conventional-changelog-lintrc b/fixtures/empty-object-file/.conventional-changelog-lintrc
deleted file mode 100644
index 0967ef424b..0000000000
--- a/fixtures/empty-object-file/.conventional-changelog-lintrc
+++ /dev/null
@@ -1 +0,0 @@
-{}
diff --git a/fixtures/extends-empty/.conventional-changelog-lintrc b/fixtures/extends-empty/.conventional-changelog-lintrc
deleted file mode 100644
index aeafb3d13b..0000000000
--- a/fixtures/extends-empty/.conventional-changelog-lintrc
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "extends": []
-}
diff --git a/fixtures/extends-invalid/.conventional-changelog-lintrc b/fixtures/extends-invalid/.conventional-changelog-lintrc
deleted file mode 100644
index 625abf16df..0000000000
--- a/fixtures/extends-invalid/.conventional-changelog-lintrc
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "extends": ["____foooooo"]
-}
diff --git a/fixtures/overridden-extended-type-enums/.conventional-changelog-lintrc b/fixtures/overridden-extended-type-enums/.conventional-changelog-lintrc
deleted file mode 100644
index d5892f0605..0000000000
--- a/fixtures/overridden-extended-type-enums/.conventional-changelog-lintrc
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "rules": {
- "type-enum": [ 2, "always", [ "a", "b", "c", "d" ]]
- }
-}
diff --git a/fixtures/overridden-type-enums/.conventional-changelog-lintrc b/fixtures/overridden-type-enums/.conventional-changelog-lintrc
deleted file mode 100644
index f28046c6e5..0000000000
--- a/fixtures/overridden-type-enums/.conventional-changelog-lintrc
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "extends": ["angular"],
- "rules": {
- "type-enum": [ 2, "always", [ "a", "b", "c", "d" ]]
- }
-}
diff --git a/lerna.json b/lerna.json
new file mode 100644
index 0000000000..80244051fe
--- /dev/null
+++ b/lerna.json
@@ -0,0 +1,7 @@
+{
+ "lerna": "2.0.0",
+ "packages": [
+ "@commitlint/*"
+ ],
+ "version": "3.0.1"
+}
diff --git a/license.md b/license.md
index 5efba1e0fa..8ee039e63b 100644
--- a/license.md
+++ b/license.md
@@ -19,6 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-
----
-Copyright 2016 by [Mario Nebl](https://github.com/marionebl) and [contributors](./graphs/contributors). Released under the [MIT license]('./license.md').
diff --git a/package.json b/package.json
index dd44fe6e3e..2b36fa295b 100644
--- a/package.json
+++ b/package.json
@@ -1,125 +1,39 @@
{
- "name": "conventional-changelog-lint",
- "version": "2.1.1",
- "description": "Lint commit messages against a conventional-changelog preset and ruleset",
- "main": "distribution/index.js",
- "files": [
- "distribution"
- ],
- "bin": {
- "conventional-changelog-lint": "distribution/cli.js"
- },
+ "name": "@commitlint/root",
+ "description": "Lint commit messages",
+ "private": true,
+ "version": "1.0.0",
+ "license": "MIT",
"scripts": {
- "start": "npm run watch",
- "build": "cross-env BABEL_ENV=production babel source --out-dir distribution",
- "watch": "npm run build -- --watch",
- "commit": "git-cz",
- "commitmsg": "npm run build && node distribution/cli.js --edit",
- "changelog": "conventional-changelog --preset angular --infile changelog.md --same-file --output-unreleased",
- "push": "git push && git push --tags && hub release create \"v$npm_package_version\" --message=\"v$npm_package_version\n$(conventional-changelog -p angular)\" && npm publish",
- "prepretest": "npm run lint",
- "pretest": "npm run deps",
- "test": "nyc ava -c=4",
- "lint": "xo",
- "deps": "npm run build && dependency-check . --missing && dependency-check . --extra --no-dev -i conventional-changelog-angular -i conventional-changelog-lint-config-angular",
- "commitlint": "node distribution/cli.js --from=HEAD~1",
- "preversion": "npm run build && npm test",
- "release": "npm version --no-git-tag-version $(conventional-recommended-bump -p angular)",
- "version": "npm run changelog && git add .",
- "postversion": "git commit -m \"chore(release): v$npm_package_version\n$(conventional-changelog -p angular)\" && git tag -a v$npm_package_version -m \"$(conventional-changelog -p angular)\"",
- "travis:lint:commits": "./scripts/lint:commits.sh"
- },
- "ava": {
- "files": [
- "source/**/*.test.js",
- "!distribution/**/*"
- ],
- "source": [
- "source/**/*.js",
- "!distribution/**/*"
- ],
- "babel": "inherit",
- "require": [
- "babel-register",
- "babel-polyfill"
- ]
- },
- "babel": {
- "env": {
- "development": {
- "sourceMaps": "inline",
- "plugins": [
- "add-module-exports",
- "istanbul",
- [
- "transform-runtime",
- {
- "polyfill": false,
- "regenerator": true
- }
- ]
- ]
- },
- "production": {
- "ignore": [
- "**/*.test.js"
- ]
- }
- },
- "presets": [
- [
- "env",
- {
- "targets": {
- "node": 4
- }
- }
- ],
- "stage-0"
- ],
- "plugins": [
- "add-module-exports",
- [
- "transform-runtime",
- {
- "polyfill": false,
- "regenerator": true
- }
- ]
- ]
- },
- "nyc": {
- "all": true,
- "sourceMap": false,
- "instrument": false,
- "include": [
- "source/**/*.js"
- ]
+ "clean": "lerna clean -- --yes && lerna run clean",
+ "commit": "./@commitlint/prompt-cli/cli.js",
+ "commitmsg": "./@commitlint/cli/cli.js -e",
+ "docs": "docsify serve docs",
+ "lerna": "lerna",
+ "postinstall": "lerna bootstrap",
+ "pretest": "xo",
+ "reinstall": "npm run clean && npm install",
+ "publish": "lerna publish --conventional-commits",
+ "test": "lerna run test",
+ "travis": "trevor"
},
"xo": {
- "plugins": [
- "flow-check"
- ],
- "rules": {
- "flow-check/check": "error"
- }
- },
- "config": {
- "commitizen": {
- "path": "cz-conventional-changelog-lint"
- }
+ "ignores": [
+ "@commitlint/**/lib/**",
+ "@commitlint/**/node_modules"
+ ]
},
"engines": {
"node": ">=4"
},
"repository": {
"type": "git",
- "url": "https://github.com/marionebl/conventional-changelog-lint.git"
+ "url": "https://github.com/marionebl/commitlint.git"
},
"bugs": {
- "url": "https://github.com/marionebl/conventional-changelog-lint/issues"
+ "url": "https://github.com/marionebl/commitlint/issues"
},
- "homepage": "https://github.com/marionebl/conventional-changelog-lint#readme",
+ "homepage": "https://github.com/marionebl/commitlint#readme",
"keywords": [
"conventional",
"conventional-changelog",
@@ -132,51 +46,11 @@
"name": "Mario Nebl",
"email": "hello@herebecode.com"
},
- "license": "MIT",
"devDependencies": {
- "ansi-styles": "3.1.0",
- "ava": "0.18.2",
- "babel-cli": "6.18.0",
- "babel-plugin-add-module-exports": "0.2.1",
- "babel-plugin-istanbul": "4.1.3",
- "babel-plugin-transform-runtime": "6.23.0",
- "babel-polyfill": "6.20.0",
- "babel-preset-env": "1.2.1",
- "babel-preset-stage-0": "6.16.0",
- "babel-register": "6.24.1",
- "conventional-changelog-cli": "1.2.0",
- "conventional-recommended-bump": "0.3.0",
- "cross-env": "5.0.1",
- "cz-conventional-changelog-lint": "0.1.3",
- "denodeify": "1.2.1",
- "dependency-check": "2.7.0",
- "eslint-plugin-flow-check": "1.1.1",
- "execa": "0.6.3",
- "globby": "6.1.0",
- "has-ansi": "3.0.0",
- "nyc": "10.3.2",
- "path-exists": "3.0.0",
- "resolve-from": "3.0.0",
- "rimraf": "2.6.1",
- "xo": "0.18.2"
- },
- "dependencies": {
- "babel-polyfill": "6.20.0",
- "babel-runtime": "6.23.0",
- "chalk": "1.1.3",
- "conventional-changelog-angular": "1.3.0",
- "conventional-changelog-lint-config-angular": "^1.0.0",
- "conventional-commits-parser": "1.3.0",
- "franc": "2.0.0",
- "get-stdin": "5.0.1",
- "git-raw-commits": "1.1.2",
- "git-toplevel": "1.1.1",
- "import-from": "^2.1.0",
- "lodash": "4.17.4",
- "meow": "3.7.0",
- "mz": "2.6.0",
- "pos": "0.4.2",
- "rc": "1.1.7",
- "semver": "^5.3.0"
+ "docsify-cli": "^4.1.8",
+ "husky": "^0.14.3",
+ "lerna": "^2.0.0",
+ "trevor": "^2.3.0",
+ "xo": "^0.18.2"
}
}
diff --git a/readme.md b/readme.md
index fc80c6a3f7..d08d2c2b7a 100644
--- a/readme.md
+++ b/readme.md
@@ -1,334 +1,80 @@
> Lint commit messages
-# conventional-changelog-lint [![stability][0]][1]
-
-[![npm version][6]][7] [![Travis branch][2]][3] [![AppVeyor branch][4]][5]
+# commitlint
* 🚓 Enforce commit conventions
-* 🤖 Plays nice with `conventional-changelog`
-* 📦 Supports shareable configuration
-
-## Installation
+* 🤖 Plays nice with `conventional-changelog`
+* 📦 Supports shareable configuration
-Fetch it with `npm`
+## Getting started
-```shell
-npm install --save-dev conventional-changelog-lint
```
-
-## Usage
-
-`conventional-changelog-lint` provides a command line and node interface.
-
-### CLI
-
-
-
-The command line interface reads `.conventional-changelog-lintrc`
-resolves `extends` configurations.
-
-```shell
-❯ conventional-changelog-lint --help
- conventional-changelog-lint - Lint commit messages against a conventional-changelog preset and ruleset
-
- [input] reads from stdin if --edit, --from, --to are omitted
- --color,-c toggle formatted output, defaults to: true
- --edit,-e read last commit message found in ./git/COMMIT_EDITMSG
- --extends,-x array of shareable configurations to extend
- --from,-f lower end of the commit range to lint; applies if edit=false
- --preset,-p conventional-changelog-preset to use for commit message parsing, defaults to: angular
- --to,-t upper end of the commit range to lint; applies if edit=false
- --quiet,-q toggle console output
-
+npm install --save-dev @commitlint/{angular,cli}
+echo '{"extends": ["@commitlint/config-angular"]}' > .commitlintrc
```
-### Recipes
+## CLI
-#### git hook
-As a `commitmsg` git-hook with ["husky"](https://git.io/JDwyQg)
+* Primary way to interact with commitlint.
+* `npm install --save-dev @commitlint/cli`
+* Packages: [cli](./@commitlint/cli)
-```json
- {
- "scripts": {
- "commitmsg": "conventional-changelog-lint -e"
- }
- }
-```
+## Config
+* Configuration is picked up from `.commitlint` files
+* Packages: [cli](./@commitlint/cli), [core](./@commitlint/core)
+* See [Rules](./docs/rules) for a complete list of possible rules
+* An example configurations can be found at [@commitlint/config-angular](./@commitlint/config-angular/index.js)
-#### Last commit
-As part of `npm test`
+## Shared configurations
-```json
- {
- "scripts": {
- "test": "conventional-changelog-lint --from=HEAD~1"
- }
- }
-```
+A number of shared configurations are available to install and use with `commitlint`:
-#### Lint all commits in Pull Request
+* [@commitlint/config-angular](./@commitlint/config-angular)
+* [@commitlint/config-lerna-scopes](./@commitlint/config-lerna-scopes)
+* [@commitlint/config-patternplate](./@commitlint/config-patternplate)
+* [conventional-changelog-lint-config-atom](https://github.com/erikmueller/conventional-changelog-lint-config-atom)
+* [conventional-changelog-lint-config-canonical](https://github.com/gajus/conventional-changelog-lint-config-canonical)
-You can lint all commits in a PR by passing all commits that
-are present in `SOURCE_BRANCH` but unavailable in `BASE_BRANCH`:
-```sh
-conventional-changelog-lint --from=BASE_BRANCH to=SOURCE_BRANCH
-```
-
-Most of the time `BASE_BRANCH` will be `master` for Github Flow.
+## API
-This assumes `SOURCE_BRANCH` is available on your local checkout.
-This is not true by default for all PRs originating from clones of a repository.
+* Alternative, programatic way to interact with `commitlint`
+* `npm install --save @commitlint/core`
+* Packages: [core](./@commitlint/core)
+* See [API](./docs/api) for a complete list of methods and examples
-Given you'd like to lint all commits in PR origination from branch `remote-test` on the
-repository `github.com/other-name/test` targeting `master` on `github.com/your-name/test`:
+## Tools
-```sh
-cd test # make sure CWD is in your repository
-git remote add other-name https://github.com/other-name/test.git
-git fetch other-name
-
-conventional-changelog-lint --from=master --to=other-name/test
-```
+* [commitizen adapter](./@commitlint/prompt)
+* [prompt](./@commitlint/prompt-cli)
-See [scripts/lint:commit.sh](./scripts/lint:commit.sh#6) for an example on how to obtain `SOURCE_BRANCH` from a Github clone automatically on Travis.
+## Version Support
-#### Travis
-
-Commit Linting on CI has to handle the following cases
-
-* Direct commits
-* Branch Pull Requests
-* Fork Pull Requests
-
-An exemplary implementation is provided as bash script working on Travis CI.
-
-```yml
-# Force full git checkout
-before_install: git fetch --unshallow
-
-script:
- - ./scripts/lint:commit.sh # [1] scripts/lint:commit.sh
-```
-
-> \[1\]: See [scripts/lint:commit.sh](./scripts/lint:commit.sh) for reference
-
-### API
-
-The programming interface does not read configuration by default,
-it has to be provided as second parameter.
-
-```js
-import lint from 'conventional-changelog-lint';
-const report = lint(
- 'docs: add node api interface usage',
- {
- preset: {},
- configuration: {}
- }
-);
-```
-
-To achieve the same behavior as with the command line interface
-you can use the provided utility functions:
-
-```js
-import lint from 'conventional-changelog-lint';
-import {
- getPreset,
- getConfiguration
-} from 'conventional-changelog-lint';
-
-const report = lint(
- 'docs: add node api interface usage',
- {
- preset: await getPreset('angular'),
- configuration: await readConfiguration('conventional-changelog-lint')
- }
-);
-```
-
-## Configuration
-
-`conventional-changelog-lint` is configured via
-`.conventional-changelog-lintrc` and shareable configuration.
-
-When no `.conventional-changelog-lintrc` is found it will use the
-[angular](https://github.com/marionebl/conventional-changelog-lint-config-angular#rules)
-shareable config.
-See the documentation there for default rules.
-
-When a `.conventional-changelog-lintrc` is found it will **not** load any preset
-unless specified via [extends](#extends) configuration.
-
-### extends
-
-```js
-{
- "extends": ["angular"]
-}
-```
-
-Array of shareable configurations to extend.
-Configurations are resolved as `conventional-changelog-lint-config-${name}`
-and have to be installed.
-See [npm search](https://www.npmjs.com/search?q=conventional-changelog-lint-config)
-for available shareable configurations.
-
----
-
-⇨ See [shareable-config](./documentation/shareable-config.md) for details
-
-### preset
-
-```js
-{
- "preset": "angular"
-}
-```
-
-`conventional-changelog` preset name to use for parsing of commit messages.
-
----
-
-⇨ See [conventional-changelog](https://github.com/ajoslin/conventional-changelog#preset)
-for details
-
-### rules
-
-```js
-{
- "rules": {
- "body-leading-blank": [1, "always"],
- "header-max-length": [1, "always", 72],
- "subject-full-stop": [1, "never", "."]
- }
-}
-```
-
-Rules applicable to the linted commit messages.
-By default all rules are turned off via a level of 0.
-They can be enabled by shareable configuration,
-such as the
-[angular config](https://github.com/marionebl/conventional-changelog-lint-config-angular),
-which is loaded by default.
-
----
-
-⇨ See [rules](./documentation/rules.md) for details
-
-### wildcards
-
-Patterns to exclude from linting
-
-```js
-wildcards: {
- merge: [
- '/^(Merge pull request)|(Merge (.*?) into (.*?)$)/'
- ],
- release: [
- '/^\\d.\\d.\\d$/'
- ],
- revert: [
- '/^revert: (.*)/'
- ]
-}
-```
-
-## Shallow clones
-
-### TL;DR
-
-Perform `git fetch --shallow` before linting.
-
-Most likely you are reading this because you where presented with an error message:
-
-```
- 'Could not get git history from shallow clone.
- Use git fetch --shallow before linting.
- Original issue: https://git.io/vyKMq\n Refer to https://git.io/vyKMv for details.'
-```
-
-### Explanation
-
-git supports checking out `shallow` clones of a repository to save bandwith in times.
-These limited copies do not contain a full git history. This makes `conventional-changelog-lint`
-fail, especially when running on large commit ranges.
-To ensure linting works every time you should convert a shallow git repo to a complete one.
-Use `git fetch --shallow` to do so.
-
-### Travis
-
-Ensure full git checkouts on TravisCI, add to `.travis.yml`:
-
-```yml
-before_install:
- - git fetch --unshallow
-```
-
-### Appveyor
-
-Ensure full git checkouts on AppVeyor, add to `appveyor.yml`:
-
-```yml
-shallow_clone: false
-```
-
-## Supported Node.js versions
-
-conventional-changelog-lint supports the active Node.js [LTS](https://github.com/nodejs/LTS#lts-schedule) version and higher: `>= 4`
+* Node.js [LTS](https://github.com/nodejs/LTS#lts-schedule) version and higher: `>= 4`
+* git `>= 2`
## Related projects
-* [angular-precommit](https://git.io/vwTDd)
-– Pre commit with angular conventions
+* [angular-precommit](https://git.io/vwTDd) – Pre commit with angular conventions
+* [conventional-changelog](https://git.io/v18sw) – Generate a changelog from conventional commit history
+* [conventional-commits-detector](https://git.io/vwTyk) – Detect what commit message convention your repository is using
+* [conventional-github-releaser](https://git.io/vwTyI) – Make a new GitHub release from git metadata
+* [commitizen](https://git.io/vwTym) – Simple commit conventions for internet citizens
-* [conventional-changelog-cli](https://git.io/vwTDA)
-– Generate a changelog from conventional commit history
+## License
+Copyright by @marionebl. All `commitlint` packages are released under the MIT license.
-* [cz-conventional-changelog-lint](https://git.io/vwTyf)
-– Let an interactive command line interface help you with creating commit
-messages matching your `conventional-changelog-lint` configuration
+## Development
-* [conventional-changelog-lint-config-angular](https://git.io/vwTy4)
-– Shareable conventional-changelog-lint config enforcing the angular
-commit convention
+`commitlint` is developed in a mono repository.
-* [conventional-changelog-lint-config-atom](https://git.io/vwTy9)
-– Shareable configuration for conventional-changelog-lint based on the
-atom commit guidelines
+### Getting started
-* [conventional-changelog-lint-config-patternplate](https://git.io/vwTyz)
-– Lint your commits, patternplate-style
-
-* [conventional-commits-detector](https://git.io/vwTyk)
-– Detect what commit message convention your repository is using
-
-* [conventional-github-releaser](https://git.io/vwTyI)
-– Make a new GitHub release from git metadata
-
-* [conventional-recommended-bump](https://git.io/vwTyL)
-– Get a recommended version bump based on conventional commits
-
-* [commitizen](https://git.io/vwTym)
-– Simple commit conventions for internet citizens
-
-* [standard-changelog](https://git.io/vwTyO)
-– Generate a changelog from conventional commit history, angular-style
-
----
-
-Copyright 2016 by [Mario Nebl](https://github.com/marionebl)
-and [contributors](./graphs/contributors).
-Released under the [MIT license]('./license.md').
-
-
-[0]: https://img.shields.io/badge/stability-experimental-orange.svg?style=flat-square
-[1]: https://nodejs.org/api/documentation.html#documentation_stability_index
-[2]: https://img.shields.io/travis/marionebl/conventional-changelog-lint/master.svg?style=flat-square
-[3]: https://travis-ci.org/marionebl/conventional-changelog-lint
-[4]: https://img.shields.io/appveyor/ci/marionebl/conventional-changelog-lint/master.svg?style=flat-square
-[5]: https://ci.appveyor.com/project/marionebl/conventional-changelog-lint
-[6]: https://img.shields.io/npm/v/conventional-changelog-lint.svg?style=flat-square
-[7]: https://npmjs.org/package/conventional-changelog-lint
+```sh
+git clone git@github.com:marionebl/commitlint.git
+cd commitlint
+npm install
+npm start # run tests, again on change
+npm run build # run build tasks
+```
diff --git a/scripts/lint:commits.sh b/scripts/lint:commits.sh
deleted file mode 100755
index a54d5cefab..0000000000
--- a/scripts/lint:commits.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-set -e
-set -u
-
-if [[ $TRAVIS_PULL_REQUEST_SLUG != "" && $TRAVIS_PULL_REQUEST_SLUG != $TRAVIS_REPO_SLUG ]]; then
- # This is a Pull Request from a different slug, hence a forked repository
- git remote add "$TRAVIS_PULL_REQUEST_SLUG" "https://github.com/$TRAVIS_PULL_REQUEST_SLUG.git"
- git fetch "$TRAVIS_PULL_REQUEST_SLUG"
-
- # Use the fetched remote pointing to the source clone for comparison
- TO="$TRAVIS_PULL_REQUEST_SLUG/$TRAVIS_PULL_REQUEST_BRANCH"
-else
- # This is a Pull Request from the same remote, no clone repository
- TO=$TRAVIS_COMMIT
-fi
-
-# Lint all commits in the PR
-# - Covers fork pull requests (when TO=slug/branch)
-# - Covers branch pull requests (when TO=branch)
-conventional-changelog-lint --from="$TRAVIS_BRANCH" --to="$TO"
-
-# Always lint the triggerig commit
-# - Covers direct commits
-conventional-changelog-lint --from="$TRAVIS_COMMIT"
diff --git a/source/cli.js b/source/cli.js
deleted file mode 100644
index 71437c97b9..0000000000
--- a/source/cli.js
+++ /dev/null
@@ -1,129 +0,0 @@
-#!/usr/bin/env node
-import 'babel-polyfill'; // eslint-disable-line import/no-unassigned-import
-
-// npm modules
-import chalk from 'chalk';
-import meow from 'meow';
-import {pick} from 'lodash';
-import stdin from 'get-stdin';
-
-import pkg from '../package.json'; // eslint-disable-line import/extensions
-import help from './help';
-import lint, {format, getConfiguration, getPreset, getMessages} from './';
-
-/**
- * Behavioural rules
- */
-const rules = {
- fromStdin: (input, flags) => input.length === 0 &&
- flags.from === null &&
- flags.to === null &&
- !flags.edit
-};
-
-const configuration = {
- string: ['from', 'to', 'preset', 'extends'],
- boolean: ['edit', 'help', 'version', 'quiet', 'color'],
- alias: {
- c: 'color',
- e: 'edit',
- f: 'from',
- p: 'preset',
- t: 'to',
- q: 'quiet',
- h: 'help',
- v: 'version',
- x: 'extends'
- },
- description: {
- color: 'toggle colored output',
- edit: 'read last commit message found in ./git/COMMIT_EDITMSG',
- extends: 'array of shareable configurations to extend',
- from: 'lower end of the commit range to lint; applies if edit=false',
- preset: 'conventional-changelog-preset to use for commit message parsing',
- to: 'upper end of the commit range to lint; applies if edit=false',
- quiet: 'toggle console output'
- },
- default: {
- color: true,
- edit: false,
- from: null,
- preset: 'angular',
- to: null,
- quiet: false
- },
- unknown(arg) {
- throw new Error(`unknown flags: ${arg}`);
- }
-};
-
-// Init meow 😸cli
-const cli = meow({
- help: `[input] reads from stdin if --edit, --from, --to are omitted\n${help(configuration)}`,
- description: `${pkg.name}@${pkg.version} - ${pkg.description}`
-}, configuration);
-
-// Assemble the engine
-async function main(options) {
- const {input: raw, flags} = options;
- const fromStdin = rules.fromStdin(raw, flags);
-
- const input = fromStdin ?
- [await stdin()] :
- await getMessages(
- pick(flags, 'edit', 'from', 'to')
- );
-
- return Promise.all(input
- .map(async commit => {
- const fmt = new chalk.constructor({enabled: flags.color});
-
- const seed = {};
- if (flags.extends) {
- seed.extends = flags.extends.split(',');
- }
-
- const report = await lint(commit, {
- preset: await getPreset(flags.preset),
- configuration: await getConfiguration(
- 'conventional-changelog-lint',
- {
- prefix: 'conventional-changelog-lint-config'
- },
- seed
- )
- });
-
- const formatted = format(report, {color: flags.color});
-
- if (!flags.quiet) {
- console.log(`${fmt.grey('⧗')} input: ${fmt.bold(commit.split('\n')[0])}`);
- console.log(formatted.join('\n'));
- }
-
- if (report.errors.length > 0) {
- const error = new Error(formatted[formatted.length - 1]);
- error.type = pkg.name;
- throw error;
- }
-
- return console.log('');
- }));
-}
-
-// Start the engine
-main(cli)
- .catch(err =>
- setTimeout(() => {
- if (err.type === pkg.name) {
- process.exit(1);
- }
- throw err;
- })
- );
-
-// Catch unhandled rejections globally
-process.on('unhandledRejection', (reason, promise) => {
- console.log('Unhandled Rejection at: Promise ', promise, ' reason: ', reason);
- throw reason;
-});
diff --git a/source/library/get-configuration.js b/source/library/get-configuration.js
deleted file mode 100644
index c31124a07a..0000000000
--- a/source/library/get-configuration.js
+++ /dev/null
@@ -1,65 +0,0 @@
-import {omit, merge, mergeWith, pick} from 'lodash';
-import rc from 'rc';
-
-import resolveExtends from './resolve-extends';
-import executeRule from './execute-rule';
-
-const defaults = {
- extends: ['angular']
-};
-
-const defaultName = 'conventional-changelog-lint';
-
-const defaultSettings = {
- prefix: 'conventional-changelog-lint-config'
-};
-
-export default async (name = defaultName, settings = defaultSettings, seed = {}) => {
- // Obtain config from .rc files
- const userConfig = omit(rc(name, settings.defaults), '_');
-
- // Use the default extends config if there is no userConfig file found
- // See https://git.io/vwT1C for reference
- const applicableDefaults = userConfig.config ? {} : defaults;
-
- // Merge passed config with file based options
- const config = merge(userConfig, seed);
- const opts = merge({}, applicableDefaults, pick(config, 'extends'));
-
- // Resolve extends key
- const extended = resolveExtends(opts, settings.prefix);
- const preset = mergeWith({}, extended, config, (a, b) => {
- if (Array.isArray(b)) {
- return b;
- }
- });
-
- // Execute rule config functions if needed
- const executed = await Promise.all(['rules', 'wildcards']
- .map(key => {
- return [key, preset[key]];
- })
- .map(async item => {
- const [key, value] = item;
- const executedValue = await Promise.all(
- Object.entries(value || {})
- .map(entry => executeRule(entry))
- );
- return [key, executedValue.reduce((registry, item) => {
- const [key, value] = item;
- return {
- ...registry,
- [key]: value
- };
- }, {})];
- }));
-
- // Merge executed config keys into preset
- return executed.reduce((registry, item) => {
- const [key, value] = item;
- return {
- ...registry,
- [key]: value
- };
- }, preset);
-};
diff --git a/source/library/get-configuration.test.js b/source/library/get-configuration.test.js
deleted file mode 100644
index 884a89d6e9..0000000000
--- a/source/library/get-configuration.test.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import path from 'path';
-import test from 'ava';
-
-import getConfiguration from './get-configuration';
-
-const cwd = process.cwd();
-
-test.afterEach.always(t => {
- t.context.back();
-});
-
-test('overridden-type-enums should return the exact type-enum', async t => {
- t.context.back = chdir('fixtures/overridden-type-enums');
- const actual = await getConfiguration();
- const expected = ['a', 'b', 'c', 'd'];
- t.deepEqual(actual.rules['type-enum'][2], expected);
-});
-
-test('overridden-extended-type-enums should return the exact type-enum', async t => {
- t.context.back = chdir('fixtures/overridden-extended-type-enums');
- const actual = await getConfiguration();
- const expected = ['a', 'b', 'c', 'd'];
- t.deepEqual(actual.rules['type-enum'][2], expected);
-});
-
-test('extends-empty should have no rules', async t => {
- t.context.back = chdir('fixtures/extends-empty');
- const actual = await getConfiguration();
- t.deepEqual(actual.rules, {});
-});
-
-/* Failing: test('invalid extend should throw', async t => {
- t.context.back = chdir('fixtures/extends-invalid');
- t.throws(getConfiguration());
-}); */
-
-test('empty file should have no rules', async t => {
- t.context.back = chdir('fixtures/empty-object-file');
- const actual = await getConfiguration();
- t.deepEqual(actual.rules, {});
-});
-
-test('empty file should extend angular', async t => {
- t.context.back = chdir('fixtures/empty-file');
- const actual = await getConfiguration();
- t.deepEqual(actual.extends, ['angular']);
-});
-
-function chdir(target) {
- const to = path.resolve(cwd, target.split('/').join(path.sep));
- process.chdir(to);
- return () => process.chdir(cwd);
-}
diff --git a/source/library/get-preset.js b/source/library/get-preset.js
deleted file mode 100644
index a356f4ed06..0000000000
--- a/source/library/get-preset.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import importFrom from 'import-from';
-
-const cwd = importFrom.bind(null, process.cwd());
-
-export default (name, require = cwd) => {
- return require(`conventional-changelog-${name}`);
-};
diff --git a/source/library/get-preset.test.js b/source/library/get-preset.test.js
deleted file mode 100644
index fc5463e486..0000000000
--- a/source/library/get-preset.test.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import test from 'ava';
-import getPreset from './get-preset';
-
-function require(id) {
- if (id !== 'conventional-changelog-existing') {
- throw new Error(`Module "${id}" not found.`);
- }
- return true;
-}
-
-test('throws when called without params', t => {
- t.throws(() => getPreset(), Error);
-});
-
-test('throws when called for non-existing module', t => {
- t.throws(() => getPreset('non-existing', require), Error);
-});
-
-test('return module when called for existing module', async t => {
- const actual = await getPreset('existing', require);
- t.is(actual, true);
-});
diff --git a/source/library/parse.js b/source/library/parse.js
deleted file mode 100644
index af3c48db54..0000000000
--- a/source/library/parse.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import {sync} from 'conventional-commits-parser';
-
-export default parse;
-
-function parse(message, options, parser = sync) {
- const parsed = parser(message, options);
- parsed.raw = message;
- return parsed;
-}
diff --git a/source/library/parse.test.js b/source/library/parse.test.js
deleted file mode 100644
index d312ed1776..0000000000
--- a/source/library/parse.test.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import test from 'ava';
-import parse from './parse';
-
-test('throws when called without params', t => {
- t.throws(() => parse(), /Expected a raw commit/);
-});
-
-test('throws when called with empty message', t => {
- t.throws(() => parse(''), /Expected a raw commit/);
-});
-
-test('returns object with raw message', t => {
- const message = 'type(scope): subject';
- const actual = parse(message);
- t.is(actual.raw, message);
-});
-
-test('calls parser with message and passed options', t => {
- const message = 'message';
- const options = {};
-
- parse(message, options, (m, o) => {
- t.is(message, m);
- t.is(options, o);
- return {};
- });
-});
-
-test('passes object up from parser function', t => {
- const message = 'message';
- const options = {};
- const result = {};
- const actual = parse(message, options, () => result);
- t.is(actual, result);
-});
diff --git a/source/library/resolve-extends.js b/source/library/resolve-extends.js
deleted file mode 100644
index 43f8b693ec..0000000000
--- a/source/library/resolve-extends.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import importFrom from 'import-from';
-import {merge, omit} from 'lodash';
-
-const cwd = importFrom.bind(null, process.cwd());
-
-// Resolve extend configs
-export default function resolveExtends(config = {}, prefix = '', key = 'extends', require = cwd) {
- const extended = loadExtends(config, prefix, key, require)
- .reduceRight((r, c) => merge(r, omit(c, [key])), config[key] ? {[key]: config[key]} : {});
-
- // Remove deprecation warning in version 3
- if (typeof c === 'object' && 'wildcards' in config) {
- console.warn(`'wildcards' found in top-level configuration ignored. Remove them from your config to silence this warning.`);
- }
-
- return merge({}, extended, config);
-}
-
-// (any, string, string, Function) => any[];
-function loadExtends(config = {}, prefix = '', key = 'extends', require = cwd) {
- const toExtend = Object.values(config[key] || []);
- return toExtend.reduce((configs, raw) => {
- const id = [prefix, raw].filter(String).join('-');
- const c = require(id);
-
- // Remove deprecation warning in version 3
- if (typeof c === 'object' && 'wildcards' in c) {
- console.warn(`'wildcards' found in '${id}' ignored. Raise an issue at 'npm repo ${id}' to remove the wildcards and silence this warning.`);
- }
-
- return [...configs, c, ...loadExtends(c, prefix, key, require)];
- }, []);
-}
diff --git a/source/library/resolve-extends.test.js b/source/library/resolve-extends.test.js
deleted file mode 100644
index bc7b51bd40..0000000000
--- a/source/library/resolve-extends.test.js
+++ /dev/null
@@ -1,148 +0,0 @@
-import test from 'ava';
-import resolveExtends from './resolve-extends';
-
-const _ = undefined;
-
-test('returns empty object when called without params', t => {
- const actual = resolveExtends();
- t.deepEqual(actual, {});
-});
-
-test('returns an equivalent object as passed in', t => {
- const expected = {foo: 'bar'};
- const actual = resolveExtends(expected);
- t.deepEqual(actual, expected);
-});
-
-test('uses empty prefix by default', t => {
- const input = {extends: ['extender-name']};
-
- resolveExtends(input, _, _, id => {
- t.is(id, 'extender-name');
- });
-});
-
-test('uses prefix as configured', t => {
- const input = {extends: ['extender-name']};
-
- resolveExtends(input, 'prefix', _, id => {
- t.is(id, 'prefix-extender-name');
- });
-});
-
-test('uses extends key as configured', t => {
- const input = {inherit: ['extender-name'], extends: ['fails']};
-
- resolveExtends(input, _, 'inherit', id => {
- t.is(id, 'extender-name');
- });
-});
-
-test('propagates return value of require function', t => {
- const input = {extends: ['extender-name']};
- const propagated = {foo: 'bar'};
-
- const actual = resolveExtends(input, _, _, () => {
- return propagated;
- });
-
- t.is(actual.foo, 'bar');
-});
-
-test('resolves extends recursively', t => {
- const input = {extends: ['extender-name']};
- const actual = [];
-
- resolveExtends(input, _, _, id => {
- actual.push(id);
- if (id === 'extender-name') {
- return {extends: ['recursive-extender-name']};
- }
- if (id === 'recursive-extender-name') {
- return {foo: 'bar'};
- }
- });
-
- t.deepEqual(actual, ['extender-name', 'recursive-extender-name']);
-});
-
-test('uses prefix key recursively', t => {
- const input = {extends: ['extender-name']};
- const actual = [];
-
- resolveExtends(input, 'prefix', _, id => {
- actual.push(id);
- if (id === 'prefix-extender-name') {
- return {extends: ['recursive-extender-name']};
- }
- if (id === 'prefix-recursive-extender-name') {
- return {foo: 'bar'};
- }
- });
-
- t.deepEqual(actual, ['prefix-extender-name', 'prefix-recursive-extender-name']);
-});
-
-test('uses extends key recursively', t => {
- const input = {inherit: ['extender-name']};
- const actual = [];
-
- resolveExtends(input, _, 'inherit', id => {
- actual.push(id);
- if (id === 'extender-name') {
- return {inherit: ['recursive-extender-name']};
- }
- if (id === 'recursive-extender-name') {
- return {foo: 'bar'};
- }
- });
-
- t.deepEqual(actual, ['extender-name', 'recursive-extender-name']);
-});
-
-test('propagates contents recursively', t => {
- const input = {extends: ['extender-name']};
-
- const actual = resolveExtends(input, _, _, id => {
- if (id === 'extender-name') {
- return {extends: ['recursive-extender-name'], foo: 'bar'};
- }
- if (id === 'recursive-extender-name') {
- return {baz: 'bar'};
- }
- });
-
- const expected = {
- extends: ['extender-name'],
- foo: 'bar',
- baz: 'bar'
- };
-
- t.deepEqual(actual, expected);
-});
-
-test('extending contents should take precedence', t => {
- const input = {extends: ['extender-name'], zero: 'root'};
-
- const actual = resolveExtends(input, _, _, id => {
- if (id === 'extender-name') {
- return {extends: ['recursive-extender-name'], zero: id, one: id};
- }
- if (id === 'recursive-extender-name') {
- return {extends: ['second-recursive-extender-name'], zero: id, one: id, two: id};
- }
- if (id === 'second-recursive-extender-name') {
- return {zero: id, one: id, two: id, three: id};
- }
- });
-
- const expected = {
- extends: ['extender-name'],
- zero: 'root',
- one: 'extender-name',
- two: 'recursive-extender-name',
- three: 'second-recursive-extender-name'
- };
-
- t.deepEqual(actual, expected);
-});
diff --git a/source/rules/body-case.test.js b/source/rules/body-case.test.js
deleted file mode 100644
index 30faa6de3a..0000000000
--- a/source/rules/body-case.test.js
+++ /dev/null
@@ -1,89 +0,0 @@
-import test from 'ava';
-import parse from '../library/parse';
-import bodyCase from './body-case';
-
-const messages = {
- empty: 'chore: subject',
- lowercase: 'chore: subject\nbody',
- mixedcase: 'chore: subject\nBody',
- uppercase: 'chore: subject\nBODY'
-};
-
-const parsed = {
- empty: parse(messages.empty),
- lowercase: parse(messages.lowercase),
- mixedcase: parse(messages.mixedcase),
- uppercase: parse(messages.uppercase)
-};
-
-test('with empty body should succeed for "never lowercase"', t => {
- const [actual] = bodyCase(parsed.empty, 'never', 'lowercase');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with empty body should succeed for "always lowercase"', t => {
- const [actual] = bodyCase(parsed.empty, 'always', 'lowercase');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with empty body should succeed for "never uppercase"', t => {
- const [actual] = bodyCase(parsed.empty, 'never', 'uppercase');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with empty body should succeed for "always uppercase"', t => {
- const [actual] = bodyCase(parsed.empty, 'always', 'uppercase');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with lowercase body should fail for "never lowercase"', t => {
- const [actual] = bodyCase(parsed.lowercase, 'never', 'lowercase');
- const expected = false;
- t.is(actual, expected);
-});
-
-test('with lowercase body should succeed for "always lowercase"', t => {
- const [actual] = bodyCase(parsed.lowercase, 'always', 'lowercase');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with mixedcase body should succeed for "never lowercase"', t => {
- const [actual] = bodyCase(parsed.mixedcase, 'never', 'lowercase');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with mixedcase body should fail for "always lowercase"', t => {
- const [actual] = bodyCase(parsed.mixedcase, 'always', 'lowercase');
- const expected = false;
- t.is(actual, expected);
-});
-
-test('with mixedcase body should succeed for "never uppercase"', t => {
- const [actual] = bodyCase(parsed.mixedcase, 'never', 'uppercase');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with mixedcase body should fail for "always uppercase"', t => {
- const [actual] = bodyCase(parsed.mixedcase, 'always', 'uppercase');
- const expected = false;
- t.is(actual, expected);
-});
-
-test('with uppercase body should fail for "never uppercase"', t => {
- const [actual] = bodyCase(parsed.uppercase, 'never', 'uppercase');
- const expected = false;
- t.is(actual, expected);
-});
-
-test('with lowercase body should succeed for "always uppercase"', t => {
- const [actual] = bodyCase(parsed.uppercase, 'always', 'uppercase');
- const expected = true;
- t.is(actual, expected);
-});
diff --git a/source/rules/body-empty.test.js b/source/rules/body-empty.test.js
deleted file mode 100644
index d27cd5f1e1..0000000000
--- a/source/rules/body-empty.test.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import test from 'ava';
-import parse from '../library/parse';
-import bodyEmpty from './body-empty';
-
-const messages = {
- empty: 'chore: subject',
- filled: 'chore: subject\nbody'
-};
-
-const parsed = {
- empty: parse(messages.empty),
- filled: parse(messages.filled)
-};
-
-test('with empty body should succeed for empty keyword', t => {
- const [actual] = bodyEmpty(parsed.empty);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with empty body should fail for "never"', t => {
- const [actual] = bodyEmpty(parsed.empty, 'never');
- const expected = false;
- t.is(actual, expected);
-});
-
-test('with empty body should succeed for "always"', t => {
- const [actual] = bodyEmpty(parsed.empty, 'always');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with body should fail for empty keyword', t => {
- const [actual] = bodyEmpty(parsed.filled);
- const expected = false;
- t.is(actual, expected);
-});
-
-test('with body should succeed for "never"', t => {
- const [actual] = bodyEmpty(parsed.filled, 'never');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with body should fail for "always"', t => {
- const [actual] = bodyEmpty(parsed.filled, 'always');
- const expected = false;
- t.is(actual, expected);
-});
diff --git a/source/rules/body-tense.test.js b/source/rules/body-tense.test.js
deleted file mode 100644
index e52791198d..0000000000
--- a/source/rules/body-tense.test.js
+++ /dev/null
@@ -1,114 +0,0 @@
-import test from 'ava';
-import parse from '../library/parse';
-import footerTense from './body-tense';
-
-const messages = {
- empty: 'chore: \n',
- presentImperative: `chore: \nwe implement things`,
- presentParticiple: `chore: \nimplementing things`,
- presentThirdPerson: `chore: \nimplements things`,
- past: `chore: \nwe did implement things`,
- mixed: `chore: \nimplement, implementing, implements, implemented`
-};
-
-const parsed = {
- empty: parse(messages.empty),
- presentImperative: parse(messages.presentImperative),
- presentParticiple: parse(messages.presentParticiple),
- presentThirdPerson: parse(messages.presentImperative),
- past: parse(messages.past),
- mixed: parse(messages.mixed)
-};
-
-test('empty succeeds', t => {
- const [actual] = footerTense(parsed.empty, '', ['present-imperative']);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('present succeeds "always present-imperative"', t => {
- const [actual] = footerTense(parsed.presentImperative, 'always', ['present-imperative']);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('present fails "never present-imperative"', t => {
- const [actual] = footerTense(parsed.presentImperative, 'never', ['present-imperative']);
- const expected = false;
- t.is(actual, expected);
-});
-
-test('present succeeds "always present-participle"', t => {
- const [actual] = footerTense(parsed.presentParticiple, 'always', ['present-participle']);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('present fails "never present-participle"', t => {
- const [actual] = footerTense(parsed.presentParticiple, 'never', ['present-participle']);
- const expected = false;
- t.is(actual, expected);
-});
-
-test('present succeeds "always present-third-person"', t => {
- const [actual] = footerTense(parsed.presentThirdPerson, 'always', ['present-third-person']);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('present fails "never present-third-person"', t => {
- const [actual] = footerTense(parsed.presentThirdPerson, 'never', ['present-third-person']);
- const expected = false;
- t.is(actual, expected);
-});
-
-test('past should succedd "always past-tense"', t => {
- const [actual] = footerTense(parsed.past, 'always', ['past-tense']);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('past fails "never past-tense"', t => {
- const [actual] = footerTense(parsed.past, 'never', ['past-tense']);
- const expected = false;
- t.is(actual, expected);
-});
-
-test('mixed fails "always present-third-person"', t => {
- const [actual] = footerTense(parsed.mixed, 'always', ['present-third-person']);
- const expected = false;
- t.is(actual, expected);
-});
-
-test('mixed fails "always present-imperative"', t => {
- const [actual] = footerTense(parsed.mixed, 'always', ['present-imperative']);
- const expected = false;
- t.is(actual, expected);
-});
-
-test('present fails "always present-participle"', t => {
- const [actual] = footerTense(parsed.mixed, 'always', ['present-participle']);
- const expected = false;
- t.is(actual, expected);
-});
-
-test('mixed fails "always past-tense"', t => {
- const [actual] = footerTense(parsed.mixed, 'always', ['past-tense']);
- const expected = false;
- t.is(actual, expected);
-});
-
-test('mixed succeeds "always present-third-person, present-imperative, present-participle, past-tense"', t => {
- const [actual] = footerTense(parsed.mixed, 'always', ['present-third-person', 'present-imperative', 'present-participle', 'past-tense']);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('mixed succeeds "never allowed: present-third-person" and matching ignored: implements', t => {
- const [actual] = footerTense(parsed.mixed, 'never', {
- allowed: ['present-third-person'],
- ignored: ['implements']
- });
- const expected = true;
- t.is(actual, expected);
-});
diff --git a/source/rules/footer-empty.test.js b/source/rules/footer-empty.test.js
deleted file mode 100644
index 033e9c9e1c..0000000000
--- a/source/rules/footer-empty.test.js
+++ /dev/null
@@ -1,69 +0,0 @@
-import test from 'ava';
-import parse from '../library/parse';
-import footerEmpty from './footer-empty';
-
-const messages = {
- simple: 'chore: subject',
- empty: 'chore: subject\nbody',
- filled: 'chore: subject\nBREAKING CHANGE: something important'
-};
-
-const parsed = {
- simple: parse(messages.simple),
- empty: parse(messages.empty),
- filled: parse(messages.filled)
-};
-
-test('with simple message should succeed for empty keyword', t => {
- const [actual] = footerEmpty(parsed.simple);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with simple message should fail for "never"', t => {
- const [actual] = footerEmpty(parsed.simple, 'never');
- const expected = false;
- t.is(actual, expected);
-});
-
-test('with simple message should succeed for "always"', t => {
- const [actual] = footerEmpty(parsed.simple, 'always');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with empty footer should succeed for empty keyword', t => {
- const [actual] = footerEmpty(parsed.empty);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with empty footer should fail for "never"', t => {
- const [actual] = footerEmpty(parsed.empty, 'never');
- const expected = false;
- t.is(actual, expected);
-});
-
-test('with empty footer should succeed for "always"', t => {
- const [actual] = footerEmpty(parsed.empty, 'always');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with footer should fail for empty keyword', t => {
- const [actual] = footerEmpty(parsed.filled);
- const expected = false;
- t.is(actual, expected);
-});
-
-test('with footer should succeed for "never"', t => {
- const [actual] = footerEmpty(parsed.filled, 'never');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with footer should fail for "always"', t => {
- const [actual] = footerEmpty(parsed.filled, 'always');
- const expected = false;
- t.is(actual, expected);
-});
diff --git a/source/rules/lang.test.js b/source/rules/lang.test.js
deleted file mode 100644
index 71cee7eefd..0000000000
--- a/source/rules/lang.test.js
+++ /dev/null
@@ -1,75 +0,0 @@
-import test from 'ava';
-import parse from '../library/parse';
-import check from './lang';
-
-const messages = {
- empty: '(): \n',
- eng: '(): this is a serious subject',
- deu: '(): Dies ist ein ernstes Subjekt'
-};
-
-const parsed = {
- empty: parse(messages.empty),
- eng: parse(messages.eng),
- deu: parse(messages.deu)
-};
-
-test('empty succeeds', t => {
- const [actual] = check(parsed.eng, '', 'eng');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('english against "eng" succeeds', t => {
- const [actual] = check(parsed.eng, '', 'eng');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('english against "always eng" succeeds', t => {
- const [actual] = check(parsed.eng, 'always', 'eng');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('english against "never eng" fails', t => {
- const [actual] = check(parsed.eng, 'never', 'eng');
- const expected = false;
- t.is(actual, expected);
-});
-
-test('english against "deu" fails', t => {
- const [actual] = check(parsed.eng, '', 'deu+');
- const expected = false;
- t.is(actual, expected);
-});
-
-test('english against "always deu" fails', t => {
- const [actual] = check(parsed.eng, 'always', 'deu');
- const expected = false;
- t.is(actual, expected);
-});
-
-test('english against "never deu" succeeds', t => {
- const [actual] = check(parsed.eng, 'never', 'deu');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('german against "deu" succeeds', t => {
- const [actual] = check(parsed.deu, '', 'deu');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('german against "always deu" succeeds', t => {
- const [actual] = check(parsed.deu, 'always', 'deu');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('german against "never deu" fails', t => {
- const [actual] = check(parsed.deu, 'never', 'deu');
- const expected = false;
- t.is(actual, expected);
-});
diff --git a/source/rules/scope-empty.test.js b/source/rules/scope-empty.test.js
deleted file mode 100644
index 8935c7f31f..0000000000
--- a/source/rules/scope-empty.test.js
+++ /dev/null
@@ -1,69 +0,0 @@
-import test from 'ava';
-import parse from '../library/parse';
-import scopeEmpty from './scope-empty';
-
-const messages = {
- plain: 'foo(bar): baz',
- superfluous: 'foo(): baz',
- empty: 'foo: baz'
-};
-
-const parsed = {
- plain: parse(messages.plain),
- superfluous: parse(messages.superfluous),
- empty: parse(messages.empty)
-};
-
-test('with plain message it should succeed for empty keyword', t => {
- const [actual] = scopeEmpty(parsed.plain);
- const expected = true;
- t.deepEqual(actual, expected);
-});
-
-test('with plain message it should succeed for "never"', t => {
- const [actual] = scopeEmpty(parsed.plain, 'never');
- const expected = true;
- t.deepEqual(actual, expected);
-});
-
-test('with plain message it should fail for "always"', t => {
- const [actual] = scopeEmpty(parsed.plain, 'always');
- const expected = false;
- t.deepEqual(actual, expected);
-});
-
-test('with superfluous message it should fail for empty keyword', t => {
- const [actual] = scopeEmpty(parsed.superfluous);
- const expected = false;
- t.deepEqual(actual, expected);
-});
-
-test('with superfluous message it should fail for "never"', t => {
- const [actual] = scopeEmpty(parsed.superfluous, 'never');
- const expected = false;
- t.deepEqual(actual, expected);
-});
-
-test('with superfluous message it should fail for "always"', t => {
- const [actual] = scopeEmpty(parsed.superfluous, 'always');
- const expected = true;
- t.deepEqual(actual, expected);
-});
-
-test('with empty message it should fail for empty keyword', t => {
- const [actual] = scopeEmpty(parsed.empty);
- const expected = false;
- t.deepEqual(actual, expected);
-});
-
-test('with empty message it should fail for "never"', t => {
- const [actual] = scopeEmpty(parsed.empty, 'never');
- const expected = false;
- t.deepEqual(actual, expected);
-});
-
-test('with empty message it should fail for "always"', t => {
- const [actual] = scopeEmpty(parsed.empty, 'always');
- const expected = true;
- t.deepEqual(actual, expected);
-});
diff --git a/source/rules/subject-empty.test.js b/source/rules/subject-empty.test.js
deleted file mode 100644
index 1994047258..0000000000
--- a/source/rules/subject-empty.test.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import test from 'ava';
-import parse from '../library/parse';
-import subjectEmpty from './subject-empty';
-
-const messages = {
- empty: 'chore: \nbody',
- filled: 'chore: subject\nbody'
-};
-
-const parsed = {
- empty: parse(messages.empty),
- filled: parse(messages.filled)
-};
-
-test('without subject should succeed for empty keyword', t => {
- const [actual] = subjectEmpty(parsed.empty);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('without subject should fail for "never"', t => {
- const [actual] = subjectEmpty(parsed.empty, 'never');
- const expected = false;
- t.is(actual, expected);
-});
-
-test('without subject should succeed for "always"', t => {
- const [actual] = subjectEmpty(parsed.empty, 'always');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with subject fail for empty keyword', t => {
- const [actual] = subjectEmpty(parsed.filled);
- const expected = false;
- t.is(actual, expected);
-});
-
-test('with subject succeed for "never"', t => {
- const [actual] = subjectEmpty(parsed.filled, 'never');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with subject fail for "always"', t => {
- const [actual] = subjectEmpty(parsed.filled, 'always');
- const expected = false;
- t.is(actual, expected);
-});
diff --git a/source/rules/subject-full-stop.test.js b/source/rules/subject-full-stop.test.js
deleted file mode 100644
index 61b8de3a4f..0000000000
--- a/source/rules/subject-full-stop.test.js
+++ /dev/null
@@ -1,51 +0,0 @@
-import test from 'ava';
-import parse from '../library/parse';
-import check from './subject-full-stop';
-
-const messages = {
- empty: 'chore:\n',
- with: `chore: subject.\n`,
- without: `chore: subject\n`
-};
-
-const parsed = {
- empty: parse(messages.empty),
- with: parse(messages.with),
- without: parse(messages.without)
-};
-
-test('empty against "always" should succeed', t => {
- const [actual] = check(parsed.empty, 'always', '.');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('empty against "never ." should succeed', t => {
- const [actual] = check(parsed.empty, 'never', '.');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with against "always ." should succeed', t => {
- const [actual] = check(parsed.with, 'always', '.');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with against "never ." should fail', t => {
- const [actual] = check(parsed.with, 'never', '.');
- const expected = false;
- t.is(actual, expected);
-});
-
-test('without against "always ." should fail', t => {
- const [actual] = check(parsed.without, 'always', '.');
- const expected = false;
- t.is(actual, expected);
-});
-
-test('without against "never ." should succeed', t => {
- const [actual] = check(parsed.without, 'never', '.');
- const expected = true;
- t.is(actual, expected);
-});
diff --git a/source/rules/subject-leading-capital.test.js b/source/rules/subject-leading-capital.test.js
deleted file mode 100644
index 63bc277a14..0000000000
--- a/source/rules/subject-leading-capital.test.js
+++ /dev/null
@@ -1,69 +0,0 @@
-import test from 'ava';
-import parse from '../library/parse';
-import check from './subject-leading-capital';
-
-const messages = {
- empty: 'chore:\n',
- with: `chore: Subject\n`,
- without: `chore: subject\n`
-};
-
-const parsed = {
- empty: parse(messages.empty),
- with: parse(messages.with),
- without: parse(messages.without)
-};
-
-test('empty should succeed', t => {
- const [actual] = check(parsed.empty);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('empty against "always" should succeed', t => {
- const [actual] = check(parsed.empty, 'always', 'uppercase');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('empty against "never" should succeed', t => {
- const [actual] = check(parsed.empty, 'never', 'uppercase');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with should succeed', t => {
- const [actual] = check(parsed.with);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with against "always" should succeed', t => {
- const [actual] = check(parsed.with, 'always', 'uppercase');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with against "never" should fail', t => {
- const [actual] = check(parsed.with, 'never', 'uppercase');
- const expected = false;
- t.is(actual, expected);
-});
-
-test('without should fail', t => {
- const [actual] = check(parsed.without, 'always', 'uppercase');
- const expected = false;
- t.is(actual, expected);
-});
-
-test('without against "always" should fail', t => {
- const [actual] = check(parsed.without, 'always', 'uppercase');
- const expected = false;
- t.is(actual, expected);
-});
-
-test('without against "never" should succeed', t => {
- const [actual] = check(parsed.without, 'never', 'uppercase');
- const expected = true;
- t.is(actual, expected);
-});
diff --git a/source/rules/subject-tense.test.js b/source/rules/subject-tense.test.js
deleted file mode 100644
index 0f0ce273c1..0000000000
--- a/source/rules/subject-tense.test.js
+++ /dev/null
@@ -1,114 +0,0 @@
-import test from 'ava';
-import parse from '../library/parse';
-import footerTense from './subject-tense';
-
-const messages = {
- empty: 'chore: \n',
- presentImperative: `chore: we implement things`,
- presentParticiple: `chore: implementing things`,
- presentThirdPerson: `chore: implements things`,
- past: `chore: we did implement things`,
- mixed: `chore: implement, implementing, implements, implemented`
-};
-
-const parsed = {
- empty: parse(messages.empty),
- presentImperative: parse(messages.presentImperative),
- presentParticiple: parse(messages.presentParticiple),
- presentThirdPerson: parse(messages.presentImperative),
- past: parse(messages.past),
- mixed: parse(messages.mixed)
-};
-
-test('empty succeeds', t => {
- const [actual] = footerTense(parsed.empty, '', ['present-imperative']);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('present succeeds "always present-imperative"', t => {
- const [actual] = footerTense(parsed.presentImperative, 'always', ['present-imperative']);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('present fails "never present-imperative"', t => {
- const [actual] = footerTense(parsed.presentImperative, 'never', ['present-imperative']);
- const expected = false;
- t.is(actual, expected);
-});
-
-test('present succeeds "always present-participle"', t => {
- const [actual] = footerTense(parsed.presentParticiple, 'always', ['present-participle']);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('present fails "never present-participle"', t => {
- const [actual] = footerTense(parsed.presentParticiple, 'never', ['present-participle']);
- const expected = false;
- t.is(actual, expected);
-});
-
-test('present succeeds "always present-third-person"', t => {
- const [actual] = footerTense(parsed.presentThirdPerson, 'always', ['present-third-person']);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('present fails "never present-third-person"', t => {
- const [actual] = footerTense(parsed.presentThirdPerson, 'never', ['present-third-person']);
- const expected = false;
- t.is(actual, expected);
-});
-
-test('past should succedd "always past-tense"', t => {
- const [actual] = footerTense(parsed.past, 'always', ['past-tense']);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('past fails "never past-tense"', t => {
- const [actual] = footerTense(parsed.past, 'never', ['past-tense']);
- const expected = false;
- t.is(actual, expected);
-});
-
-test('mixed fails "always present-third-person"', t => {
- const [actual] = footerTense(parsed.mixed, 'always', ['present-third-person']);
- const expected = false;
- t.is(actual, expected);
-});
-
-test('mixed fails "always present-imperative"', t => {
- const [actual] = footerTense(parsed.mixed, 'always', ['present-imperative']);
- const expected = false;
- t.is(actual, expected);
-});
-
-test('present fails "always present-participle"', t => {
- const [actual] = footerTense(parsed.mixed, 'always', ['present-participle']);
- const expected = false;
- t.is(actual, expected);
-});
-
-test('mixed fails "always past-tense"', t => {
- const [actual] = footerTense(parsed.mixed, 'always', ['past-tense']);
- const expected = false;
- t.is(actual, expected);
-});
-
-test('mixed succeeds "always present-third-person, present-imperative, present-participle, past-tense"', t => {
- const [actual] = footerTense(parsed.mixed, 'always', ['present-third-person', 'present-imperative', 'present-participle', 'past-tense']);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('mixed succeeds "never allowed: present-third-person" and matching ignored: implements', t => {
- const [actual] = footerTense(parsed.mixed, 'never', {
- allowed: ['present-third-person'],
- ignored: ['implements']
- });
- const expected = true;
- t.is(actual, expected);
-});
diff --git a/source/rules/type-case.test.js b/source/rules/type-case.test.js
deleted file mode 100644
index 673c31525b..0000000000
--- a/source/rules/type-case.test.js
+++ /dev/null
@@ -1,89 +0,0 @@
-import test from 'ava';
-import parse from '../library/parse';
-import typeCase from './type-case';
-
-const messages = {
- empty: '(scope): subject',
- lowercase: 'type: subject',
- mixedcase: 'tYpE: subject',
- uppercase: 'TYPE: subject'
-};
-
-const parsed = {
- empty: parse(messages.empty),
- lowercase: parse(messages.lowercase),
- mixedcase: parse(messages.mixedcase),
- uppercase: parse(messages.uppercase)
-};
-
-test('with empty type should succeed for "never lowercase"', t => {
- const [actual] = typeCase(parsed.empty, 'never', 'lowercase');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with empty type should succeed for "always lowercase"', t => {
- const [actual] = typeCase(parsed.empty, 'always', 'lowercase');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with empty type should succeed for "never uppercase"', t => {
- const [actual] = typeCase(parsed.empty, 'never', 'uppercase');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with empty type should succeed for "always uppercase"', t => {
- const [actual] = typeCase(parsed.empty, 'always', 'uppercase');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with lowercase type should fail for "never lowercase"', t => {
- const [actual] = typeCase(parsed.lowercase, 'never', 'lowercase');
- const expected = false;
- t.is(actual, expected);
-});
-
-test('with lowercase type should succeed for "always lowercase"', t => {
- const [actual] = typeCase(parsed.lowercase, 'always', 'lowercase');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with mixedcase type should succeed for "never lowercase"', t => {
- const [actual] = typeCase(parsed.mixedcase, 'never', 'lowercase');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with mixedcase type should fail for "always lowercase"', t => {
- const [actual] = typeCase(parsed.mixedcase, 'always', 'lowercase');
- const expected = false;
- t.is(actual, expected);
-});
-
-test('with mixedcase type should succeed for "never uppercase"', t => {
- const [actual] = typeCase(parsed.mixedcase, 'never', 'uppercase');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with mixedcase type should fail for "always uppercase"', t => {
- const [actual] = typeCase(parsed.mixedcase, 'always', 'uppercase');
- const expected = false;
- t.is(actual, expected);
-});
-
-test('with uppercase type should fail for "never uppercase"', t => {
- const [actual] = typeCase(parsed.uppercase, 'never', 'uppercase');
- const expected = false;
- t.is(actual, expected);
-});
-
-test('with lowercase type should succeed for "always uppercase"', t => {
- const [actual] = typeCase(parsed.uppercase, 'always', 'uppercase');
- const expected = true;
- t.is(actual, expected);
-});
diff --git a/source/rules/type-empty.test.js b/source/rules/type-empty.test.js
deleted file mode 100644
index 9036a4586e..0000000000
--- a/source/rules/type-empty.test.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import test from 'ava';
-import parse from '../library/parse';
-import typeEmpty from './type-empty';
-
-const messages = {
- empty: '(scope):',
- filled: 'type: subject'
-};
-
-const parsed = {
- empty: parse(messages.empty),
- filled: parse(messages.filled)
-};
-
-test('without type should succeed for empty keyword', t => {
- const [actual] = typeEmpty(parsed.empty);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('without type should fail for "never"', t => {
- const [actual] = typeEmpty(parsed.empty, 'never');
- const expected = false;
- t.is(actual, expected);
-});
-
-test('without type should succeed for "always"', t => {
- const [actual] = typeEmpty(parsed.empty, 'always');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with type fail for empty keyword', t => {
- const [actual] = typeEmpty(parsed.filled);
- const expected = false;
- t.is(actual, expected);
-});
-
-test('with type succeed for "never"', t => {
- const [actual] = typeEmpty(parsed.filled, 'never');
- const expected = true;
- t.is(actual, expected);
-});
-
-test('with type fail for "always"', t => {
- const [actual] = typeEmpty(parsed.filled, 'always');
- const expected = false;
- t.is(actual, expected);
-});
diff --git a/source/rules/type-enum.test.js b/source/rules/type-enum.test.js
deleted file mode 100644
index 4d45b9cdf2..0000000000
--- a/source/rules/type-enum.test.js
+++ /dev/null
@@ -1,123 +0,0 @@
-import test from 'ava';
-import parse from '../library/parse';
-import check from './type-enum';
-
-const messages = {
- empty: '(): \n',
- a: 'a(): \n',
- b: 'b(): \n'
-};
-
-const parsed = {
- empty: parse(messages.empty),
- a: parse(messages.a),
- b: parse(messages.b)
-};
-
-test('empty succeeds', t => {
- const [actual] = check(parsed.empty);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('empty on "a" succeeds', t => {
- const [actual] = check(parsed.empty, '', ['a']);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('empty on "always a" succeeds', t => {
- const [actual] = check(parsed.empty, 'always', ['a']);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('empty on "never a" succeeds', t => {
- const [actual] = check(parsed.empty, 'never', ['a']);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('empty on "always a, b" succeeds', t => {
- const [actual] = check(parsed.empty, 'always', ['a', 'b']);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('empty on "never a, b" succeeds', t => {
- const [actual] = check(parsed.empty, 'neber', ['a', 'b']);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('a on "a" succeeds', t => {
- const [actual] = check(parsed.a, '', ['a']);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('a on "always a" succeeds', t => {
- const [actual] = check(parsed.a, 'always', ['a']);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('a on "never a" fails', t => {
- const [actual] = check(parsed.a, 'never', ['a']);
- const expected = false;
- t.is(actual, expected);
-});
-
-test('b on "b" succeeds', t => {
- const [actual] = check(parsed.b, '', ['b']);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('b on "always b" succeeds', t => {
- const [actual] = check(parsed.b, 'always', ['b']);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('b on "never b" fails', t => {
- const [actual] = check(parsed.b, 'never', ['b']);
- const expected = false;
- t.is(actual, expected);
-});
-
-test('a on "a, b" succeeds', t => {
- const [actual] = check(parsed.a, '', ['a', 'b']);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('a on "always a, b" succeeds', t => {
- const [actual] = check(parsed.a, 'always', ['a', 'b']);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('a on "never a, b" fails', t => {
- const [actual] = check(parsed.a, 'never', ['a', 'b']);
- const expected = false;
- t.is(actual, expected);
-});
-
-test('b on "a, b" succeeds', t => {
- const [actual] = check(parsed.b, '', ['a', 'b']);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('b on "always a, b" succeeds', t => {
- const [actual] = check(parsed.b, 'always', ['a', 'b']);
- const expected = true;
- t.is(actual, expected);
-});
-
-test('b on "never a, b" fails', t => {
- const [actual] = check(parsed.b, 'never', ['a', 'b']);
- const expected = false;
- t.is(actual, expected);
-});