Skip to content
This repository was archived by the owner on Dec 31, 2019. It is now read-only.

Commit 331d559

Browse files
committed
refacto(cli): isolate and test ArgParser
1 parent bb98754 commit 331d559

File tree

6 files changed

+305
-114
lines changed

6 files changed

+305
-114
lines changed

package.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
"escodegen": "^1.6.1",
4242
"esprima": "^2.1.0",
4343
"istanbul": "^0.4.0",
44-
"lodash.partial": "^3.1.0",
4544
"mkdirp": "^0.5.0",
4645
"nomnomnomnom": "^2.0.0",
4746
"object-assign": "^4.0.1",
@@ -50,14 +49,16 @@
5049
},
5150
"devDependencies": {
5251
"babel-cli": "^6.1.4",
52+
"babel-plugin-transform-object-rest-spread": "^6.1.4",
5353
"babel-polyfill": "^6.1.4",
5454
"babel-preset-es2015": "^6.1.4",
55-
"babel-plugin-transform-object-rest-spread": "^6.1.4",
5655
"chai": "^3.2.0",
5756
"douglasduteil...shelltest": "^2.0.0",
5857
"hide-stack-frames-from": "^1.0.0",
5958
"mocha": "^2.2.1",
60-
"nth": "^0.1.2"
59+
"nth": "^0.1.2",
60+
"sinon": "^1.17.2",
61+
"sinon-chai": "^2.8.0"
6162
},
6263
"scripts": {
6364
"dist": "babel src --out-dir lib",

src/cli/ArgParser.js

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
//
2+
3+
import nomnom from 'nomnomnomnom';
4+
5+
//
6+
7+
8+
export default ArgParser
9+
10+
//
11+
12+
function ArgParser (commands) {
13+
const parser = nomnom();
14+
15+
parser.command('cover')
16+
.help("transparently adds coverage information to a node command. Saves coverage.json and reports at the end of execution")
17+
18+
.option('cmd', {
19+
required: true,
20+
position: 1,
21+
help: 'ES6 js files to cover (using babel)'
22+
})
23+
24+
.option('config', {
25+
metavar: '<path-to-config>',
26+
help: 'the configuration file to use, defaults to .istanbul.yml'
27+
})
28+
.option('default-excludes', {
29+
flag: true,
30+
help: 'apply default excludes [ **/node_modules/**, **/test/**, **/tests/** ]'
31+
})
32+
.option('excludes', {
33+
abbr: 'x',
34+
default: [],
35+
help: 'one or more fileset patterns e.g. "**/vendor/**"',
36+
list: true,
37+
metavar: '<exclude-pattern>'
38+
})
39+
.option('report', {
40+
default: 'lcv',
41+
metavar: '<format>',
42+
list: true,
43+
help: 'report format'
44+
})
45+
.option('root', {
46+
metavar: '<path>',
47+
help: 'the root path to look for files to instrument'
48+
})
49+
.option('include', {
50+
default: ['**/*.js'],
51+
metavar: '<include-pattern>',
52+
list: true,
53+
abbr: 'i',
54+
help: 'one or more fileset patterns e.g. \'**/*.js\''
55+
})
56+
.option('verbose', {
57+
flag: true,
58+
abbr: 'v',
59+
help: 'verbose mode'
60+
})
61+
.option('include-all-sources', {
62+
flag: true,
63+
help: 'instrument all unused sources after running tests'
64+
})
65+
.callback(commands.cover)
66+
;
67+
68+
return parser
69+
}
+28-109
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,21 @@
11
//
22

3-
import {existsSync, writeFileSync, statSync, readdirSync} from 'fs';
4-
import path from 'path';
3+
import {existsSync, writeFileSync} from 'fs';
4+
import {Instrumenter} from '../../instrumenter';
5+
import {hook, Collector, Reporter, matcherFor, config as configuration} from 'istanbul';
6+
import mkdirp from 'mkdirp';
57
import Module from 'module';
6-
7-
//
88
import assign from 'object-assign';
9+
import path from 'path';
910
import which from 'which';
10-
import mkdirp from 'mkdirp';
11-
import partial from 'lodash.partial';
12-
import nomnom from 'nomnomnomnom';
13-
import {hook, Collector, Reporter, matcherFor, config as configuration} from 'istanbul';
14-
import {Instrumenter} from './instrumenter';
1511

1612
//
1713

14+
export default coverCmd
1815

1916
//
2017

21-
nomnom.command('cover')
22-
.help("transparently adds coverage information to a node command. Saves coverage.json and reports at the end of execution")
23-
24-
.option('cmd', {
25-
required: true,
26-
position: 1,
27-
help: 'ES6 js files to cover (using babel)'
28-
})
29-
30-
.option('config', {
31-
metavar: '<path-to-config>',
32-
help: 'the configuration file to use, defaults to .istanbul.yml'
33-
})
34-
.option('default-excludes', {
35-
flag: true,
36-
help: 'apply default excludes [ **/node_modules/**, **/test/**, **/tests/** ]'
37-
})
38-
.option('excludes', {
39-
abbr: 'x',
40-
default: [],
41-
help: 'one or more fileset patterns e.g. "**/vendor/**"',
42-
list: true,
43-
metavar: '<exclude-pattern>'
44-
})
45-
.option('report', {
46-
default: 'lcv',
47-
metavar: '<format>',
48-
list: true,
49-
help: 'report format'
50-
})
51-
.option('root', {
52-
metavar: '<path>',
53-
help: 'the root path to look for files to instrument'
54-
})
55-
.option('include', {
56-
default: ['**/*.js'],
57-
metavar: '<include-pattern>',
58-
list: true,
59-
abbr: 'i',
60-
help: 'one or more fileset patterns e.g. \'**/*.js\''
61-
})
62-
.option('verbose', {
63-
flag: true,
64-
abbr: 'v',
65-
help: 'verbose mode'
66-
})
67-
.option('include-all-sources', {
68-
flag: true,
69-
help: 'instrument all unused sources after running tests'
70-
})
71-
72-
.callback(opts => {
73-
74-
let args = opts._,
75-
files = [],
76-
cmdArgs = [];
77-
78-
args.forEach(arg => {
79-
80-
let file = lookupFiles(arg);
81-
if (file) files = files.concat(file);
82-
});
83-
84-
opts.include = opts.include.concat(files);
85-
86-
coverCmd(opts);
87-
});
88-
;
89-
90-
nomnom.nom();
91-
92-
function lookupFiles (path) {
93-
94-
if (existsSync(path)) {
95-
let stat = statSync(path);
96-
if (stat.isFile()) return path;
97-
}
98-
}
99-
100-
function callback(err){
101-
if (err){
102-
console.error(err);
103-
process.exit(1);
104-
}
105-
process.exit(0);
106-
}
107-
108-
function coverCmd(opts) {
109-
18+
function coverCmd (opts) {
11019
let config = overrideConfigWith(opts);
11120
let istanbulCoveragePath = path.resolve(config.reporting.dir());
11221
let reporter = new Reporter(config, istanbulCoveragePath);
@@ -118,7 +27,7 @@ function coverCmd(opts) {
11827
try {
11928
cmd = which.sync(cmd);
12029
} catch (ex) {
121-
return callback(`Unable to resolve file [${cmd}]`);
30+
return processEnding(`Unable to resolve file [${cmd}]`);
12231
}
12332
} else {
12433
cmd = path.resolve(cmd);
@@ -132,7 +41,7 @@ function coverCmd(opts) {
13241

13342
////
13443

135-
function overrideConfigWith(opts){
44+
function overrideConfigWith (opts) {
13645
let overrides = {
13746
verbose: opts.verbose,
13847
instrumentation: {
@@ -159,7 +68,7 @@ function coverCmd(opts) {
15968
return configuration.loadFile(opts.config, overrides);
16069
}
16170

162-
function enableHooks() {
71+
function enableHooks () {
16372
opts.reportingDir = path.resolve(config.reporting.dir());
16473
mkdirp.sync(opts.reportingDir);
16574
reporter.addAll(config.reporting.reports());
@@ -187,8 +96,8 @@ function coverCmd(opts) {
18796
.map((ext) => '**/*' + ext),
18897
excludes: excludes
18998
}, (err, matchFn) => {
190-
if (err){
191-
return callback(err);
99+
if (err) {
100+
return processEnding(err);
192101
}
193102

194103
prepareCoverage(matchFn);
@@ -197,12 +106,12 @@ function coverCmd(opts) {
197106
}
198107

199108

200-
function prepareCoverage(matchFn) {
109+
function prepareCoverage (matchFn) {
201110
let coverageVar = `$$cov_${Date.now()}$$`;
202-
let instrumenter = new Instrumenter({ coverageVariable : coverageVar });
111+
let instrumenter = new Instrumenter({ coverageVariable: coverageVar });
203112
let transformer = instrumenter.instrumentSync.bind(instrumenter);
204113

205-
hook.hookRequire(matchFn, transformer, assign({ verbose : opts.verbose }, config.instrumentation.config));
114+
hook.hookRequire(matchFn, transformer, assign({ verbose: opts.verbose }, config.instrumentation.config));
206115

207116
global[coverageVar] = {};
208117

@@ -236,7 +145,7 @@ function coverCmd(opts) {
236145
console.error(`Writing coverage reports at [${opts.reportingDir}]`);
237146
console.error(Array(80 + 1).join('='));
238147
}
239-
reporter.write(collector, true, callback);
148+
reporter.write(collector, true, processEnding);
240149
});
241150

242151
if (config.instrumentation.includeAllSources()) {
@@ -255,12 +164,22 @@ function coverCmd(opts) {
255164

256165
}
257166

258-
function runCommandFn() {
167+
function runCommandFn () {
259168
process.argv = ["node", cmd].concat(cmdArgs);
260169
if (opts.verbose) {
261170
console.log('Running: ' + process.argv.join(' '));
262171
}
263-
process.env.running_under_istanbul=1;
172+
process.env.running_under_istanbul = 1;
264173
Module.runMain(cmd, null, true);
265174
}
266175
}
176+
177+
//
178+
179+
function processEnding (err) {
180+
if (err) {
181+
console.error(err);
182+
process.exit(1);
183+
}
184+
process.exit(0);
185+
}

src/cli/index.js

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//
2+
3+
import {existsSync, statSync} from 'fs';
4+
5+
//
6+
7+
import cover from './commands/cover'
8+
import ArgParser from './ArgParser'
9+
10+
//
11+
12+
ArgParser({
13+
cover: runCoverCommand
14+
})
15+
.parse();
16+
17+
//
18+
19+
function runCoverCommand (opts) {
20+
const files = opts._.slice(1).reduce(function (memo, file) {
21+
return memo.concat(lookupFile(file) || [])
22+
}, []);
23+
24+
opts.include = opts.include.concat(files);
25+
cover(opts);
26+
}
27+
28+
function lookupFile (path) {
29+
if (existsSync(path)) {
30+
let stat = statSync(path);
31+
if (stat.isFile()) return path;
32+
}
33+
}

src/cover.js

-2
This file was deleted.

0 commit comments

Comments
 (0)