Skip to content
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

feat(rule-finder): Report any deprecated rules in use #286

Merged
merged 1 commit into from
Feb 15, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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'
]);
});
});