Skip to content

Commit 7002961

Browse files
committed
Split up cli.js
Move the actual implementation into lib/cli.js to avoid top-level returns. xo@^0.17.0 cannot parse the file with those returns present. Throw errors from lib/cli.js and write them to stderr in cli.js before exiting. XO doesn't appreciate process.exit() calls in modules. Note that the error for invalid Babel config is now written to stderr, which seems like an improvement to me.
1 parent 36b95c6 commit 7002961

File tree

3 files changed

+178
-179
lines changed

3 files changed

+178
-179
lines changed

cli.js

Lines changed: 7 additions & 177 deletions
Original file line numberDiff line numberDiff line change
@@ -13,186 +13,16 @@ var localCLI = resolveCwd('ava/cli');
1313
// see https://github.com/nodejs/node/issues/6624
1414
if (localCLI && path.relative(localCLI, __filename) !== '') {
1515
debug('Using local install of AVA');
16-
require(localCLI);
17-
return;
18-
}
19-
20-
if (debug.enabled) {
21-
require('time-require');
22-
}
23-
24-
var updateNotifier = require('update-notifier');
25-
var figures = require('figures');
26-
var arrify = require('arrify');
27-
var meow = require('meow');
28-
var Promise = require('bluebird');
29-
var pkgConf = require('pkg-conf');
30-
var isCi = require('is-ci');
31-
var hasFlag = require('has-flag');
32-
var colors = require('./lib/colors');
33-
var verboseReporter = require('./lib/reporters/verbose');
34-
var miniReporter = require('./lib/reporters/mini');
35-
var tapReporter = require('./lib/reporters/tap');
36-
var Logger = require('./lib/logger');
37-
var Watcher = require('./lib/watcher');
38-
var babelConfig = require('./lib/babel-config');
39-
var Api = require('./api');
40-
41-
// Bluebird specific
42-
Promise.longStackTraces();
43-
44-
var conf = pkgConf.sync('ava');
45-
46-
var pkgDir = path.dirname(pkgConf.filepath(conf));
47-
48-
try {
49-
conf.babel = babelConfig.validate(conf.babel);
50-
} catch (err) {
51-
console.log('\n ' + err.message);
52-
process.exit(1);
53-
}
54-
55-
var cli = meow([
56-
'Usage',
57-
' ava [<file|directory|glob> ...]',
58-
'',
59-
'Options',
60-
' --init Add AVA to your project',
61-
' --fail-fast Stop after first test failure',
62-
' --serial, -s Run tests serially',
63-
' --tap, -t Generate TAP output',
64-
' --verbose, -v Enable verbose output',
65-
' --no-cache Disable the transpiler cache',
66-
' --no-power-assert Disable Power Assert',
67-
' --match, -m Only run tests with matching title (Can be repeated)',
68-
' --watch, -w Re-run tests when tests and source files change',
69-
' --source, -S Pattern to match source files so tests can be re-run (Can be repeated)',
70-
' --timeout, -T Set global timeout',
71-
' --concurrency, -c Maximum number of test files running at the same time (EXPERIMENTAL)',
72-
'',
73-
'Examples',
74-
' ava',
75-
' ava test.js test2.js',
76-
' ava test-*.js',
77-
' ava test',
78-
' ava --init',
79-
' ava --init foo.js',
80-
'',
81-
'Default patterns when no arguments:',
82-
'test.js test-*.js test/**/*.js **/__tests__/**/*.js **/*.test.js'
83-
], {
84-
string: [
85-
'_',
86-
'timeout',
87-
'source',
88-
'match',
89-
'concurrency'
90-
],
91-
boolean: [
92-
'fail-fast',
93-
'verbose',
94-
'serial',
95-
'tap',
96-
'watch'
97-
],
98-
default: conf,
99-
alias: {
100-
t: 'tap',
101-
v: 'verbose',
102-
s: 'serial',
103-
m: 'match',
104-
w: 'watch',
105-
S: 'source',
106-
T: 'timeout',
107-
c: 'concurrency'
108-
}
109-
});
110-
111-
updateNotifier({pkg: cli.pkg}).notify();
112-
113-
if (cli.flags.init) {
114-
require('ava-init')();
115-
return;
116-
}
117-
118-
if (
119-
((hasFlag('--watch') || hasFlag('-w')) && (hasFlag('--tap') || hasFlag('-t'))) ||
120-
(conf.watch && conf.tap)
121-
) {
122-
console.error(' ' + colors.error(figures.cross) + ' The TAP reporter is not available when using watch mode.');
123-
process.exit(1);
124-
}
125-
126-
if (hasFlag('--require') || hasFlag('-r')) {
127-
console.error(' ' + colors.error(figures.cross) + ' The --require and -r flags are deprecated. Requirements should be configured in package.json - see documentation.');
128-
process.exit(1);
129-
}
130-
131-
var api = new Api({
132-
failFast: cli.flags.failFast,
133-
serial: cli.flags.serial,
134-
require: arrify(conf.require),
135-
cacheEnabled: cli.flags.cache !== false,
136-
powerAssert: cli.flags.powerAssert !== false,
137-
explicitTitles: cli.flags.watch,
138-
match: arrify(cli.flags.match),
139-
babelConfig: conf.babel,
140-
resolveTestsFrom: cli.input.length === 0 ? pkgDir : process.cwd(),
141-
timeout: cli.flags.timeout,
142-
concurrency: cli.flags.concurrency ? parseInt(cli.flags.concurrency, 10) : 0
143-
});
144-
145-
var reporter;
146-
147-
if (cli.flags.tap && !cli.flags.watch) {
148-
reporter = tapReporter();
149-
} else if (cli.flags.verbose || isCi) {
150-
reporter = verboseReporter();
16+
require(localCLI); // eslint-disable-line import/no-dynamic-require
15117
} else {
152-
reporter = miniReporter({watching: cli.flags.watch});
153-
}
154-
155-
reporter.api = api;
156-
var logger = new Logger(reporter);
157-
158-
logger.start();
159-
160-
api.on('test-run', function (runStatus) {
161-
reporter.api = runStatus;
162-
runStatus.on('test', logger.test);
163-
runStatus.on('error', logger.unhandledError);
164-
165-
runStatus.on('stdout', logger.stdout);
166-
runStatus.on('stderr', logger.stderr);
167-
});
168-
169-
var files = cli.input.length ? cli.input : arrify(conf.files);
18+
if (debug.enabled) {
19+
require('time-require'); // eslint-disable-line import/no-unassigned-import
20+
}
17021

