Skip to content

Commit

Permalink
Use broccoli-persistent-filter
Browse files Browse the repository at this point in the history
  • Loading branch information
Nick Iaconis committed Mar 11, 2016
1 parent e09267d commit 7ca5a74
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 31 deletions.
85 changes: 61 additions & 24 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
/* eslint global-require: 0, consistent-return: 0 */

const Filter = require('broccoli-filter');
const Filter = require('broccoli-persistent-filter');
const CLIEngine = require('eslint').CLIEngine;
const md5Hex = require('md5-hex');
const stringify = require('json-stable-stringify');
const sep = require('path').sep;
const BUILD_DIR_REGEX = new RegExp('(' + sep + ')?build(' + sep + ')?$');


/**
* Calculates the severity of a eslint.linter.verify result
Expand Down Expand Up @@ -67,19 +72,26 @@ function EslintValidationFilter(inputTree, options) {
if (!(this instanceof EslintValidationFilter)) {
return new EslintValidationFilter(inputTree, options);
}

this.options = options || {};
this.eslintOptions = options.options || {};
const eslintOptions = this.options.options || {};

if (typeof this.options.persist === 'undefined') {
this.options.persist = true;
}

// call base class constructor
Filter.call(this, inputTree);
Filter.call(this, inputTree, this.options);

// set formatter
this.formatter = require(this.options.format || 'eslint/lib/formatters/stylish'); // eslint-disable-line global-require
// eslint-disable-next-line global-require
this.formatter = require(this.options.format || 'eslint/lib/formatters/stylish');

this.cli = new CLIEngine(this.eslintOptions);
this.cli = new CLIEngine(eslintOptions);

this.eslintrc = inputTree;

this.testGenerator = options.testGenerator;
this.testGenerator = this.options.testGenerator;
if (this.testGenerator) {
this.targetExtension = 'eslint-test.js';
}
Expand All @@ -91,38 +103,63 @@ EslintValidationFilter.prototype.constructor = EslintValidationFilter;
EslintValidationFilter.prototype.extensions = ['js'];
EslintValidationFilter.prototype.targetExtension = 'js';

EslintValidationFilter.prototype.processString = function processString(content, relativePath) {
'use strict'; // eslint-disable-line strict
EslintValidationFilter.prototype.baseDir = function baseDir() {
return __dirname.replace(BUILD_DIR_REGEX, '');
};

EslintValidationFilter.prototype.cacheKeyProcessString = function cacheKeyProcessString(content, relativePath) {
return md5Hex([content, relativePath, stringify(this.options, {
replacer(key, value) {
if (typeof value === 'function') {
return value.toString();
}
return value;
}
})]);
};

EslintValidationFilter.prototype.processString = function processString(content, relativePath) {
// verify file content
const output = this.cli.executeOnText(content, this.eslintrc + '/' + relativePath);
const filteredOutput = filterAllIgnoredFileMessages(output);
const toCache = {
lint: filteredOutput,
output: content
};

if (this.testGenerator) {
let messages = [];

if (filteredOutput.results.length) {
messages = filteredOutput.results[0].messages || [];
}

toCache.output = this.testGenerator(relativePath, messages);
}

return toCache;
};

EslintValidationFilter.prototype.postProcess = function postProcess(fromCache) {
const lint = fromCache.lint;
const output = fromCache.output;

// if verification has result
if (filteredOutput.results.length &&
filteredOutput.results[0].messages.length) {
if (lint.results.length &&
lint.results[0].messages.length) {

// log formatter output
console.log(this.formatter(filteredOutput.results));
console.log(this.formatter(lint.results));

if (getResultSeverity(filteredOutput.results) > 0) {
if (getResultSeverity(lint.results) > 0) {
if ('throwOnError' in this.internalOptions && this.internalOptions.throwOnError === true) {
// throw error if severe messages exist
throw new Error('severe rule errors');
}
}
}

if (this.testGenerator) {
let messages = [];

if (filteredOutput.results.length) {
messages = filteredOutput.results[0].messages || [];
}

return this.testGenerator(relativePath, messages);
}

// return unmodified string
return content;
return {
output
};
};
12 changes: 8 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"prepublish": "npm run babelify",
"lint": "eslint .",
"babelify-tests": "babel test --ignore fixture --out-dir build/test",
"test": "npm run babelify && npm run babelify-tests && npm run lint && node_modules/mocha/bin/mocha build/test/test.js"
"test": "npm run babelify && npm run babelify-tests && npm run lint && node_modules/mocha/bin/mocha build/test/common.js build/test/test.js"
},
"repository": {
"type": "git",
Expand Down Expand Up @@ -37,8 +37,10 @@
},
"homepage": "https://github.com/jonathanKingston/broccoli-lint-eslint",
"dependencies": {
"broccoli-filter": "^1.2.3",
"eslint": "^2.3.0"
"broccoli-persistent-filter": "^1.2.0",
"eslint": "^2.3.0",
"json-stable-stringify": "^1.0.1",
"md5-hex": "^1.2.1"
},
"devDependencies": {
"babel-cli": "^6.4.5",
Expand All @@ -49,6 +51,8 @@
"chai": "^3.5.0",
"eslint-config-nightmare-mode": "^2.3.0",
"mocha": "^2.2.4",
"rimraf": "^2.5.1"
"rimraf": "^2.5.1",
"sinon": "^1.17.3",
"sinon-chai": "^2.8.0"
}
}
7 changes: 7 additions & 0 deletions test/.eslintrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
{
"env": {
"mocha": true
},
"globals": {
"chai": false,
"expect": false
},
"rules": {
"no-invalid-this": 0
}
}
16 changes: 16 additions & 0 deletions test/common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/* eslint-disable no-invalid-this, newline-after-var */

