Skip to content

Commit 1b58d3d

Browse files
Merge pull request #24 from pjozsef/master
Add option to ignore single line comments
2 parents 63618bc + a6ebf9f commit 1b58d3d

22 files changed

+442
-33
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
node_modules/
1+
node_modules/
2+
.idea

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,17 +70,21 @@ look like this:
7070
"exclude": {
7171
7272
"files": ["readme.md", "history.md", "AUTHORS"],
73-
"directories": ["bin"]
73+
"directories": ["bin"],
74+
"comments": { ".rb": "#", ".js": "//" }
7475
},
7576
"threshold": 20
7677
}
7778
```
7879

80+
With `exclude.comments`, you can exclude lines starting with specific characters
81+
for the given file extensions.
82+
7983
### command line argument
8084

8185
You may also pass the location of your config file ar runtime with the `--config`
8286
option. This allows you to store your config in whatever file you would like. It
8387
still needs to conform to the structure laid out above though.
8488

8589
As an example: `pythia --config pythia.json` where `pythia.json` is at the root
86-
of your project.
90+
of your project.

fixtures/.empty-config

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
{
2+
}

fixtures/.exclude-comments-config

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"exclude": {
3+
"comments": {
4+
"rb": "#",
5+
"js": "//"
6+
}
7+
}
8+
}

fixtures/.exclude-directories-config

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"exclude": {
3+
"directories": [
4+
"dir-one",
5+
"dir-two"
6+
]
7+
}
8+
}

fixtures/.exclude-files-config

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"exclude": {
3+
"files": [
4+
"file-one",
5+
"file-two"
6+
]
7+
}
8+
}

fixtures/.exclude-users-config

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"exclude": {
3+
"users": [
4+
5+
6+
]
7+
}
8+
}

fixtures/.malformed-config

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
malformed: json
3+
}

fixtures/.pythia-config

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"threshold": 43,
3+
"exclude": {
4+
"users": [
5+
6+
7+
],
8+
"directories": [
9+
"dir-one",
10+
"dir-two"
11+
],
12+
"files": [
13+
"file-one",
14+
"file-two"
15+
],
16+
"comments": {
17+
"rb": "#",
18+
"js": "//"
19+
}
20+
}
21+
}

fixtures/.threshold-config

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"threshold": 42
3+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
9a93ec03 (<[email protected]> 2018-04-03 22:10:49 -0600 1) # ruby comment here
2+
9a93ec03 (<[email protected]> 2018-04-03 22:10:49 -0600 1) puts 'hello'

fixtures/blame-multiple-comment.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
9a93ec03 (<[email protected]> 2018-04-03 22:10:49 -0600 1) // js comment here 1
2+
9a93ec03 (<[email protected]> 2018-04-03 22:10:49 -0600 2) console.log("a");
3+
9a93ec03 (<[email protected]> 2018-04-03 22:10:49 -0600 3) // js comment here 2
4+
9a93ec03 (<[email protected]> 2018-04-03 22:10:49 -0600 4) console.log("b");
5+
8a83ec04 (<[email protected]> 2018-04-03 23:10:49 -0600 5) console.log("c");
6+
8a83ec04 (<[email protected]> 2018-04-03 23:10:49 -0600 6) // js comment here 3

fixtures/blame.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
9a93ec03 (<[email protected]> 2018-04-03 22:10:49 -0600 1) line 1
2+
9a93ec03 (<[email protected]> 2018-04-03 22:10:49 -0600 2) line 2
3+
8a83ec04 (<[email protected]> 2018-04-03 23:10:49 -0600 3) line 3
4+
9a93ec03 (<[email protected]> 2018-04-03 22:10:49 -0600 4) line 4

fixtures/code-comment-single-line.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// js comment here 1
2+
console.log("a");
3+
// js comment here 2
4+
console.log("b");
5+
console.log("c");
6+
// js comment here 3

fixtures/code-comment-single-line.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# ruby comment here
2+
puts 'hello'

modules/check-ownership.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const cp = require('child_process')
22
path = require('path');
33

4-
module.exports = (details, publish) => {
4+
module.exports.checkOwnership = (details, publish) => {
55
const { size, ownedLines, author, filePath, currentAuthor, threshold } = details;
66
const ownership = (ownedLines / size) * 100;
77

modules/process-file.js

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,39 @@
11
const fs = require('fs'),
2-
cp = require('child_process'),
3-
split = require('split'),
4-
getEmail = require('./get-email'),
5-
checkOwnership = require('./check-ownership'),
6-
getFileLength = (file, cb) => {
7-
let lines = 0;
8-
9-
if (!file) {
10-
cb();
11-
return;
2+
cp = require('child_process'),
3+
path = require('path'),
4+
split = require('split'),
5+
getEmail = require('./get-email'),
6+
utils = require('./check-ownership'),
7+
isCommentLine = (line, commentChar) => commentChar && RegExp(`^\\s*${commentChar}.*`, 'i').test(line),
8+
getFileLength = (file, config, cb) => {
9+
const commentChar = config.exclude.comments[path.extname(file)];
10+
const commentIndices = [];
11+
let currentLine = 0;
12+
13+
if (!file) {
14+
cb();
15+
return;
16+
}
17+
18+
fs.createReadStream(file)
19+
.pipe(split(null, null, { trailing: false }))
20+
.on('data', (chunk) => {
21+
if (isCommentLine(chunk, commentChar)) {
22+
commentIndices.push(currentLine);
1223
}
13-
14-
fs.createReadStream(file)
15-
.pipe(split())
16-
.on('data', (chunk) => {
17-
lines++;
18-
})
19-
.on('end', () => {
20-
cb(lines);
21-
});
22-
};
24+
currentLine++;
25+
})
26+
.on('end', () => {
27+
cb(currentLine - commentIndices.length, commentIndices);
28+
});
29+
};
2330

2431
module.exports = ({file, excludeUsers, publish, currentAuthor, config}) => {
25-
getFileLength(file, (lines) => {
32+
getFileLength(file, config, (lines, commentIndices) => {
2633
cp.exec(`git blame -w --show-email HEAD~1 -- ${file}`, (error, stdout, stderr) => {
27-
const emails = stdout.split('\n').reduce((accum, line) => {
34+
const emails = stdout.split('\n').reduce((accum, line, lineIndex) => {
2835
const email = getEmail(line);
29-
if (!email || excludeUsers.includes(email)) {
36+
if (!email || excludeUsers.includes(email) || commentIndices.includes(lineIndex)) {
3037
return accum;
3138
}
3239

@@ -37,14 +44,15 @@ module.exports = ({file, excludeUsers, publish, currentAuthor, config}) => {
3744
}
3845

3946
return accum;
47+
4048
}, {});
4149

4250
Object.keys(emails).forEach((email) => {
4351
// { size, ownedLines, author, file_path }
44-
checkOwnership({
45-
size: lines,
52+
utils.checkOwnership({
53+
size: lines,
4654
ownedLines: emails[email],
47-
author: email,
55+
author: email,
4856
filePath: file,
4957
currentAuthor,
5058
threshold: config.threshold

modules/process-file.test.js

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
const test = require('ava');
2+
const sinon = require('sinon');
3+
const processFile = require('./process-file');
4+
const fs = require('fs');
5+
const cp = require('child_process');
6+
const utils = require('./check-ownership');
7+
8+
const sandbox = sinon.createSandbox();
9+
10+
test.beforeEach(t => sandbox.restore());
11+
12+
test.cb('calls checkOwnership with the appropriate arguments', t => {
13+
t.plan(4);
14+
15+
const blameResult = fs.readFileSync('fixtures/blame.txt', 'utf8');
16+
17+
const checkOwnershipStub = sandbox.stub(utils, 'checkOwnership');
18+
const file = 'fixtures/blame.txt';
19+
sandbox.stub(cp, 'exec').withArgs(`git blame -w --show-email HEAD~1 -- ${file}`).yields(undefined, blameResult, undefined);
20+
const excludeUsers = [];
21+
const publish = false;
22+
const currentAuthor = '[email protected]';
23+
const config = { threshold: 42, exclude: { comments: {} } };
24+
25+
checkOwnershipStub.onCall(0).callsFake((params, givenPublish) => {
26+
t.deepEqual(params, {
27+
author: '[email protected]',
28+
currentAuthor: currentAuthor,
29+
filePath: file,
30+
ownedLines: 3,
31+
size: 4,
32+
threshold: config.threshold,
33+
});
34+
t.is(givenPublish, publish);
35+
});
36+
checkOwnershipStub.onCall(1).callsFake((params, givenPublish) => {
37+
t.deepEqual(params, {
38+
author: '[email protected]',
39+
currentAuthor: currentAuthor,
40+
filePath: file,
41+
ownedLines: 1,
42+
size: 4,
43+
threshold: config.threshold,
44+
});
45+
t.is(givenPublish, publish);
46+
t.end();
47+
});
48+
49+
processFile({ file, excludeUsers, publish, currentAuthor, config });
50+
});
51+
52+
test.cb('calls checkOwnership without commented lines', t => {
53+
54+
const blameResult = fs.readFileSync('fixtures/blame-comment-single-line.txt', 'utf8');
55+
56+
const checkOwnershipStub = sandbox.stub(utils, 'checkOwnership');
57+
const file = 'fixtures/code-comment-single-line.rb';
58+
sandbox.stub(cp, 'exec').withArgs(`git blame -w --show-email HEAD~1 -- ${file}`).yields(undefined, blameResult, undefined);
59+
const excludeUsers = [];
60+
const publish = false;
61+
const currentAuthor = '[email protected]';
62+
const config = {
63+
threshold: 42,
64+
exclude: {
65+
comments: { '.rb': '#' }
66+
}
67+
};
68+
69+
checkOwnershipStub.onCall(0).callsFake((params, givenPublish) => {
70+
t.deepEqual(params, {
71+
author: '[email protected]',
72+
currentAuthor: currentAuthor,
73+
filePath: file,
74+
ownedLines: 1,
75+
size: 1,
76+
threshold: config.threshold,
77+
});
78+
t.end();
79+
});
80+
81+
processFile({ file, excludeUsers, publish, currentAuthor, config });
82+
});
83+
84+
test.cb('calls checkOwnership with comments and multiple authors', t => {
85+
t.plan(2);
86+
87+
const blameResult = fs.readFileSync('fixtures/blame-multiple-comment.txt', 'utf8');
88+
89+
const checkOwnershipStub = sandbox.stub(utils, 'checkOwnership');
90+
const file = 'fixtures/code-comment-single-line.js';
91+
sandbox.stub(cp, 'exec').withArgs(`git blame -w --show-email HEAD~1 -- ${file}`).yields(undefined, blameResult, undefined);
92+
const excludeUsers = [];
93+
const publish = false;
94+
const currentAuthor = '[email protected]';
95+
const config = { threshold: 42, exclude: { comments: {'.js': '//'} } };
96+
97+
checkOwnershipStub.onCall(0).callsFake((params, givenPublish) => {
98+
t.deepEqual(params, {
99+
author: '[email protected]',
100+
currentAuthor: currentAuthor,
101+
filePath: file,
102+
ownedLines: 2,
103+
size: 3,
104+
threshold: config.threshold,
105+
});
106+
});
107+
checkOwnershipStub.onCall(1).callsFake((params, givenPublish) => {
108+
t.deepEqual(params, {
109+
author: '[email protected]',
110+
currentAuthor: currentAuthor,
111+
filePath: file,
112+
ownedLines: 1,
113+
size: 3,
114+
threshold: config.threshold,
115+
});
116+
t.end();
117+
});
118+
119+
processFile({ file, excludeUsers, publish, currentAuthor, config });
120+
});

modules/read-config.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,12 @@ module.exports = (location = '.pythia-config') => {
1414
}
1515
});
1616

17+
if (typeof exclude.comments === 'undefined') {
18+
exclude.comments = {};
19+
}
20+
1721
return {
1822
exclude,
1923
threshold
2024
};
21-
};
25+
};

0 commit comments

Comments
 (0)