Browse Source

Upgrade XO (#1080)

* Avoid running XO on older Node.js versions

* xo@^0.17.0

* 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.

* Actually register loud-rejection
replace-snapshot
Mark Wubben 8 years ago
committed by Sindre Sorhus
parent
commit
257b34d27b
  1. 185
      cli.js
  2. 170
      lib/cli.js
  3. 2
      lib/logger.js
  4. 2
      lib/process-adapter.js
  5. 8
      lib/reporters/mini.js
  6. 4
      package.json
  7. 4
      profile.js
  8. 10
      scripts/xo.js
  9. 6
      test/cli.js
  10. 2
      test/visual/lorem-ipsum.js
  11. 2
      test/visual/run-visual-tests.js
  12. 4
      test/watcher.js

185
cli.js

@ -13,187 +13,16 @@ var localCLI = resolveCwd('ava/cli');
// see https://github.com/nodejs/node/issues/6624
if (localCLI && path.relative(localCLI, __filename) !== '') {
debug('Using local install of AVA');
require(localCLI);
return;
}
if (debug.enabled) {
require('time-require');
}
var updateNotifier = require('update-notifier');
var figures = require('figures');
var arrify = require('arrify');
var meow = require('meow');
var Promise = require('bluebird');
var pkgConf = require('pkg-conf');
var isCi = require('is-ci');
var hasFlag = require('has-flag');
var colors = require('./lib/colors');
var verboseReporter = require('./lib/reporters/verbose');
var miniReporter = require('./lib/reporters/mini');
var tapReporter = require('./lib/reporters/tap');
var Logger = require('./lib/logger');
var Watcher = require('./lib/watcher');
var babelConfig = require('./lib/babel-config');
var Api = require('./api');
// Bluebird specific
Promise.longStackTraces();
var conf = pkgConf.sync('ava');
var pkgDir = path.dirname(pkgConf.filepath(conf));
try {
conf.babel = babelConfig.validate(conf.babel);
} catch (err) {
console.log('\n ' + err.message);
process.exit(1);
}
var cli = meow([
'Usage',
' ava [<file|directory|glob> ...]',
'',
'Options',
' --init Add AVA to your project',
' --fail-fast Stop after first test failure',
' --serial, -s Run tests serially',
' --tap, -t Generate TAP output',
' --verbose, -v Enable verbose output',
' --no-cache Disable the transpiler cache',
' --no-power-assert Disable Power Assert',
' --match, -m Only run tests with matching title (Can be repeated)',
' --watch, -w Re-run tests when tests and source files change',
' --source, -S Pattern to match source files so tests can be re-run (Can be repeated)',
' --timeout, -T Set global timeout',
' --concurrency, -c Maximum number of test files running at the same time (EXPERIMENTAL)',
'',
'Examples',
' ava',
' ava test.js test2.js',
' ava test-*.js',
' ava test',
' ava --init',
' ava --init foo.js',
'',
'Default patterns when no arguments:',
'test.js test-*.js test/**/*.js **/__tests__/**/*.js **/*.test.js'
], {
string: [
'_',
'timeout',
'source',
'match',
'concurrency'
],
boolean: [
'fail-fast',
'verbose',
'serial',
'tap',
'watch'
],
default: conf,
alias: {
t: 'tap',
v: 'verbose',
s: 'serial',
m: 'match',
w: 'watch',
S: 'source',
T: 'timeout',
c: 'concurrency'
}
});
updateNotifier({pkg: cli.pkg}).notify();
if (cli.flags.init) {
require('ava-init')();
return;
}
if (
((hasFlag('--watch') || hasFlag('-w')) && (hasFlag('--tap') || hasFlag('-t'))) ||
(conf.watch && conf.tap)
) {
console.error(' ' + colors.error(figures.cross) + ' The TAP reporter is not available when using watch mode.');
process.exit(1);
}
if (hasFlag('--require') || hasFlag('-r')) {
console.error(' ' + colors.error(figures.cross) + ' The --require and -r flags are deprecated. Requirements should be configured in package.json - see documentation.');
process.exit(1);
}
var api = new Api({
failFast: cli.flags.failFast,
serial: cli.flags.serial,
require: arrify(conf.require),
cacheEnabled: cli.flags.cache !== false,
powerAssert: cli.flags.powerAssert !== false,
explicitTitles: cli.flags.watch,
match: arrify(cli.flags.match),
babelConfig: conf.babel,
resolveTestsFrom: cli.input.length === 0 ? pkgDir : process.cwd(),
pkgDir: pkgDir,
timeout: cli.flags.timeout,
concurrency: cli.flags.concurrency ? parseInt(cli.flags.concurrency, 10) : 0
});
var reporter;
if (cli.flags.tap && !cli.flags.watch) {
reporter = tapReporter();
} else if (cli.flags.verbose || isCi) {
reporter = verboseReporter();
require(localCLI); // eslint-disable-line import/no-dynamic-require
} else {
reporter = miniReporter({watching: cli.flags.watch});
}
reporter.api = api;
var logger = new Logger(reporter);
logger.start();
api.on('test-run', function (runStatus) {
reporter.api = runStatus;
runStatus.on('test', logger.test);
runStatus.on('error', logger.unhandledError);
runStatus.on('stdout', logger.stdout);
runStatus.on('stderr', logger.stderr);
});
var files = cli.input.length ? cli.input : arrify(conf.files);
if (debug.enabled) {
require('time-require'); // eslint-disable-line import/no-unassigned-import
}
if (cli.flags.watch) {
try {
var watcher = new Watcher(logger, api, files, arrify(cli.flags.source));
watcher.observeStdin(process.stdin);
require('./lib/cli').run();
} catch (err) {
if (err.name === 'AvaError') {
// An AvaError may be thrown if chokidar is not installed. Log it nicely.
console.error(' ' + colors.error(figures.cross) + ' ' + err.message);
logger.exit(1);
} else {
// Rethrow so it becomes an uncaught exception.
throw err;
}
console.error('\n ' + err.message);
process.exit(1);
}
} else {
api.run(files)
.then(function (runStatus) {
logger.finish(runStatus);
logger.exit(runStatus.failCount > 0 || runStatus.rejectionCount > 0 || runStatus.exceptionCount > 0 ? 1 : 0);
})
.catch(function (err) {
// Don't swallow exceptions. Note that any expected error should already
// have been logged.
setImmediate(function () {
throw err;
});
});
}

170
lib/cli.js

@ -0,0 +1,170 @@
'use strict';
var path = require('path');
var updateNotifier = require('update-notifier');
var figures = require('figures');
var arrify = require('arrify');
var meow = require('meow');
var Promise = require('bluebird');
var pkgConf = require('pkg-conf');
var isCi = require('is-ci');
var hasFlag = require('has-flag');
var Api = require('../api');
var colors = require('./colors');
var verboseReporter = require('./reporters/verbose');
var miniReporter = require('./reporters/mini');
var tapReporter = require('./reporters/tap');
var Logger = require('./logger');
var Watcher = require('./watcher');
var babelConfig = require('./babel-config');
// Bluebird specific
Promise.longStackTraces();
exports.run = function () {
var conf = pkgConf.sync('ava');
var pkgDir = path.dirname(pkgConf.filepath(conf));
var cli = meow([
'Usage',
' ava [<file|directory|glob> ...]',
'',
'Options',
' --init Add AVA to your project',
' --fail-fast Stop after first test failure',
' --serial, -s Run tests serially',
' --tap, -t Generate TAP output',
' --verbose, -v Enable verbose output',
' --no-cache Disable the transpiler cache',
' --no-power-assert Disable Power Assert',
' --match, -m Only run tests with matching title (Can be repeated)',
' --watch, -w Re-run tests when tests and source files change',
' --source, -S Pattern to match source files so tests can be re-run (Can be repeated)',
' --timeout, -T Set global timeout',
' --concurrency, -c Maximum number of test files running at the same time (EXPERIMENTAL)',
'',
'Examples',
' ava',
' ava test.js test2.js',
' ava test-*.js',
' ava test',
' ava --init',
' ava --init foo.js',
'',
'Default patterns when no arguments:',
'test.js test-*.js test/**/*.js **/__tests__/**/*.js **/*.test.js'
], {
string: [
'_',
'timeout',
'source',
'match',
'concurrency'
],
boolean: [
'fail-fast',
'verbose',
'serial',
'tap',
'watch'
],
default: conf,
alias: {
t: 'tap',
v: 'verbose',
s: 'serial',
m: 'match',
w: 'watch',
S: 'source',
T: 'timeout',
c: 'concurrency'
}
});
updateNotifier({pkg: cli.pkg}).notify();
if (cli.flags.init) {
require('ava-init')();
return;
}
if (
((hasFlag('--watch') || hasFlag('-w')) && (hasFlag('--tap') || hasFlag('-t'))) ||
(conf.watch && conf.tap)
) {
throw new Error(colors.error(figures.cross) + ' The TAP reporter is not available when using watch mode.');
}
if (hasFlag('--require') || hasFlag('-r')) {
throw new Error(colors.error(figures.cross) + ' The --require and -r flags are deprecated. Requirements should be configured in package.json - see documentation.');
}
var api = new Api({
failFast: cli.flags.failFast,
serial: cli.flags.serial,
require: arrify(conf.require),
cacheEnabled: cli.flags.cache !== false,
powerAssert: cli.flags.powerAssert !== false,
explicitTitles: cli.flags.watch,
match: arrify(cli.flags.match),
babelConfig: babelConfig.validate(conf.babel),
resolveTestsFrom: cli.input.length === 0 ? pkgDir : process.cwd(),
pkgDir: pkgDir,
timeout: cli.flags.timeout,
concurrency: cli.flags.concurrency ? parseInt(cli.flags.concurrency, 10) : 0
});
var reporter;
if (cli.flags.tap && !cli.flags.watch) {
reporter = tapReporter();
} else if (cli.flags.verbose || isCi) {
reporter = verboseReporter();
} else {
reporter = miniReporter({watching: cli.flags.watch});
}
reporter.api = api;
var logger = new Logger(reporter);
logger.start();
api.on('test-run', function (runStatus) {
reporter.api = runStatus;
runStatus.on('test', logger.test);
runStatus.on('error', logger.unhandledError);
runStatus.on('stdout', logger.stdout);
runStatus.on('stderr', logger.stderr);
});
var files = cli.input.length ? cli.input : arrify(conf.files);
if (cli.flags.watch) {
try {
var watcher = new Watcher(logger, api, files, arrify(cli.flags.source));
watcher.observeStdin(process.stdin);
} catch (err) {
if (err.name === 'AvaError') {
// An AvaError may be thrown if chokidar is not installed. Log it nicely.
console.error(' ' + colors.error(figures.cross) + ' ' + err.message);
logger.exit(1);
} else {
// Rethrow so it becomes an uncaught exception.
throw err;
}
}
} else {
api.run(files)
.then(function (runStatus) {
logger.finish(runStatus);
logger.exit(runStatus.failCount > 0 || runStatus.rejectionCount > 0 || runStatus.exceptionCount > 0 ? 1 : 0);
})
.catch(function (err) {
// Don't swallow exceptions. Note that any expected error should already
// have been logged.
setImmediate(function () {
throw err;
});
});
}
};

2
lib/logger.js

@ -98,6 +98,6 @@ Logger.prototype.exit = function (code) {
// timeout required to correctly flush IO on Node.js 0.10 on Windows
setTimeout(function () {
process.exit(code); // eslint-disable-line xo/no-process-exit
process.exit(code); // eslint-disable-line unicorn/no-process-exit
}, process.env.AVA_APPVEYOR ? 500 : 0);
};

2
lib/process-adapter.js

@ -16,7 +16,7 @@ if (!isForked) {
console.log();
console.error('Test files must be run with the AVA CLI:\n\n ' + chalk.grey.dim('$') + ' ' + chalk.cyan('ava ' + fp) + '\n');
process.exit(1); // eslint-disable-line xo/no-process-exit
process.exit(1); // eslint-disable-line unicorn/no-process-exit
}
exports.send = function (name, data) {

8
lib/reporters/mini.js

@ -159,12 +159,8 @@ MiniReporter.prototype.finish = function (runStatus) {
status += '\n ' + colors.error(runStatus.previousFailCount, 'previous', plur('failure', runStatus.previousFailCount), 'in test files that were not rerun');
}
var i = 0;
if (this.knownFailureCount > 0) {
runStatus.knownFailures.forEach(function (test) {
i++;
var title = test.title;
status += '\n\n ' + colors.title(title);
@ -179,8 +175,6 @@ MiniReporter.prototype.finish = function (runStatus) {
return;
}
i++;
var title = test.error ? test.title : 'Unhandled Error';
var description;
var errorTitle = ' ' + test.error.message + '\n';
@ -210,8 +204,6 @@ MiniReporter.prototype.finish = function (runStatus) {
return;
}
i++;
if (err.type === 'exception' && err.name === 'AvaError') {
status += '\n\n ' + colors.error(cross + ' ' + err.message);
} else {

4
package.json

@ -43,7 +43,7 @@
"node": ">=0.10.0"
},
"scripts": {
"test": "xo && nyc --cache --reporter=lcov --reporter=text tap --no-cov --timeout=150 test/*.js test/reporters/*.js",
"test": "scripts/xo.js && nyc --cache --reporter=lcov --reporter=text tap --no-cov --timeout=150 test/*.js test/reporters/*.js",
"test-win": "tap --no-cov --reporter=classic --timeout=150 test/*.js test/reporters/*.js",
"visual": "node test/visual/run-visual-tests.js",
"prepublish": "npm run make-ts",
@ -184,7 +184,7 @@
"source-map-fixtures": "^2.1.0",
"tap": "^7.1.2",
"touch": "^1.0.0",
"xo": "^0.16.0",
"xo": "^0.17.0",
"zen-observable": "^0.3.0"
},
"xo": {

4
profile.js

@ -112,7 +112,7 @@ events.on('results', function (data) {
if (process.exit) {
// Delay is For Node 0.10 which emits uncaughtExceptions async.
setTimeout(function () {
process.exit(data.stats.failCount + uncaughtExceptionCount); // eslint-disable-line xo/no-process-exit
process.exit(data.stats.failCount + uncaughtExceptionCount); // eslint-disable-line unicorn/no-process-exit
}, 20);
}
});
@ -139,5 +139,5 @@ if (console.profile) {
}
setImmediate(function () {
require('./lib/test-worker');
require('./lib/test-worker'); // eslint-disable-line import/no-unassigned-import
});

10
scripts/xo.js

@ -0,0 +1,10 @@
#!/usr/bin/env node
/* eslint-disable import/no-unassigned-import */
'use strict';
var major = Number(process.version.match(/^v(\d+)/)[1]);
if (major >= 4) {
require('xo/cli');
} else {
console.warn('Linting requires Node.js >=4');
}

6
test/cli.js

@ -70,7 +70,7 @@ function execCli(args, opts, cb) {
}
test('disallow invalid babel config shortcuts', function (t) {
execCli('es2015.js', {dirname: 'fixture/invalid-babel-config'}, function (err, stdout) {
execCli('es2015.js', {dirname: 'fixture/invalid-babel-config'}, function (err, stdout, stderr) {
t.ok(err);
var expectedOutput = '\n ';
@ -78,7 +78,7 @@ test('disallow invalid babel config shortcuts', function (t) {
expectedOutput += ' See ' + chalk.underline('https://github.com/avajs/ava#es2015-support') + ' for allowed values.';
expectedOutput += '\n';
t.is(stdout, expectedOutput);
t.is(stderr, expectedOutput);
t.end();
});
});
@ -363,7 +363,7 @@ test('prefers local version of ava', function (t) {
}
proxyquire('../cli', {
'debug': debugStub,
debug: debugStub,
'resolve-cwd': resolveCwdStub
});

2
test/visual/lorem-ipsum.js

@ -1,7 +1,7 @@
'use strict';
var delay = require('delay');
var test = require('../../');
require('./print-lorem-ipsum');
require('./print-lorem-ipsum'); // eslint-disable-line import/no-unassigned-import
async function testFn(t) {
await delay(40);

2
test/visual/run-visual-tests.js

@ -1,5 +1,5 @@
'use strict';
require('loud-rejection');
require('loud-rejection/register'); // eslint-disable-line import/no-unassigned-import
var path = require('path');
var childProcess = require('child_process');
var chalk = require('chalk');

4
test/watcher.js

@ -53,8 +53,8 @@ group('chokidar', function (beforeEach, test, group) {
function proxyWatcher(opts) {
return proxyquire.noCallThru().load('../lib/watcher', opts ||
{
'chokidar': chokidar,
'debug': function (name) {
chokidar: chokidar,
debug: function (name) {
return function () {
var args = [name];
args.push.apply(args, arguments);

Loading…
Cancel
Save