global.chai = require('chai');
global.expect = chai.expect;
const sinon = require('sinon');

const sinonChai = require('sinon-chai');
chai.use(sinonChai);

beforeEach(function beforeEachAndEvery() {
this.sinon = sinon.sandbox.create();
});

afterEach(function afterEachAndEvery() {
this.sinon.restore();
});
43 changes: 40 additions & 3 deletions test/test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const expect = require('chai').expect;
const eslint = require('../index');
const runEslint = require('./helpers/run-eslint');
const FIXTURES = 'test/fixture';
const CAMELCASE = '(camelcase)';
Expand All @@ -8,7 +8,7 @@ const DOUBLEQUOTE = 'Strings must use doublequote.';
const FILEPATH = 'fixture/1.js';

describe('EslintValidationFilter', function describeEslintValidationFilter() {
it('should report errors', function shouldReportErrors() {
function shouldReportErrors() {
// lint test fixtures
const promise = runEslint(FIXTURES);

Expand All @@ -19,7 +19,9 @@ describe('EslintValidationFilter', function describeEslintValidationFilter() {
expect(buildLog, 'Used relative config - single quotes').to.not.have.string(DOUBLEQUOTE);
expect(buildLog, 'No custom rules defined').to.not.have.string(CUSTOM_RULES);
});
});
}

it('should report errors', shouldReportErrors);

it('should accept rule paths', function shouldAcceptRulePaths() {
// lint test fixtures using a custom rule
Expand Down Expand Up @@ -47,4 +49,39 @@ describe('EslintValidationFilter', function describeEslintValidationFilter() {
expect(buildLog, 'Used alternate config - double quotes').to.have.string(DOUBLEQUOTE);
});
});

it('should cache results, but still log errors', function shouldHaveCachedResults() {
// spy on filter process methods
const processStringSpy = this.sinon.spy(eslint.prototype, 'processString');
const postProcessSpy = this.sinon.spy(eslint.prototype, 'postProcess');

// run first test again, should use cache but still log errors
return shouldReportErrors()
.then(function assertCaching() {
// check that it actually used the cache
expect(processStringSpy, 'Used cache').to.have.callCount(0);
expect(postProcessSpy, 'Logged errors').to.have.callCount(3);
});
});

it('should allow disabling the cache', function shouldAllowDisablingCache() {
// spy on filter process methods
const processStringSpy = this.sinon.spy(eslint.prototype, 'processString');
const postProcessSpy = this.sinon.spy(eslint.prototype, 'postProcess');

function runNonpersistent() {
return runEslint(FIXTURES, {
persist: false
});
}

// Run twice to guarantee one run would be from cache if persisting
const promise = runNonpersistent().then(runNonpersistent);

return promise.then(function assertCaching() {
// check that it did not use the cache
expect(processStringSpy, 'Didn\'t use cache (twice)').to.have.callCount(6);
expect(postProcessSpy, 'Logged errors (twice)').to.have.callCount(6);
});
});
});

0 comments on commit 7ca5a74

Please sign in to comment.