Skip to content

Do not raise error on unsupported plugins #204

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Mar 14, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions bin/eslint.js
Original file line number Diff line number Diff line change
@@ -8,10 +8,11 @@ process.chdir(CODE_DIR);
var stdout = console.log;
console.log = console.error;

var eslint = require('../lib/eslint-patch')(require('eslint'));

var eslint = require('../lib/eslint-patch')();

var CLIEngine = eslint.CLIEngine;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gordondiggs @maxjacobson I felt this was misplaced and was impacting the way we load/patch eslint, so I decoupled it from eslint instance. Haven't found any other use of eslint.docs so I think we are good, but wanted to hight light this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool. We use the docs for the issue contents - as long as those still work we're probably ok

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed docs are still showing up correctly

var docs = eslint.docs;
var docs = require('../lib/docs')();
var fs = require("fs");
var glob = require("glob");
var options = { extensions: [".js"], ignore: true, reset: false, useEslintrc: true };
30 changes: 11 additions & 19 deletions lib/docs.js
Original file line number Diff line number Diff line change
@@ -7,13 +7,14 @@
var fs = require('fs')
, path = require('path');

//------------------------------------------------------------------------------
// Privates
//------------------------------------------------------------------------------
function Docs() {

var docs = Object.create(null);
var docs = Object.create(null);

function get(ruleId) {
return docs[ruleId];
}

function load() {
var docsDir = path.join(__dirname, '/docs/rules');

fs.readdirSync(docsDir).forEach(function(file) {
@@ -22,19 +23,10 @@ function load() {
// Remove the .md extension from the filename
docs[file.slice(0, -3)] = content;
});
}

//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------

exports.get = function(ruleId) {
return docs[ruleId];
};

//------------------------------------------------------------------------------
// Initialization
//------------------------------------------------------------------------------
return {
get: get
};
}

// loads existing docs
load();
module.exports = Docs;
6 changes: 6 additions & 0 deletions lib/empty-plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
rules: {},
configs: {
recommended: {}
}
};
81 changes: 48 additions & 33 deletions lib/eslint-patch.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,50 @@
'use strict';
var meld = require('meld');
var docs = require('./docs');
var Config = require("eslint/lib/config");
var ConfigUpgrader = require('./config_upgrader');

var supportedPlugins = ['react', 'babel'];

module.exports = function patcher(eslint) {

meld.around(eslint.CLIEngine, 'loadPlugins', function(joinPoint) {
var pluginNames = joinPoint.args[0];
var filteredPluginNames = pluginNames.filter(function(pluginName) {
return supportedPlugins.indexOf(pluginName) >= 0;
});
return joinPoint.proceed(filteredPluginNames);
});

meld.around(eslint.CLIEngine, 'addPlugin', function() {
return;
});

// meld.around(eslint.CLIEngine.Config, 'loadPackage', function(joinPoint) {
// var filePath = joinPoint.args[0];
// if (filePath.match(/^eslint-config-airbnb.*/)) {
// return joinPoint.proceed();
// } else {
// return {};
// }
// });
"use strict";

const Plugins = require("eslint/lib/config/plugins");
const ModuleResolver = require("eslint/lib/util/module-resolver");

const ConfigFile = require("eslint/lib/config/config-file");

const Config = require("eslint/lib/config");
const ConfigUpgrader = require("./config_upgrader");

module.exports = function patch() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was removed because this hook doesn't ever actually fire? (I think I saw another comment you made about this)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maxjacobson The function is only implemented on eslint 1.9 and lower, so my understanding is it wasn't doing anything.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It never fired on my tests...


const skippedModules = [];
function warnModuleNotSupported(name) {
if(skippedModules.indexOf(name) < 0) {
skippedModules.push(name);
console.error(`Module not supported: ${name}`);
}
}

const resolve = ModuleResolver.prototype.resolve;
ModuleResolver.prototype.resolve = function(name, path) {
try {
return resolve.apply(this, [name, path]);
} catch(e) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should log something here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maxjacobson Any suggestions about the message? I am feeling it would just duplicate what we do at line 34.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think using the same message sounds fine. Are you saying it's already being printed? I didn't see it when doing local QA, so I thought it wasn't included there

warnModuleNotSupported(name);
return `${__dirname}/empty-plugin.js`;
}
};

Plugins.loadAll = function(pluginNames) {
const loadedPlugins = Object.keys(Plugins.getAll());
if(loadedPlugins.length > 0) {
return;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be a wild guess, but it feels like this early return is the culprit of #214. If loadAll is called multiple times with different arguments then only the first invocation does something, while the others won't do anything (and maybe expected plugins won't be loaded/defined anymore), while the original implementation on ESLint does not checks for getAll and always attempt to load each given plugin.

Maybe we can monkey patch this again with only the try/catch around each load call and see how it goes. 🤔

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other alternative would be to only patch Plugins.load(pluginName), which is where the require we want to wrap happens. https://github.com/eslint/eslint/blob/7d1af86851bb4af03ed05515ffc5e59e5c0cda52/lib/config/plugins.js#L127-L141

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lucasmazza On it! :)

}

for(const index in pluginNames) {
const name = pluginNames[index];
try {

Plugins.load(name);

} catch(e) {
warnModuleNotSupported(`eslint-plugin-${name}`);
}
}
};

