Skip to content

Commit

Permalink
feat(rule-finder): Report any deprecated rules in use
Browse files Browse the repository at this point in the history
  • Loading branch information
randycoulman committed Feb 2, 2018
1 parent f6e4158 commit 31b7448
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 32 deletions.
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,14 @@ The intended usage is as an npm script:
Then run it with: `$ npm run --silent eslint-find-option-rules` (the `--silent` is to silence npm output).

```
available options are -c|--current, -a|--all-available, -p|--plugin, -u|--unused
available options are -a|--all-available, -c|--current, -d|--deprecated, -p|--plugin, -u|--unused
available flags are -n|--no-error, --no-core, and -i/--include deprecated
```

By default it will error out only for `-u|--unused`,
however if you do not want the `process` to `exit` with a `non-zero` exit code, use the `-n|--no-error` flag along with `-u|--unused`.
By default it will error out only for `-d|--deprecated` and `-u|--unused`,
however if you do not want the `process` to `exit` with a `non-zero` exit code, use the `-n|--no-error` flag along with `-d|--deprecated` or `-u|--unused`.

By default, core rules will be included in the output of `-c|--current`, `-a|--all-available`, and `-u|--unused`. If you want to report on plugin rules only, use the `--no-core` flag.
By default, core rules will be included in the output of `-a|--all-available`, `-c|--current`, `-d|--deprecated`, and `-u|--unused`. If you want to report on plugin rules only, use the `--no-core` flag.

By default, deprecated rules will be omitted from the output of `-a|--all-available`, `-p|--plugin` and `-u|--unused`. If you want to report on deprecated rules as well, use the `--include=deprecated` or `-i deprecated` flag.

Expand All @@ -63,7 +63,7 @@ By default, deprecated rules will be omitted from the output of `-a|--all-availa
This is really handy in an actual config module (like [eslint-config-kentcdodds](https://github.com/kentcdodds/eslint-config-kentcdodds)) where you could also do:

```
// available options are -c|--current, -a|--all-available, -p|--plugin, -u|--unused
// available options are -a|--all-available, -c|--current, -d|--deprecated, -p|--plugin, -u|--unused
eslint-find-rules --option ./index.js
```

Expand Down Expand Up @@ -110,6 +110,8 @@ ruleFinder.getPluginRules()
ruleFinder.getAllAvailableRules()
ruleFinder.getUnusedRules()
ruleFinder.getDeprecatedRules()
```

