From f6506fe3d261094041b40a4c21dc2f44d0dab0b2 Mon Sep 17 00:00:00 2001 From: Luca <61409445+melusc@users.noreply.github.com> Date: Sat, 20 Jul 2024 14:38:22 +0200 Subject: [PATCH 1/4] fix: Don't override `n/no-unsupported-features/*` if user has customised them --- lib/options-manager.js | 6 ++--- test/cli.js | 23 +++++++++++++++++++ .../index.js | 1 + .../overrides.js | 1 + .../package.json | 20 ++++++++++++++++ 5 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 test/fixtures/disable-n-no-unsupported-features/index.js create mode 100644 test/fixtures/disable-n-no-unsupported-features/overrides.js create mode 100644 test/fixtures/disable-n-no-unsupported-features/package.json diff --git a/lib/options-manager.js b/lib/options-manager.js index 7d7b1573..67a1f5ba 100644 --- a/lib/options-manager.js +++ b/lib/options-manager.js @@ -354,9 +354,9 @@ const buildXOConfig = options => config => { } if (options.nodeVersion) { - config.baseConfig.rules['n/no-unsupported-features/es-builtins'] = ['error', {version: options.nodeVersion}]; - config.baseConfig.rules['n/no-unsupported-features/es-syntax'] = ['error', {version: options.nodeVersion, ignores: ['modules']}]; - config.baseConfig.rules['n/no-unsupported-features/node-builtins'] = ['error', {version: options.nodeVersion}]; + config.baseConfig.rules['n/no-unsupported-features/es-builtins'] ??= ['error', {version: options.nodeVersion}]; + config.baseConfig.rules['n/no-unsupported-features/es-syntax'] ??= ['error', {version: options.nodeVersion, ignores: ['modules']}]; + config.baseConfig.rules['n/no-unsupported-features/node-builtins'] ??= ['error', {version: options.nodeVersion}]; } if (options.space && !options.prettier) { diff --git a/test/cli.js b/test/cli.js index 43db20d4..7bc51273 100644 --- a/test/cli.js +++ b/test/cli.js @@ -197,3 +197,26 @@ test('print-config flag without filename', async t => { ); t.is(error.stderr.trim(), 'The `--print-config` flag must be used with exactly one filename'); }); + +test('Disable n/no-unsupported-features/*', async t => { + const cwd = path.join(__dirname, 'fixtures/disable-n-no-unsupported-features'); + const {stdout} = await main(['--print-config', 'index.js'], {cwd}); + const config = JSON.parse(stdout); + t.like(config, { + rules: { + 'n/no-unsupported-features/es-builtins': ['off'], + 'n/no-unsupported-features/es-syntax': ['off'], + 'n/no-unsupported-features/node-builtins': ['off'], + }, + }); + + const {stdout: stdoutOverrides} = await main(['--print-config', 'overrides.js'], {cwd}); + const configOverrides = JSON.parse(stdoutOverrides); + t.like(configOverrides, { + rules: { + 'n/no-unsupported-features/es-builtins': ['off'], + 'n/no-unsupported-features/es-syntax': ['off'], + 'n/no-unsupported-features/node-builtins': ['error'], + }, + }); +}); diff --git a/test/fixtures/disable-n-no-unsupported-features/index.js b/test/fixtures/disable-n-no-unsupported-features/index.js new file mode 100644 index 00000000..9ff9f503 --- /dev/null +++ b/test/fixtures/disable-n-no-unsupported-features/index.js @@ -0,0 +1 @@ +const _ = new FormData(); diff --git a/test/fixtures/disable-n-no-unsupported-features/overrides.js b/test/fixtures/disable-n-no-unsupported-features/overrides.js new file mode 100644 index 00000000..9ff9f503 --- /dev/null +++ b/test/fixtures/disable-n-no-unsupported-features/overrides.js @@ -0,0 +1 @@ +const _ = new FormData(); diff --git a/test/fixtures/disable-n-no-unsupported-features/package.json b/test/fixtures/disable-n-no-unsupported-features/package.json new file mode 100644 index 00000000..264624c8 --- /dev/null +++ b/test/fixtures/disable-n-no-unsupported-features/package.json @@ -0,0 +1,20 @@ +{ + "xo": { + "rules": { + "n/no-unsupported-features/es-builtins": "off", + "n/no-unsupported-features/es-syntax": "off", + "n/no-unsupported-features/node-builtins": "off" + }, + "overrides": [ + { + "files": "overrides.js", + "rules": { + "n/no-unsupported-features/node-builtins": "error" + } + } + ] + }, + "engines": { + "node": ">=18" + } +} From 1c4356950f93df97b81a0567dd5ec37972bc545a Mon Sep 17 00:00:00 2001 From: Luca <61409445+melusc@users.noreply.github.com> Date: Sat, 20 Jul 2024 15:09:45 +0200 Subject: [PATCH 2/4] Assert it doesn't throw on `index.js`, but does still throw on `overrides.js` --- test/cli.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/cli.js b/test/cli.js index 7bc51273..0e4044f3 100644 --- a/test/cli.js +++ b/test/cli.js @@ -219,4 +219,7 @@ test('Disable n/no-unsupported-features/*', async t => { 'n/no-unsupported-features/node-builtins': ['error'], }, }); + + await t.notThrowsAsync(() => main(['index.js'], {cwd})); + await t.throwsAsync(() => main(['overrides.js'], {cwd})); }); From 8bdc3b34a8aa7aff3ded204cd6514dafb5b7c6aa Mon Sep 17 00:00:00 2001 From: Luca <61409445+melusc@users.noreply.github.com> Date: Mon, 22 Jul 2024 14:45:43 +0200 Subject: [PATCH 3/4] Rename test to be more general --- test/cli.js | 4 ++-- .../index.js | 0 .../overrides.js | 0 .../package.json | 0 4 files changed, 2 insertions(+), 2 deletions(-) rename test/fixtures/{disable-n-no-unsupported-features => no-override-user-config}/index.js (100%) rename test/fixtures/{disable-n-no-unsupported-features => no-override-user-config}/overrides.js (100%) rename test/fixtures/{disable-n-no-unsupported-features => no-override-user-config}/package.json (100%) diff --git a/test/cli.js b/test/cli.js index 0e4044f3..0cbbedf1 100644 --- a/test/cli.js +++ b/test/cli.js @@ -198,8 +198,8 @@ test('print-config flag without filename', async t => { t.is(error.stderr.trim(), 'The `--print-config` flag must be used with exactly one filename'); }); -test('Disable n/no-unsupported-features/*', async t => { - const cwd = path.join(__dirname, 'fixtures/disable-n-no-unsupported-features'); +test('Do not override user-config', async t => { + const cwd = path.join(__dirname, 'fixtures/no-override-user-config'); const {stdout} = await main(['--print-config', 'index.js'], {cwd}); const config = JSON.parse(stdout); t.like(config, { diff --git a/test/fixtures/disable-n-no-unsupported-features/index.js b/test/fixtures/no-override-user-config/index.js similarity index 100% rename from test/fixtures/disable-n-no-unsupported-features/index.js rename to test/fixtures/no-override-user-config/index.js diff --git a/test/fixtures/disable-n-no-unsupported-features/overrides.js b/test/fixtures/no-override-user-config/overrides.js similarity index 100% rename from test/fixtures/disable-n-no-unsupported-features/overrides.js rename to test/fixtures/no-override-user-config/overrides.js diff --git a/test/fixtures/disable-n-no-unsupported-features/package.json b/test/fixtures/no-override-user-config/package.json similarity index 100% rename from test/fixtures/disable-n-no-unsupported-features/package.json rename to test/fixtures/no-override-user-config/package.json From 752e01a724c77521013887c6d60f63943fa24ec0 Mon Sep 17 00:00:00 2001 From: Luca <61409445+melusc@users.noreply.github.com> Date: Mon, 22 Jul 2024 14:48:01 +0200 Subject: [PATCH 4/4] Use nullish assignment for all rules to respect user's config --- lib/options-manager.js | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/options-manager.js b/lib/options-manager.js index 67a1f5ba..424a2d21 100644 --- a/lib/options-manager.js +++ b/lib/options-manager.js @@ -348,7 +348,7 @@ const buildXOConfig = options => config => { 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.baseConfig.rules[rule] = ruleConfig[minVersion]; + config.baseConfig.rules[rule] ??= ruleConfig[minVersion]; } } } @@ -361,45 +361,45 @@ const buildXOConfig = options => config => { if (options.space && !options.prettier) { if (options.ts) { - config.baseConfig.rules['@typescript-eslint/indent'] = ['error', spaces, {SwitchCase: 1}]; + config.baseConfig.rules['@typescript-eslint/indent'] ??= ['error', spaces, {SwitchCase: 1}]; } else { - config.baseConfig.rules.indent = ['error', spaces, {SwitchCase: 1}]; + config.baseConfig.rules.indent ??= ['error', spaces, {SwitchCase: 1}]; } // Only apply if the user has the React plugin if (options.cwd && resolveFrom.silent('eslint-plugin-react', options.cwd)) { config.baseConfig.plugins.push('react'); - config.baseConfig.rules['react/jsx-indent-props'] = ['error', spaces]; - config.baseConfig.rules['react/jsx-indent'] = ['error', spaces]; + config.baseConfig.rules['react/jsx-indent-props'] ??= ['error', spaces]; + config.baseConfig.rules['react/jsx-indent'] ??= ['error', spaces]; } } if (options.semicolon === false && !options.prettier) { if (options.ts) { - config.baseConfig.rules['@typescript-eslint/semi'] = ['error', 'never']; + config.baseConfig.rules['@typescript-eslint/semi'] ??= ['error', 'never']; } else { - config.baseConfig.rules.semi = ['error', 'never']; + config.baseConfig.rules.semi ??= ['error', 'never']; } - config.baseConfig.rules['semi-spacing'] = ['error', { + config.baseConfig.rules['semi-spacing'] ??= ['error', { before: false, after: true, }]; } if (options.ts) { - config.baseConfig.rules['unicorn/import-style'] = 'off'; - config.baseConfig.rules['node/file-extension-in-import'] = 'off'; + config.baseConfig.rules['unicorn/import-style'] ??= 'off'; + config.baseConfig.rules['node/file-extension-in-import'] ??= 'off'; // Disabled because of https://github.com/benmosher/eslint-plugin-import/issues/1590 - config.baseConfig.rules['import/export'] = 'off'; + config.baseConfig.rules['import/export'] ??= 'off'; // Does not work when the TS definition exports a default const. - config.baseConfig.rules['import/default'] = 'off'; + config.baseConfig.rules['import/default'] ??= 'off'; // Disabled as it doesn't work with TypeScript. // This issue and some others: https://github.com/benmosher/eslint-plugin-import/issues/1341 - config.baseConfig.rules['import/named'] = 'off'; + config.baseConfig.rules['import/named'] ??= 'off'; } config.baseConfig.settings['import/resolver'] = gatherImportResolvers(options); @@ -444,7 +444,7 @@ const buildPrettierConfig = (options, prettierConfig) => config => { config.baseConfig.extends.push('plugin:prettier/recommended'); // The `prettier/prettier` rule reports errors if the code is not formatted in accordance to Prettier - config.baseConfig.rules['prettier/prettier'] = ['error', mergeWithPrettierConfig(options, prettierConfig)]; + config.baseConfig.rules['prettier/prettier'] ??= ['error', mergeWithPrettierConfig(options, prettierConfig)]; } return config;