Skip to content

Commit 9790ebc

Browse files
committed
refactor: move utility functions into library, expose them
1 parent dab1c20 commit 9790ebc

File tree

8 files changed

+169
-115
lines changed

8 files changed

+169
-115
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "conventional-changelog-lint",
33
"version": "0.0.0",
4-
"description": "Lint commit messages against a conventional-changelog preset",
4+
"description": "Lint commit messages against a conventional-changelog preset and ruleset",
55
"main": "distribution/index.js",
66
"bin": {
77
"conventional-changelog-lint": "distribution/cli.js"

source/cli.js

+39-114
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,57 @@
11
// polyfills
22
import 'babel-polyfill';
33

4-
// core modules
5-
import {
6-
readFile as readFileNodeback
7-
} from 'fs';
8-
94
// npm modules
105
import chalk from 'chalk';
11-
import denodeify from 'denodeify';
12-
import gitRawCommits from 'git-raw-commits';
136
import meow from 'meow';
14-
import merge from 'lodash.merge';
157
import pick from 'lodash.pick';
168
import stdin from 'get-stdin';
17-
import rc from 'rc';
189

1910
// local modules
11+
import help from './help';
2012
import lint from './';
21-
import pkg from '../package';
13+
import {
14+
format,
15+
getConfiguration,
16+
getPreset,
17+
getMessages
18+
} from './';
2219

23-
// denodeifications
24-
const readFile = denodeify(readFileNodeback);
20+
import pkg from '../package';
2521

2622
/**
2723
* Behavioural rules
2824
*/
2925
const rules = {
30-
fromStdin: (input, settings) => input.length === 0 &&
31-
settings.from === null &&
32-
settings.to === null &&
33-
settings.edit === null
26+
fromStdin: (input, flags) => input.length === 0 &&
27+
flags.from === null &&
28+
flags.to === null &&
29+
!flags.edit
3430
};
3531

36-
// Init meow 😸cli
37-
const cli = meow({
38-
help: [''],
39-
description: `${pkg.name}@${pkg.version} - ${pkg.description}`
40-
}, {
32+
const configuration = {
4133
// flags of string type
4234
string: ['from', 'to', 'preset'],
4335
// flags of bool type
44-
boolean: ['edit', 'quiet', 'color'],
36+
boolean: ['edit', 'help', 'version', 'quiet', 'color'],
4537
// flag aliases
4638
alias: {
4739
c: 'color',
4840
e: 'edit',
4941
f: 'from',
5042
p: 'preset',
5143
t: 'to',
52-
q: 'quiet'
44+
q: 'quiet',
45+
h: 'help',
46+
v: 'version'
47+
},
48+
description: {
49+
color: 'toggle formatted output',
50+
edit: 'read last commit message found in ./git/COMMIT_EDITMSG',
51+
from: 'lower end of the commit range to lint; applies if edit=false',
52+
preset: 'conventional-changelog-preset to use for commit message parsing',
53+
to: 'upper end of the commit range to lint; applies if edit=false',
54+
quiet: 'toggle console output'
5355
},
5456
// flag defaults
5557
default: {
@@ -64,92 +66,13 @@ const cli = meow({
6466
unknown(arg) {
6567
throw new Error(`unknown flags: ${arg}`);
6668
}
67-
});
68-
69-
// Get commit messages
70-
// TODO: move this to an own moduleddd
71-
function getCommits(options) {
72-
return new Promise((resolve, reject) => {
73-
const data = [];
74-
gitRawCommits(options)
75-
.on('data', chunk => data.push(chunk.toString('utf-8')))
76-
.on('error', reject)
77-
.on('end', () => {
78-
resolve(data);
79-
});
80-
});
81-
}
82-
83-
// Get commit messages
84-
// TODO: move this to an own module
85-
async function getMessages(settings) {
86-
const {from, to, edit} = settings;
87-
88-
if (edit) {
89-
const editFile = await readFile(`.git/COMMIT_EDITMSG`);
90-
return [editFile.toString('utf-8')];
91-
} else {
92-
return await getCommits({
93-
from,
94-
to
95-
});
96-
}
97-
}
98-
99-
// Resolve extend configs
100-
// TODO: move this to own module
101-
function resolveExtends(config, prefix = '', key = 'extends') {
102-
return Object.values(config[key] || [])
103-
.reduce((merged, extender) => {
104-
const name = [prefix, extender]
105-
.filter(String)
106-
.join('-');
107-
return merge(
108-
{},
109-
merged,
110-
resolveExtends(require(name))
111-
);
112-
}, config);
113-
}
114-
115-
// Get linting config
116-
// TODO: move this to own module
117-
function getConfiguration(name, settings) {
118-
const config = rc(name, settings.defaults);
119-
return resolveExtends(config, settings.prefix);
120-
}
121-
122-
// Get commit messages
123-
// TODO: move this to an own module
124-
function format(report, options = {}) {
125-
const {signs, colors, color: enabled} = options;
126-
const fmt = new chalk.constructor({enabled});
127-
128-
const problems = [...report.errors, ...report.warnings]
129-
.map(problem => {
130-
const sign = signs[problem.level];
131-
const color = colors[problem.level];
132-
const decoration = fmt[color](sign);
133-
const name = chalk.grey(`[${problem.name}]`);
134-
return `${decoration} ${problem.message} ${name}`
135-
});
136-
137-
const sign = report.errors.length ?
138-
'✖' :
139-
report.warnings.length ?
140-
'⚠' :
141-
'✔' ;
142-
143-
const color = report.errors.length ?
144-
'red' :
145-
report.warnings.length ?
146-
'yellow' :
147-
'green' ;
69+
};
14870

149-
const decoration = fmt[color](sign);
150-
const summary = `${decoration} found ${report.errors.length} problems, ${report.warnings.length} warnings`;
151-
return [...problems, chalk.bold(summary)];
152-
}
71+
// Init meow 😸cli
72+
const cli = meow({
73+
help: `[input] reads from stdin if --edit, --from, --to are omitted\n${help(configuration)}`,
74+
description: `${pkg.name}@${pkg.version} - ${pkg.description}`
75+
}, configuration);
15376

15477
// Assemble the engine
15578
async function main(options) {
@@ -164,11 +87,11 @@ async function main(options) {
16487

16588
return Promise.all(input
16689
.map(async commit => {
90+
const fmt = new chalk.constructor({enabled: flags.color});
91+
16792
const report = lint(commit, {
168-
preset: await require(`conventional-changelog-${flags.preset}`),
169-
configuration: getConfiguration('conventional-changelog-lint', {
170-
prefix: `conventional-changelog-lint-config`
171-
})
93+
preset: await getPreset(flags.preset),
94+
configuration: await getConfiguration()
17295
});
17396

17497
const formatted = format(report, {
@@ -178,15 +101,17 @@ async function main(options) {
178101
});
179102

180103
if (!flags.quiet) {
181-
console.log(`validating: ${commit.split('\n')[0]}`);
104+
console.log(`${fmt.grey('⧗')} input: ${fmt.bold(commit.split('\n')[0])}`);
182105
console.log(
183106
formatted
184107
.join('\n')
185108
);
186109
}
187110

188111
if (report.errors.length > 0) {
189-
throw new Error(formatted[formatted.length - 1]);
112+
const error = new Error(formatted[formatted.length - 1]);
113+
error.type = pkg.name;
114+
throw error;
190115
}
191116

192117
console.log('');

source/help.js

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
export default configuration => {
2+
const lines = Object.entries(configuration.description)
3+
.map(entry => {
4+
const [name, desc] = entry;
5+
const alias = Object.entries(configuration.alias)
6+
.find(entry => entry[1] === name)
7+
.map(entry => entry[0])[0];
8+
const defaults = configuration.default[name];
9+
return [[name, alias].filter(Boolean), desc, defaults].filter(Boolean);
10+
});
11+
12+
const longest = lines
13+
.map(line => {
14+
const [flags] = line;
15+
return flags.reduce((sum, flag) => sum + flag.length, 0);
16+
})
17+
.sort(Number)[0];
18+
19+
return lines
20+
.map(line => {
21+
const [flags, desc, defaults] = line;
22+
const fs = flags.map(flag => flag.length > 1 ? `--${flag}` : `-${flag}`);
23+
const ds = defaults ? `, defaults to: ${defaults}` : '';
24+
const length = flags.reduce((sum, flag) => sum + flag.length, 0);
25+
return `${fs.join(',')}${' '.repeat(4 + longest - length)}${desc}${ds}`;
26+
})
27+
.join('\n');
28+
};

source/index.js

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import {sync as parse} from 'conventional-commits-parser';
22

3+
export getConfiguration from './library/get-configuration';
4+
export getMessages from './library/get-messages';
5+
export getPreset from './library/get-preset';
6+
export format from './library/format';
7+
38
export default (message, options = {}) => {
49
const {
510
preset: {

source/library/format.js

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import chalk from 'chalk';
2+
3+
export default function format(report, options = {}) {
4+
const {signs, colors, color: enabled} = options;
5+
const fmt = new chalk.constructor({enabled});
6+
7+
const problems = [...report.errors, ...report.warnings]
8+
.map(problem => {
9+
const sign = signs[problem.level];
10+
const color = colors[problem.level];
11+
const decoration = fmt[color](sign);
12+
const name = chalk.grey(`[${problem.name}]`);
13+
return `${decoration} ${problem.message} ${name}`;
14+
});
15+
16+
const sign = report.errors.length ? // eslint-disable-line no-nested-ternary
17+
'✖' :
18+
report.warnings.length ?
19+
'⚠' :
20+
'✔';
21+
22+
const color = report.errors.length ? // eslint-disable-line no-nested-ternary
23+
'red' :
24+
report.warnings.length ?
25+
'yellow' :
26+
'green';
27+
28+
const decoration = fmt[color](sign);
29+
const summary = `${decoration} found ${report.errors.length} problems, ${report.warnings.length} warnings`;
30+
return [...problems, chalk.bold(summary)];
31+
}

source/library/get-configuration.js

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import merge from 'lodash.merge';
2+
import rc from 'rc';
3+
4+
// Resolve extend configs
5+
function resolveExtends(config, prefix = '', key = 'extends') {
6+
return Object.values(config[key] || [])
7+
.reduce((merged, extender) => {
8+
const name = [prefix, extender]
9+
.filter(String)
10+
.join('-');
11+
return merge(
12+
{},
13+
merged,
14+
resolveExtends(require(name))
15+
);
16+
}, config);
17+
}
18+
19+
// Get linting config
20+
export default (name = 'conventional-changelog-lint', settings = {
21+
prefix: 'conventional-changelog-lint-config'
22+
}) => {
23+
const config = rc(name, settings.defaults);
24+
return resolveExtends(config, settings.prefix);
25+
};

source/library/get-messages.js

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// core modules
2+
import {
3+
readFile as readFileNodeback
4+
} from 'fs';
5+
6+
import denodeify from 'denodeify';
7+
import gitRawCommits from 'git-raw-commits';
8+
9+
const readFile = denodeify(readFileNodeback);
10+
11+
// Get commit messages
12+
function getCommits(options) {
13+
return new Promise((resolve, reject) => {
14+
const data = [];
15+
gitRawCommits(options)
16+
.on('data', chunk => data.push(chunk.toString('utf-8')))
17+
.on('error', reject)
18+
.on('end', () => {
19+
resolve(data);
20+
});
21+
});
22+
}
23+
24+
// Get commit messages
25+
export default async settings => {
26+
const {from, to, edit} = settings;
27+
28+
if (edit) {
29+
const editFile = await readFile(`.git/COMMIT_EDITMSG`);
30+
return [editFile.toString('utf-8')];
31+
} else {
32+
return await getCommits({
33+
from,
34+
to
35+
});
36+
}
37+
}

source/library/get-preset.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default async name => {
2+
return await require(`conventional-changelog-${name}`);
3+
};

0 commit comments

Comments
 (0)