diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..864e43e --- /dev/null +++ b/.eslintrc @@ -0,0 +1,32 @@ +{ + "root": true, + + "extends": "@ljharb", + + "rules": { + "func-style": ["error", "declaration"], + "max-lines-per-function": "off", + "max-statements-per-line": ["error", { "max": 2 }], + "no-plusplus": "warn", + "sort-keys": "off", + }, + + "overrides": [ + { + "files": "bin/**", + "env": { + "node": true, + }, + "rules": { + "no-process-exit": "off", + } + }, + { + "files": "example/**", + "rules": { + "no-magic-numbers": "off", + "no-plusplus": "off", + }, + }, + ], +} diff --git a/bin/cmd.js b/bin/cmd.js index 489a7c2..2c12800 100755 --- a/bin/cmd.js +++ b/bin/cmd.js @@ -1,4 +1,7 @@ #!/usr/bin/env node + +'use strict'; + var faucet = require('../'); var minimist = require('minimist'); var defined = require('defined'); @@ -9,52 +12,50 @@ var spawn = require('child_process').spawn; var fs = require('fs'); var path = require('path'); +function jsFile(x) { return (/\.js$/i).test(x); } + var argv = minimist(process.argv.slice(2)); var tap = faucet({ - width: defined(argv.w, argv.width, process.stdout.isTTY - ? process.stdout.columns - 5 - : 0 - ) + width: defined(argv.w, argv.width, process.stdout.isTTY + ? process.stdout.columns - 5 + : 0) }); process.on('exit', function (code) { - if (code === 0 && tap.exitCode !== 0) { - process.exit(tap.exitCode); - } + if (code === 0 && tap.exitCode !== 0) { + process.exit(tap.exitCode); + } }); process.stdout.on('error', function () {}); if (!process.stdin.isTTY || argv._[0] === '-') { - process.stdin.pipe(tap).pipe(process.stdout); - return; + process.stdin.pipe(tap).pipe(process.stdout); + return; } var files = argv._.reduce(function (acc, file) { - if (fs.statSync(file).isDirectory()) { - return acc.concat(fs.readdirSync(file).map(function (x) { - return path.join(file, x); - }).filter(jsFile)); - } - else return acc.concat(file); + if (fs.statSync(file).isDirectory()) { + return acc.concat(fs.readdirSync(file).map(function (x) { + return path.join(file, x); + }).filter(jsFile)); + } + return acc.concat(file); }, []); if (files.length === 0 && fs.existsSync('test')) { - files.push.apply(files, fs.readdirSync('test').map(function (x) { - return path.join('test', x); - }).filter(jsFile)); + files.push.apply(files, fs.readdirSync('test').map(function (x) { + return path.join('test', x); + }).filter(jsFile)); } if (files.length === 0 && fs.existsSync('tests')) { - files.push.apply(files, fs.readdirSync('tests').map(function (x) { - return path.join('tests', x); - }).filter(jsFile)); + files.push.apply(files, fs.readdirSync('tests').map(function (x) { + return path.join('tests', x); + }).filter(jsFile)); } if (files.length === 0) { - console.error('usage: `faucet [FILES]` or `| faucet`\n'); - console.error( - 'No test files or stdin provided and no files in test/ or tests/' - + ' directories found.' - ); - return process.exit(1); + console.error('usage: `faucet [FILES]` or `| faucet`\n'); + console.error('No test files or stdin provided and no files in test/ or tests/ directories found.'); + process.exit(1); } var tape = spawn(tapeCmd, files); @@ -62,12 +63,10 @@ tape.stderr.pipe(process.stderr); tape.stdout.pipe(tap).pipe(process.stdout); var tapeCode; -tape.on('exit', function (code) { tapeCode = code }); +tape.on('exit', function (code) { tapeCode = code; }); process.on('exit', function (code) { - if (code === 0 && tapeCode !== 0) { - console.error('# non-zero exit from the `tape` command'); - process.exit(tapeCode); - } + if (code === 0 && tapeCode !== 0) { + console.error('# non-zero exit from the `tape` command'); + process.exit(tapeCode); + } }); - -function jsFile (x) { return /\.js$/i.test(x) } diff --git a/example/tape.js b/example/tape.js index 5ab9c11..4df3914 100644 --- a/example/tape.js +++ b/example/tape.js @@ -1,13 +1,17 @@ +'use strict'; + var test = require('tape'); test('beep boop', function (t) { - t.plan(2); - - t.equal(1 + 1, 2); - setTimeout(function () { - t.deepEqual( - 'ABC'.toLowerCase().split(''), - ['a','b','c'] - ); - }); + t.plan(2); + + t.equal(1 + 1, 2); + setTimeout(function () { + t.deepEqual( + 'ABC'.toLowerCase().split(''), + [ + 'a', 'b', 'c' + ] + ); + }); }); diff --git a/example/test.js b/example/test.js index 947a4cf..6cf55f9 100644 --- a/example/test.js +++ b/example/test.js @@ -1,27 +1,33 @@ +'use strict'; + var test = require('tape'); -function getMessage () { - var msgs = [ 'yes', 'equals', 'matches', 'yep', 'pretty much', 'woo' ]; - return msgs[Math.floor(Math.random() * msgs.length)]; + +function getMessage() { + var msgs = [ + 'yes', 'equals', 'matches', 'yep', 'pretty much', 'woo' + ]; + return msgs[Math.floor(Math.random() * msgs.length)]; } test('beep affirmative', function (t) { - t.plan(24); - var i = 0, n = 0; - var iv = setInterval(function () { - t.equal(i++, n++, getMessage()); - if (i === 24) clearInterval(iv); - }, 50); + t.plan(24); + var i = 0, + n = 0; + var iv = setInterval(function () { + t.equal(i++, n++, getMessage()); + if (i === 24) { clearInterval(iv); } + }, 50); }); test('boop exterminate', function (t) { - t.plan(20); - var i = 0, n = 0; - var iv = setInterval(function () { - if ((i + 2) % 8 === 0) { - t.equal(i, n + 6, getMessage()) - } - else t.equal(i, n, getMessage()); - i++; n++; - if (i === 20) clearInterval(iv); - }, 100); + t.plan(20); + var i = 0, + n = 0; + var iv = setInterval(function () { + if ((i + 2) % 8 === 0) { + t.equal(i, n + 6, getMessage()); + } else { t.equal(i, n, getMessage()); } + i++; n++; + if (i === 20) { clearInterval(iv); } + }, 100); }); diff --git a/index.js b/index.js index 3871866..3d82113 100644 --- a/index.js +++ b/index.js @@ -1,123 +1,115 @@ +'use strict'; + var through2 = require('through2'); var duplexer = require('duplexer'); var parser = require('tap-parser'); var sprintf = require('sprintf-js').sprintf; module.exports = function (opts) { - if (!opts) opts = {}; - var tap = parser(); - var out = through2(); - var test, lastAssert; - - tap.on('comment', function (comment) { - if (comment === 'fail 0') return; // a mocha thing - - if (test && test.ok && test.assertions.length === 0 - && /^(tests|pass)\s+\d+$/.test(test.name)) { - out.push('\r' + trim(test.name)); - } - else if (test && test.ok) { - var s = updateName(test.offset + 1, '✓ ' + test.name, 32); - out.push('\r' + s); - } - - test = { - name: comment, - assertions: [], - offset: 0, - ok: true - }; - out.push('\r' + trim('# ' + comment) + '\x1b[K\n'); - }); - - tap.on('assert', function (res) { - var ok = res.ok ? 'ok' : 'not ok'; - var c = res.ok ? 32 : 31; - if (!test) { - // mocha produces TAP results this way, whatever - var s = trim(res.name.trim()); - out.push(sprintf( - '\x1b[1m\x1b[' + c + 'm%s\x1b[0m\n', - trim((res.ok ? '✓' : '⨯') + ' ' + s) - )); - return; - } - - var fmt = '\r %s \x1b[1m\x1b[' + c + 'm%d\x1b[0m %s\x1b[K'; - var str = sprintf(fmt, ok, res.number, res.name); - - if (!res.ok) { - var y = (++ test.offset) + 1; - str += '\n'; - if (test.ok) { - str += updateName(y, '⨯ ' + test.name, 31) - } - test.ok = false; - } - out.push(str); - test.assertions.push(res); - }); - - tap.on('extra', function (extra) { - if (!test || test.assertions.length === 0) return; - var last = test.assertions[test.assertions.length-1]; - if (!last.ok) { - out.push(extra.split('\n').map(function (line) { - return ' ' + line; - }).join('\n') + '\n'); - } - }); - - tap.on('results', function (res) { - if (test && /^fail\s+\d+$/.test(test.name)) { - out.push(updateName(test.offset + 1, '⨯ ' + test.name, 31)); - } - else if (test && test.ok) { - out.push(updateName(test.offset + 1, '✓ ' + test.name, 32)); - } - - res.errors.forEach(function (err, ix) { - out.push(sprintf( - 'not ok \x1b[1m\x1b[31m%d\x1b[0m %s\n', - ix + 1 + res.asserts.length, err.message - )); - }); - - if (!res.ok && !/^fail\s+\d+$/.test(test && test.name)) { - out.push(sprintf( - '\r\x1b[1m\x1b[31m⨯ fail %s\x1b[0m\x1b[K\n', - (res.errors.length + res.fail.length) || '' - )); - } - - out.push(null); - - dup.emit('results', res); - if (!res.ok) dup.emit('fail'); - dup.exitCode = res.ok ? 0 : 1; - }); - - var dup = duplexer(tap, out); - return dup; - - function showTest (test) { - out.push('\r'); - } - - function trim (s) { - if (opts.width && s.length > opts.width - 2) { - s = s.slice(0, opts.width - 5) + '...'; - } - return s; - } - - function updateName (y, str, c) { - return '\x1b[' + y + 'A' - + '\x1b[1G' - + '\x1b[1m\x1b[' + c + 'm' - + trim(str) - + '\x1b[0m' - + '\x1b[' + y + 'B\x1b[1G' - ; - } + var tap = parser(); + var out = through2(); + + function trim(s) { + if (opts && opts.width && s.length > opts.width - 2) { + return s.slice(0, opts.width - 5) + '...'; + } + return s; + } + + function updateName(y, str, c) { + return '\x1b[' + y + 'A\x1b[1G\x1b[1m\x1b[' + c + 'm' + trim(str) + '\x1b[0m\x1b[' + y + 'B\x1b[1G'; + } + + var test; + + tap.on('comment', function (comment) { + if (comment === 'fail 0') { return; } // a mocha thing + + if (test && test.ok && test.assertions.length === 0 + && (/^(tests|pass)\s+\d+$/).test(test.name)) { + out.push('\r' + trim(test.name)); + } else if (test && test.ok) { + var s = updateName(test.offset + 1, '✓ ' + test.name, 32); + out.push('\r' + s); + } + + test = { + name: comment, + assertions: [], + offset: 0, + ok: true + }; + out.push('\r' + trim('# ' + comment) + '\x1b[K\n'); + }); + + tap.on('assert', function (res) { + var ok = res.ok ? 'ok' : 'not ok'; + var c = res.ok ? 32 : 31; + if (!test) { + // mocha produces TAP results this way, whatever + var s = trim(res.name.trim()); + out.push(sprintf( + '\x1b[1m\x1b[' + c + 'm%s\x1b[0m\n', + trim((res.ok ? '✓' : '⨯') + ' ' + s) + )); + return; + } + + var fmt = '\r %s \x1b[1m\x1b[' + c + 'm%d\x1b[0m %s\x1b[K'; + var str = sprintf(fmt, ok, res.number, res.name); + + if (!res.ok) { + var y = ++test.offset + 1; + str += '\n'; + if (test.ok) { + str += updateName(y, '⨯ ' + test.name, 31); + } + test.ok = false; + } + out.push(str); + test.assertions.push(res); + }); + + tap.on('extra', function (extra) { + if (!test || test.assertions.length === 0) { return; } + var last = test.assertions[test.assertions.length - 1]; + if (!last.ok) { + out.push(extra.split('\n').map(function (line) { + return ' ' + line; + }).join('\n') + '\n'); + } + }); + + var dup = duplexer(tap, out); + + tap.on('results', function (res) { + if (test && (/^fail\s+\d+$/).test(test.name)) { + out.push(updateName(test.offset + 1, '⨯ ' + test.name, 31)); + } else if (test && test.ok) { + out.push(updateName(test.offset + 1, '✓ ' + test.name, 32)); + } + + res.errors.forEach(function (err, ix) { + out.push(sprintf( + 'not ok \x1b[1m\x1b[31m%d\x1b[0m %s\n', + ix + 1 + res.asserts.length, + err.message + )); + }); + + if (!res.ok && !(/^fail\s+\d+$/).test(test && test.name)) { + out.push(sprintf( + '\r\x1b[1m\x1b[31m⨯ fail %s\x1b[0m\x1b[K\n', + (res.errors.length + res.fail.length) || '' + )); + } + + out.push(null); + + dup.emit('results', res); + if (!res.ok) { dup.emit('fail'); } + dup.exitCode = res.ok ? 0 : 1; + }); + + return dup; }; diff --git a/package.json b/package.json index ec4d6a6..276309e 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,10 @@ "bin": { "faucet": "bin/cmd.js" }, + "scripts": { + "lint": "eslint --ext=js,mjs .", + "pretest": "npm run lint" + }, "dependencies": { "defined": "^0.0.0", "duplexer": "^0.1.2", @@ -34,5 +38,9 @@ "email": "mail@substack.net", "url": "http://substack.net" }, - "license": "MIT" + "license": "MIT", + "devDependencies": { + "@ljharb/eslint-config": "^21.0.0", + "eslint": "=8.8.0" + } }