171-
if (cli.flags.watch) {
17222
try {
173-
var watcher = new Watcher(logger, api, files, arrify(cli.flags.source));
174-
watcher.observeStdin(process.stdin);
23+
require('./lib/cli').run();
17524
} catch (err) {
176-
if (err.name === 'AvaError') {
177-
// An AvaError may be thrown if chokidar is not installed. Log it nicely.
178-
console.error(' ' + colors.error(figures.cross) + ' ' + err.message);
179-
logger.exit(1);
180-
} else {
181-
// Rethrow so it becomes an uncaught exception.
182-
throw err;
183-
}
25+
console.error('\n ' + err.message);
26+
process.exit(1);
18427
}
185-
} else {
186-
api.run(files)
187-
.then(function (runStatus) {
188-
logger.finish(runStatus);
189-
logger.exit(runStatus.failCount > 0 || runStatus.rejectionCount > 0 || runStatus.exceptionCount > 0 ? 1 : 0);
190-
})
191-
.catch(function (err) {
192-
// Don't swallow exceptions. Note that any expected error should already
193-
// have been logged.
194-
setImmediate(function () {
195-
throw err;
196-
});
197-
});
19828
}

lib/cli.js

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
'use strict';
2+
var path = require('path');
3+
var updateNotifier = require('update-notifier');
4+
var figures = require('figures');
5+
var arrify = require('arrify');
6+
var meow = require('meow');
7+
var Promise = require('bluebird');
8+
var pkgConf = require('pkg-conf');
9+
var isCi = require('is-ci');
10+
var hasFlag = require('has-flag');
11+
var Api = require('../api');
12+
var colors = require('./colors');
13+
var verboseReporter = require('./reporters/verbose');
14+
var miniReporter = require('./reporters/mini');
15+
var tapReporter = require('./reporters/tap');
16+
var Logger = require('./logger');
17+
var Watcher = require('./watcher');
18+
var babelConfig = require('./babel-config');
19+
20+
// Bluebird specific
21+
Promise.longStackTraces();
22+
23+
exports.run = function () {
24+
var conf = pkgConf.sync('ava');
25+
var pkgDir = path.dirname(pkgConf.filepath(conf));
26+
27+
var cli = meow([
28+
'Usage',
29+
' ava [<file|directory|glob> ...]',
30+
'',
31+
'Options',
32+
' --init Add AVA to your project',
33+
' --fail-fast Stop after first test failure',
34+
' --serial, -s Run tests serially',
35+
' --tap, -t Generate TAP output',
36+
' --verbose, -v Enable verbose output',
37+
' --no-cache Disable the transpiler cache',
38+
' --no-power-assert Disable Power Assert',
39+
' --match, -m Only run tests with matching title (Can be repeated)',
40+
' --watch, -w Re-run tests when tests and source files change',
41+
' --source, -S Pattern to match source files so tests can be re-run (Can be repeated)',
42+
' --timeout, -T Set global timeout',
43+
' --concurrency, -c Maximum number of test files running at the same time (EXPERIMENTAL)',
44+
'',
45+
'Examples',
46+
' ava',
47+
' ava test.js test2.js',
48+
' ava test-*.js',
49+
' ava test',
50+
' ava --init',
51+
' ava --init foo.js',
52+
'',
53+
'Default patterns when no arguments:',
54+
'test.js test-*.js test/**/*.js **/__tests__/**/*.js **/*.test.js'
55+
], {
56+
string: [
57+
'_',
58+
'timeout',
59+
'source',
60+
'match',
61+
'concurrency'
62+
],
63+
boolean: [
64+
'fail-fast',
65+
'verbose',
66+
'serial',
67+
'tap',
68+
'watch'
69+
],
70+
default: conf,
71+
alias: {
72+
t: 'tap',
73+
v: 'verbose',
74+
s: 'serial',
75+
m: 'match',
76+
w: 'watch',
77+
S: 'source',
78+
T: 'timeout',
79+
c: 'concurrency'
80+
}
81+
});
82+
83+
updateNotifier({pkg: cli.pkg}).notify();
84+
85+
if (cli.flags.init) {
86+
require('ava-init')();
87+
return;
88+
}
89+
90+
if (
91+
((hasFlag('--watch') || hasFlag('-w')) && (hasFlag('--tap') || hasFlag('-t'))) ||
92+
(conf.watch && conf.tap)
93+
) {
94+
throw new Error(colors.error(figures.cross) + ' The TAP reporter is not available when using watch mode.');
95+
}
96+
97+
if (hasFlag('--require') || hasFlag('-r')) {
98+
throw new Error(colors.error(figures.cross) + ' The --require and -r flags are deprecated. Requirements should be configured in package.json - see documentation.');
99+
}
100+
101+
var api = new Api({
102+
failFast: cli.flags.failFast,
103+
serial: cli.flags.serial,
104+
require: arrify(conf.require),
105+
cacheEnabled: cli.flags.cache !== false,
106+
powerAssert: cli.flags.powerAssert !== false,
107+
explicitTitles: cli.flags.watch,
108+
match: arrify(cli.flags.match),
109+
babelConfig: babelConfig.validate(conf.babel),
110+
resolveTestsFrom: cli.input.length === 0 ? pkgDir : process.cwd(),
111+
timeout: cli.flags.timeout,
112+
concurrency: cli.flags.concurrency ? parseInt(cli.flags.concurrency, 10) : 0
113+
});
114+
115+
var reporter;
116+
117+
if (cli.flags.tap && !cli.flags.watch) {
118+
reporter = tapReporter();
119+
} else if (cli.flags.verbose || isCi) {
120+
reporter = verboseReporter();
121+
} else {
122+
reporter = miniReporter({watching: cli.flags.watch});
123+
}
124+
125+
reporter.api = api;
126+
var logger = new Logger(reporter);
127+
128+
logger.start();
129+
130+
api.on('test-run', function (runStatus) {
131+
reporter.api = runStatus;
132+
runStatus.on('test', logger.test);
133+
runStatus.on('error', logger.unhandledError);
134+
135+
runStatus.on('stdout', logger.stdout);
136+
runStatus.on('stderr', logger.stderr);
137+
});
138+
139+
var files = cli.input.length ? cli.input : arrify(conf.files);
140+
141+
if (cli.flags.watch) {
142+
try {
143+
var watcher = new Watcher(logger, api, files, arrify(cli.flags.source));
144+
watcher.observeStdin(process.stdin);
145+
} catch (err) {
146+
if (err.name === 'AvaError') {
147+
// An AvaError may be thrown if chokidar is not installed. Log it nicely.
148+
console.error(' ' + colors.error(figures.cross) + ' ' + err.message);
149+
logger.exit(1);
150+
} else {
151+
// Rethrow so it becomes an uncaught exception.
152+
throw err;
153+
}
154+
}
155+
} else {
156+
api.run(files)
157+
.then(function (runStatus) {
158+
logger.finish(runStatus);
159+
logger.exit(runStatus.failCount > 0 || runStatus.rejectionCount > 0 || runStatus.exceptionCount > 0 ? 1 : 0);
160+
})
161+
.catch(function (err) {
162+
// Don't swallow exceptions. Note that any expected error should already
163+
// have been logged.
164+
setImmediate(function () {
165+
throw err;
166+
});
167+
});
168+
}
169+
};

test/cli.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,15 @@ function execCli(args, opts, cb) {
6868
}
6969

7070
test('disallow invalid babel config shortcuts', function (t) {
71-
execCli('es2015.js', {dirname: 'fixture/invalid-babel-config'}, function (err, stdout) {
71+
execCli('es2015.js', {dirname: 'fixture/invalid-babel-config'}, function (err, stdout, stderr) {
7272
t.ok(err);
7373

7474
var expectedOutput = '\n ';
7575
expectedOutput += colors.error(figures.cross) + ' Unexpected Babel configuration for AVA.';
7676
expectedOutput += ' See ' + chalk.underline('https://github.com/avajs/ava#es2015-support') + ' for allowed values.';
7777
expectedOutput += '\n';
7878

79-
t.is(stdout, expectedOutput);
79+
t.is(stderr, expectedOutput);
8080
t.end();
8181
});
8282
});

0 commit comments

Comments
 (0)