From 90c1a5cc519309ef6f926ee836e328770a2a09ae Mon Sep 17 00:00:00 2001 From: Eric Cornelissen Date: Sun, 15 Dec 2024 18:36:00 +0100 Subject: [PATCH] Configure ESLint's own rules (#1815) Update the ESLint config to enable and configure most of its own rules. This iteration on the configuration is mostly focussed on enabling rules the code base already complies with or can trivially comply with to ensure consistency going forward. Any rule that has been disabled may be reconsidered in the future. Note that rules marked as "deprecated" by ESLint were not included here. Notably, the `prefer-object-has-own` rule was disabled because it is not compatible with Node v14.18.0, which is still supported by this package. This also re-enables the rule `ava/prefer-t-regex` disabled earlier because of incompatibility in 56004b9b8b4f5ebc86a7d010cbc528877efb70ce. --- config/eslint.js | 245 +++++++++++++++++- package-lock.json | 8 +- package.json | 2 +- script/release/bump-changelog.js | 12 +- test/_arbitraries.js | 8 +- test/_runners.js | 10 +- test/breakage/index.test.js | 7 +- test/breakage/testing.test.js | 14 +- test/compat/testing.test.js | 1 + .../constructor/constructor.test.js | 5 +- test/integration/testing/commonjs.test.js | 2 + test/unit/_macros.js | 2 +- test/unit/options/parse-options.test.js | 2 +- .../unit/reflection/checked-to-string.test.js | 2 + test/unit/unix/shells.test.js | 6 +- 15 files changed, 287 insertions(+), 39 deletions(-) diff --git a/config/eslint.js b/config/eslint.js index 72c13e963..53844b55f 100644 --- a/config/eslint.js +++ b/config/eslint.js @@ -16,6 +16,10 @@ export default [ languageOptions: { ecmaVersion: "latest", sourceType: "module", + globals: { + console: "readonly", + URL: "readonly", + }, }, }, { @@ -35,7 +39,7 @@ export default [ // https://github.com/gajus/eslint-plugin-jsdoc#readme "jsdoc/check-access": ["off"], "jsdoc/check-alignment": ["error"], - "jsdoc/check-examples": ["off"], // TODO: enable & configure when possible + "jsdoc/check-examples": ["off"], "jsdoc/check-indentation": [ "error", { @@ -270,23 +274,23 @@ export default [ tags: [ "example", - // file + // File "overview", "module", "version", "license", - // function + // Function "param", "returns", "throws", "since", - // constant + // Constant "constant", "type", - // misc + // Miscellaneous "deprecated", ], }, @@ -536,6 +540,220 @@ export default [ files: ["**/*.js"], plugins: { unicorn }, rules: { + "accessor-pairs": ["error"], + "array-callback-return": ["error"], + "arrow-body-style": ["error"], + "block-scoped-var": ["error"], + camelcase: ["error"], + "capitalized-comments": [ + "error", + "always", + { + ignoreConsecutiveComments: true, + }, + ], + "class-methods-use-this": ["off"], + complexity: ["error"], + "consistent-return": ["off"], + "consistent-this": ["error"], + "constructor-super": ["error"], + curly: ["error"], + "default-case-last": ["error"], + "default-case": ["off"], + "default-param-last": ["error"], + "dot-notation": ["error"], + eqeqeq: ["error"], + "for-direction": ["error"], + "func-name-matching": ["error"], + "func-names": ["error"], + "func-style": ["off"], + "getter-return": ["error"], + "grouped-accessor-pairs": ["error"], + "guard-for-in": ["error"], + "id-denylist": ["error"], + "id-length": ["error"], + "id-match": ["error"], + "init-declarations": ["off"], + "logical-assignment-operators": ["error"], + "max-classes-per-file": ["off"], + "max-depth": ["error"], + "max-lines": ["off"], + "max-lines-per-function": ["off"], + "max-nested-callbacks": ["error"], + "max-params": ["error"], + "max-statements": ["off"], + "new-cap": ["error"], + "no-alert": ["error"], + "no-array-constructor": ["error"], + "no-async-promise-executor": ["error"], + "no-await-in-loop": ["error"], + "no-bitwise": ["error"], + "no-caller": ["error"], + "no-case-declarations": ["error"], + "no-class-assign": ["error"], + "no-compare-neg-zero": ["error"], + "no-cond-assign": ["error"], + "no-console": ["error"], + "no-constant-binary-expression": ["error"], + "no-constant-condition": ["error"], + "no-const-assign": ["error"], + "no-constructor-return": ["error"], + "no-continue": ["off"], + "no-control-regex": ["off"], + "no-debugger": ["error"], + "no-delete-var": ["error"], + "no-div-regex": ["error"], + "no-dupe-args": ["error"], + "no-dupe-class-members": ["error"], + "no-dupe-else-if": ["error"], + "no-dupe-keys": ["error"], + "no-duplicate-case": ["error"], + "no-duplicate-imports": ["error"], + "no-else-return": ["off"], + "no-empty-character-class": ["error"], + "no-empty-function": ["error"], + "no-empty": ["error"], + "no-empty-pattern": ["error"], + "no-empty-static-block": ["error"], + "no-eq-null": ["error"], + "no-eval": ["error"], + "no-ex-assign": ["error"], + "no-extend-native": ["error"], + "no-extra-bind": ["error"], + "no-extra-boolean-cast": ["error"], + "no-extra-label": ["error"], + "no-fallthrough": ["error"], + "no-func-assign": ["error"], + "no-global-assign": ["error"], + "no-implicit-coercion": ["error"], + "no-implicit-globals": ["error"], + "no-implied-eval": ["error"], + "no-import-assign": ["error"], + "no-inline-comments": ["off"], + "no-inner-declarations": ["error"], + "no-invalid-regexp": ["error"], + "no-invalid-this": ["error"], + "no-irregular-whitespace": ["error"], + "no-iterator": ["error"], + "no-labels": ["error"], + "no-label-var": ["error"], + "no-lone-blocks": ["error"], + "no-lonely-if": ["off"], + "no-loop-func": ["error"], + "no-loss-of-precision": ["error"], + "no-magic-numbers": [ + "error", + { + ignore: [0, 1, 0xa0], + }, + ], + "no-misleading-character-class": ["error"], + "no-multi-assign": ["error"], + "no-multi-str": ["error"], + "no-negated-condition": ["error"], + "no-nested-ternary": ["off"], + "no-new-func": ["error"], + "no-new": ["error"], + "no-new-native-nonconstructor": ["error"], + "no-new-wrappers": ["error"], + "no-nonoctal-decimal-escape": ["error"], + "no-obj-calls": ["error"], + "no-object-constructor": ["error"], + "no-octal-escape": ["error"], + "no-octal": ["error"], + "no-param-reassign": ["off"], + "no-plusplus": ["error"], + "no-promise-executor-return": ["error"], + "no-proto": ["error"], + "no-prototype-builtins": ["error"], + "no-redeclare": ["error"], + "no-regex-spaces": ["error"], + "no-restricted-exports": ["error"], + "no-restricted-globals": ["error"], + "no-restricted-imports": ["error"], + "no-restricted-properties": ["error"], + "no-restricted-syntax": ["error"], + "no-return-assign": ["error"], + "no-script-url": ["error"], + "no-self-assign": ["error"], + "no-self-compare": ["error"], + "no-sequences": ["error"], + "no-setter-return": ["error"], + "no-shadow": ["off"], + "no-shadow-restricted-names": ["error"], + "no-sparse-arrays": ["error"], + "no-template-curly-in-string": ["error"], + "no-ternary": ["off"], + "no-this-before-super": ["error"], + "no-throw-literal": ["error"], + "no-undefined": ["off"], + "no-undef-init": ["error"], + "no-undef": ["error"], + "no-underscore-dangle": ["off"], + "no-unexpected-multiline": ["error"], + "no-unmodified-loop-condition": ["error"], + "no-unneeded-ternary": ["off"], + "no-unreachable-loop": ["error"], + "no-unreachable": ["error"], + "no-unsafe-finally": ["error"], + "no-unsafe-negation": ["error"], + "no-unsafe-optional-chaining": ["error"], + "no-unused-expressions": ["error"], + "no-unused-labels": ["error"], + "no-unused-private-class-members": ["error"], + "no-unused-vars": [ + "error", + { + argsIgnorePattern: "_.+", + }, + ], + "no-use-before-define": ["off"], + "no-useless-assignment": ["error"], + "no-useless-backreference": ["error"], + "no-useless-call": ["error"], + "no-useless-catch": ["error"], + "no-useless-computed-key": ["error"], + "no-useless-concat": ["error"], + "no-useless-constructor": ["off"], + "no-useless-escape": ["error"], + "no-useless-rename": ["error"], + "no-useless-return": ["error"], + "no-var": ["error"], + "no-void": ["error"], + "no-warning-comments": ["error"], + "no-with": ["error"], + "object-shorthand": ["error"], + "one-var": ["off"], + "operator-assignment": ["error"], + "prefer-arrow-callback": ["error"], + "prefer-const": ["error"], + "prefer-destructuring": ["off"], + "prefer-exponentiation-operator": ["error"], + "prefer-named-capture-group": ["off"], + "prefer-numeric-literals": ["error"], + "prefer-object-has-own": ["off"], + "prefer-object-spread": ["error"], + "prefer-promise-reject-errors": ["error"], + "prefer-regex-literals": ["error"], + "prefer-rest-params": ["error"], + "prefer-spread": ["error"], + "prefer-template": ["error"], + radix: ["error"], + "require-atomic-updates": ["error"], + "require-await": ["error"], + "require-unicode-regexp": ["error"], + "require-yield": ["error"], + "sort-imports": ["off"], + "sort-keys": ["off"], + "sort-vars": ["off"], + strict: ["error"], + "symbol-description": ["off"], + "unicode-bom": ["error"], + "use-isnan": ["error"], + "valid-typeof": ["error"], + "vars-on-top": ["error"], + yoda: ["error"], + // https://github.com/sindresorhus/eslint-plugin-unicorn#readme "unicorn/better-regex": ["error"], "unicorn/catch-error-name": ["error"], @@ -699,6 +917,16 @@ export default [ files: ["test/**/*.js"], plugins: { ava, jsdoc }, rules: { + "guard-for-in": ["off"], + "id-length": [ + "error", + { + exceptions: ["_", "t"], + }, + ], + "no-magic-numbers": ["off"], + "max-params": ["off"], + // https://github.com/gajus/eslint-plugin-jsdoc#readme "jsdoc/check-values": [ "error", @@ -728,7 +956,7 @@ export default [ "ava/no-unknown-modifiers": ["error"], "ava/prefer-async-await": ["error"], "ava/prefer-power-assert": ["off"], - "ava/prefer-t-regex": ["off"], // TODO: disabled because of incompatibility with ESLint v9 + "ava/prefer-t-regex": ["error"], "ava/test-title": ["error"], "ava/test-title-format": [ "error", @@ -751,6 +979,9 @@ export default [ files: [".github/**/*.js", "script/**/*.js"], plugins: { jsdoc, unicorn }, rules: { + "no-console": ["off"], + "no-magic-numbers": ["off"], + // https://github.com/gajus/eslint-plugin-jsdoc#readme "jsdoc/check-values": [ "error", @@ -769,6 +1000,8 @@ export default [ files: ["config/**/*"], plugins: { jsdoc }, rules: { + "no-magic-numbers": ["off"], + // https://github.com/gajus/eslint-plugin-jsdoc#readme "jsdoc/require-file-overview": ["off"], "jsdoc/require-jsdoc": ["off"], diff --git a/package-lock.json b/package-lock.json index f58c7aa45..7ce83fbdc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "depreman": "0.3.4", "dotenv": "16.3.1", "eslint": "9.14.0", - "eslint-plugin-ava": "15.0.0", + "eslint-plugin-ava": "15.0.1", "eslint-plugin-depend": "0.12.0", "eslint-plugin-jsdoc": "50.4.1", "eslint-plugin-regexp": "2.6.0", @@ -5519,9 +5519,9 @@ } }, "node_modules/eslint-plugin-ava": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-ava/-/eslint-plugin-ava-15.0.0.tgz", - "integrity": "sha512-yqiP+fB98g07a431P8QVRzWSWEpyK+I9qSlmWaRD7UTapcioAJ5rNn2pd5Qm8dzNdq4KlHv8m6OzF6SjjcvRzg==", + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-ava/-/eslint-plugin-ava-15.0.1.tgz", + "integrity": "sha512-eRX7mLFPvalGDWztJ4zm+anez2X6J/88r9CqLFfPAIMvFlGyJ+dUoFppoohgUQZLV09mIBNz5guP07zFJOLF8g==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index ab4068c26..9bef72726 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "depreman": "0.3.4", "dotenv": "16.3.1", "eslint": "9.14.0", - "eslint-plugin-ava": "15.0.0", + "eslint-plugin-ava": "15.0.1", "eslint-plugin-depend": "0.12.0", "eslint-plugin-jsdoc": "50.4.1", "eslint-plugin-regexp": "2.6.0", diff --git a/script/release/bump-changelog.js b/script/release/bump-changelog.js index 33fbd1c0a..d9bd94528 100644 --- a/script/release/bump-changelog.js +++ b/script/release/bump-changelog.js @@ -40,10 +40,12 @@ const month = _month < 10 ? `0${_month}` : _month; const _day = date.getDate(); const day = _day < 10 ? `0${_day}` : _day; -const updatedChangelog = - changelog.slice(0, unreleasedTitleIndex + STR_UNRELEASED.length) + - `\n\n${STR_NO_CHANGES}` + - `\n\n## [${version}] - ${year}-${month}-${day}` + - changelog.slice(unreleasedTitleIndex + STR_UNRELEASED.length); +const updatedChangelog = `${changelog.slice(0, unreleasedTitleIndex + STR_UNRELEASED.length)} + +${STR_NO_CHANGES} + +## [${version}] - ${year}-${month}-${day} +${changelog.slice(unreleasedTitleIndex + STR_UNRELEASED.length + 1)} +`; fs.writeFileSync(changelogFile, updatedChangelog); diff --git a/test/_arbitraries.js b/test/_arbitraries.js index 85849cbbb..d6c7c655b 100644 --- a/test/_arbitraries.js +++ b/test/_arbitraries.js @@ -51,7 +51,9 @@ export const process = () => fc .record({ abort: fc.func(fc.constant(undefined)), - allowedNodeEnvironmentFlags: fc.array(fc.string()).map((v) => new Set(v)), + allowedNodeEnvironmentFlags: fc + .array(fc.string()) + .map((flags) => new Set(flags)), arch: fc.constantFrom( "arm", "arm64", @@ -233,7 +235,7 @@ export const unsupportedUnixShell = () => windowsShell(), fc .stringMatching(/^[0-9A-Za-z]+$/u) - .filter((v) => !constants.shellsUnix.includes(v)), + .filter((shell) => !constants.shellsUnix.includes(shell)), ); /** @@ -247,7 +249,7 @@ export const unsupportedWindowsShell = () => unixShell(), fc .stringMatching(/^[0-9A-Za-z]+\.exe$/u) - .filter((v) => !constants.shellsWindows.includes(v)), + .filter((shell) => !constants.shellsWindows.includes(shell)), ); /** diff --git a/test/_runners.js b/test/_runners.js index 98ae5d300..544ffe531 100644 --- a/test/_runners.js +++ b/test/_runners.js @@ -111,7 +111,7 @@ export function execQuote({ arg, shell }) { execOptions, (error, stdout) => { if (error) { - reject(`an unexpected error occurred: ${error}`); + reject(error); } else { const result = stdout; const expected = getExpectedOutput(arg, shescapeOptions); @@ -187,7 +187,7 @@ export function execEscape({ arg, shell }) { execOptions, (error, stdout) => { if (error) { - reject(`an unexpected error occurred: ${error}`); + reject(error); } else { const result = stdout; const expected = getExpectedOutput(arg, shescapeOptions, true); @@ -267,7 +267,7 @@ export function execFile({ arg, shell }) { execFileOptions, (error, stdout) => { if (error) { - reject(`an unexpected error occurred: ${error}`); + reject(error); } else { const result = stdout; const expected = getExpectedOutput(arg, shescapeOptions); @@ -343,7 +343,7 @@ export function fork(arg) { const echo = cp.fork(constants.echoScript, [safeArg], forkOptions); echo.on("error", (error) => { - reject(`an unexpected error occurred: ${error}`); + reject(error); }); echo.stdout.on("data", (data) => { @@ -389,7 +389,7 @@ export function spawn({ arg, shell }) { ); child.on("error", (error) => { - reject(`an unexpected error occurred: ${error}`); + reject(error); }); child.stdout.on("data", (data) => { diff --git a/test/breakage/index.test.js b/test/breakage/index.test.js index 6e333c4b5..7721fa57f 100644 --- a/test/breakage/index.test.js +++ b/test/breakage/index.test.js @@ -15,17 +15,18 @@ testProp( "Shescape#constructor", [arbitrary.shescapeOptions()], (t, options) => { - let shescape, previouscape; let errored, previousErrored; try { - shescape = new Shescape(options); + // eslint-disable-next-line no-new + new Shescape(options); } catch { errored = true; } try { - previouscape = new Previouscape(options); + // eslint-disable-next-line no-new + new Previouscape(options); } catch { previousErrored = true; } diff --git a/test/breakage/testing.test.js b/test/breakage/testing.test.js index 76ec9ea08..daa647be7 100644 --- a/test/breakage/testing.test.js +++ b/test/breakage/testing.test.js @@ -18,17 +18,18 @@ testProp( "Stubscape#constructor", [arbitrary.shescapeOptions()], (t, options) => { - let stubscape, previoustub; let errored, previousErrored; try { - stubscape = new Stubscape(options); + // eslint-disable-next-line no-new + new Stubscape(options); } catch { errored = true; } try { - previoustub = new Previoustub(options); + // eslint-disable-next-line no-new + new Previoustub(options); } catch { previousErrored = true; } @@ -155,17 +156,18 @@ testProp( "Throwscape#constructor", [arbitrary.shescapeOptions()], (t, options) => { - let throwscape, previousthrow; let errored, previousErrored; try { - throwscape = new Throwscape(options); + // eslint-disable-next-line no-new + new Throwscape(options); } catch { errored = true; } try { - previousthrow = new Previousthrow(options); + // eslint-disable-next-line no-new + new Previousthrow(options); } catch { previousErrored = true; } diff --git a/test/compat/testing.test.js b/test/compat/testing.test.js index 7086f1185..7660d6e11 100644 --- a/test/compat/testing.test.js +++ b/test/compat/testing.test.js @@ -118,6 +118,7 @@ export function testThrowscapeConstructor() { fc.assert( fc.property(arbitrary.shescapeOptions(), (options) => { try { + // eslint-disable-next-line no-new new Throwscape(options); } catch (error) { const knownErrors = ["Can't be instantiated"]; diff --git a/test/integration/constructor/constructor.test.js b/test/integration/constructor/constructor.test.js index 86ce4e517..eaec622e2 100644 --- a/test/integration/constructor/constructor.test.js +++ b/test/integration/constructor/constructor.test.js @@ -33,8 +33,11 @@ testProp( ], (t, options) => { try { + // eslint-disable-next-line no-new new Shescape(options); - } catch {} + } catch { + // Not concerned about functional correctness + } ppTestKit.check(options); t.pass(); diff --git a/test/integration/testing/commonjs.test.js b/test/integration/testing/commonjs.test.js index c0f7739a5..ddca30ab7 100644 --- a/test/integration/testing/commonjs.test.js +++ b/test/integration/testing/commonjs.test.js @@ -214,12 +214,14 @@ testProp( let erroredEsm, erroredCjs; try { + // eslint-disable-next-line no-new new Throwscape(options); } catch { erroredEsm = true; } try { + // eslint-disable-next-line no-new new ThrowscapeCjs(options); } catch { erroredCjs = true; diff --git a/test/unit/_macros.js b/test/unit/_macros.js index 7f83a9c43..d3ec79dc3 100644 --- a/test/unit/_macros.js +++ b/test/unit/_macros.js @@ -117,7 +117,7 @@ export const duration = test.macro({ try { fn(...args); } catch { - // not concerned about functional correctness + // Not concerned about functional correctness } const endTime = performance.now(); diff --git a/test/unit/options/parse-options.test.js b/test/unit/options/parse-options.test.js index e75951acb..272e93428 100644 --- a/test/unit/options/parse-options.test.js +++ b/test/unit/options/parse-options.test.js @@ -17,7 +17,7 @@ const arbitraryInput = () => fc .tuple(arbitrary.shescapeOptions(), arbitrary.env()) .map(([options, env]) => { - options = options || {}; + options ||= {}; return { env, options }; }); diff --git a/test/unit/reflection/checked-to-string.test.js b/test/unit/reflection/checked-to-string.test.js index 2b3d24647..cb1de17d2 100644 --- a/test/unit/reflection/checked-to-string.test.js +++ b/test/unit/reflection/checked-to-string.test.js @@ -1,3 +1,5 @@ +/* eslint-disable no-extend-native */ + /** * @overview Contains unit tests for the `checkedToString` function. * @license MIT diff --git a/test/unit/unix/shells.test.js b/test/unit/unix/shells.test.js index 682af9fa3..8903d3de3 100644 --- a/test/unit/unix/shells.test.js +++ b/test/unit/unix/shells.test.js @@ -50,9 +50,9 @@ for (const [shellName, shellExports] of Object.entries(shells)) { setup: shellExports.getEscapeFunction, }); - for (const i in redosFixtures) { - const input = redosFixtures[i]; - test(`${shellName}, ReDoS #${i}`, (t) => { + for (const index in redosFixtures) { + const input = redosFixtures[index]; + test(`${shellName}, ReDoS #${index}`, (t) => { const escape = shellExports.getEscapeFunction(); escape(input); t.pass();