Skip to content

Commit

Permalink
Fix: enable @scope/plugin/ruleId-style specifier (refs #6362) (#6939)
Browse files Browse the repository at this point in the history
  • Loading branch information
mysticatea authored and nzakas committed Aug 23, 2016
1 parent d6fd064 commit 3a1763c
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 28 deletions.
34 changes: 20 additions & 14 deletions lib/config/plugins.js
Expand Up @@ -9,7 +9,7 @@
//------------------------------------------------------------------------------

const Environments = require("./environments"),
rules = require("../rules");
Rules = require("../rules");

const debug = require("debug")("eslint:plugins");

Expand Down Expand Up @@ -66,17 +66,21 @@ module.exports = {
* @returns {void}
*/
define(pluginName, plugin) {
const pluginNameWithoutNamespace = removeNamespace(pluginName),
pluginNameWithoutPrefix = removePrefix(pluginNameWithoutNamespace);

plugins[pluginNameWithoutPrefix] = plugin;
const pluginNamespace = getNamespace(pluginName),
pluginNameWithoutNamespace = removeNamespace(pluginName),
pluginNameWithoutPrefix = removePrefix(pluginNameWithoutNamespace),
shortName = pluginNamespace + pluginNameWithoutPrefix;

// load up environments and rules
Environments.importPlugin(plugin, pluginNameWithoutPrefix);
plugins[shortName] = plugin;
Environments.importPlugin(plugin, shortName);
Rules.importPlugin(plugin, shortName);

if (plugin.rules) {
rules.import(plugin.rules, pluginNameWithoutPrefix);
}
// load up environments and rules for the name that '@scope/' was omitted
// 3 lines below will be removed by 4.0.0
plugins[pluginNameWithoutPrefix] = plugin;
Environments.importPlugin(plugin, pluginNameWithoutPrefix);
Rules.importPlugin(plugin, pluginNameWithoutPrefix);
},

/**
Expand Down Expand Up @@ -105,18 +109,20 @@ module.exports = {
load(pluginName) {
const pluginNamespace = getNamespace(pluginName),
pluginNameWithoutNamespace = removeNamespace(pluginName),
pluginNameWithoutPrefix = removePrefix(pluginNameWithoutNamespace);
pluginNameWithoutPrefix = removePrefix(pluginNameWithoutNamespace),
shortName = pluginNamespace + pluginNameWithoutPrefix,
longName = pluginNamespace + PLUGIN_NAME_PREFIX + pluginNameWithoutPrefix;
let plugin = null;

if (!plugins[pluginNameWithoutPrefix]) {
if (!plugins[shortName]) {
try {
plugin = require(pluginNamespace + PLUGIN_NAME_PREFIX + pluginNameWithoutPrefix);
plugin = require(longName);
} catch (err) {
debug("Failed to load plugin eslint-plugin-" + pluginNameWithoutPrefix + ". Proceeding without it.");
debug("Failed to load plugin " + longName + ".");
err.message = "Failed to load plugin " + pluginName + ": " + err.message;
err.messageTemplate = "plugin-missing";
err.messageData = {
pluginName: pluginNameWithoutPrefix
pluginName: longName
};
throw err;
}
Expand Down
20 changes: 11 additions & 9 deletions lib/rules.js
Expand Up @@ -47,17 +47,19 @@ function load(rulesDir, cwd) {

/**
* Registers all given rules of a plugin.
* @param {Object} pluginRules A key/value map of rule definitions.
* @param {Object} plugin The plugin object to import.
* @param {string} pluginName The name of the plugin without prefix (`eslint-plugin-`).
* @returns {void}
*/
function importPlugin(pluginRules, pluginName) {
Object.keys(pluginRules).forEach(function(ruleId) {
const qualifiedRuleId = pluginName + "/" + ruleId,
rule = pluginRules[ruleId];

define(qualifiedRuleId, rule);
});
function importPlugin(plugin, pluginName) {
if (plugin.rules) {
Object.keys(plugin.rules).forEach(function(ruleId) {
const qualifiedRuleId = pluginName + "/" + ruleId,
rule = plugin.rules[ruleId];

define(qualifiedRuleId, rule);
});
}
}

/**
Expand Down Expand Up @@ -85,7 +87,7 @@ function testClear() {
module.exports = {
define,
load,
import: importPlugin,
importPlugin,
get: getHandler,
testClear,

Expand Down
6 changes: 3 additions & 3 deletions messages/plugin-missing.txt
@@ -1,9 +1,9 @@
ESLint couldn't find the plugin "eslint-plugin-<%- pluginName %>". This can happen for a couple different reasons:
ESLint couldn't find the plugin "<%- pluginName %>". This can happen for a couple different reasons:

1. If ESLint is installed globally, then make sure eslint-plugin-<%- pluginName %> is also installed globally. A globally-installed ESLint cannot find a locally-installed plugin.
1. If ESLint is installed globally, then make sure <%- pluginName %> is also installed globally. A globally-installed ESLint cannot find a locally-installed plugin.

2. If ESLint is installed locally, then it's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm i eslint-plugin-<%- pluginName %>@latest --save-dev
npm i <%- pluginName %>@latest --save-dev

If you still can't figure out the problem, please stop by https://gitter.im/eslint/eslint to chat with the team.
62 changes: 61 additions & 1 deletion tests/lib/config/plugins.js
Expand Up @@ -31,14 +31,17 @@ describe("Plugins", function() {
let StubbedPlugins,
Rules,
Environments,
plugin;
plugin,
scopedPlugin;

beforeEach(function() {
plugin = {};
scopedPlugin = {};
Environments = require("../../../lib/config/environments");
Rules = require("../../../lib/rules");
StubbedPlugins = proxyquire("../../../lib/config/plugins", {
"eslint-plugin-example": plugin,
"@scope/eslint-plugin-example": scopedPlugin,
"./environments": Environments,
"../rules": Rules
});
Expand Down Expand Up @@ -88,6 +91,63 @@ describe("Plugins", function() {
}, /Failed to load plugin/);
});

it("should load a scoped plugin when referenced by short name", () => {
StubbedPlugins.load("@scope/example");
assert.equal(StubbedPlugins.get("@scope/example"), scopedPlugin);
});

it("should load a scoped plugin when referenced by long name", () => {
StubbedPlugins.load("@scope/eslint-plugin-example");
assert.equal(StubbedPlugins.get("@scope/example"), scopedPlugin);
});

it("should register environments when scoped plugin has environments", () => {
scopedPlugin.environments = {
foo: {}
};
StubbedPlugins.load("@scope/eslint-plugin-example");

assert.equal(Environments.get("@scope/example/foo"), scopedPlugin.environments.foo);
});

it("should register rules when scoped plugin has rules", () => {
scopedPlugin.rules = {
foo: {}
};
StubbedPlugins.load("@scope/eslint-plugin-example");

assert.equal(Rules.get("@scope/example/foo"), scopedPlugin.rules.foo);
});

describe("(NOTE: those behavior will be removed by 4.0.0)", () => {
it("should load a scoped plugin when referenced by short name, and should get the plugin even if '@scope/' is omitted", () => {
StubbedPlugins.load("@scope/example");
assert.equal(StubbedPlugins.get("example"), scopedPlugin);
});

it("should load a scoped plugin when referenced by long name, and should get the plugin even if '@scope/' is omitted", () => {
StubbedPlugins.load("@scope/eslint-plugin-example");
assert.equal(StubbedPlugins.get("example"), scopedPlugin);
});

it("should register environments when scoped plugin has environments, and should get the environment even if '@scope/' is omitted", () => {
scopedPlugin.environments = {
foo: {}
};
StubbedPlugins.load("@scope/eslint-plugin-example");

assert.equal(Environments.get("example/foo"), scopedPlugin.environments.foo);
});

it("should register rules when scoped plugin has rules, and should get the rule even if '@scope/' is omitted", () => {
scopedPlugin.rules = {
foo: {}
};
StubbedPlugins.load("@scope/eslint-plugin-example");

assert.equal(Rules.get("example/foo"), scopedPlugin.rules.foo);
});
});
});

describe("loadAll()", function() {
Expand Down
2 changes: 1 addition & 1 deletion tests/lib/rules.js
Expand Up @@ -64,7 +64,7 @@ describe("rules", function() {
pluginName = "custom-plugin";

it("should define all plugin rules with a qualified rule id", function() {
rules.import(customPlugin.rules, pluginName);
rules.importPlugin(customPlugin, pluginName);

assert.isDefined(rules.get("custom-plugin/custom-rule"));
assert.equal(rules.get("custom-plugin/custom-rule"), customPlugin.rules["custom-rule"]);
Expand Down

0 comments on commit 3a1763c

Please sign in to comment.