### Log the difference between two config files
Expand Down
3 changes: 2 additions & 1 deletion src/bin/find.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const options = {
getPluginRules: ['plugin', 'p'],
getAllAvailableRules: ['all-available', 'a'],
getUnusedRules: ['unused', 'u'],
getDeprecatedRules: ['deprecated', 'd'],
n: [],
error: ['error'],
core: ['core'],
Expand Down Expand Up @@ -36,7 +37,7 @@ const ruleFinder = getRuleFinder(specifiedFile, finderOptions);
const errorOut = argv.error && !argv.n;
let processExitCode = argv.u && errorOut ? 1 : 0;

if (!argv.c && !argv.p && !argv.a && !argv.u) {
if (!argv.c && !argv.p && !argv.a && !argv.u && !argv.d) {
console.log('no option provided, please provide a valid option'); // eslint-disable-line no-console
console.log('usage:'); // eslint-disable-line no-console
console.log('eslint-find-rules [option] <file> [flag]'); // eslint-disable-line no-console
Expand Down
64 changes: 40 additions & 24 deletions src/lib/rule-finder.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ function _getConfig(configFile) {
return cliEngine.getConfigForFile();
}

function _getCurrentRules(config) {
function _getCurrentNamesRules(config) {
return Object.keys(config.rules);
}

Expand All @@ -48,32 +48,36 @@ function _normalizePluginName(name) {
}

function _isDeprecated(rule) {
return rule.meta && rule.meta.deprecated;
return rule && rule.meta && rule.meta.deprecated;
}

function _getPluginRules(config, {includeDeprecated}) {
let pluginRules = [];
function _notDeprecated(rule) {
return !_isDeprecated(rule);
}

function _getPluginRules(config) {
const pluginRules = new Map();
const plugins = config.plugins;
if (plugins) {
plugins.forEach(plugin => {
const normalized = _normalizePluginName(plugin);
const pluginConfig = require(normalized.module); // eslint-disable-line import/no-dynamic-require

const rules = pluginConfig.rules === undefined ? {} : pluginConfig.rules;
pluginRules = pluginRules.concat(
Object.keys(rules)
.filter(rule => includeDeprecated || !_isDeprecated(rules[rule]))
.map(rule => `${normalized.prefix}/${rule}`)

Object.keys(rules).forEach(ruleName =>
pluginRules.set(`${normalized.prefix}/${ruleName}`, rules[ruleName])
);
});
}
return pluginRules;
}

function _getCoreRules({includeDeprecated}) {
const rules = eslint.linter.getRules();
return Array.from(rules.keys())
.filter(rule => includeDeprecated || !_isDeprecated(rules.get(rule)));
function _getCoreRules() {
return eslint.linter.getRules();
}

function _filterRuleNames(ruleNames, rules, predicate) {
return ruleNames.filter(ruleName => predicate(rules.get(ruleName)));
}

function _isNotCore(rule) {
Expand All @@ -84,29 +88,41 @@ function RuleFinder(specifiedFile, options) {
const {omitCore, includeDeprecated} = options;
const configFile = _getConfigFile(specifiedFile);
const config = _getConfig(configFile);
let currentRules = _getCurrentRules(config);
const pluginRules = _getPluginRules(config, {includeDeprecated});
const coreRules = _getCoreRules({includeDeprecated});
const allRules = omitCore ? pluginRules : [...coreRules, ...pluginRules];
const dedupedRules = [...new Set(allRules)];
let currentRuleNames = _getCurrentNamesRules(config);
if (omitCore) {
currentRules = currentRules.filter(_isNotCore);
currentRuleNames = currentRuleNames.filter(_isNotCore);
}
const unusedRules = difference(dedupedRules, currentRules); // eslint-disable-line vars-on-top

const pluginRules = _getPluginRules(config); // eslint-disable-line vars-on-top
const coreRules = _getCoreRules();
const allRules = omitCore ? pluginRules : new Map([...coreRules, ...pluginRules]);

let allRuleNames = [...allRules.keys()];
let pluginRuleNames = [...pluginRules.keys()];
if (!includeDeprecated) {
allRuleNames = _filterRuleNames(allRuleNames, allRules, _notDeprecated);
pluginRuleNames = _filterRuleNames(pluginRuleNames, pluginRules, _notDeprecated);
}
const deprecatedRuleNames = _filterRuleNames(currentRuleNames, allRules, _isDeprecated);
const dedupedRuleNames = [...new Set(allRuleNames)];
const unusedRuleNames = difference(dedupedRuleNames, currentRuleNames);

// Get all the current rules instead of referring the extended files or documentation
this.getCurrentRules = () => getSortedRules(currentRules);
this.getCurrentRules = () => getSortedRules(currentRuleNames);

// Get all the current rules' particular configuration
this.getCurrentRulesDetailed = () => config.rules;

// Get all the plugin rules instead of referring the extended files or documentation
this.getPluginRules = () => getSortedRules(pluginRules);
this.getPluginRules = () => getSortedRules(pluginRuleNames);

// Get all the available rules instead of referring eslint and plugin packages or documentation
this.getAllAvailableRules = () => getSortedRules(dedupedRules);
this.getAllAvailableRules = () => getSortedRules(dedupedRuleNames);

this.getUnusedRules = () => getSortedRules(unusedRuleNames);

this.getUnusedRules = () => getSortedRules(unusedRules);
// Get all the current rules that are deprecated
this.getDeprecatedRules = () => getSortedRules(deprecatedRuleNames);
}

module.exports = function (specifiedFile, options = {}) {
Expand Down
45 changes: 43 additions & 2 deletions test/bin/find.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const getCurrentRules = sinon.stub().returns(['current', 'rules']);
const getPluginRules = sinon.stub().returns(['plugin', 'rules']);
const getAllAvailableRules = sinon.stub().returns(['all', 'available']);
const getUnusedRules = sinon.stub().returns(['unused', 'rules']);
const getDeprecatedRules = sinon.stub().returns(['deprecated', 'rules']);

let stub;

Expand All @@ -20,13 +21,14 @@ describe('bin', () => {
getCurrentRules,
getPluginRules,
getAllAvailableRules,
getUnusedRules
getUnusedRules,
getDeprecatedRules
};
}
};

console.log = (...args) => { // eslint-disable-line no-console
if (args[0].match(/(current|plugin|all-available|unused|rules found)/)) {
if (args[0].match(/(current|plugin|all-available|unused|deprecated|rules found)/)) {
return;
}
consoleLog(...args);
Expand Down Expand Up @@ -117,6 +119,45 @@ describe('bin', () => {
assert.ok(getUnusedRules.called);
});

it('option -d|--deprecated', () => {
process.exit = status => {
assert.equal(status, 1);
};
process.argv[2] = '-d';
proxyquire('../../src/bin/find', stub);
assert.ok(getDeprecatedRules.called);
});

it('options -d|--deprecated and no deprecated rules found', () => {
getDeprecatedRules.returns([]);
process.exit = status => {
assert.equal(status, 0);
};
process.argv[2] = '-d';
proxyquire('../../src/bin/find', stub);
assert.ok(getDeprecatedRules.called);
});

it('option -d|--deprecated along with -n', () => {
process.exit = status => {
assert.equal(status, 0);
};
process.argv[2] = '-d';
process.argv[3] = '-n';
proxyquire('../../src/bin/find', stub);
assert.ok(getDeprecatedRules.called);
});

it('option -d|--deprecated along with --no-error', () => {
process.exit = status => {
assert.equal(status, 0);
};
process.argv[2] = '-d';
process.argv[3] = '--no-error';
proxyquire('../../src/bin/find', stub);
assert.ok(getDeprecatedRules.called);
});

it('logs verbosely', () => {
process.argv[2] = '-c';
process.argv[3] = '-v';
Expand Down
14 changes: 14 additions & 0 deletions test/fixtures/eslint-with-deprecated-rules.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"plugins": [
"plugin",
"@scope/scoped-plugin"
],
"rules": {
"foo-rule": [2],
"old-rule": [2],
"plugin/foo-rule": [2],
"plugin/old-plugin-rule": [2],
"scoped-plugin/foo-rule": [2],
"scoped-plugin/old-plugin-rule": [2]
}
}
15 changes: 15 additions & 0 deletions test/lib/rule-finder.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ const specifiedFileRelative = './test/fixtures/eslint.json';
const specifiedFileAbsolute = path.join(process.cwd(), specifiedFileRelative);
const noRulesFile = path.join(process.cwd(), './test/fixtures/eslint-with-plugin-with-no-rules.json');
const noDuplicateRulesFiles = './test/fixtures/eslint-dedupe-plugin-rules.json';
const usingDeprecatedRulesFile = path.join(process.cwd(), './test/fixtures/eslint-with-deprecated-rules.json');

describe('rule-finder', () => {
afterEach(() => {
Expand Down Expand Up @@ -379,4 +380,18 @@ describe('rule-finder', () => {
'plugin/duplicate-foo-rule'
]);
});

it('specifiedFile (absolute path) without deprecated rules - deprecated rules', () => {
const ruleFinder = getRuleFinder(specifiedFileAbsolute);
assert.deepEqual(ruleFinder.getDeprecatedRules(), []);
});

it('specifiedFile (absolute path) with deprecated rules - deprecated rules', () => {
const ruleFinder = getRuleFinder(usingDeprecatedRulesFile);
assert.deepEqual(ruleFinder.getDeprecatedRules(), [
'old-rule',
'plugin/old-plugin-rule',
'scoped-plugin/old-plugin-rule'
]);
});
});

0 comments on commit 31b7448

Please sign in to comment.