UNPKG

affiance

Version:

A configurable and extendable Git hook manager for node projects

112 lines (100 loc) 3.64 kB
'use strict'; // TODO: write tests for this hook const HookMessage = require('../Message'); const PreCommitBase = require('./Base'); const utils = require('../../utils'); /** * @class StylusLint * @extends PreCommitBase * @classdesc Run stylint on changed files */ module.exports = class StylusLint extends PreCommitBase { /** * Run `stylint` against files that apply. * Uses `#spawnPromise` directly because stylint can only * run against single files or specific directories. * * @returns {Promise} * @resolves {HookMessage[]} An array of hook messages produced by the hook * @rejects {Error} An Error thrown or emitted while running the hook */ run() { return new Promise((resolve, reject) => { let applicableFiles = this.applicableFiles(); if (!applicableFiles.length) { return resolve('pass'); } let flags = this.flags(); let command = this.command(); let commandPromises = applicableFiles.map((currentFile) => { let commandArgs = flags.concat(currentFile); return this.spawnPromise(command, commandArgs); }); // Once all commands have finished, parse output and append results // This is done once all commands are finished so that the output order is consistent. Promise.all(commandPromises).then((commandResults) => { let outputMessages = []; let reachedError = false; commandResults.forEach((result) => { // If we've already had an error, don't try to run again if (reachedError) { return; } // The process can't be started. if (result.error) { reachedError = true; let resultErrorOutput = result.stderr ? result.stderr : (result.error.message || result.error.toString()); return resolve(['fail', command + ' encountered an error\n' + resultErrorOutput]); } outputMessages = outputMessages.concat(this.parseStylintResult(result)); }); resolve(outputMessages); }, reject); }); } /** * Parse a stylint result to generate an array of * HookMessage objects. the reporter we use produces a json object like: * { * severity: 'error'|'warning', * message: 'error or warning message', * rule: 'rule-name', * line: 15, * column: 16 * } * * @param {SpawnResult} result - the result of the stylint run * @returns {HookMessage[]} */ parseStylintResult(result) { let resultHash = null; // Sometimes JSON.parse fails as if there is no output provided. // This is unexpected, so we've added some debug logging to try to understand try { resultHash = JSON.parse(result.stdout.toString().trim()); } catch(e) { utils.logger().debug( 'Error parsing stylus output:\n' + `status: ${result.status}\n` + `signal: ${result.signal}\n` + `stdout: ${result.stdout.toString()}\n` + `stderr: ${result.stderr.toString()}` ); return [new HookMessage( 'warning', '', 0, `Error parsing stylus output:\nstdout: ${result.stdout.toString().trim()}` )]; } let hookMessages = []; let fileResult = resultHash[0]; for (let i in fileResult.messages) { let currentMessage = fileResult.messages[i]; let type = currentMessage.severity.toLowerCase(); hookMessages.push(new HookMessage( type, fileResult.filePath, currentMessage.line, fileResult.filePath + ':' + currentMessage.line + ':' + type + ' ' + currentMessage.message )); } return hookMessages; } };