From 73cd4c4f62d0f1ab534c7b9bd6f1686bf3f59e38 Mon Sep 17 00:00:00 2001 From: Pierre Vanduynslager Date: Wed, 12 Feb 2020 22:34:53 -0500 Subject: [PATCH] Disable rules based on Node version --- config/plugins.js | 25 +++++++-------- lib/options-manager.js | 69 +++++++++++++++++++++++++++-------------- test/lint-files.js | 3 +- test/lint-text.js | 6 ++-- test/options-manager.js | 55 ++++++++++++++++++++------------ 5 files changed, 94 insertions(+), 64 deletions(-) diff --git a/config/plugins.js b/config/plugins.js index 1838bf9a..767863e6 100644 --- a/config/plugins.js +++ b/config/plugins.js @@ -54,8 +54,7 @@ module.exports = { // ], 'unicorn/prevent-abbreviations': 'off', - // TODO: Enable this when targeting Node.js 12. - 'unicorn/prefer-flat-map': 'off', + 'unicorn/prefer-flat-map': 'error', // TODO: Remove this override when the rule is more stable. 'unicorn/consistent-function-scoping': 'off', @@ -217,15 +216,14 @@ module.exports = { 'always' ], - // Enable these when targeting Node.js 12. - // 'node/prefer-global/text-decoder': [ - // 'error', - // 'always' - // ], - // 'node/prefer-global/text-encoder': [ - // 'error', - // 'always' - // ], + 'node/prefer-global/text-decoder': [ + 'error', + 'always' + ], + 'node/prefer-global/text-encoder': [ + 'error', + 'always' + ], 'node/prefer-global/url-search-params': [ 'error', @@ -236,9 +234,8 @@ module.exports = { 'always' ], - // Enable these when targeting Node.js 12. - // 'node/prefer-promises/dns': 'error', - // 'node/prefer-promises/fs': 'error', + 'node/prefer-promises/dns': 'error', + 'node/prefer-promises/fs': 'error', 'eslint-comments/disable-enable-pair': [ 'error', diff --git a/lib/options-manager.js b/lib/options-manager.js index a437b475..caacbc78 100644 --- a/lib/options-manager.js +++ b/lib/options-manager.js @@ -43,12 +43,12 @@ const DEFAULT_CONFIG = { }; /** - * Define the rules that are enabled only for specific version of Node.js based on `engines.node` in package.json or the `node-version` option. + * Define the rules config that are overwritten only for specific version of Node.js based on `engines.node` in package.json or the `node-version` option. * * The keys are rule names and the values are an Object with a valid semver (`4.0.0` is valid `4` is not) as keys and the rule configuration as values. * - * Each entry define the rule config and the minimum Node.js version for which to set it. - * The entry with the highest version that is compliant with the `engines.node`/`node-version` range will be used. + * Each entry define the rule config and the maximum Node.js version for which to set it. + * The entry with the lowest version that is compliant with the `engines.node`/`node-version` range will be used. * * @type {Object} * @@ -66,26 +66,50 @@ const DEFAULT_CONFIG = { * With `engines.node` set to `>=8` the rule `plugin/rule` will be used with the config `{prop: 'node-8-conf'}`. */ const ENGINE_RULES = { - 'node/prefer-global/text-decoder': { - '11.0.0': [ - 'error', - 'always' - ] + 'unicorn/prefer-spread': { + '5.0.0': 'off' + }, + 'unicorn/no-new-buffer': { + '5.10.0': 'off' + }, + 'prefer-rest-params': { + '6.0.0': 'off' + }, + 'prefer-destructuring': { + '6.0.0': 'off' + }, + 'promise/prefer-await-to-then': { + '7.6.0': 'off' + }, + 'prefer-object-spread': { + '8.3.0': 'off' + }, + 'node/prefer-global/url-search-params': { + '10.0.0': 'off' + }, + 'node/prefer-global/url': { + '10.0.0': 'off' + }, + 'no-useless-catch': { + '10.0.0': 'off' + }, + 'prefer-named-capture-group': { + '10.0.0': 'off' }, 'node/prefer-global/text-encoder': { - '11.0.0': [ - 'error', - 'always' - ] + '11.0.0': 'off' + }, + 'node/prefer-global/text-decoder': { + '11.0.0': 'off' + }, + 'unicorn/prefer-flat-map': { + '11.0.0': 'off' }, 'node/prefer-promises/dns': { - '12.0.0': 'error' + '11.14.0': 'off' }, 'node/prefer-promises/fs': { - '12.0.0': 'error' - }, - 'unicorn/prefer-flat-map': { - '11.0.0': 'error' + '11.14.0': 'off' } }; @@ -200,13 +224,10 @@ const buildConfig = options => { ); const spaces = normalizeSpaces(options); - if (options.nodeVersion) { - for (const rule of Object.keys(ENGINE_RULES)) { - // Use the rule value for the highest version that is lower or equal to the oldest version of Node.js supported - for (const minVersion of Object.keys(ENGINE_RULES[rule]).sort(semver.compare)) { - if (!semver.intersects(options.nodeVersion, `<${minVersion}`)) { - config.rules[rule] = ENGINE_RULES[rule][minVersion]; - } + for (const [rule, ruleConfig] of Object.entries(ENGINE_RULES)) { + for (const minVersion of Object.keys(ruleConfig).sort(semver.rcompare)) { + if (!options.nodeVersion || semver.intersects(options.nodeVersion, `<${minVersion}`)) { + config.rules[rule] = ruleConfig[minVersion]; } } } diff --git a/test/lint-files.js b/test/lint-files.js index 2217863b..67afbfc6 100644 --- a/test/lint-files.js +++ b/test/lint-files.js @@ -100,8 +100,7 @@ test('multiple negative patterns should act as positive patterns', async t => { t.deepEqual(paths, ['!!unicorn.js', '!unicorn.js']); }); -// TODO: We need a new fixture. Help welcome. -test.failing('enable rules based on nodeVersion', async t => { +test('enable rules based on nodeVersion', async t => { const {results} = await fn.lintFiles('**/*', {cwd: 'fixtures/engines-overrides'}); // The transpiled file (as specified in `overrides`) should use `await` diff --git a/test/lint-text.js b/test/lint-text.js index 1774acff..d2e9c59f 100644 --- a/test/lint-text.js +++ b/test/lint-text.js @@ -212,8 +212,7 @@ test('lint eslintignored files if filename is not given', async t => { t.true(results[0].errorCount > 0); }); -// TODO: We need a new fixture. Help welcome. -test.failing('enable rules based on nodeVersion', async t => { +test('enable rules based on nodeVersion', async t => { const cwd = path.join(__dirname, 'fixtures', 'engines-overrides'); const filename = path.join(cwd, 'promise-then.js'); const text = await readFile(filename, 'utf8'); @@ -225,8 +224,7 @@ test.failing('enable rules based on nodeVersion', async t => { t.false(hasRule(results, 'promise/prefer-await-to-then')); }); -// TODO: We need a new fixture. Help welcome. -test.failing('enable rules based on nodeVersion in override', async t => { +test('enable rules based on nodeVersion in override', async t => { const cwd = path.join(__dirname, 'fixtures', 'engines-overrides'); const filename = path.join(cwd, 'promise-then.js'); const text = await readFile(filename, 'utf8'); diff --git a/test/options-manager.js b/test/options-manager.js index 653bd13b..fa4be655 100644 --- a/test/options-manager.js +++ b/test/options-manager.js @@ -65,7 +65,7 @@ test('buildConfig: space: 4', t => { }); test('buildConfig: semicolon', t => { - const config = manager.buildConfig({semicolon: false}); + const config = manager.buildConfig({semicolon: false, nodeVersion: '12'}); t.deepEqual(config.rules, { semi: ['error', 'never'], 'semi-spacing': ['error', { @@ -202,38 +202,53 @@ test('buildConfig: engines: undefined', t => { const config = manager.buildConfig({}); // Do not include any Node.js version specific rules - t.is(config.rules['prefer-spread'], undefined); - t.is(config.rules['prefer-rest-params'], undefined); - t.is(config.rules['prefer-destructuring'], undefined); - t.is(config.rules['promise/prefer-await-to-then'], undefined); + t.is(config.rules['prefer-object-spread'], 'off'); + t.is(config.rules['prefer-rest-params'], 'off'); + t.is(config.rules['prefer-destructuring'], 'off'); + t.is(config.rules['promise/prefer-await-to-then'], 'off'); + t.is(config.rules['unicorn/prefer-flat-map'], 'off'); + t.is(config.rules['node/prefer-promises/dns'], 'off'); + t.is(config.rules['node/prefer-promises/fs'], 'off'); }); test('buildConfig: nodeVersion: false', t => { const config = manager.buildConfig({nodeVersion: false}); - // Do not include any Node.js version specific rules - t.is(config.rules['prefer-spread'], undefined); - t.is(config.rules['prefer-rest-params'], undefined); - t.is(config.rules['prefer-destructuring'], undefined); - t.is(config.rules['promise/prefer-await-to-then'], undefined); + // Override all the rules specific to Node.js version + t.is(config.rules['prefer-object-spread'], 'off'); + t.is(config.rules['prefer-rest-params'], 'off'); + t.is(config.rules['prefer-destructuring'], 'off'); + t.is(config.rules['promise/prefer-await-to-then'], 'off'); + t.is(config.rules['unicorn/prefer-flat-map'], 'off'); + t.is(config.rules['node/prefer-promises/dns'], 'off'); + t.is(config.rules['node/prefer-promises/fs'], 'off'); }); test('buildConfig: nodeVersion: invalid range', t => { const config = manager.buildConfig({nodeVersion: '4'}); - // Do not include any Node.js version specific rules - t.is(config.rules['prefer-spread'], undefined); - t.is(config.rules['prefer-rest-params'], undefined); - t.is(config.rules['prefer-destructuring'], undefined); - t.is(config.rules['promise/prefer-await-to-then'], undefined); + // Override all the rules specific to Node.js version + t.is(config.rules['prefer-object-spread'], 'off'); + t.is(config.rules['prefer-rest-params'], 'off'); + t.is(config.rules['prefer-destructuring'], 'off'); + t.is(config.rules['promise/prefer-await-to-then'], 'off'); + t.is(config.rules['unicorn/prefer-flat-map'], 'off'); + t.is(config.rules['node/prefer-promises/dns'], 'off'); + t.is(config.rules['node/prefer-promises/fs'], 'off'); }); -// TODO: We need a new fixture. Help welcome. -test.failing('buildConfig: nodeVersion: >=8', t => { +test('buildConfig: nodeVersion: >=6', t => { + const config = manager.buildConfig({nodeVersion: '>=6'}); + + // Turn off rule if we support Node.js below 7.6.0 + t.is(config.rules['promise/prefer-await-to-then'], 'off'); +}); + +test('buildConfig: nodeVersion: >=8', t => { const config = manager.buildConfig({nodeVersion: '>=8'}); - // Include rules for Node.js 8 and above - t.is(config.rules['promise/prefer-await-to-then'], 'error'); + // Do not turn off rule if we support only Node.js above 7.6.0 + t.is(config.rules['promise/prefer-await-to-then'], undefined); }); test('mergeWithPrettierConfig: use `singleQuote`, `trailingComma`, `bracketSpacing` and `jsxBracketSameLine` from `prettier` config if defined', t => { @@ -326,7 +341,7 @@ test('mergeWithPrettierConfig: throw error is `space`/`tabWidth` conflicts', t = test('buildConfig: rules', t => { const rules = {'object-curly-spacing': ['error', 'always']}; - const config = manager.buildConfig({rules}); + const config = manager.buildConfig({rules, nodeVersion: '12'}); t.deepEqual(config.rules, rules); });