const originalGetConfig = Config.prototype.getConfig;
Config.prototype.getConfig = function(filePath) {
@@ -37,7 +54,5 @@ module.exports = function patcher(eslint) {
return configUpgrader.upgrade(originalConfig);
};

eslint.docs = docs;

return eslint;
return require('eslint');
};
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -55,12 +55,12 @@
"eslint-plugin-standard": "^2.1.1",
"eslint-plugin-vue": "^2.0.1",
"eslint-plugin-xogroup": "^1.1.0",
"glob": "^7.0.6",
"meld": "^1.3.2"
"glob": "^7.0.6"
},
"devDependencies": {
"chai": "^3.5.0",
"mocha": "^2.5.3",
"sinon": "^1.17.7",
"temp": "^0.8.3"
},
"scripts": {
86 changes: 86 additions & 0 deletions test/eslint-patch_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
const expect = require("chai").expect;
const sinon = require("sinon");

const Plugins = require("eslint/lib/config/plugins");
const ModuleResolver = require("eslint/lib/util/module-resolver");
const eslintPatch = require("../lib/eslint-patch");

describe("eslint-patch", function() {
let loadAll;

before(function() {
loadAll = Plugins.loadAll;
});

after(function() {
Plugins.loadAll = loadAll;
});

it("intercepts plugins", function() {
eslintPatch();
expect(loadAll).to.not.equal(Plugins.loadAll, "Plugins.loadAll is not patched");
});

describe("Plugins.loadAll", function() {
before(function() {
eslintPatch();
});

it("delegates each plugin to be loaded", function () {
Plugins.getAll = sinon.stub().returns([]);
Plugins.load = sinon.spy();

Plugins.loadAll([ "jasmine", "mocha" ]);

expect(Plugins.load.calledWith("jasmine")).to.be.true;
expect(Plugins.load.calledWith("mocha")).to.be.true;
});

it("only load plugins once", function () {
Plugins.getAll = sinon.stub().returns([ "node" ]);
Plugins.load = sinon.spy();

Plugins.loadAll([ "node" ]);

expect(Plugins.load.called).to.be.false;
});

it("does not raise exception for unsupported plugins", function() {
Plugins.getAll = sinon.stub().returns([]);
Plugins.load = sinon.stub().throws();

function loadPlugin() {
Plugins.loadAll([ "unsupported-plugin" ]);
}

expect(loadPlugin).to.not.throw();
});
});

describe("loading extends configuration", function() {
it("patches module resolver", function() {
const resolve = ModuleResolver.prototype.resolve;

eslintPatch();
expect(ModuleResolver.prototype.resolve).to.not.eql(resolve);
});

it("returns fake config for skipped modules", function() {
eslintPatch();
Plugins.loadAll(['invalidplugin']);
expect(new ModuleResolver().resolve('eslint-plugin-invalidplugin')).to.match(/.+empty-plugin.js/);
});

it("does not warn user repeatedly about not supported modules", function() {
console.error = sinon.spy();
eslintPatch();

for(var i=0; i<3; i++) {
new ModuleResolver().resolve('eslint-plugin-bogus');
}

expect(console.error.callCount).to.eql(1);
});
});

});
37 changes: 33 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
@@ -978,6 +978,12 @@ foreach@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"

formatio@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/formatio/-/formatio-1.1.1.tgz#5ed3ccd636551097383465d996199100e86161e9"
dependencies:
samsam "~1.1"

fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@@ -1109,6 +1115,10 @@ inherits@2, inherits@^2.0.1, inherits@~2.0.1:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"

inherits@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"

inquirer@^0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e"
@@ -1320,6 +1330,10 @@ lodash@^4.0.0, lodash@^4.11.1, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.2.0, lo
version "4.17.2"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.2.tgz#34a3055babe04ce42467b607d700072c7ff6bf42"

lolex@1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/lolex/-/lolex-1.3.2.tgz#7c3da62ffcb30f0f5a80a2566ca24e45d8a01f31"

loose-envify@^1.0.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.0.tgz#6b26248c42f6d4fa4b0d8542f78edfcde35642a8"
@@ -1330,10 +1344,6 @@ lru-cache@2:
version "2.7.3"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952"

meld@^1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/meld/-/meld-1.3.2.tgz#8c3235fb5001b8796f8768818e9e4563b0de8066"

minimatch@0.3:
version "0.3.0"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.3.0.tgz#275d8edaac4f1bb3326472089e7949c8394699dd"
@@ -1606,6 +1616,10 @@ safe-regex@^1.1.0:
dependencies:
ret "~0.1.10"

samsam@1.1.2, samsam@~1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.2.tgz#bec11fdc83a9fda063401210e40176c3024d1567"

semver@5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
@@ -1622,6 +1636,15 @@ sigmund@~1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590"

sinon@^1.17.7:
version "1.17.7"
resolved "https://registry.yarnpkg.com/sinon/-/sinon-1.17.7.tgz#4542a4f49ba0c45c05eb2e9dd9d203e2b8efe0bf"
dependencies:
formatio "1.1.1"
lolex "1.3.2"
samsam "1.1.2"
util ">=0.10.3 <1"

slash@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
@@ -1759,6 +1782,12 @@ util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"

"util@>=0.10.3 <1":
version "0.10.3"
resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
dependencies:
inherits "2.0.1"

wordwrap@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"