Skip to content

Instantly share code, notes, and snippets.

@MattMcFarland
Created May 26, 2016 15:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MattMcFarland/e34c357cbf8c21d147b5d08768bf4efa to your computer and use it in GitHub Desktop.
Save MattMcFarland/e34c357cbf8c21d147b5d08768bf4efa to your computer and use it in GitHub Desktop.
node js watch, run mocha on __tests__, run eslint on files, syntax checking
import sane from 'sane';
import { resolve as resolvePath } from 'path';
import { spawn, fork } from 'child_process';
import { existsSync as fileExists} from 'fs';
import {
red, green, yellow, blue,
magenta, cyan, white, gray
} from 'chalk';
process.env.PATH += ':./node_modules/.bin';
const cmd = resolvePath(__dirname);
const srcDir = cmd;
function exec(command, options) {
return new Promise(function (resolve, reject) {
var child = spawn(command, options, {
cmd,
env: process.env,
stdio: 'inherit'
});
// Checking steps
child.on('exit', function (code) {
if (code === 0) {
resolve(true);
} else {
reject(new Error('Error code: ' + code));
}
});
});
}
var watcher = sane(cmd, { glob: ['src/**/*.*'] })
.on('ready', startWatch)
.on('add', changeFile)
.on('delete', deleteFile)
.on('change', changeFile);
process.on('exit', function () {
watcher.close();
});
process.on('SIGINT', function () {
console.log(' ', CLEARLINE + yellow(invert('Watch Ended')));
watcher.close();
});
process.on('uncaughtException', (err) => {
console.log(' ', CLEARLINE + red(invert(`Caught exception: ${err}`)));
watcher.close();
process.exit(1);
});
function startWatch() {
process.stdout.write(
green(invert('Watching...')
+ '\n')
);
}
var isChecking;
var needsCheck;
var toCheck = {};
var timeout;
var resetServer = false;
var shouldCopy = false;
var haltOperation = false;
var shouldUpdateSchema = false;
function changeFile(filepath, root, stat) {
if (!stat.isDirectory()) {
toCheck[filepath] = true;
debouncedCheck();
}
}
// logging
function logTask(str) {
console.log('\n', yellow('=========='), str, '\n');
}
function logError(msg) {
haltOperation = true;
console.log(yellow('\n An exception has been caught!\n'));
console.log(red('\t', msg));
}
function logWaiting() {
console.log('\n', green(invert('Waiting...')));
}
function deleteFile(filepath) {
delete toCheck[filepath];
debouncedCheck();
}
function debouncedCheck() {
haltOperation = false;
needsCheck = true;
clearTimeout(timeout);
timeout = setTimeout(guardedCheck, 250);
}
function guardedCheck() {
if (isChecking || !needsCheck) {
return;
}
isChecking = true;
var filepaths = Object.keys(toCheck);
toCheck = {};
needsCheck = false;
checkFiles(filepaths).then(() => {
isChecking = false;
process.nextTick(guardedCheck);
});
}
function checkFiles(filepaths) {
console.log('\u001b[2J');
return parseFiles(filepaths)
.then(() => runTests(filepaths))
.then(testSuccess => lintFiles(filepaths)
.then(lintSuccess =>
testSuccess && lintSuccess)).catch(err => logError(err))
.then(() => logWaiting())
.catch(err => logError(err));
}
function parseFiles(filepaths) {
if (haltOperation) {
filepaths = []
}
logTask('Checking Syntax');
return Promise.all(filepaths.map(filepath => {
if (isJS(filepath) && !isTest(filepath)) {
return exec('babel', [
'--optional', 'runtime',
'--out-file', '/dev/null',
srcPath(filepath)
]);
}
}));
}
function runTests(filepaths) {
if (haltOperation) {
filepaths = [];
}
logTask('Running Tests');
return exec('mocha', [
'--require', 'scripts/mocha-bootload'
].concat(
allTests(filepaths) ?
filepaths.map(srcPath) :
[
'src/**/__tests__/**/*.js'
]
)).catch(() => logError('Unit tests failed'));
}
function lintFiles(filepaths) {
if (haltOperation) {
filepaths = [];
}
logTask('Linting Code');
return filepaths.reduce((prev, filepath) => prev.then(prevSuccess => {
process.stdout.write(' ' + filepath + ' ...');
return exec('eslint', [
srcPath(filepath)])
.catch(() => logError('Linting Code failed'))
.then(success => {
console.log(CLEARLINE + ' ' + (success ? CHECK : X) + ' ' + filepath);
return prevSuccess && success;
});
}), Promise.resolve(true));
}
// Filepath
function srcPath(filepath) {
if(~filepath.indexOf('__tests__')){
return resolvePath(srcDir, filepath);
}
return getTestFilePath(filepath);
}
// Predicates
function isJS(filepath) {
return filepath.indexOf('.js') === filepath.length - 3;
}
function allTests(filepaths) {
return filepaths.length > 0 && filepaths.every(isTest);
}
function getTestFilePath(filepath) {
var parts = filepath.split('/');
return resolvePath(
srcDir,
parts.slice(0, parts.length - 1).join('/'),
'__tests__',
parts[parts.length - 1].replace('.', '.test.'));
}
function isTest(filepath) {
return isJS(filepath) && ( ~filepath.indexOf('__tests__/') || fileExists(getTestFilePath(filepath)));
}
var CLEARSCREEN = '\u001b[2J';
var CLEARLINE = '\r\x1B[K';
var CHECK = green('\u2713');
var X = red('\u2718');
function invert(str) {
return `\u001b[7m ${str} \u001b[27m`;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment