eslint-compare-config icon indicating copy to clipboard operation
eslint-compare-config copied to clipboard

Can't compare configs that extend one another

Open stevenvachon opened this issue 6 years ago • 7 comments

If we compare A and B, where A extends B, it won't tell us which rules are redundant.

stevenvachon avatar May 01 '18 16:05 stevenvachon

That makes sense, because it's looking at the resultant ruleset. It might work if you try the --literal mode.

scottnonnenberg avatar May 01 '18 17:05 scottnonnenberg

I thought that --literal only removes the auto-discovery of .eslintrc files within directories?

stevenvachon avatar May 01 '18 18:05 stevenvachon

Here's a script that I wrote for getting the job done:

'use strict';
// @todo replace this when possible: https://github.com/scottnonnenberg/eslint-compare-config/issues/7

const {green, grey, red, yellow} = require('chalk');
const {readFileSync} = require('fs');
const stripComments = require('strip-json-comments'); // used by eslint v4

const filterRules = (configA, configB) => {
    const matching = [];
    const unmatching = [];

    Object.keys(configA.rules).forEach(ruleName => {
        const configARule = configA.rules[ruleName];
        const configBRule = configB.rules[ruleName];

        const isMatchingError = isErrorRule(configARule) && isErrorRule(configBRule);
        const isMatchingOff = isOffRule(configARule) && isOffRule(configBRule);
        const isMatchingWarn = isWarnRule(configARule) && isWarnRule(configBRule);

        if (isMatchingError || isMatchingOff || isMatchingWarn) {
            matching.push(ruleName);
        } else {
            unmatching.push(ruleName);
        }
    });

    return {
        matching: matching.sort(),
        unmatching: unmatching.sort()
    };
};

const getRuleValue = rule => {
    if (Array.isArray(rule)) {
        return rule[0];
    } else {
        return rule;
    }
};

const isErrorRule = rule => isRuleType(rule, 2, 'error');

const isOffRule = rule => isRuleType(rule, 0, 'off');

const isRuleType = (rule, number, string) => {
    const ruleValue = getRuleValue(rule);
    return ruleValue === number || ruleValue === string;
};

const isWarnRule = rule => isRuleType(rule, 1, 'warn');

const loadConfigs = () => ({
    configA: JSON.parse(stripComments(readFileSync(`${__dirname}/../.eslintrc`, 'utf8'))),
    configB: require('eslint/conf/eslint-recommended')
});

const run = () => {
    const {configA, configB} = loadConfigs();
    const {matching, unmatching} = filterRules(configA, configB);

    const logRules = (rules, isMatching) => {
        if (rules.length > 0) {
            const matchingColor = isMatching ? green : red;

            rules.forEach(ruleName => {
                const configARule = getRuleValue(configA.rules[ruleName]);
                const configBRule = getRuleValue(configB.rules[ruleName]);
                console.log(`${yellow(ruleName)} ${grey('--')} project: ${matchingColor(configARule)}, base: ${matchingColor(configBRule)}`);
            });
        } else {
            console.log(grey('(none)'));
        }
    };

    console.log(grey('Comparing project .eslintrc with its base, "eslint:recommended"'));

    console.log(`\nMATCHING/REDUNDANT RULES: ${grey('(didn\'t check options)')}`);
    logRules(matching, true);

    console.log('\nUNMATCHING RULES:');
    logRules(unmatching, false);
};

run();

stevenvachon avatar May 01 '18 19:05 stevenvachon

The --literal option disables the code that computes the final ruleset (using both extended configs and any locally-specified rule). So in theory you could do what you're looking to do by using the --literal mode and pointing this tool to the eslint-recommended config you reference in that code.

But I do like this feature generally, answering the question "Are we re-declaring things already declared in a base config?"

It's very similar to one of the things I wanted to do early on with this tool, figuring out if your manually-provided values for a given rule were redundant given the defaults. It's just that I didn't have ready access to the defaults. :0/

scottnonnenberg avatar May 01 '18 19:05 scottnonnenberg

npx eslint-compare-config --literal ./.eslintrc node_modules/eslint/conf/eslint-recommended.js
Unexpected token :
npx eslint-compare-config ./.eslintrc node_modules/eslint/conf/eslint-recommended.js --literal
Unexpected token :

stevenvachon avatar May 01 '18 20:05 stevenvachon

Yeah, the tool doesn't support all formats eslint supports... :0/

scottnonnenberg avatar May 01 '18 20:05 scottnonnenberg

Please consider continuing maintenance.

stevenvachon avatar May 01 '18 20:05 stevenvachon