diff --git a/.eslint-doc-generatorrc.js b/.eslint-doc-generatorrc.js index d4f35daa9a..684d15167d 100644 --- a/.eslint-doc-generatorrc.js +++ b/.eslint-doc-generatorrc.js @@ -20,9 +20,6 @@ const config = { 'requiresTypeChecking', ], urlConfigs: 'https://github.com/sindresorhus/eslint-plugin-unicorn#recommended-config', - // Workaround for https://github.com/bmish/eslint-doc-generator/issues/615 - pathRuleDoc: '../docs/rules/{name}.md', - pathRuleList: '../readme.md', }; export default config; diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fae844b02d..467b0ac796 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,15 +15,15 @@ jobs: fail-fast: false matrix: node-version: - - 23 - - 18 + - 24 + - 20 os: - ubuntu-latest - windows-latest # Even numbers of node-version - # include: - # - os: ubuntu-latest - # node-version: 18 + include: + - os: ubuntu-latest + node-version: 22 steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 @@ -43,7 +43,7 @@ jobs: - uses: actions/setup-node@v4 with: # Locked due to the difference of `zlib.gzipSync()` between Node.js versions - node-version: 22 + node-version: 24 - run: npm install - run: npm run lint - run: npx del-cli test/snapshots --verbose @@ -52,7 +52,7 @@ jobs: env: AVA_FORCE_CI: not-ci - run: git diff --exit-code - - uses: codecov/codecov-action@v4 + - uses: codecov/codecov-action@v5 with: fail_ci_if_error: true disable_search: true @@ -63,8 +63,6 @@ jobs: steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 - with: - node-version: 22 - run: npm install - run: npm run run-rules-on-codebase integration: @@ -88,7 +86,5 @@ jobs: steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 - with: - node-version: 22 - run: npm install - run: npm run integration -- --group ${{ matrix.group }} diff --git a/.markdownlint.json b/.markdownlint.json index f547864a81..7146da4ba8 100644 --- a/.markdownlint.json +++ b/.markdownlint.json @@ -4,5 +4,6 @@ "no-hard-tabs": false, "ul-style": { "style": "dash" - } + }, + "descriptive-link-text": false } diff --git a/docs/rules/consistent-destructuring.md b/docs/rules/consistent-destructuring.md index 1ef07d1a1e..f702b6f5e1 100644 --- a/docs/rules/consistent-destructuring.md +++ b/docs/rules/consistent-destructuring.md @@ -2,7 +2,7 @@ 🚫 This rule is _disabled_ in the βœ… `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#recommended-config). -πŸ”§πŸ’‘ This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix) and manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions). +πŸ’‘ This rule is manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions). diff --git a/docs/rules/error-message.md b/docs/rules/error-message.md index ba76bb79b0..a23a43e5f1 100644 --- a/docs/rules/error-message.md +++ b/docs/rules/error-message.md @@ -7,34 +7,39 @@ This rule enforces a `message` value to be passed in when creating an instance of a built-in [`Error`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) object, which leads to more readable and debuggable code. -## Fail +## Examples ```js -throw Error(); -``` +// ❌ +throw new Error(); -```js -throw Error(''); +// ❌ +throw new Error(''); + +// βœ… +throw new Error('Unexpected property.'); ``` ```js +// ❌ throw new TypeError(); + +// βœ… +throw new TypeError('Array expected.'); ``` ```js +// ❌ const error = new AggregateError(errors); -``` - -## Pass -```js -throw Error('Unexpected property.'); +// βœ… +const error = new AggregateError(errors, 'Promises rejected.'); ``` ```js -throw new TypeError('Array expected.'); -``` +// ❌ +const error = new SuppressedError(error, suppressed); -```js -const error = new AggregateError(errors, 'Promises rejected.'); +// βœ… +const error = new SuppressedError(error, suppressed, 'This is a suppressed error.'); ``` diff --git a/docs/rules/no-array-reverse.md b/docs/rules/no-array-reverse.md new file mode 100644 index 0000000000..d66117dcc5 --- /dev/null +++ b/docs/rules/no-array-reverse.md @@ -0,0 +1,39 @@ +# Prefer `Array#toReversed()` over `Array#reverse()` + +πŸ’Ό This rule is enabled in the βœ… `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#recommended-config). + +πŸ’‘ This rule is manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions). + + + + +Prefer using [`Array#toReversed()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse) over [`Array#reverse()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse). + +## Examples + +```js +// ❌ +const reversed = [...array].reverse(); + +// βœ… +const reversed = [...array].toReversed(); +``` + +## Options + +Type: `object` + +### allowExpressionStatement + +Type: `boolean`\ +Default: `true` + +This rule allow `array.reverse()` as an expression statement by default. +Pass `allowExpressionStatement: false` to forbid `Array#reverse()` even it's an expression statement. + +#### Fail + +```js +// eslint unicorn/no-array-reverse: ["error", {"allowExpressionStatement": true}] +array.reverse(); +``` diff --git a/docs/rules/no-useless-error-capture-stack-trace.md b/docs/rules/no-useless-error-capture-stack-trace.md new file mode 100644 index 0000000000..3c497555f1 --- /dev/null +++ b/docs/rules/no-useless-error-capture-stack-trace.md @@ -0,0 +1,31 @@ +# Disallow unnecessary `Error.captureStackTrace(…)` + +πŸ’Ό This rule is enabled in the βœ… `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#recommended-config). + +πŸ”§ This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). + + + + +Calling [`Error.captureStackTrace(…)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/captureStackTrace) inside the constructor of a built-in `Error` subclass is unnecessary, since the `Error` constructor calls it automatically. + +## Examples + +```js +class MyError extends Error { + constructor() { + // ❌ + Error.captureStackTrace(this, MyError); + // ❌ + Error.captureStackTrace?.(this, MyError); + // ❌ + Error.captureStackTrace(this, this.constructor); + // ❌ + Error.captureStackTrace?.(this, this.constructor); + // ❌ + Error.captureStackTrace(this, new.target); + // ❌ + Error.captureStackTrace?.(this, new.target); + } +} +``` diff --git a/docs/rules/prefer-class-fields.md b/docs/rules/prefer-class-fields.md new file mode 100644 index 0000000000..0e65dd26fc --- /dev/null +++ b/docs/rules/prefer-class-fields.md @@ -0,0 +1,73 @@ +# Prefer class field declarations over `this` assignments in constructors + +πŸ’Ό This rule is enabled in the βœ… `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#recommended-config). + +πŸ”§πŸ’‘ This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix) and manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions). + + + + +Enforces declaring property defaults with class fields instead of setting them inside the constructor. + +> To avoid leaving empty constructors after autofixing, use the [`no-useless-constructor` rule](https://eslint.org/docs/latest/rules/no-useless-constructor). + +## Examples + +```js +// ❌ +class Foo { + constructor() { + this.foo = 'foo'; + } +} + +// βœ… +class Foo { + foo = 'foo'; +} +``` + +```js +// ❌ +class MyError extends Error { + constructor(message: string) { + super(message); + this.name = 'MyError'; + } +} + +// βœ… +class MyError extends Error { + name = 'MyError' +} +``` + +```js +// ❌ +class Foo { + foo = 'foo'; + constructor() { + this.foo = 'bar'; + } +} + +// βœ… +class Foo { + foo = 'bar'; +} +``` + +```js +// ❌ +class Foo { + #foo = 'foo'; + constructor() { + this.#foo = 'bar'; + } +} + +// βœ… +class Foo { + #foo = 'bar'; +} +``` diff --git a/docs/rules/prefer-default-parameters.md b/docs/rules/prefer-default-parameters.md index ae5691f861..4ada93d767 100644 --- a/docs/rules/prefer-default-parameters.md +++ b/docs/rules/prefer-default-parameters.md @@ -2,7 +2,7 @@ πŸ’Ό This rule is enabled in the βœ… `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#recommended-config). -πŸ”§πŸ’‘ This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix) and manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions). +πŸ’‘ This rule is manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions). diff --git a/docs/rules/require-module-specifiers.md b/docs/rules/require-module-specifiers.md new file mode 100644 index 0000000000..4fa686d4b9 --- /dev/null +++ b/docs/rules/require-module-specifiers.md @@ -0,0 +1,41 @@ +# Require non-empty specifier list in import and export statements + +πŸ’Ό This rule is enabled in the βœ… `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#recommended-config). + +πŸ”§πŸ’‘ This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix) and manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions). + + + + +Enforce non-empty specifier list in `import` and `export` statements. Use a [side-effect import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#import_a_module_for_its_side_effects_only) if needed, or remove the statement. + +## Examples + +```js +// ❌ +import {} from 'foo'; + +// βœ… +import 'foo'; +``` + +```js +// ❌ +import foo, {} from 'foo'; + +// βœ… +import foo from 'foo'; +``` + +```js +// ❌ +export {} from 'foo'; + +// βœ… +import 'foo'; +``` + +```js +// ❌ +export {} +``` diff --git a/eslint.config.js b/eslint.config.js index f6dd7f3e83..8274290566 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -21,9 +21,8 @@ const config = [ 'coverage', '.cache-eslint-remote-tester', 'eslint-remote-tester-results', - 'rules/utils/lodash.js', 'test/integration/{fixtures,fixtures-local}/**', - 'workaround-for-eslint-doc-generator', + '**/*.ts', ], }, { @@ -42,6 +41,7 @@ const config = [ 'import/order': 'off', 'func-names': 'off', '@stylistic/function-paren-newline': 'off', + '@stylistic/curly-newline': 'off', }, }, { @@ -66,6 +66,9 @@ const config = [ }, }, { + files: [ + '**/*.js', + ], plugins: { jsdoc, }, diff --git a/eslint.dogfooding.config.js b/eslint.dogfooding.config.js index d25356b52a..901ac6a275 100644 --- a/eslint.dogfooding.config.js +++ b/eslint.dogfooding.config.js @@ -11,6 +11,7 @@ const config = [ 'n/no-unsupported-features/es-syntax', 'eslint-plugin/require-meta-default-options', 'internal/no-restricted-property-access', + '@stylistic/max-len', ]), { linterOptions: { @@ -22,7 +23,6 @@ const config = [ 'coverage', 'test/integration/fixtures', 'test/integration/fixtures-local', - 'rules/utils/lodash.js', ], }, { diff --git a/index.js b/index.js index 61f4f5a12d..fb612c0429 100644 --- a/index.js +++ b/index.js @@ -1,8 +1,11 @@ import createDeprecatedRules from './rules/utils/create-deprecated-rules.js'; import flatConfigBase from './configs/flat-config-base.js'; -import rules from './rules/index.js'; +import * as rawRules from './rules/index.js'; +import {createRules} from './rules/utils/rule.js'; import packageJson from './package.json' with {type: 'json'}; +const rules = createRules(rawRules); + const deprecatedRules = createDeprecatedRules({ // {ruleId: {message: string, replacedBy: string[]}} 'no-instanceof-array': { @@ -58,7 +61,7 @@ const unicorn = { version: packageJson.version, }, rules: { - ...rules, + ...createRules(rules), ...deprecatedRules, }, }; diff --git a/package.json b/package.json index 47486ab9a8..5b623d6cfb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-unicorn", - "version": "59.0.1", + "version": "60.0.0", "description": "More than 100 powerful ESLint rules", "license": "MIT", "repository": "sindresorhus/eslint-plugin-unicorn", @@ -17,19 +17,18 @@ }, "sideEffects": false, "engines": { - "node": "^18.20.0 || ^20.10.0 || >=21.0.0" + "node": "^20.10.0 || >=21.0.0" }, "scripts": { - "bundle-lodash": "echo export {defaultsDeep, camelCase, kebabCase, snakeCase, upperFirst, lowerFirst} from 'lodash-es'; | npx esbuild --bundle --outfile=rules/utils/lodash.js --format=esm", "create-rule": "node ./scripts/create-rule.js && npm run create-rules-index-file && npm run fix:eslint-docs", "create-rules-index-file": "node ./scripts/create-rules-index-file.js", - "fix": "run-p --continue-on-error fix:*", - "fix:eslint-docs": "eslint-doc-generator workaround-for-eslint-doc-generator", + "fix": "run-p --continue-on-error \"fix:*\"", + "fix:eslint-docs": "eslint-doc-generator", "fix:js": "npm run lint:js -- --fix", "fix:markdown": "npm run lint:markdown -- --fix", "fix:snapshots": "ava --update-snapshots", "integration": "node ./test/integration/test.js", - "lint": "run-p --continue-on-error lint:*", + "lint": "run-p --continue-on-error \"lint:*\"", "lint:eslint-docs": "npm run fix:eslint-docs -- --check", "lint:js": "eslint", "lint:markdown": "markdownlint \"**/*.md\"", @@ -37,7 +36,7 @@ "rename-rule": "node ./scripts/rename-rule.js && npm run create-rules-index-file && npm run fix:eslint-docs", "run-rules-on-codebase": "eslint --config=./eslint.dogfooding.config.js", "smoke": "eslint-remote-tester --config ./test/smoke/eslint-remote-tester.config.js", - "test": "npm-run-all --continue-on-error lint test:*", + "test": "npm-run-all --continue-on-error lint \"test:*\"", "test:js": "c8 ava" }, "files": [ @@ -57,60 +56,60 @@ "xo" ], "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "@eslint-community/eslint-utils": "^4.5.1", - "@eslint/plugin-kit": "^0.2.7", - "ci-info": "^4.2.0", + "@babel/helper-validator-identifier": "^7.27.1", + "@eslint-community/eslint-utils": "^4.7.0", + "@eslint/plugin-kit": "^0.3.3", + "change-case": "^5.4.4", + "ci-info": "^4.3.0", "clean-regexp": "^1.0.0", - "core-js-compat": "^3.41.0", + "core-js-compat": "^3.44.0", "esquery": "^1.6.0", "find-up-simple": "^1.0.1", - "globals": "^16.0.0", + "globals": "^16.3.0", "indent-string": "^5.0.0", "is-builtin-module": "^5.0.0", "jsesc": "^3.1.0", "pluralize": "^8.0.0", "regexp-tree": "^0.1.27", "regjsparser": "^0.12.0", - "semver": "^7.7.1", + "semver": "^7.7.2", "strip-indent": "^4.0.0" }, "devDependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/core": "^7.26.10", - "@babel/eslint-parser": "^7.26.10", - "@eslint/eslintrc": "^3.3.0", + "@babel/code-frame": "^7.27.1", + "@babel/core": "^7.28.0", + "@babel/eslint-parser": "^7.28.0", + "@eslint/eslintrc": "^3.3.1", "@lubien/fixture-beta-package": "^1.0.0-beta.1", - "@typescript-eslint/parser": "^8.26.1", - "ava": "^6.2.0", + "@typescript-eslint/parser": "^8.37.0", + "ava": "^6.4.1", "c8": "^10.1.3", "enquirer": "^2.4.1", - "eslint": "^9.22.0", + "eslint": "^9.29.0", "eslint-ava-rule-tester": "^5.0.1", - "eslint-config-xo": "^0.46.0", - "eslint-doc-generator": "^2.1.2", - "eslint-plugin-eslint-plugin": "^6.4.0", - "eslint-plugin-jsdoc": "^50.6.8", - "eslint-remote-tester": "^4.0.1", - "eslint-remote-tester-repositories": "^2.0.1", - "espree": "^10.3.0", - "listr2": "^8.2.5", + "eslint-config-xo": "^0.47.0", + "eslint-doc-generator": "^2.2.2", + "eslint-plugin-eslint-plugin": "^6.5.0", + "eslint-plugin-jsdoc": "^51.4.1", + "eslint-remote-tester": "^4.0.2", + "eslint-remote-tester-repositories": "^2.0.2", + "espree": "^10.4.0", + "listr2": "^9.0.1", "lodash-es": "^4.17.21", - "markdownlint-cli": "^0.44.0", - "memoize": "^10.1.0", - "nano-spawn": "^0.2.0", - "node-style-text": "^0.0.7", - "npm-package-json-lint": "^8.0.0", - "npm-run-all2": "^7.0.2", + "markdownlint-cli": "^0.45.0", + "nano-spawn": "^1.0.2", + "node-style-text": "^1.0.0", + "npm-package-json-lint": "^9.0.0", + "npm-run-all2": "^8.0.4", "open-editor": "^5.1.0", "outdent": "^0.8.0", "pretty-ms": "^9.2.0", - "typescript": "^5.8.2", - "vue-eslint-parser": "^10.1.1", - "yaml": "^2.7.0" + "typescript": "^5.8.3", + "vue-eslint-parser": "^10.2.0", + "yaml": "^2.8.0" }, "peerDependencies": { - "eslint": ">=9.22.0" + "eslint": ">=9.29.0" }, "ava": { "files": [ diff --git a/readme.md b/readme.md index e59d8ca305..d1f8b686de 100644 --- a/readme.md +++ b/readme.md @@ -60,7 +60,7 @@ export default [ | [catch-error-name](docs/rules/catch-error-name.md) | Enforce a specific parameter name in catch clauses. | βœ… | πŸ”§ | | | [consistent-assert](docs/rules/consistent-assert.md) | Enforce consistent assertion style with `node:assert`. | βœ… | πŸ”§ | | | [consistent-date-clone](docs/rules/consistent-date-clone.md) | Prefer passing `Date` directly to the constructor when cloning. | βœ… | πŸ”§ | | -| [consistent-destructuring](docs/rules/consistent-destructuring.md) | Use destructured variables over properties. | | πŸ”§ | πŸ’‘ | +| [consistent-destructuring](docs/rules/consistent-destructuring.md) | Use destructured variables over properties. | | | πŸ’‘ | | [consistent-empty-array-spread](docs/rules/consistent-empty-array-spread.md) | Prefer consistent types when spreading a ternary in an array literal. | βœ… | πŸ”§ | | | [consistent-existence-index-check](docs/rules/consistent-existence-index-check.md) | Enforce consistent style for element existence checks with `indexOf()`, `lastIndexOf()`, `findIndex()`, and `findLastIndex()`. | βœ… | πŸ”§ | | | [consistent-function-scoping](docs/rules/consistent-function-scoping.md) | Move function definitions to the highest possible scope. | βœ… | | | @@ -80,6 +80,7 @@ export default [ | [no-array-for-each](docs/rules/no-array-for-each.md) | Prefer `for…of` over the `forEach` method. | βœ… | πŸ”§ | πŸ’‘ | | [no-array-method-this-argument](docs/rules/no-array-method-this-argument.md) | Disallow using the `this` argument in array methods. | βœ… | πŸ”§ | πŸ’‘ | | [no-array-reduce](docs/rules/no-array-reduce.md) | Disallow `Array#reduce()` and `Array#reduceRight()`. | βœ… | | | +| [no-array-reverse](docs/rules/no-array-reverse.md) | Prefer `Array#toReversed()` over `Array#reverse()`. | βœ… | | πŸ’‘ | | [no-await-expression-member](docs/rules/no-await-expression-member.md) | Disallow member access from await expression. | βœ… | πŸ”§ | | | [no-await-in-promise-methods](docs/rules/no-await-in-promise-methods.md) | Disallow using `await` in `Promise` method parameters. | βœ… | | πŸ’‘ | | [no-console-spaces](docs/rules/no-console-spaces.md) | Do not use leading/trailing space between `console.log` parameters. | βœ… | πŸ”§ | | @@ -115,6 +116,7 @@ export default [ | [no-unreadable-array-destructuring](docs/rules/no-unreadable-array-destructuring.md) | Disallow unreadable array destructuring. | βœ… | πŸ”§ | | | [no-unreadable-iife](docs/rules/no-unreadable-iife.md) | Disallow unreadable IIFEs. | βœ… | | | | [no-unused-properties](docs/rules/no-unused-properties.md) | Disallow unused object properties. | | | | +| [no-useless-error-capture-stack-trace](docs/rules/no-useless-error-capture-stack-trace.md) | Disallow unnecessary `Error.captureStackTrace(…)`. | βœ… | πŸ”§ | | | [no-useless-fallback-in-spread](docs/rules/no-useless-fallback-in-spread.md) | Disallow useless fallback when spreading in object literals. | βœ… | πŸ”§ | | | [no-useless-length-check](docs/rules/no-useless-length-check.md) | Disallow useless array length check. | βœ… | πŸ”§ | | | [no-useless-promise-resolve-reject](docs/rules/no-useless-promise-resolve-reject.md) | Disallow returning/yielding `Promise.resolve/reject()` in async functions or promise callbacks | βœ… | πŸ”§ | | @@ -132,9 +134,10 @@ export default [ | [prefer-array-some](docs/rules/prefer-array-some.md) | Prefer `.some(…)` over `.filter(…).length` check and `.{find,findLast,findIndex,findLastIndex}(…)`. | βœ… | πŸ”§ | πŸ’‘ | | [prefer-at](docs/rules/prefer-at.md) | Prefer `.at()` method for index access and `String#charAt()`. | βœ… | πŸ”§ | πŸ’‘ | | [prefer-blob-reading-methods](docs/rules/prefer-blob-reading-methods.md) | Prefer `Blob#arrayBuffer()` over `FileReader#readAsArrayBuffer(…)` and `Blob#text()` over `FileReader#readAsText(…)`. | βœ… | | | +| [prefer-class-fields](docs/rules/prefer-class-fields.md) | Prefer class field declarations over `this` assignments in constructors. | βœ… | πŸ”§ | πŸ’‘ | | [prefer-code-point](docs/rules/prefer-code-point.md) | Prefer `String#codePointAt(…)` over `String#charCodeAt(…)` and `String.fromCodePoint(…)` over `String.fromCharCode(…)`. | βœ… | | πŸ’‘ | | [prefer-date-now](docs/rules/prefer-date-now.md) | Prefer `Date.now()` to get the number of milliseconds since the Unix Epoch. | βœ… | πŸ”§ | | -| [prefer-default-parameters](docs/rules/prefer-default-parameters.md) | Prefer default parameters over reassignment. | βœ… | πŸ”§ | πŸ’‘ | +| [prefer-default-parameters](docs/rules/prefer-default-parameters.md) | Prefer default parameters over reassignment. | βœ… | | πŸ’‘ | | [prefer-dom-node-append](docs/rules/prefer-dom-node-append.md) | Prefer `Node#append()` over `Node#appendChild()`. | βœ… | πŸ”§ | | | [prefer-dom-node-dataset](docs/rules/prefer-dom-node-dataset.md) | Prefer using `.dataset` on DOM elements over calling attribute methods. | βœ… | πŸ”§ | | | [prefer-dom-node-remove](docs/rules/prefer-dom-node-remove.md) | Prefer `childNode.remove()` over `parentNode.removeChild(childNode)`. | βœ… | πŸ”§ | πŸ’‘ | @@ -179,6 +182,7 @@ export default [ | [prevent-abbreviations](docs/rules/prevent-abbreviations.md) | Prevent abbreviations. | βœ… | πŸ”§ | | | [relative-url-style](docs/rules/relative-url-style.md) | Enforce consistent relative URL style. | βœ… | πŸ”§ | πŸ’‘ | | [require-array-join-separator](docs/rules/require-array-join-separator.md) | Enforce using the separator argument with `Array#join()`. | βœ… | πŸ”§ | | +| [require-module-specifiers](docs/rules/require-module-specifiers.md) | Require non-empty specifier list in import and export statements. | βœ… | πŸ”§ | πŸ’‘ | | [require-number-to-fixed-digits-argument](docs/rules/require-number-to-fixed-digits-argument.md) | Enforce using the digits argument with `Number#toFixed()`. | βœ… | πŸ”§ | | | [require-post-message-target-origin](docs/rules/require-post-message-target-origin.md) | Enforce using the `targetOrigin` argument with `window.postMessage()`. | | | πŸ’‘ | | [string-content](docs/rules/string-content.md) | Enforce better string content. | | πŸ”§ | πŸ’‘ | diff --git a/rules/ast/call-or-new-expression.js b/rules/ast/call-or-new-expression.js index 26c44ac065..564be72c8d 100644 --- a/rules/ast/call-or-new-expression.js +++ b/rules/ast/call-or-new-expression.js @@ -11,6 +11,7 @@ } | string | string[] } CallOrNewExpressionCheckOptions */ +// eslint-disable-next-line complexity function create(node, options, types) { if (!types.includes(node?.type)) { return false; diff --git a/rules/ast/is-member-expression.js b/rules/ast/is-member-expression.js index b8fb9b3c78..9507cfd13a 100644 --- a/rules/ast/is-member-expression.js +++ b/rules/ast/is-member-expression.js @@ -1,3 +1,4 @@ +/* eslint-disable complexity */ /** @param { { diff --git a/rules/catch-error-name.js b/rules/catch-error-name.js index 032bc193fe..ac82b8af7e 100644 --- a/rules/catch-error-name.js +++ b/rules/catch-error-name.js @@ -1,6 +1,6 @@ import {isRegExp} from 'node:util/types'; import {findVariable} from '@eslint-community/eslint-utils'; -import {getAvailableVariableName} from './utils/index.js'; +import {getAvailableVariableName, upperFirst} from './utils/index.js'; import {renameVariable} from './fix/index.js'; import {isMethodCall} from './ast/index.js'; @@ -47,7 +47,7 @@ const create = context => { name === expectedName || ignore.some(regexp => regexp.test(name)) || name.endsWith(expectedName) - || name.endsWith(expectedName.charAt(0).toUpperCase() + expectedName.slice(1)); + || name.endsWith(upperFirst(expectedName)); return { Identifier(node) { diff --git a/rules/consistent-destructuring.js b/rules/consistent-destructuring.js index 369e7854a7..5b471ae64f 100644 --- a/rules/consistent-destructuring.js +++ b/rules/consistent-destructuring.js @@ -157,7 +157,6 @@ const config = { description: 'Use destructured variables over properties.', recommended: false, }, - fixable: 'code', hasSuggestions: true, messages: { [MESSAGE_ID]: 'Use destructured variables over properties.', diff --git a/rules/custom-error-definition.js b/rules/custom-error-definition.js index f5b29e126e..08e5ad7832 100644 --- a/rules/custom-error-definition.js +++ b/rules/custom-error-definition.js @@ -1,4 +1,4 @@ -import {upperFirst} from './utils/lodash.js'; +import {upperFirst} from './utils/index.js'; const MESSAGE_ID_INVALID_EXPORT = 'invalidExport'; const messages = { diff --git a/rules/error-message.js b/rules/error-message.js index 02c7973de7..dadbdb8f96 100644 --- a/rules/error-message.js +++ b/rules/error-message.js @@ -1,5 +1,4 @@ import {getStaticValue} from '@eslint-community/eslint-utils'; -import isShadowed from './utils/is-shadowed.js'; import {isCallOrNewExpression} from './ast/index.js'; import builtinErrors from './shared/builtin-errors.js'; @@ -12,23 +11,28 @@ const messages = { [MESSAGE_ID_NOT_STRING]: 'Error message should be a string.', }; +const messageArgumentIndexes = new Map([ + ['AggregateError', 1], + ['SuppressedError', 2], +]); + /** @param {import('eslint').Rule.RuleContext} context */ const create = context => { context.on(['CallExpression', 'NewExpression'], expression => { - if (!isCallOrNewExpression(expression, { - names: builtinErrors, - optional: false, - })) { - return; - } - - const scope = context.sourceCode.getScope(expression); - if (isShadowed(scope, expression.callee)) { + if (!( + isCallOrNewExpression(expression, { + names: builtinErrors, + optional: false, + }) + && context.sourceCode.isGlobalReference(expression.callee) + )) { return; } const constructorName = expression.callee.name; - const messageArgumentIndex = constructorName === 'AggregateError' ? 1 : 0; + const messageArgumentIndex = messageArgumentIndexes.has(constructorName) + ? messageArgumentIndexes.get(constructorName) + : 0; const callArguments = expression.arguments; // If message is `SpreadElement` or there is `SpreadElement` before message @@ -54,7 +58,7 @@ const create = context => { }; } - const staticResult = getStaticValue(node, scope); + const staticResult = getStaticValue(node, context.sourceCode.getScope(node)); // We don't know the value of `message` if (!staticResult) { diff --git a/rules/expiring-todo-comments.js b/rules/expiring-todo-comments.js index 7e8a8485d4..11de1c6a2e 100644 --- a/rules/expiring-todo-comments.js +++ b/rules/expiring-todo-comments.js @@ -317,6 +317,7 @@ const create = context => { }); const rules = baseRule.create(fakeContext); + // eslint-disable-next-line complexity function processComment(comment) { if (ignoreRegexes.some(ignore => ignore.test(comment.value))) { return; diff --git a/rules/filename-case.js b/rules/filename-case.js index bd0d6cf513..bc190e16ae 100644 --- a/rules/filename-case.js +++ b/rules/filename-case.js @@ -4,8 +4,8 @@ import { camelCase, kebabCase, snakeCase, - upperFirst, -} from './utils/lodash.js'; + pascalCase, +} from 'change-case'; import cartesianProductSamples from './utils/cartesian-product-samples.js'; const MESSAGE_ID = 'filename-case'; @@ -15,35 +15,10 @@ const messages = { [MESSAGE_ID_EXTENSION]: 'File extension `{{extension}}` is not in lowercase. Rename it to `{{filename}}`.', }; -const pascalCase = string => upperFirst(camelCase(string)); -const numberRegex = /\d+/; -const PLACEHOLDER = '\uFFFF\uFFFF\uFFFF'; -const PLACEHOLDER_REGEX = new RegExp(PLACEHOLDER, 'i'); const isIgnoredChar = char => !/^[a-z\d-_]$/i.test(char); const ignoredByDefault = new Set(['index.js', 'index.mjs', 'index.cjs', 'index.ts', 'index.tsx', 'index.vue']); const isLowerCase = string => string === string.toLowerCase(); -function ignoreNumbers(caseFunction) { - return string => { - const stack = []; - let execResult = numberRegex.exec(string); - - while (execResult) { - stack.push(execResult[0]); - string = string.replace(execResult[0], PLACEHOLDER); - execResult = numberRegex.exec(string); - } - - let withCase = caseFunction(string); - - while (stack.length > 0) { - withCase = withCase.replace(PLACEHOLDER_REGEX, stack.shift()); - } - - return withCase; - }; -} - const cases = { camelCase: { fn: camelCase, @@ -172,7 +147,7 @@ const create = context => { return new RegExp(item, 'u'); }); const multipleFileExtensions = options.multipleFileExtensions !== false; - const chosenCasesFunctions = chosenCases.map(case_ => ignoreNumbers(cases[case_].fn)); + const chosenCasesFunctions = chosenCases.map(case_ => cases[case_].fn); const filenameWithExtension = context.physicalFilename; if (filenameWithExtension === '' || filenameWithExtension === '') { diff --git a/rules/fix/replace-node-or-token-and-spaces-before.js b/rules/fix/replace-node-or-token-and-spaces-before.js index 7508193f13..76dbe8cf07 100644 --- a/rules/fix/replace-node-or-token-and-spaces-before.js +++ b/rules/fix/replace-node-or-token-and-spaces-before.js @@ -1,5 +1,6 @@ import {getParentheses} from '../utils/parentheses.js'; +// eslint-disable-next-line max-params export default function * replaceNodeOrTokenAndSpacesBefore(nodeOrToken, replacement, fixer, sourceCode, tokenStore = sourceCode) { const tokens = getParentheses(nodeOrToken, tokenStore); diff --git a/rules/import-style.js b/rules/import-style.js index 12ede51cdf..934a085e4b 100644 --- a/rules/import-style.js +++ b/rules/import-style.js @@ -1,5 +1,4 @@ import {getStringIfConstant} from '@eslint-community/eslint-utils'; -import {defaultsDeep} from './utils/lodash.js'; import {isCallExpression} from './ast/index.js'; const MESSAGE_ID = 'importStyle'; @@ -141,7 +140,10 @@ const create = context => { ] = context.options; styles = extendDefaultStyles - ? defaultsDeep({}, styles, defaultStyles) + ? Object.fromEntries( + [...Object.keys(defaultStyles), ...Object.keys(styles)] + .map(name => [name, styles[name] === false ? {} : {...defaultStyles[name], ...styles[name]}]), + ) : styles; styles = new Map( @@ -153,6 +155,7 @@ const create = context => { const {sourceCode} = context; + // eslint-disable-next-line max-params const report = (node, moduleName, actualImportStyles, allowedImportStyles, isRequire = false) => { if (!allowedImportStyles || allowedImportStyles.size === 0) { return; diff --git a/rules/index.js b/rules/index.js index df4fe31c36..e6c4278a57 100644 --- a/rules/index.js +++ b/rules/index.js @@ -1,268 +1,136 @@ // Generated file, DO NOT edit -import {createRule} from './utils/rule.js'; -import betterRegex from './better-regex.js'; -import catchErrorName from './catch-error-name.js'; -import consistentAssert from './consistent-assert.js'; -import consistentDateClone from './consistent-date-clone.js'; -import consistentDestructuring from './consistent-destructuring.js'; -import consistentEmptyArraySpread from './consistent-empty-array-spread.js'; -import consistentExistenceIndexCheck from './consistent-existence-index-check.js'; -import consistentFunctionScoping from './consistent-function-scoping.js'; -import customErrorDefinition from './custom-error-definition.js'; -import emptyBraceSpaces from './empty-brace-spaces.js'; -import errorMessage from './error-message.js'; -import escapeCase from './escape-case.js'; -import expiringTodoComments from './expiring-todo-comments.js'; -import explicitLengthCheck from './explicit-length-check.js'; -import filenameCase from './filename-case.js'; -import importStyle from './import-style.js'; -import newForBuiltins from './new-for-builtins.js'; -import noAbusiveEslintDisable from './no-abusive-eslint-disable.js'; -import noAccessorRecursion from './no-accessor-recursion.js'; -import noAnonymousDefaultExport from './no-anonymous-default-export.js'; -import noArrayCallbackReference from './no-array-callback-reference.js'; -import noArrayForEach from './no-array-for-each.js'; -import noArrayMethodThisArgument from './no-array-method-this-argument.js'; -import noArrayReduce from './no-array-reduce.js'; -import noAwaitExpressionMember from './no-await-expression-member.js'; -import noAwaitInPromiseMethods from './no-await-in-promise-methods.js'; -import noConsoleSpaces from './no-console-spaces.js'; -import noDocumentCookie from './no-document-cookie.js'; -import noEmptyFile from './no-empty-file.js'; -import noForLoop from './no-for-loop.js'; -import noHexEscape from './no-hex-escape.js'; -import noInstanceofBuiltins from './no-instanceof-builtins.js'; -import noInvalidFetchOptions from './no-invalid-fetch-options.js'; -import noInvalidRemoveEventListener from './no-invalid-remove-event-listener.js'; -import noKeywordPrefix from './no-keyword-prefix.js'; -import noLonelyIf from './no-lonely-if.js'; -import noMagicArrayFlatDepth from './no-magic-array-flat-depth.js'; -import noNamedDefault from './no-named-default.js'; -import noNegatedCondition from './no-negated-condition.js'; -import noNegationInEqualityCheck from './no-negation-in-equality-check.js'; -import noNestedTernary from './no-nested-ternary.js'; -import noNewArray from './no-new-array.js'; -import noNewBuffer from './no-new-buffer.js'; -import noNull from './no-null.js'; -import noObjectAsDefaultParameter from './no-object-as-default-parameter.js'; -import noProcessExit from './no-process-exit.js'; -import noSinglePromiseInPromiseMethods from './no-single-promise-in-promise-methods.js'; -import noStaticOnlyClass from './no-static-only-class.js'; -import noThenable from './no-thenable.js'; -import noThisAssignment from './no-this-assignment.js'; -import noTypeofUndefined from './no-typeof-undefined.js'; -import noUnnecessaryArrayFlatDepth from './no-unnecessary-array-flat-depth.js'; -import noUnnecessaryArraySpliceCount from './no-unnecessary-array-splice-count.js'; -import noUnnecessaryAwait from './no-unnecessary-await.js'; -import noUnnecessaryPolyfills from './no-unnecessary-polyfills.js'; -import noUnnecessarySliceEnd from './no-unnecessary-slice-end.js'; -import noUnreadableArrayDestructuring from './no-unreadable-array-destructuring.js'; -import noUnreadableIife from './no-unreadable-iife.js'; -import noUnusedProperties from './no-unused-properties.js'; -import noUselessFallbackInSpread from './no-useless-fallback-in-spread.js'; -import noUselessLengthCheck from './no-useless-length-check.js'; -import noUselessPromiseResolveReject from './no-useless-promise-resolve-reject.js'; -import noUselessSpread from './no-useless-spread.js'; -import noUselessSwitchCase from './no-useless-switch-case.js'; -import noUselessUndefined from './no-useless-undefined.js'; -import noZeroFractions from './no-zero-fractions.js'; -import numberLiteralCase from './number-literal-case.js'; -import numericSeparatorsStyle from './numeric-separators-style.js'; -import preferAddEventListener from './prefer-add-event-listener.js'; -import preferArrayFind from './prefer-array-find.js'; -import preferArrayFlatMap from './prefer-array-flat-map.js'; -import preferArrayFlat from './prefer-array-flat.js'; -import preferArrayIndexOf from './prefer-array-index-of.js'; -import preferArraySome from './prefer-array-some.js'; -import preferAt from './prefer-at.js'; -import preferBlobReadingMethods from './prefer-blob-reading-methods.js'; -import preferCodePoint from './prefer-code-point.js'; -import preferDateNow from './prefer-date-now.js'; -import preferDefaultParameters from './prefer-default-parameters.js'; -import preferDomNodeAppend from './prefer-dom-node-append.js'; -import preferDomNodeDataset from './prefer-dom-node-dataset.js'; -import preferDomNodeRemove from './prefer-dom-node-remove.js'; -import preferDomNodeTextContent from './prefer-dom-node-text-content.js'; -import preferEventTarget from './prefer-event-target.js'; -import preferExportFrom from './prefer-export-from.js'; -import preferGlobalThis from './prefer-global-this.js'; -import preferImportMetaProperties from './prefer-import-meta-properties.js'; -import preferIncludes from './prefer-includes.js'; -import preferJsonParseBuffer from './prefer-json-parse-buffer.js'; -import preferKeyboardEventKey from './prefer-keyboard-event-key.js'; -import preferLogicalOperatorOverTernary from './prefer-logical-operator-over-ternary.js'; -import preferMathMinMax from './prefer-math-min-max.js'; -import preferMathTrunc from './prefer-math-trunc.js'; -import preferModernDomApis from './prefer-modern-dom-apis.js'; -import preferModernMathApis from './prefer-modern-math-apis.js'; -import preferModule from './prefer-module.js'; -import preferNativeCoercionFunctions from './prefer-native-coercion-functions.js'; -import preferNegativeIndex from './prefer-negative-index.js'; -import preferNodeProtocol from './prefer-node-protocol.js'; -import preferNumberProperties from './prefer-number-properties.js'; -import preferObjectFromEntries from './prefer-object-from-entries.js'; -import preferOptionalCatchBinding from './prefer-optional-catch-binding.js'; -import preferPrototypeMethods from './prefer-prototype-methods.js'; -import preferQuerySelector from './prefer-query-selector.js'; -import preferReflectApply from './prefer-reflect-apply.js'; -import preferRegexpTest from './prefer-regexp-test.js'; -import preferSetHas from './prefer-set-has.js'; -import preferSetSize from './prefer-set-size.js'; -import preferSingleCall from './prefer-single-call.js'; -import preferSpread from './prefer-spread.js'; -import preferStringRaw from './prefer-string-raw.js'; -import preferStringReplaceAll from './prefer-string-replace-all.js'; -import preferStringSlice from './prefer-string-slice.js'; -import preferStringStartsEndsWith from './prefer-string-starts-ends-with.js'; -import preferStringTrimStartEnd from './prefer-string-trim-start-end.js'; -import preferStructuredClone from './prefer-structured-clone.js'; -import preferSwitch from './prefer-switch.js'; -import preferTernary from './prefer-ternary.js'; -import preferTopLevelAwait from './prefer-top-level-await.js'; -import preferTypeError from './prefer-type-error.js'; -import preventAbbreviations from './prevent-abbreviations.js'; -import relativeUrlStyle from './relative-url-style.js'; -import requireArrayJoinSeparator from './require-array-join-separator.js'; -import requireNumberToFixedDigitsArgument from './require-number-to-fixed-digits-argument.js'; -import requirePostMessageTargetOrigin from './require-post-message-target-origin.js'; -import stringContent from './string-content.js'; -import switchCaseBraces from './switch-case-braces.js'; -import templateIndent from './template-indent.js'; -import textEncodingIdentifierCase from './text-encoding-identifier-case.js'; -import throwNewError from './throw-new-error.js'; - -const rules = { - 'better-regex': createRule(betterRegex, 'better-regex'), - 'catch-error-name': createRule(catchErrorName, 'catch-error-name'), - 'consistent-assert': createRule(consistentAssert, 'consistent-assert'), - 'consistent-date-clone': createRule(consistentDateClone, 'consistent-date-clone'), - 'consistent-destructuring': createRule(consistentDestructuring, 'consistent-destructuring'), - 'consistent-empty-array-spread': createRule(consistentEmptyArraySpread, 'consistent-empty-array-spread'), - 'consistent-existence-index-check': createRule(consistentExistenceIndexCheck, 'consistent-existence-index-check'), - 'consistent-function-scoping': createRule(consistentFunctionScoping, 'consistent-function-scoping'), - 'custom-error-definition': createRule(customErrorDefinition, 'custom-error-definition'), - 'empty-brace-spaces': createRule(emptyBraceSpaces, 'empty-brace-spaces'), - 'error-message': createRule(errorMessage, 'error-message'), - 'escape-case': createRule(escapeCase, 'escape-case'), - 'expiring-todo-comments': createRule(expiringTodoComments, 'expiring-todo-comments'), - 'explicit-length-check': createRule(explicitLengthCheck, 'explicit-length-check'), - 'filename-case': createRule(filenameCase, 'filename-case'), - 'import-style': createRule(importStyle, 'import-style'), - 'new-for-builtins': createRule(newForBuiltins, 'new-for-builtins'), - 'no-abusive-eslint-disable': createRule(noAbusiveEslintDisable, 'no-abusive-eslint-disable'), - 'no-accessor-recursion': createRule(noAccessorRecursion, 'no-accessor-recursion'), - 'no-anonymous-default-export': createRule(noAnonymousDefaultExport, 'no-anonymous-default-export'), - 'no-array-callback-reference': createRule(noArrayCallbackReference, 'no-array-callback-reference'), - 'no-array-for-each': createRule(noArrayForEach, 'no-array-for-each'), - 'no-array-method-this-argument': createRule(noArrayMethodThisArgument, 'no-array-method-this-argument'), - 'no-array-reduce': createRule(noArrayReduce, 'no-array-reduce'), - 'no-await-expression-member': createRule(noAwaitExpressionMember, 'no-await-expression-member'), - 'no-await-in-promise-methods': createRule(noAwaitInPromiseMethods, 'no-await-in-promise-methods'), - 'no-console-spaces': createRule(noConsoleSpaces, 'no-console-spaces'), - 'no-document-cookie': createRule(noDocumentCookie, 'no-document-cookie'), - 'no-empty-file': createRule(noEmptyFile, 'no-empty-file'), - 'no-for-loop': createRule(noForLoop, 'no-for-loop'), - 'no-hex-escape': createRule(noHexEscape, 'no-hex-escape'), - 'no-instanceof-builtins': createRule(noInstanceofBuiltins, 'no-instanceof-builtins'), - 'no-invalid-fetch-options': createRule(noInvalidFetchOptions, 'no-invalid-fetch-options'), - 'no-invalid-remove-event-listener': createRule(noInvalidRemoveEventListener, 'no-invalid-remove-event-listener'), - 'no-keyword-prefix': createRule(noKeywordPrefix, 'no-keyword-prefix'), - 'no-lonely-if': createRule(noLonelyIf, 'no-lonely-if'), - 'no-magic-array-flat-depth': createRule(noMagicArrayFlatDepth, 'no-magic-array-flat-depth'), - 'no-named-default': createRule(noNamedDefault, 'no-named-default'), - 'no-negated-condition': createRule(noNegatedCondition, 'no-negated-condition'), - 'no-negation-in-equality-check': createRule(noNegationInEqualityCheck, 'no-negation-in-equality-check'), - 'no-nested-ternary': createRule(noNestedTernary, 'no-nested-ternary'), - 'no-new-array': createRule(noNewArray, 'no-new-array'), - 'no-new-buffer': createRule(noNewBuffer, 'no-new-buffer'), - 'no-null': createRule(noNull, 'no-null'), - 'no-object-as-default-parameter': createRule(noObjectAsDefaultParameter, 'no-object-as-default-parameter'), - 'no-process-exit': createRule(noProcessExit, 'no-process-exit'), - 'no-single-promise-in-promise-methods': createRule(noSinglePromiseInPromiseMethods, 'no-single-promise-in-promise-methods'), - 'no-static-only-class': createRule(noStaticOnlyClass, 'no-static-only-class'), - 'no-thenable': createRule(noThenable, 'no-thenable'), - 'no-this-assignment': createRule(noThisAssignment, 'no-this-assignment'), - 'no-typeof-undefined': createRule(noTypeofUndefined, 'no-typeof-undefined'), - 'no-unnecessary-array-flat-depth': createRule(noUnnecessaryArrayFlatDepth, 'no-unnecessary-array-flat-depth'), - 'no-unnecessary-array-splice-count': createRule(noUnnecessaryArraySpliceCount, 'no-unnecessary-array-splice-count'), - 'no-unnecessary-await': createRule(noUnnecessaryAwait, 'no-unnecessary-await'), - 'no-unnecessary-polyfills': createRule(noUnnecessaryPolyfills, 'no-unnecessary-polyfills'), - 'no-unnecessary-slice-end': createRule(noUnnecessarySliceEnd, 'no-unnecessary-slice-end'), - 'no-unreadable-array-destructuring': createRule(noUnreadableArrayDestructuring, 'no-unreadable-array-destructuring'), - 'no-unreadable-iife': createRule(noUnreadableIife, 'no-unreadable-iife'), - 'no-unused-properties': createRule(noUnusedProperties, 'no-unused-properties'), - 'no-useless-fallback-in-spread': createRule(noUselessFallbackInSpread, 'no-useless-fallback-in-spread'), - 'no-useless-length-check': createRule(noUselessLengthCheck, 'no-useless-length-check'), - 'no-useless-promise-resolve-reject': createRule(noUselessPromiseResolveReject, 'no-useless-promise-resolve-reject'), - 'no-useless-spread': createRule(noUselessSpread, 'no-useless-spread'), - 'no-useless-switch-case': createRule(noUselessSwitchCase, 'no-useless-switch-case'), - 'no-useless-undefined': createRule(noUselessUndefined, 'no-useless-undefined'), - 'no-zero-fractions': createRule(noZeroFractions, 'no-zero-fractions'), - 'number-literal-case': createRule(numberLiteralCase, 'number-literal-case'), - 'numeric-separators-style': createRule(numericSeparatorsStyle, 'numeric-separators-style'), - 'prefer-add-event-listener': createRule(preferAddEventListener, 'prefer-add-event-listener'), - 'prefer-array-find': createRule(preferArrayFind, 'prefer-array-find'), - 'prefer-array-flat-map': createRule(preferArrayFlatMap, 'prefer-array-flat-map'), - 'prefer-array-flat': createRule(preferArrayFlat, 'prefer-array-flat'), - 'prefer-array-index-of': createRule(preferArrayIndexOf, 'prefer-array-index-of'), - 'prefer-array-some': createRule(preferArraySome, 'prefer-array-some'), - 'prefer-at': createRule(preferAt, 'prefer-at'), - 'prefer-blob-reading-methods': createRule(preferBlobReadingMethods, 'prefer-blob-reading-methods'), - 'prefer-code-point': createRule(preferCodePoint, 'prefer-code-point'), - 'prefer-date-now': createRule(preferDateNow, 'prefer-date-now'), - 'prefer-default-parameters': createRule(preferDefaultParameters, 'prefer-default-parameters'), - 'prefer-dom-node-append': createRule(preferDomNodeAppend, 'prefer-dom-node-append'), - 'prefer-dom-node-dataset': createRule(preferDomNodeDataset, 'prefer-dom-node-dataset'), - 'prefer-dom-node-remove': createRule(preferDomNodeRemove, 'prefer-dom-node-remove'), - 'prefer-dom-node-text-content': createRule(preferDomNodeTextContent, 'prefer-dom-node-text-content'), - 'prefer-event-target': createRule(preferEventTarget, 'prefer-event-target'), - 'prefer-export-from': createRule(preferExportFrom, 'prefer-export-from'), - 'prefer-global-this': createRule(preferGlobalThis, 'prefer-global-this'), - 'prefer-import-meta-properties': createRule(preferImportMetaProperties, 'prefer-import-meta-properties'), - 'prefer-includes': createRule(preferIncludes, 'prefer-includes'), - 'prefer-json-parse-buffer': createRule(preferJsonParseBuffer, 'prefer-json-parse-buffer'), - 'prefer-keyboard-event-key': createRule(preferKeyboardEventKey, 'prefer-keyboard-event-key'), - 'prefer-logical-operator-over-ternary': createRule(preferLogicalOperatorOverTernary, 'prefer-logical-operator-over-ternary'), - 'prefer-math-min-max': createRule(preferMathMinMax, 'prefer-math-min-max'), - 'prefer-math-trunc': createRule(preferMathTrunc, 'prefer-math-trunc'), - 'prefer-modern-dom-apis': createRule(preferModernDomApis, 'prefer-modern-dom-apis'), - 'prefer-modern-math-apis': createRule(preferModernMathApis, 'prefer-modern-math-apis'), - 'prefer-module': createRule(preferModule, 'prefer-module'), - 'prefer-native-coercion-functions': createRule(preferNativeCoercionFunctions, 'prefer-native-coercion-functions'), - 'prefer-negative-index': createRule(preferNegativeIndex, 'prefer-negative-index'), - 'prefer-node-protocol': createRule(preferNodeProtocol, 'prefer-node-protocol'), - 'prefer-number-properties': createRule(preferNumberProperties, 'prefer-number-properties'), - 'prefer-object-from-entries': createRule(preferObjectFromEntries, 'prefer-object-from-entries'), - 'prefer-optional-catch-binding': createRule(preferOptionalCatchBinding, 'prefer-optional-catch-binding'), - 'prefer-prototype-methods': createRule(preferPrototypeMethods, 'prefer-prototype-methods'), - 'prefer-query-selector': createRule(preferQuerySelector, 'prefer-query-selector'), - 'prefer-reflect-apply': createRule(preferReflectApply, 'prefer-reflect-apply'), - 'prefer-regexp-test': createRule(preferRegexpTest, 'prefer-regexp-test'), - 'prefer-set-has': createRule(preferSetHas, 'prefer-set-has'), - 'prefer-set-size': createRule(preferSetSize, 'prefer-set-size'), - 'prefer-single-call': createRule(preferSingleCall, 'prefer-single-call'), - 'prefer-spread': createRule(preferSpread, 'prefer-spread'), - 'prefer-string-raw': createRule(preferStringRaw, 'prefer-string-raw'), - 'prefer-string-replace-all': createRule(preferStringReplaceAll, 'prefer-string-replace-all'), - 'prefer-string-slice': createRule(preferStringSlice, 'prefer-string-slice'), - 'prefer-string-starts-ends-with': createRule(preferStringStartsEndsWith, 'prefer-string-starts-ends-with'), - 'prefer-string-trim-start-end': createRule(preferStringTrimStartEnd, 'prefer-string-trim-start-end'), - 'prefer-structured-clone': createRule(preferStructuredClone, 'prefer-structured-clone'), - 'prefer-switch': createRule(preferSwitch, 'prefer-switch'), - 'prefer-ternary': createRule(preferTernary, 'prefer-ternary'), - 'prefer-top-level-await': createRule(preferTopLevelAwait, 'prefer-top-level-await'), - 'prefer-type-error': createRule(preferTypeError, 'prefer-type-error'), - 'prevent-abbreviations': createRule(preventAbbreviations, 'prevent-abbreviations'), - 'relative-url-style': createRule(relativeUrlStyle, 'relative-url-style'), - 'require-array-join-separator': createRule(requireArrayJoinSeparator, 'require-array-join-separator'), - 'require-number-to-fixed-digits-argument': createRule(requireNumberToFixedDigitsArgument, 'require-number-to-fixed-digits-argument'), - 'require-post-message-target-origin': createRule(requirePostMessageTargetOrigin, 'require-post-message-target-origin'), - 'string-content': createRule(stringContent, 'string-content'), - 'switch-case-braces': createRule(switchCaseBraces, 'switch-case-braces'), - 'template-indent': createRule(templateIndent, 'template-indent'), - 'text-encoding-identifier-case': createRule(textEncodingIdentifierCase, 'text-encoding-identifier-case'), - 'throw-new-error': createRule(throwNewError, 'throw-new-error'), -}; - -export default rules; +export {default as 'better-regex'} from './better-regex.js'; +export {default as 'catch-error-name'} from './catch-error-name.js'; +export {default as 'consistent-assert'} from './consistent-assert.js'; +export {default as 'consistent-date-clone'} from './consistent-date-clone.js'; +export {default as 'consistent-destructuring'} from './consistent-destructuring.js'; +export {default as 'consistent-empty-array-spread'} from './consistent-empty-array-spread.js'; +export {default as 'consistent-existence-index-check'} from './consistent-existence-index-check.js'; +export {default as 'consistent-function-scoping'} from './consistent-function-scoping.js'; +export {default as 'custom-error-definition'} from './custom-error-definition.js'; +export {default as 'empty-brace-spaces'} from './empty-brace-spaces.js'; +export {default as 'error-message'} from './error-message.js'; +export {default as 'escape-case'} from './escape-case.js'; +export {default as 'expiring-todo-comments'} from './expiring-todo-comments.js'; +export {default as 'explicit-length-check'} from './explicit-length-check.js'; +export {default as 'filename-case'} from './filename-case.js'; +export {default as 'import-style'} from './import-style.js'; +export {default as 'new-for-builtins'} from './new-for-builtins.js'; +export {default as 'no-abusive-eslint-disable'} from './no-abusive-eslint-disable.js'; +export {default as 'no-accessor-recursion'} from './no-accessor-recursion.js'; +export {default as 'no-anonymous-default-export'} from './no-anonymous-default-export.js'; +export {default as 'no-array-callback-reference'} from './no-array-callback-reference.js'; +export {default as 'no-array-for-each'} from './no-array-for-each.js'; +export {default as 'no-array-method-this-argument'} from './no-array-method-this-argument.js'; +export {default as 'no-array-reduce'} from './no-array-reduce.js'; +export {default as 'no-array-reverse'} from './no-array-reverse.js'; +export {default as 'no-await-expression-member'} from './no-await-expression-member.js'; +export {default as 'no-await-in-promise-methods'} from './no-await-in-promise-methods.js'; +export {default as 'no-console-spaces'} from './no-console-spaces.js'; +export {default as 'no-document-cookie'} from './no-document-cookie.js'; +export {default as 'no-empty-file'} from './no-empty-file.js'; +export {default as 'no-for-loop'} from './no-for-loop.js'; +export {default as 'no-hex-escape'} from './no-hex-escape.js'; +export {default as 'no-instanceof-builtins'} from './no-instanceof-builtins.js'; +export {default as 'no-invalid-fetch-options'} from './no-invalid-fetch-options.js'; +export {default as 'no-invalid-remove-event-listener'} from './no-invalid-remove-event-listener.js'; +export {default as 'no-keyword-prefix'} from './no-keyword-prefix.js'; +export {default as 'no-lonely-if'} from './no-lonely-if.js'; +export {default as 'no-magic-array-flat-depth'} from './no-magic-array-flat-depth.js'; +export {default as 'no-named-default'} from './no-named-default.js'; +export {default as 'no-negated-condition'} from './no-negated-condition.js'; +export {default as 'no-negation-in-equality-check'} from './no-negation-in-equality-check.js'; +export {default as 'no-nested-ternary'} from './no-nested-ternary.js'; +export {default as 'no-new-array'} from './no-new-array.js'; +export {default as 'no-new-buffer'} from './no-new-buffer.js'; +export {default as 'no-null'} from './no-null.js'; +export {default as 'no-object-as-default-parameter'} from './no-object-as-default-parameter.js'; +export {default as 'no-process-exit'} from './no-process-exit.js'; +export {default as 'no-single-promise-in-promise-methods'} from './no-single-promise-in-promise-methods.js'; +export {default as 'no-static-only-class'} from './no-static-only-class.js'; +export {default as 'no-thenable'} from './no-thenable.js'; +export {default as 'no-this-assignment'} from './no-this-assignment.js'; +export {default as 'no-typeof-undefined'} from './no-typeof-undefined.js'; +export {default as 'no-unnecessary-array-flat-depth'} from './no-unnecessary-array-flat-depth.js'; +export {default as 'no-unnecessary-array-splice-count'} from './no-unnecessary-array-splice-count.js'; +export {default as 'no-unnecessary-await'} from './no-unnecessary-await.js'; +export {default as 'no-unnecessary-polyfills'} from './no-unnecessary-polyfills.js'; +export {default as 'no-unnecessary-slice-end'} from './no-unnecessary-slice-end.js'; +export {default as 'no-unreadable-array-destructuring'} from './no-unreadable-array-destructuring.js'; +export {default as 'no-unreadable-iife'} from './no-unreadable-iife.js'; +export {default as 'no-unused-properties'} from './no-unused-properties.js'; +export {default as 'no-useless-error-capture-stack-trace'} from './no-useless-error-capture-stack-trace.js'; +export {default as 'no-useless-fallback-in-spread'} from './no-useless-fallback-in-spread.js'; +export {default as 'no-useless-length-check'} from './no-useless-length-check.js'; +export {default as 'no-useless-promise-resolve-reject'} from './no-useless-promise-resolve-reject.js'; +export {default as 'no-useless-spread'} from './no-useless-spread.js'; +export {default as 'no-useless-switch-case'} from './no-useless-switch-case.js'; +export {default as 'no-useless-undefined'} from './no-useless-undefined.js'; +export {default as 'no-zero-fractions'} from './no-zero-fractions.js'; +export {default as 'number-literal-case'} from './number-literal-case.js'; +export {default as 'numeric-separators-style'} from './numeric-separators-style.js'; +export {default as 'prefer-add-event-listener'} from './prefer-add-event-listener.js'; +export {default as 'prefer-array-find'} from './prefer-array-find.js'; +export {default as 'prefer-array-flat-map'} from './prefer-array-flat-map.js'; +export {default as 'prefer-array-flat'} from './prefer-array-flat.js'; +export {default as 'prefer-array-index-of'} from './prefer-array-index-of.js'; +export {default as 'prefer-array-some'} from './prefer-array-some.js'; +export {default as 'prefer-at'} from './prefer-at.js'; +export {default as 'prefer-blob-reading-methods'} from './prefer-blob-reading-methods.js'; +export {default as 'prefer-class-fields'} from './prefer-class-fields.js'; +export {default as 'prefer-code-point'} from './prefer-code-point.js'; +export {default as 'prefer-date-now'} from './prefer-date-now.js'; +export {default as 'prefer-default-parameters'} from './prefer-default-parameters.js'; +export {default as 'prefer-dom-node-append'} from './prefer-dom-node-append.js'; +export {default as 'prefer-dom-node-dataset'} from './prefer-dom-node-dataset.js'; +export {default as 'prefer-dom-node-remove'} from './prefer-dom-node-remove.js'; +export {default as 'prefer-dom-node-text-content'} from './prefer-dom-node-text-content.js'; +export {default as 'prefer-event-target'} from './prefer-event-target.js'; +export {default as 'prefer-export-from'} from './prefer-export-from.js'; +export {default as 'prefer-global-this'} from './prefer-global-this.js'; +export {default as 'prefer-import-meta-properties'} from './prefer-import-meta-properties.js'; +export {default as 'prefer-includes'} from './prefer-includes.js'; +export {default as 'prefer-json-parse-buffer'} from './prefer-json-parse-buffer.js'; +export {default as 'prefer-keyboard-event-key'} from './prefer-keyboard-event-key.js'; +export {default as 'prefer-logical-operator-over-ternary'} from './prefer-logical-operator-over-ternary.js'; +export {default as 'prefer-math-min-max'} from './prefer-math-min-max.js'; +export {default as 'prefer-math-trunc'} from './prefer-math-trunc.js'; +export {default as 'prefer-modern-dom-apis'} from './prefer-modern-dom-apis.js'; +export {default as 'prefer-modern-math-apis'} from './prefer-modern-math-apis.js'; +export {default as 'prefer-module'} from './prefer-module.js'; +export {default as 'prefer-native-coercion-functions'} from './prefer-native-coercion-functions.js'; +export {default as 'prefer-negative-index'} from './prefer-negative-index.js'; +export {default as 'prefer-node-protocol'} from './prefer-node-protocol.js'; +export {default as 'prefer-number-properties'} from './prefer-number-properties.js'; +export {default as 'prefer-object-from-entries'} from './prefer-object-from-entries.js'; +export {default as 'prefer-optional-catch-binding'} from './prefer-optional-catch-binding.js'; +export {default as 'prefer-prototype-methods'} from './prefer-prototype-methods.js'; +export {default as 'prefer-query-selector'} from './prefer-query-selector.js'; +export {default as 'prefer-reflect-apply'} from './prefer-reflect-apply.js'; +export {default as 'prefer-regexp-test'} from './prefer-regexp-test.js'; +export {default as 'prefer-set-has'} from './prefer-set-has.js'; +export {default as 'prefer-set-size'} from './prefer-set-size.js'; +export {default as 'prefer-single-call'} from './prefer-single-call.js'; +export {default as 'prefer-spread'} from './prefer-spread.js'; +export {default as 'prefer-string-raw'} from './prefer-string-raw.js'; +export {default as 'prefer-string-replace-all'} from './prefer-string-replace-all.js'; +export {default as 'prefer-string-slice'} from './prefer-string-slice.js'; +export {default as 'prefer-string-starts-ends-with'} from './prefer-string-starts-ends-with.js'; +export {default as 'prefer-string-trim-start-end'} from './prefer-string-trim-start-end.js'; +export {default as 'prefer-structured-clone'} from './prefer-structured-clone.js'; +export {default as 'prefer-switch'} from './prefer-switch.js'; +export {default as 'prefer-ternary'} from './prefer-ternary.js'; +export {default as 'prefer-top-level-await'} from './prefer-top-level-await.js'; +export {default as 'prefer-type-error'} from './prefer-type-error.js'; +export {default as 'prevent-abbreviations'} from './prevent-abbreviations.js'; +export {default as 'relative-url-style'} from './relative-url-style.js'; +export {default as 'require-array-join-separator'} from './require-array-join-separator.js'; +export {default as 'require-module-specifiers'} from './require-module-specifiers.js'; +export {default as 'require-number-to-fixed-digits-argument'} from './require-number-to-fixed-digits-argument.js'; +export {default as 'require-post-message-target-origin'} from './require-post-message-target-origin.js'; +export {default as 'string-content'} from './string-content.js'; +export {default as 'switch-case-braces'} from './switch-case-braces.js'; +export {default as 'template-indent'} from './template-indent.js'; +export {default as 'text-encoding-identifier-case'} from './text-encoding-identifier-case.js'; +export {default as 'throw-new-error'} from './throw-new-error.js'; diff --git a/rules/no-anonymous-default-export.js b/rules/no-anonymous-default-export.js index c0a98ae187..6386bb2c7a 100644 --- a/rules/no-anonymous-default-export.js +++ b/rules/no-anonymous-default-export.js @@ -1,10 +1,10 @@ import path from 'node:path'; import {getFunctionHeadLocation, getFunctionNameWithKind, isOpeningParenToken} from '@eslint-community/eslint-utils'; import helperValidatorIdentifier from '@babel/helper-validator-identifier'; +import {camelCase} from 'change-case'; import getClassHeadLocation from './utils/get-class-head-location.js'; -import {upperFirst, camelCase} from './utils/lodash.js'; import {getParenthesizedRange} from './utils/parentheses.js'; -import {getScopes, getAvailableVariableName} from './utils/index.js'; +import {getScopes, getAvailableVariableName, upperFirst} from './utils/index.js'; import {isMemberExpression} from './ast/index.js'; const {isIdentifierName} = helperValidatorIdentifier; diff --git a/rules/no-array-callback-reference.js b/rules/no-array-callback-reference.js index 1b7aef9885..a0f7b68945 100644 --- a/rules/no-array-callback-reference.js +++ b/rules/no-array-callback-reference.js @@ -234,7 +234,6 @@ const create = context => ({ minimumArguments: 1, maximumArguments: 2, optionalCall: false, - optionalMember: false, computed: false, }) || callExpression.callee.property.type !== 'Identifier' diff --git a/rules/no-array-reverse.js b/rules/no-array-reverse.js new file mode 100644 index 0000000000..5321c6b665 --- /dev/null +++ b/rules/no-array-reverse.js @@ -0,0 +1,109 @@ +import {isMethodCall} from './ast/index.js'; +import {getParenthesizedText} from './utils/index.js'; + +const MESSAGE_ID_ERROR = 'no-array-reverse/error'; +const MESSAGE_ID_SUGGESTION_ONLY_FIX_METHOD = 'no-array-reverse/suggestion-only-fix-method'; +const MESSAGE_ID_SUGGESTION_SPREADING_ARRAY = 'no-array-reverse/suggestion-spreading-array'; +const MESSAGE_ID_SUGGESTION_NOT_SPREADING_ARRAY = 'no-array-reverse/suggestion-not-spreading-array'; +const messages = { + [MESSAGE_ID_ERROR]: 'Use `Array#toReversed()` instead of `Array#reverse()`.', + [MESSAGE_ID_SUGGESTION_ONLY_FIX_METHOD]: 'Switch to `.toReversed()`.', + [MESSAGE_ID_SUGGESTION_SPREADING_ARRAY]: 'The spreading object is an array', + [MESSAGE_ID_SUGGESTION_NOT_SPREADING_ARRAY]: 'The spreading object is NOT an array', +}; + +/** @param {import('eslint').Rule.RuleContext} context */ +const create = context => { + const {sourceCode} = context; + const {allowExpressionStatement} = context.options[0]; + + return { + CallExpression(callExpression) { + if (!isMethodCall(callExpression, { + method: 'reverse', + argumentsLength: 0, + optionalCall: false, + })) { + return; + } + + const array = callExpression.callee.object; + + // `[...array].reverse()` + const isSpreadAndReverse = array.type === 'ArrayExpression' + && array.elements.length === 1 + && array.elements[0].type === 'SpreadElement'; + + if (allowExpressionStatement && !isSpreadAndReverse) { + const maybeExpressionStatement = callExpression.parent.type === 'ChainExpression' + ? callExpression.parent.parent + : callExpression.parent; + if (maybeExpressionStatement.type === 'ExpressionStatement') { + return; + } + } + + const reverseProperty = callExpression.callee.property; + const suggestions = []; + const fixMethodName = fixer => fixer.replaceText(reverseProperty, 'toReversed'); + + /* + For `[...array].reverse()`, provide two suggestion, let user choose if the object can be unwrapped, + otherwise only change `.reverse()` to `.toReversed()` + */ + if (isSpreadAndReverse) { + suggestions.push({ + messageId: MESSAGE_ID_SUGGESTION_SPREADING_ARRAY, + * fix(fixer) { + const text = getParenthesizedText(array.elements[0].argument, sourceCode); + yield fixer.replaceText(array, text); + yield fixMethodName(fixer); + }, + }); + } + + suggestions.push({ + messageId: isSpreadAndReverse + ? MESSAGE_ID_SUGGESTION_NOT_SPREADING_ARRAY + : MESSAGE_ID_SUGGESTION_ONLY_FIX_METHOD, + fix: fixMethodName, + }); + + return { + node: reverseProperty, + messageId: MESSAGE_ID_ERROR, + suggest: suggestions, + }; + }, + }; +}; + +const schema = [ + { + type: 'object', + additionalProperties: false, + properties: { + allowExpressionStatement: { + type: 'boolean', + }, + }, + }, +]; + +/** @type {import('eslint').Rule.RuleModule} */ +const config = { + create, + meta: { + type: 'suggestion', + docs: { + description: 'Prefer `Array#toReversed()` over `Array#reverse()`.', + recommended: true, + }, + hasSuggestions: true, + schema, + defaultOptions: [{allowExpressionStatement: true}], + messages, + }, +}; + +export default config; diff --git a/rules/no-nested-ternary.js b/rules/no-nested-ternary.js index 1035acc5b0..5be075b1ef 100644 --- a/rules/no-nested-ternary.js +++ b/rules/no-nested-ternary.js @@ -4,7 +4,7 @@ const MESSAGE_ID_TOO_DEEP = 'too-deep'; const MESSAGE_ID_SHOULD_PARENTHESIZED = 'should-parenthesized'; const messages = { [MESSAGE_ID_TOO_DEEP]: 'Do not nest ternary expressions.', - [MESSAGE_ID_SHOULD_PARENTHESIZED]: 'Nest ternary expression should be parenthesized.', + [MESSAGE_ID_SHOULD_PARENTHESIZED]: 'Nested ternary expression should be parenthesized.', }; /** @param {import('eslint').Rule.RuleContext} context */ @@ -19,7 +19,7 @@ const create = context => ({ } const {sourceCode} = context; - const ancestors = sourceCode.getAncestors(node).reverse(); + const ancestors = sourceCode.getAncestors(node).toReversed(); const nestLevel = ancestors.findIndex(node => node.type !== 'ConditionalExpression'); if (nestLevel === 1 && !isParenthesized(node, sourceCode)) { diff --git a/rules/no-typeof-undefined.js b/rules/no-typeof-undefined.js index ae0f51a1e0..0e28b72015 100644 --- a/rules/no-typeof-undefined.js +++ b/rules/no-typeof-undefined.js @@ -7,7 +7,7 @@ import { needsSemicolon, isParenthesized, isOnSameLine, - isShadowed, + isUnresolvedVariable, } from './utils/index.js'; const MESSAGE_ID_ERROR = 'no-typeof-undefined/error'; @@ -49,7 +49,7 @@ const create = context => { const valueNode = typeofNode.argument; const isGlobalVariable = valueNode.type === 'Identifier' - && !isShadowed(sourceCode.getScope(valueNode), valueNode); + && (sourceCode.isGlobalReference(valueNode) || isUnresolvedVariable(valueNode, context)); if (!checkGlobalVariables && isGlobalVariable) { return; diff --git a/rules/no-unnecessary-array-flat-depth.js b/rules/no-unnecessary-array-flat-depth.js index 5c10aff087..c9a2229786 100644 --- a/rules/no-unnecessary-array-flat-depth.js +++ b/rules/no-unnecessary-array-flat-depth.js @@ -1,6 +1,5 @@ import {isMethodCall, isLiteral} from './ast/index.js'; import {removeArgument} from './fix/index.js'; -import {} from './utils/index.js'; const MESSAGE_ID = 'no-unnecessary-array-flat-depth'; const messages = { diff --git a/rules/no-unnecessary-await.js b/rules/no-unnecessary-await.js index 1fda0e903c..e150f7fb50 100644 --- a/rules/no-unnecessary-await.js +++ b/rules/no-unnecessary-await.js @@ -38,7 +38,11 @@ function notPromise(node) { /** @param {import('eslint').Rule.RuleContext} context */ const create = context => ({ AwaitExpression(node) { - if (!notPromise(node.argument)) { + if ( + // F#-style pipeline operator, `Promise.resolve() |> await` + !node.argument + || !notPromise(node.argument) + ) { return; } diff --git a/rules/no-unnecessary-polyfills.js b/rules/no-unnecessary-polyfills.js index 73f36612a5..b58183018e 100644 --- a/rules/no-unnecessary-polyfills.js +++ b/rules/no-unnecessary-polyfills.js @@ -1,6 +1,6 @@ import path from 'node:path'; import coreJsCompat from 'core-js-compat'; -import {camelCase} from './utils/lodash.js'; +import {camelCase} from 'change-case'; import isStaticRequire from './ast/is-static-require.js'; import {readPackageJson} from './shared/package-json.js'; diff --git a/rules/no-useless-error-capture-stack-trace.js b/rules/no-useless-error-capture-stack-trace.js new file mode 100644 index 0000000000..3aab75c382 --- /dev/null +++ b/rules/no-useless-error-capture-stack-trace.js @@ -0,0 +1,146 @@ +import {findVariable} from '@eslint-community/eslint-utils'; +import { + isMethodCall, + isMemberExpression, +} from './ast/index.js'; +import builtinErrors from './shared/builtin-errors.js'; + +const MESSAGE_ID_ERROR = 'no-useless-error-capture-stack-trace/error'; +const messages = { + [MESSAGE_ID_ERROR]: 'Unnecessary `Error.captureStackTrace(…)` call.', +}; + +const isSubclassOfBuiltinErrors = (node, context) => + node?.superClass + && node.superClass.type === 'Identifier' + && builtinErrors.includes(node.superClass.name) + && context.sourceCode.isGlobalReference(node.superClass); + +const isClassReference = (node, classNode, context) => { + // `new.target` + if ( + node.type === 'MetaProperty' + && node.meta.type === 'Identifier' + && node.meta.name === 'new' + && node.property.type === 'Identifier' + && node.property.name === 'target' + ) { + return true; + } + + // `this.constructor` + if ( + isMemberExpression(node, { + property: 'constructor', + computed: false, + optional: false, + }) + && node.object.type === 'ThisExpression' + ) { + return true; + } + + if (node.type !== 'Identifier' || !classNode.id) { + return false; + } + + const scope = context.sourceCode.getScope(node); + const variable = findVariable(scope, node); + + return variable + && variable.defs.length === 1 + && variable.defs[0].type === 'ClassName' + && variable.defs[0].node === classNode; +}; + +const isClassConstructor = (node, classNode) => + node + && node.parent.type === 'MethodDefinition' + && node.parent.kind === 'constructor' + && node.parent.value === node + && classNode.body.body.includes(node.parent); + +/** @param {import('eslint').Rule.RuleContext} context */ +const create = context => { + const classStack = []; + const thisScopeStack = []; + + context.on(['ClassDeclaration', 'ClassExpression'], classNode => { + classStack.push(classNode); + thisScopeStack.push(classNode); + }); + + context.onExit(['ClassDeclaration', 'ClassExpression'], () => { + classStack.pop(); + thisScopeStack.pop(); + }); + + context.on(['FunctionDeclaration', 'FunctionExpression'], functionNode => { + thisScopeStack.push(functionNode); + }); + + context.onExit(['FunctionDeclaration', 'FunctionExpression'], () => { + thisScopeStack.pop(); + }); + + context.on('CallExpression', callExpression => { + const errorClass = classStack.at(-1); + + if (!( + isSubclassOfBuiltinErrors(errorClass, context) + && isClassConstructor(thisScopeStack.at(-1), errorClass) + && isMethodCall(callExpression, { + object: 'Error', + method: 'captureStackTrace', + argumentsLength: 2, + optionalMember: false, + }) + && context.sourceCode.isGlobalReference(callExpression.callee.object) + )) { + return; + } + + const [firstArgument, secondArgument] = callExpression.arguments; + + if ( + firstArgument.type !== 'ThisExpression' + || !isClassReference(secondArgument, errorClass, context) + ) { + return; + } + + const problem = { + node: callExpression, + messageId: MESSAGE_ID_ERROR, + }; + + const maybeExpressionStatement = callExpression.parent === 'ChainExpression' + ? callExpression.parent.parent + : callExpression.parent; + + if ( + maybeExpressionStatement.type === 'ExpressionStatement' + && maybeExpressionStatement.parent.type === 'BlockStatement' + ) { + problem.fix = fixer => fixer.remove(maybeExpressionStatement); + } + + return problem; + }); +}; + +/** @type {import('eslint').Rule.RuleModule} */ +const config = { + create, + meta: { + type: 'suggestion', + docs: { + description: 'Disallow unnecessary `Error.captureStackTrace(…)`.', + recommended: true, + }, + fixable: 'code', + messages, + }, +}; + +export default config; diff --git a/rules/prefer-at.js b/rules/prefer-at.js index 94b4ff5962..a657dcf4c3 100644 --- a/rules/prefer-at.js +++ b/rules/prefer-at.js @@ -17,7 +17,12 @@ import { removeLengthNode, } from './shared/negative-index.js'; import {removeMemberExpressionProperty, removeMethodCall} from './fix/index.js'; -import {isLiteral, isCallExpression, isMethodCall} from './ast/index.js'; +import { + isLiteral, + isCallExpression, + isMethodCall, + isMemberExpression, +} from './ast/index.js'; const MESSAGE_ID_NEGATIVE_INDEX = 'negative-index'; const MESSAGE_ID_INDEX = 'index'; @@ -45,28 +50,22 @@ const isLiteralNegativeInteger = node => && node.argument.type === 'Literal' && Number.isInteger(node.argument.value) && node.argument.value > 0; -const isZeroIndexAccess = node => { - const {parent} = node; - return parent.type === 'MemberExpression' - && !parent.optional - && parent.computed - && parent.object === node - && isLiteral(parent.property, 0); -}; - -const isArrayPopOrShiftCall = (node, method) => { - const {parent} = node; - return parent.type === 'MemberExpression' - && !parent.optional - && !parent.computed - && parent.object === node - && parent.property.type === 'Identifier' - && parent.property.name === method - && parent.parent.type === 'CallExpression' - && parent.parent.callee === parent - && !parent.parent.optional - && parent.parent.arguments.length === 0; -}; +const isZeroIndexAccess = node => + isMemberExpression(node.parent, { + optional: false, + computed: true, + }) + && node.parent.object === node + && isLiteral(node.parent.property, 0); + +const isArrayPopOrShiftCall = (node, method) => + isMethodCall(node.parent.parent, { + method, + argumentsLength: 0, + optionalCall: false, + optionalMember: false, + }) + && node.parent.object === node; const isArrayPopCall = node => isArrayPopOrShiftCall(node, 'pop'); const isArrayShiftCall = node => isArrayPopOrShiftCall(node, 'shift'); diff --git a/rules/prefer-class-fields.js b/rules/prefer-class-fields.js new file mode 100644 index 0000000000..c55a2a9248 --- /dev/null +++ b/rules/prefer-class-fields.js @@ -0,0 +1,158 @@ +import {isSemicolonToken} from '@eslint-community/eslint-utils'; +import getIndentString from './utils/get-indent-string.js'; + +const MESSAGE_ID_ERROR = 'prefer-class-fields/error'; +const MESSAGE_ID_SUGGESTION = 'prefer-class-fields/suggestion'; +const messages = { + [MESSAGE_ID_ERROR]: + 'Prefer class field declaration over `this` assignment in constructor for static values.', + [MESSAGE_ID_SUGGESTION]: + 'Encountered same-named class field declaration and `this` assignment in constructor. Replace the class field declaration with the value from `this` assignment.', +}; + +/** +@param {import('eslint').Rule.Node} node +@param {import('eslint').Rule.RuleContext['sourceCode']} sourceCode +@param {import('eslint').Rule.RuleFixer} fixer +*/ +const removeFieldAssignment = (node, sourceCode, fixer) => { + const {line} = sourceCode.getLoc(node).start; + const nodeText = sourceCode.getText(node); + const lineText = sourceCode.lines[line - 1]; + const isOnlyNodeOnLine = lineText.trim() === nodeText; + + return isOnlyNodeOnLine + ? fixer.removeRange([ + sourceCode.getIndexFromLoc({line, column: 0}), + sourceCode.getIndexFromLoc({line: line + 1, column: 0}), + ]) + : fixer.remove(node); +}; + +/** +@type {import('eslint').Rule.RuleModule['create']} +*/ +const create = context => { + const {sourceCode} = context; + + return { + ClassBody(classBody) { + const constructor = classBody.body.find(node => + node.kind === 'constructor' + && !node.computed + && !node.static + && node.type === 'MethodDefinition' + && node.value.type === 'FunctionExpression', + ); + + if (!constructor) { + return; + } + + const node = constructor.value.body.body.find(node => node.type !== 'EmptyStatement'); + + if (!( + node?.type === 'ExpressionStatement' + && node.expression.type === 'AssignmentExpression' + && node.expression.operator === '=' + && node.expression.left.type === 'MemberExpression' + && node.expression.left.object.type === 'ThisExpression' + && !node.expression.left.computed + && ['Identifier', 'PrivateIdentifier'].includes(node.expression.left.property.type) + && node.expression.right.type === 'Literal' + )) { + return; + } + + const propertyName = node.expression.left.property.name; + const propertyValue = node.expression.right.raw; + const propertyType = node.expression.left.property.type; + const existingProperty = classBody.body.find(node => + node.type === 'PropertyDefinition' + && !node.computed + && !node.static + && node.key.type === propertyType + && node.key.name === propertyName, + ); + + const problem = { + node, + messageId: MESSAGE_ID_ERROR, + }; + + /** + @param {import('eslint').Rule.RuleFixer} fixer + */ + function * fix(fixer) { + yield removeFieldAssignment(node, sourceCode, fixer); + + if (existingProperty) { + if (existingProperty.value) { + yield fixer.replaceText(existingProperty.value, propertyValue); + return; + } + + const text = ` = ${propertyValue}`; + const lastToken = sourceCode.getLastToken(existingProperty); + if (isSemicolonToken(lastToken)) { + yield fixer.insertTextBefore(lastToken, text); + return; + } + + yield fixer.insertTextAfter(existingProperty, `${text};`); + return; + } + + const closingBrace = sourceCode.getLastToken(classBody); + const indent = getIndentString(constructor, sourceCode); + + let text = `${indent}${propertyName} = ${propertyValue};\n`; + + const characterBefore = sourceCode.getText()[sourceCode.getRange(closingBrace)[0] - 1]; + if (characterBefore !== '\n') { + text = `\n${text}`; + } + + const lastProperty = classBody.body.at(-1); + if ( + lastProperty.type === 'PropertyDefinition' + && sourceCode.getLastToken(lastProperty).value !== ';' + ) { + text = `;${text}`; + } + + yield fixer.insertTextBefore(closingBrace, text); + } + + if (existingProperty?.value) { + problem.suggest = [ + { + messageId: MESSAGE_ID_SUGGESTION, + fix, + }, + ]; + return problem; + } + + problem.fix = fix; + return problem; + }, + }; +}; + +/** @type {import('eslint').Rule.RuleModule} */ +const config = { + create, + meta: { + type: 'suggestion', + docs: { + description: 'Prefer class field declarations over `this` assignments in constructors.', + recommended: true, + }, + fixable: 'code', + hasSuggestions: true, + messages, + }, +}; + +export default config; diff --git a/rules/prefer-default-parameters.js b/rules/prefer-default-parameters.js index 8b8df12d16..efe3c7e840 100644 --- a/rules/prefer-default-parameters.js +++ b/rules/prefer-default-parameters.js @@ -207,7 +207,6 @@ const config = { description: 'Prefer default parameters over reassignment.', recommended: true, }, - fixable: 'code', hasSuggestions: true, messages: { [MESSAGE_ID]: 'Prefer default parameters over reassignment.', diff --git a/rules/prefer-math-min-max.js b/rules/prefer-math-min-max.js index 8345efaad9..db4b57856a 100644 --- a/rules/prefer-math-min-max.js +++ b/rules/prefer-math-min-max.js @@ -1,3 +1,4 @@ +/* eslint-disable complexity */ import {isBigIntLiteral, isCallExpression} from './ast/index.js'; import {fixSpaceAroundKeyword} from './fix/index.js'; diff --git a/rules/prefer-modern-dom-apis.js b/rules/prefer-modern-dom-apis.js index 96394674fb..d14d80a15f 100644 --- a/rules/prefer-modern-dom-apis.js +++ b/rules/prefer-modern-dom-apis.js @@ -132,7 +132,9 @@ const config = { meta: { type: 'suggestion', docs: { - description: 'Prefer `.before()` over `.insertBefore()`, `.replaceWith()` over `.replaceChild()`, prefer one of `.before()`, `.after()`, `.append()` or `.prepend()` over `insertAdjacentText()` and `insertAdjacentElement()`.', + description: + // eslint-disable-next-line @stylistic/max-len + 'Prefer `.before()` over `.insertBefore()`, `.replaceWith()` over `.replaceChild()`, prefer one of `.before()`, `.after()`, `.append()` or `.prepend()` over `insertAdjacentText()` and `insertAdjacentElement()`.', recommended: true, }, fixable: 'code', diff --git a/rules/prefer-module.js b/rules/prefer-module.js index db58aabb85..b5097d4179 100644 --- a/rules/prefer-module.js +++ b/rules/prefer-module.js @@ -1,7 +1,6 @@ -import isShadowed from './utils/is-shadowed.js'; import assertToken from './utils/assert-token.js'; import {getCallExpressionTokens} from './utils/index.js'; -import {isStaticRequire, isReferenceIdentifier, isFunction} from './ast/index.js'; +import {isStaticRequire, isFunction} from './ast/index.js'; import {removeParentheses, replaceReferenceIdentifier, removeSpacesAfter} from './fix/index.js'; const ERROR_USE_STRICT_DIRECTIVE = 'error/use-strict-directive'; @@ -56,6 +55,14 @@ const suggestions = new Map([ ], ]); +const commonJsGlobals = new Set([ + 'exports', + 'require', + 'module', + '__filename', + '__dirname', +]); + function fixRequireCall(node, sourceCode) { if (!isStaticRequire(node.parent) || node.parent.callee !== node) { return; @@ -281,16 +288,7 @@ function create(context) { }); context.on('Identifier', node => { - if ( - !isReferenceIdentifier(node, [ - 'exports', - 'require', - 'module', - '__filename', - '__dirname', - ]) - || isShadowed(sourceCode.getScope(node), node) - ) { + if (!commonJsGlobals.has(node.name) || !context.sourceCode.isGlobalReference(node)) { return; } diff --git a/rules/prefer-set-has.js b/rules/prefer-set-has.js index d65ff438a1..a06d89a40e 100644 --- a/rules/prefer-set-has.js +++ b/rules/prefer-set-has.js @@ -27,21 +27,14 @@ const arrayMethodsReturnsArray = [ 'with', ]; -const isIncludesCall = node => { - const {type, optional, callee, arguments: includesArguments} = node.parent.parent ?? {}; - return ( - type === 'CallExpression' - && !optional - && callee.type === 'MemberExpression' - && !callee.computed - && !callee.optional - && callee.object === node - && callee.property.type === 'Identifier' - && callee.property.name === 'includes' - && includesArguments.length === 1 - && includesArguments[0].type !== 'SpreadElement' - ); -}; +const isIncludesCall = node => + isMethodCall(node.parent.parent, { + method: 'includes', + optionalCall: false, + optionalMember: false, + argumentsLength: 1, + }) + && node.parent.object === node; const multipleCallNodeTypes = new Set([ 'ForOfStatement', diff --git a/rules/prefer-string-raw.js b/rules/prefer-string-raw.js index f608b5bb37..32ea732218 100644 --- a/rules/prefer-string-raw.js +++ b/rules/prefer-string-raw.js @@ -11,28 +11,14 @@ const BACKSLASH = '\\'; function unescapeBackslash(raw) { const quote = raw.charAt(0); - raw = raw.slice(1, -1); - - let result = ''; - for (let position = 0; position < raw.length; position++) { - const character = raw[position]; - if (character === BACKSLASH) { - const nextCharacter = raw[position + 1]; - if (nextCharacter === BACKSLASH || nextCharacter === quote) { - result += nextCharacter; - position++; - continue; - } - } - - result += character; - } - - return result; + return raw + .slice(1, -1) + .replaceAll(new RegExp(String.raw`\\(?[\\${quote}])`, 'g'), '$'); } /** @param {import('eslint').Rule.RuleContext} context */ const create = context => { + // eslint-disable-next-line complexity context.on('Literal', node => { if ( !isStringLiteral(node) diff --git a/rules/prefer-string-replace-all.js b/rules/prefer-string-replace-all.js index 6d5dd4ed15..226a879141 100644 --- a/rules/prefer-string-replace-all.js +++ b/rules/prefer-string-replace-all.js @@ -1,6 +1,5 @@ import {getStaticValue} from '@eslint-community/eslint-utils'; import regjsparser from 'regjsparser'; -import escapeString from './utils/escape-string.js'; import {isRegexLiteral, isNewExpression, isMethodCall} from './ast/index.js'; const {parse: parseRegExp} = regjsparser; @@ -11,6 +10,8 @@ const messages = { [MESSAGE_ID_USE_STRING]: 'This pattern can be replaced with {{replacement}}.', }; +const QUOTE = '\''; + function getPatternReplacement(node) { if (!isRegexLiteral(node)) { return; @@ -39,10 +40,42 @@ function getPatternReplacement(node) { return; } - // TODO: Preserve escape - const string = String.fromCodePoint(...parts.map(part => part.codePoint)); + return QUOTE + + parts.map(part => { + const {kind, codePoint, raw} = part; + + if (kind === 'controlLetter') { + if (codePoint === 13) { + return String.raw`\r`; + } + + if (codePoint === 10) { + return String.raw`\n`; + } + + if (codePoint === 9) { + return String.raw`\t`; + } + + return `\\u{${codePoint.toString(16)}}`; + } + + if (kind === 'octal') { + return `\\u{${codePoint.toString(16)}}`; + } + + let character = raw; + if (kind === 'identifier') { + character = character.slice(1); + } + + if (character === QUOTE || character === '\\') { + return `\\${character}`; + } - return escapeString(string); + return character; + }).join('') + + QUOTE; } const isRegExpWithGlobalFlag = (node, scope) => { diff --git a/rules/prefer-ternary.js b/rules/prefer-ternary.js index e7a9e6ba4d..86782f2ce4 100644 --- a/rules/prefer-ternary.js +++ b/rules/prefer-ternary.js @@ -59,6 +59,7 @@ const create = context => { return text; }; + // eslint-disable-next-line complexity function merge(options, mergeOptions) { const { before = '', diff --git a/rules/prevent-abbreviations.js b/rules/prevent-abbreviations.js index ad891256d5..ec051a9f42 100644 --- a/rules/prevent-abbreviations.js +++ b/rules/prevent-abbreviations.js @@ -1,6 +1,5 @@ import path from 'node:path'; import {isRegExp} from 'node:util/types'; -import {defaultsDeep, upperFirst, lowerFirst} from './utils/lodash.js'; import { getAvailableVariableName, cartesianProductSamples, @@ -8,6 +7,8 @@ import { isShorthandImportLocal, getVariableIdentifiers, getScopes, + upperFirst, + lowerFirst, } from './utils/index.js'; import {defaultReplacements, defaultAllowList, defaultIgnore} from './shared/abbreviations.js'; import {renameVariable} from './fix/index.js'; @@ -43,11 +44,14 @@ const prepareOptions = ({ ignore = [], } = {}) => { const mergedReplacements = extendDefaultReplacements - ? defaultsDeep({}, replacements, defaultReplacements) + ? Object.fromEntries( + [...Object.keys(defaultReplacements), ...Object.keys(replacements)] + .map(name => [name, replacements[name] === false ? {} : {...defaultReplacements[name], ...replacements[name]}]), + ) : replacements; const mergedAllowList = extendDefaultAllowList - ? defaultsDeep({}, allowList, defaultAllowList) + ? {...defaultAllowList, ...allowList} : allowList; ignore = [...defaultIgnore, ...ignore]; diff --git a/rules/require-module-specifiers.js b/rules/require-module-specifiers.js new file mode 100644 index 0000000000..05609b1a6b --- /dev/null +++ b/rules/require-module-specifiers.js @@ -0,0 +1,158 @@ +import {isClosingBraceToken} from '@eslint-community/eslint-utils'; + +const MESSAGE_ID_ERROR = 'error'; +const MESSAGE_ID_SUGGESTION_REMOVE_DECLARATION = 'suggestion/remove-declaration'; +const MESSAGE_ID_SUGGESTION_TO_SIDE_EFFECT_IMPORT = 'suggestion/to-side-effect-import'; +const messages = { + [MESSAGE_ID_ERROR]: '{{type}} statement without specifiers is not allowed.', + [MESSAGE_ID_SUGGESTION_REMOVE_DECLARATION]: 'Remove this {{type}} statement.', + [MESSAGE_ID_SUGGESTION_TO_SIDE_EFFECT_IMPORT]: 'Switch to side effect import.', +}; + +const isFromToken = token => token.type === 'Identifier' && token.value === 'from'; + +/** @param {import('eslint').Rule.RuleContext} context */ +const create = context => { + const {sourceCode} = context; + + context.on('ImportDeclaration', importDeclaration => { + const {specifiers} = importDeclaration; + + if (specifiers.some(node => node.type === 'ImportSpecifier' || node.type === 'ImportNamespaceSpecifier')) { + return; + } + + const {source, importKind} = importDeclaration; + const fromToken = sourceCode.getTokenBefore(source); + if (!isFromToken(fromToken)) { + return; + } + + const closingBraceToken = sourceCode.getTokenBefore(fromToken); + if (!isClosingBraceToken(closingBraceToken)) { + return; + } + + const openingBraceToken = sourceCode.getTokenBefore(closingBraceToken); + + const problem = { + node: importDeclaration, + loc: { + start: sourceCode.getLoc(openingBraceToken).start, + end: sourceCode.getLoc(closingBraceToken).end, + }, + messageId: MESSAGE_ID_ERROR, + data: { + type: 'import', + }, + }; + + // If there is a `ImportDefaultSpecifier`, it has to be the first. + const importDefaultSpecifier = specifiers.length === 1 ? specifiers[0] : undefined; + if (importKind === 'type' && !importDefaultSpecifier) { + problem.fix = fixer => fixer.remove(importDeclaration); + return problem; + } + + if (importDefaultSpecifier) { + problem.fix = function * (fixer) { + yield fixer.remove(closingBraceToken); + yield fixer.remove(openingBraceToken); + + const commaToken = sourceCode.getTokenBefore(openingBraceToken); + yield fixer.remove(commaToken); + + if (sourceCode.getRange(closingBraceToken)[1] === sourceCode.getRange(fromToken)[0]) { + yield fixer.insertTextBefore(fromToken, ' '); + } + }; + + return problem; + } + + problem.suggest = [ + { + messageId: MESSAGE_ID_SUGGESTION_REMOVE_DECLARATION, + fix: fixer => fixer.remove(importDeclaration), + }, + { + messageId: MESSAGE_ID_SUGGESTION_TO_SIDE_EFFECT_IMPORT, + * fix(fixer) { + yield fixer.remove(openingBraceToken); + yield fixer.remove(closingBraceToken); + yield fixer.remove(fromToken); + }, + }, + ]; + + return problem; + }); + + context.on('ExportNamedDeclaration', exportDeclaration => { + const {specifiers, declaration} = exportDeclaration; + + if (declaration || specifiers.length > 0) { + return; + } + + const {source, exportKind} = exportDeclaration; + const fromToken = source ? sourceCode.getTokenBefore(source) : undefined; + const closingBraceToken = fromToken + ? sourceCode.getTokenBefore(fromToken) + : sourceCode.getLastToken(exportDeclaration, isClosingBraceToken); + const openingBraceToken = sourceCode.getTokenBefore(closingBraceToken); + + const problem = { + node: exportDeclaration, + loc: { + start: sourceCode.getLoc(openingBraceToken).start, + end: sourceCode.getLoc(closingBraceToken).end, + }, + messageId: MESSAGE_ID_ERROR, + data: { + type: 'export', + }, + }; + + if (!source || exportKind === 'type') { + problem.fix = fixer => fixer.remove(exportDeclaration); + return problem; + } + + problem.suggest = [ + { + messageId: MESSAGE_ID_SUGGESTION_REMOVE_DECLARATION, + fix: fixer => fixer.remove(exportDeclaration), + }, + { + messageId: MESSAGE_ID_SUGGESTION_TO_SIDE_EFFECT_IMPORT, + * fix(fixer) { + const exportToken = sourceCode.getFirstToken(exportDeclaration); + yield fixer.replaceText(exportToken, 'import'); + yield fixer.remove(openingBraceToken); + yield fixer.remove(closingBraceToken); + yield fixer.remove(fromToken); + }, + }, + ]; + + return problem; + }); +}; + +/** @type {import('eslint').Rule.RuleModule} */ +const config = { + create, + meta: { + type: 'suggestion', + docs: { + description: 'Require non-empty specifier list in import and export statements.', + recommended: true, + }, + fixable: 'code', + hasSuggestions: true, + messages, + }, +}; + +export default config; diff --git a/rules/shared/builtin-errors.js b/rules/shared/builtin-errors.js index 2ee1b45c34..43e12df555 100644 --- a/rules/shared/builtin-errors.js +++ b/rules/shared/builtin-errors.js @@ -7,8 +7,8 @@ const builtinErrors = [ 'SyntaxError', 'TypeError', 'URIError', - 'InternalError', 'AggregateError', + 'SuppressedError', ]; export default builtinErrors; diff --git a/rules/template-indent.js b/rules/template-indent.js index 06c41ae993..458cc6dfda 100644 --- a/rules/template-indent.js +++ b/rules/template-indent.js @@ -129,7 +129,7 @@ const create = context => { } if (options.selectors.length > 0) { - const ancestors = sourceCode.getAncestors(node).reverse(); + const ancestors = sourceCode.getAncestors(node).toReversed(); if (options.selectors.some(selector => esquery.matches(node, parseEsquerySelector(selector), ancestors))) { return true; } diff --git a/rules/text-encoding-identifier-case.js b/rules/text-encoding-identifier-case.js index 32459a23fe..c059a03a6e 100644 --- a/rules/text-encoding-identifier-case.js +++ b/rules/text-encoding-identifier-case.js @@ -1,4 +1,5 @@ import {replaceStringRaw} from './fix/index.js'; +import {isMethodCall} from './ast/index.js'; const MESSAGE_ID_ERROR = 'text-encoding-identifier/error'; const MESSAGE_ID_SUGGESTION = 'text-encoding-identifier/suggestion'; @@ -24,15 +25,13 @@ const getReplacement = encoding => { // `fs.{readFile,readFileSync}()` const isFsReadFileEncoding = node => - node.parent.type === 'CallExpression' - && !node.parent.optional + isMethodCall(node.parent, { + methods: ['readFile', 'readFileSync'], + optionalCall: false, + optionalMember: false, + }) && node.parent.arguments[1] === node - && node.parent.arguments[0].type !== 'SpreadElement' - && node.parent.callee.type === 'MemberExpression' - && !node.parent.callee.optional - && !node.parent.callee.computed - && node.parent.callee.property.type === 'Identifier' - && (node.parent.callee.property.name === 'readFile' || node.parent.callee.property.name === 'readFileSync'); + && node.parent.arguments[0].type !== 'SpreadElement'; /** @param {import('eslint').Rule.RuleContext} context */ const create = () => ({ diff --git a/rules/utils/global-reference-tracker.js b/rules/utils/global-reference-tracker.js index dc6346ee24..920464fa73 100644 --- a/rules/utils/global-reference-tracker.js +++ b/rules/utils/global-reference-tracker.js @@ -3,7 +3,7 @@ import {ReferenceTracker} from '@eslint-community/eslint-utils'; const createTraceMap = (object, type) => { let map = {[type]: true}; - const path = object.split('.').reverse(); + const path = object.split('.').toReversed(); for (const name of path) { map = {[name]: map}; } diff --git a/rules/utils/index.js b/rules/utils/index.js index a8e65a5aaf..bebc2868e5 100644 --- a/rules/utils/index.js +++ b/rules/utils/index.js @@ -40,7 +40,7 @@ export {default as isNodeValueNotFunction} from './is-node-value-not-function.js export {default as isOnSameLine} from './is-on-same-line.js'; export {default as isSameIdentifier} from './is-same-identifier.js'; export {default as isSameReference} from './is-same-reference.js'; -export {default as isShadowed} from './is-shadowed.js'; +export {default as isUnresolvedVariable} from './is-unresolved-variable.js'; export {default as isShorthandImportLocal} from './is-shorthand-import-local.js'; export {default as isShorthandPropertyValue} from './is-shorthand-property-value.js'; export {default as isValueNotUsable} from './is-value-not-usable.js'; @@ -52,3 +52,4 @@ export {default as shouldAddParenthesesToMemberExpressionObject} from './should- export {default as singular} from './singular.js'; export {default as toLocation} from './to-location.js'; export {default as getAncestor} from './get-ancestor.js'; +export * from './string-cases.js'; diff --git a/rules/utils/is-number.js b/rules/utils/is-number.js index 15db497c51..fc3dba9145 100644 --- a/rules/utils/is-number.js +++ b/rules/utils/is-number.js @@ -114,6 +114,7 @@ const isLengthProperty = node => // `+` and `>>>` operators are handled separately const mathOperators = new Set(['-', '*', '/', '%', '**', '<<', '>>', '|', '^', '&']); +// eslint-disable-next-line complexity export default function isNumber(node, scope) { if ( isNumberLiteral(node) diff --git a/rules/utils/is-shadowed.js b/rules/utils/is-shadowed.js deleted file mode 100644 index 18591d2d38..0000000000 --- a/rules/utils/is-shadowed.js +++ /dev/null @@ -1,31 +0,0 @@ -/** -Finds the eslint-scope reference in the given scope. - -@param {Object} scope The scope to search. -@param {ASTNode} node The identifier node. -@returns {Reference|undefined} Returns the found reference or null if none were found. -*/ -function findReference(scope, node) { - const references = scope.references - .filter(reference => reference.identifier === node); - - if (references.length === 1) { - return references[0]; - } -} - -/** -Checks if the given identifier node is shadowed in the given scope. - -@param {Object} scope The current scope. -@param {string} node The identifier node to check -@returns {boolean} Whether or not the name is shadowed. -*/ -export default function isShadowed(scope, node) { - const reference = findReference(scope, node); - - return ( - Boolean(reference?.resolved) - && reference.resolved.defs.length > 0 - ); -} diff --git a/rules/utils/is-unresolved-variable.js b/rules/utils/is-unresolved-variable.js new file mode 100644 index 0000000000..b89b9c4e93 --- /dev/null +++ b/rules/utils/is-unresolved-variable.js @@ -0,0 +1,14 @@ +import {findVariable} from '@eslint-community/eslint-utils'; + +/** +Checks if the given identifier node is shadowed in the given scope. + +@param {string} node - The identifier node to check +@param {import('eslint').Rule.RuleContext} context - The ESLint rule context. +@returns {boolean} Whether or not the name is unresolved. +*/ +export default function isUnresolvedVariable(node, context) { + const scope = context.sourceCode.getScope(node); + const variable = findVariable(scope, node); + return !variable; +} diff --git a/rules/utils/lodash.js b/rules/utils/lodash.js deleted file mode 100644 index c9ed9d8d3f..0000000000 --- a/rules/utils/lodash.js +++ /dev/null @@ -1,1567 +0,0 @@ -// node_modules/lodash-es/_freeGlobal.js -var freeGlobal = typeof global == "object" && global && global.Object === Object && global; -var freeGlobal_default = freeGlobal; - -// node_modules/lodash-es/_root.js -var freeSelf = typeof self == "object" && self && self.Object === Object && self; -var root = freeGlobal_default || freeSelf || Function("return this")(); -var root_default = root; - -// node_modules/lodash-es/_Symbol.js -var Symbol = root_default.Symbol; -var Symbol_default = Symbol; - -// node_modules/lodash-es/_getRawTag.js -var objectProto = Object.prototype; -var hasOwnProperty = objectProto.hasOwnProperty; -var nativeObjectToString = objectProto.toString; -var symToStringTag = Symbol_default ? Symbol_default.toStringTag : void 0; -function getRawTag(value) { - var isOwn = hasOwnProperty.call(value, symToStringTag), tag = value[symToStringTag]; - try { - value[symToStringTag] = void 0; - var unmasked = true; - } catch (e) { - } - var result = nativeObjectToString.call(value); - if (unmasked) { - if (isOwn) { - value[symToStringTag] = tag; - } else { - delete value[symToStringTag]; - } - } - return result; -} -var getRawTag_default = getRawTag; - -// node_modules/lodash-es/_objectToString.js -var objectProto2 = Object.prototype; -var nativeObjectToString2 = objectProto2.toString; -function objectToString(value) { - return nativeObjectToString2.call(value); -} -var objectToString_default = objectToString; - -// node_modules/lodash-es/_baseGetTag.js -var nullTag = "[object Null]"; -var undefinedTag = "[object Undefined]"; -var symToStringTag2 = Symbol_default ? Symbol_default.toStringTag : void 0; -function baseGetTag(value) { - if (value == null) { - return value === void 0 ? undefinedTag : nullTag; - } - return symToStringTag2 && symToStringTag2 in Object(value) ? getRawTag_default(value) : objectToString_default(value); -} -var baseGetTag_default = baseGetTag; - -// node_modules/lodash-es/isObjectLike.js -function isObjectLike(value) { - return value != null && typeof value == "object"; -} -var isObjectLike_default = isObjectLike; - -// node_modules/lodash-es/isSymbol.js -var symbolTag = "[object Symbol]"; -function isSymbol(value) { - return typeof value == "symbol" || isObjectLike_default(value) && baseGetTag_default(value) == symbolTag; -} -var isSymbol_default = isSymbol; - -// node_modules/lodash-es/_arrayMap.js -function arrayMap(array, iteratee) { - var index = -1, length = array == null ? 0 : array.length, result = Array(length); - while (++index < length) { - result[index] = iteratee(array[index], index, array); - } - return result; -} -var arrayMap_default = arrayMap; - -// node_modules/lodash-es/isArray.js -var isArray = Array.isArray; -var isArray_default = isArray; - -// node_modules/lodash-es/_baseToString.js -var INFINITY = 1 / 0; -var symbolProto = Symbol_default ? Symbol_default.prototype : void 0; -var symbolToString = symbolProto ? symbolProto.toString : void 0; -function baseToString(value) { - if (typeof value == "string") { - return value; - } - if (isArray_default(value)) { - return arrayMap_default(value, baseToString) + ""; - } - if (isSymbol_default(value)) { - return symbolToString ? symbolToString.call(value) : ""; - } - var result = value + ""; - return result == "0" && 1 / value == -INFINITY ? "-0" : result; -} -var baseToString_default = baseToString; - -// node_modules/lodash-es/isObject.js -function isObject(value) { - var type = typeof value; - return value != null && (type == "object" || type == "function"); -} -var isObject_default = isObject; - -// node_modules/lodash-es/identity.js -function identity(value) { - return value; -} -var identity_default = identity; - -// node_modules/lodash-es/isFunction.js -var asyncTag = "[object AsyncFunction]"; -var funcTag = "[object Function]"; -var genTag = "[object GeneratorFunction]"; -var proxyTag = "[object Proxy]"; -function isFunction(value) { - if (!isObject_default(value)) { - return false; - } - var tag = baseGetTag_default(value); - return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag; -} -var isFunction_default = isFunction; - -// node_modules/lodash-es/_coreJsData.js -var coreJsData = root_default["__core-js_shared__"]; -var coreJsData_default = coreJsData; - -// node_modules/lodash-es/_isMasked.js -var maskSrcKey = function() { - var uid = /[^.]+$/.exec(coreJsData_default && coreJsData_default.keys && coreJsData_default.keys.IE_PROTO || ""); - return uid ? "Symbol(src)_1." + uid : ""; -}(); -function isMasked(func) { - return !!maskSrcKey && maskSrcKey in func; -} -var isMasked_default = isMasked; - -// node_modules/lodash-es/_toSource.js -var funcProto = Function.prototype; -var funcToString = funcProto.toString; -function toSource(func) { - if (func != null) { - try { - return funcToString.call(func); - } catch (e) { - } - try { - return func + ""; - } catch (e) { - } - } - return ""; -} -var toSource_default = toSource; - -// node_modules/lodash-es/_baseIsNative.js -var reRegExpChar = /[\\^$.*+?()[\]{}|]/g; -var reIsHostCtor = /^\[object .+?Constructor\]$/; -var funcProto2 = Function.prototype; -var objectProto3 = Object.prototype; -var funcToString2 = funcProto2.toString; -var hasOwnProperty2 = objectProto3.hasOwnProperty; -var reIsNative = RegExp( - "^" + funcToString2.call(hasOwnProperty2).replace(reRegExpChar, "\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, "$1.*?") + "$" -); -function baseIsNative(value) { - if (!isObject_default(value) || isMasked_default(value)) { - return false; - } - var pattern = isFunction_default(value) ? reIsNative : reIsHostCtor; - return pattern.test(toSource_default(value)); -} -var baseIsNative_default = baseIsNative; - -// node_modules/lodash-es/_getValue.js -function getValue(object, key) { - return object == null ? void 0 : object[key]; -} -var getValue_default = getValue; - -// node_modules/lodash-es/_getNative.js -function getNative(object, key) { - var value = getValue_default(object, key); - return baseIsNative_default(value) ? value : void 0; -} -var getNative_default = getNative; - -// node_modules/lodash-es/_baseCreate.js -var objectCreate = Object.create; -var baseCreate = /* @__PURE__ */ function() { - function object() { - } - return function(proto) { - if (!isObject_default(proto)) { - return {}; - } - if (objectCreate) { - return objectCreate(proto); - } - object.prototype = proto; - var result = new object(); - object.prototype = void 0; - return result; - }; -}(); -var baseCreate_default = baseCreate; - -// node_modules/lodash-es/_apply.js -function apply(func, thisArg, args) { - switch (args.length) { - case 0: - return func.call(thisArg); - case 1: - return func.call(thisArg, args[0]); - case 2: - return func.call(thisArg, args[0], args[1]); - case 3: - return func.call(thisArg, args[0], args[1], args[2]); - } - return func.apply(thisArg, args); -} -var apply_default = apply; - -// node_modules/lodash-es/_copyArray.js -function copyArray(source, array) { - var index = -1, length = source.length; - array || (array = Array(length)); - while (++index < length) { - array[index] = source[index]; - } - return array; -} -var copyArray_default = copyArray; - -// node_modules/lodash-es/_shortOut.js -var HOT_COUNT = 800; -var HOT_SPAN = 16; -var nativeNow = Date.now; -function shortOut(func) { - var count = 0, lastCalled = 0; - return function() { - var stamp = nativeNow(), remaining = HOT_SPAN - (stamp - lastCalled); - lastCalled = stamp; - if (remaining > 0) { - if (++count >= HOT_COUNT) { - return arguments[0]; - } - } else { - count = 0; - } - return func.apply(void 0, arguments); - }; -} -var shortOut_default = shortOut; - -// node_modules/lodash-es/constant.js -function constant(value) { - return function() { - return value; - }; -} -var constant_default = constant; - -// node_modules/lodash-es/_defineProperty.js -var defineProperty = function() { - try { - var func = getNative_default(Object, "defineProperty"); - func({}, "", {}); - return func; - } catch (e) { - } -}(); -var defineProperty_default = defineProperty; - -// node_modules/lodash-es/_baseSetToString.js -var baseSetToString = !defineProperty_default ? identity_default : function(func, string) { - return defineProperty_default(func, "toString", { - "configurable": true, - "enumerable": false, - "value": constant_default(string), - "writable": true - }); -}; -var baseSetToString_default = baseSetToString; - -// node_modules/lodash-es/_setToString.js -var setToString = shortOut_default(baseSetToString_default); -var setToString_default = setToString; - -// node_modules/lodash-es/_isIndex.js -var MAX_SAFE_INTEGER = 9007199254740991; -var reIsUint = /^(?:0|[1-9]\d*)$/; -function isIndex(value, length) { - var type = typeof value; - length = length == null ? MAX_SAFE_INTEGER : length; - return !!length && (type == "number" || type != "symbol" && reIsUint.test(value)) && (value > -1 && value % 1 == 0 && value < length); -} -var isIndex_default = isIndex; - -// node_modules/lodash-es/_baseAssignValue.js -function baseAssignValue(object, key, value) { - if (key == "__proto__" && defineProperty_default) { - defineProperty_default(object, key, { - "configurable": true, - "enumerable": true, - "value": value, - "writable": true - }); - } else { - object[key] = value; - } -} -var baseAssignValue_default = baseAssignValue; - -// node_modules/lodash-es/eq.js -function eq(value, other) { - return value === other || value !== value && other !== other; -} -var eq_default = eq; - -// node_modules/lodash-es/_assignValue.js -var objectProto4 = Object.prototype; -var hasOwnProperty3 = objectProto4.hasOwnProperty; -function assignValue(object, key, value) { - var objValue = object[key]; - if (!(hasOwnProperty3.call(object, key) && eq_default(objValue, value)) || value === void 0 && !(key in object)) { - baseAssignValue_default(object, key, value); - } -} -var assignValue_default = assignValue; - -// node_modules/lodash-es/_copyObject.js -function copyObject(source, props, object, customizer) { - var isNew = !object; - object || (object = {}); - var index = -1, length = props.length; - while (++index < length) { - var key = props[index]; - var newValue = customizer ? customizer(object[key], source[key], key, object, source) : void 0; - if (newValue === void 0) { - newValue = source[key]; - } - if (isNew) { - baseAssignValue_default(object, key, newValue); - } else { - assignValue_default(object, key, newValue); - } - } - return object; -} -var copyObject_default = copyObject; - -// node_modules/lodash-es/_overRest.js -var nativeMax = Math.max; -function overRest(func, start, transform) { - start = nativeMax(start === void 0 ? func.length - 1 : start, 0); - return function() { - var args = arguments, index = -1, length = nativeMax(args.length - start, 0), array = Array(length); - while (++index < length) { - array[index] = args[start + index]; - } - index = -1; - var otherArgs = Array(start + 1); - while (++index < start) { - otherArgs[index] = args[index]; - } - otherArgs[start] = transform(array); - return apply_default(func, this, otherArgs); - }; -} -var overRest_default = overRest; - -// node_modules/lodash-es/_baseRest.js -function baseRest(func, start) { - return setToString_default(overRest_default(func, start, identity_default), func + ""); -} -var baseRest_default = baseRest; - -// node_modules/lodash-es/isLength.js -var MAX_SAFE_INTEGER2 = 9007199254740991; -function isLength(value) { - return typeof value == "number" && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER2; -} -var isLength_default = isLength; - -// node_modules/lodash-es/isArrayLike.js -function isArrayLike(value) { - return value != null && isLength_default(value.length) && !isFunction_default(value); -} -var isArrayLike_default = isArrayLike; - -// node_modules/lodash-es/_isIterateeCall.js -function isIterateeCall(value, index, object) { - if (!isObject_default(object)) { - return false; - } - var type = typeof index; - if (type == "number" ? isArrayLike_default(object) && isIndex_default(index, object.length) : type == "string" && index in object) { - return eq_default(object[index], value); - } - return false; -} -var isIterateeCall_default = isIterateeCall; - -// node_modules/lodash-es/_createAssigner.js -function createAssigner(assigner) { - return baseRest_default(function(object, sources) { - var index = -1, length = sources.length, customizer = length > 1 ? sources[length - 1] : void 0, guard = length > 2 ? sources[2] : void 0; - customizer = assigner.length > 3 && typeof customizer == "function" ? (length--, customizer) : void 0; - if (guard && isIterateeCall_default(sources[0], sources[1], guard)) { - customizer = length < 3 ? void 0 : customizer; - length = 1; - } - object = Object(object); - while (++index < length) { - var source = sources[index]; - if (source) { - assigner(object, source, index, customizer); - } - } - return object; - }); -} -var createAssigner_default = createAssigner; - -// node_modules/lodash-es/_isPrototype.js -var objectProto5 = Object.prototype; -function isPrototype(value) { - var Ctor = value && value.constructor, proto = typeof Ctor == "function" && Ctor.prototype || objectProto5; - return value === proto; -} -var isPrototype_default = isPrototype; - -// node_modules/lodash-es/_baseTimes.js -function baseTimes(n, iteratee) { - var index = -1, result = Array(n); - while (++index < n) { - result[index] = iteratee(index); - } - return result; -} -var baseTimes_default = baseTimes; - -// node_modules/lodash-es/_baseIsArguments.js -var argsTag = "[object Arguments]"; -function baseIsArguments(value) { - return isObjectLike_default(value) && baseGetTag_default(value) == argsTag; -} -var baseIsArguments_default = baseIsArguments; - -// node_modules/lodash-es/isArguments.js -var objectProto6 = Object.prototype; -var hasOwnProperty4 = objectProto6.hasOwnProperty; -var propertyIsEnumerable = objectProto6.propertyIsEnumerable; -var isArguments = baseIsArguments_default(/* @__PURE__ */ function() { - return arguments; -}()) ? baseIsArguments_default : function(value) { - return isObjectLike_default(value) && hasOwnProperty4.call(value, "callee") && !propertyIsEnumerable.call(value, "callee"); -}; -var isArguments_default = isArguments; - -// node_modules/lodash-es/stubFalse.js -function stubFalse() { - return false; -} -var stubFalse_default = stubFalse; - -// node_modules/lodash-es/isBuffer.js -var freeExports = typeof exports == "object" && exports && !exports.nodeType && exports; -var freeModule = freeExports && typeof module == "object" && module && !module.nodeType && module; -var moduleExports = freeModule && freeModule.exports === freeExports; -var Buffer = moduleExports ? root_default.Buffer : void 0; -var nativeIsBuffer = Buffer ? Buffer.isBuffer : void 0; -var isBuffer = nativeIsBuffer || stubFalse_default; -var isBuffer_default = isBuffer; - -// node_modules/lodash-es/_baseIsTypedArray.js -var argsTag2 = "[object Arguments]"; -var arrayTag = "[object Array]"; -var boolTag = "[object Boolean]"; -var dateTag = "[object Date]"; -var errorTag = "[object Error]"; -var funcTag2 = "[object Function]"; -var mapTag = "[object Map]"; -var numberTag = "[object Number]"; -var objectTag = "[object Object]"; -var regexpTag = "[object RegExp]"; -var setTag = "[object Set]"; -var stringTag = "[object String]"; -var weakMapTag = "[object WeakMap]"; -var arrayBufferTag = "[object ArrayBuffer]"; -var dataViewTag = "[object DataView]"; -var float32Tag = "[object Float32Array]"; -var float64Tag = "[object Float64Array]"; -var int8Tag = "[object Int8Array]"; -var int16Tag = "[object Int16Array]"; -var int32Tag = "[object Int32Array]"; -var uint8Tag = "[object Uint8Array]"; -var uint8ClampedTag = "[object Uint8ClampedArray]"; -var uint16Tag = "[object Uint16Array]"; -var uint32Tag = "[object Uint32Array]"; -var typedArrayTags = {}; -typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = typedArrayTags[uint32Tag] = true; -typedArrayTags[argsTag2] = typedArrayTags[arrayTag] = typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = typedArrayTags[dataViewTag] = typedArrayTags[dateTag] = typedArrayTags[errorTag] = typedArrayTags[funcTag2] = typedArrayTags[mapTag] = typedArrayTags[numberTag] = typedArrayTags[objectTag] = typedArrayTags[regexpTag] = typedArrayTags[setTag] = typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false; -function baseIsTypedArray(value) { - return isObjectLike_default(value) && isLength_default(value.length) && !!typedArrayTags[baseGetTag_default(value)]; -} -var baseIsTypedArray_default = baseIsTypedArray; - -// node_modules/lodash-es/_baseUnary.js -function baseUnary(func) { - return function(value) { - return func(value); - }; -} -var baseUnary_default = baseUnary; - -// node_modules/lodash-es/_nodeUtil.js -var freeExports2 = typeof exports == "object" && exports && !exports.nodeType && exports; -var freeModule2 = freeExports2 && typeof module == "object" && module && !module.nodeType && module; -var moduleExports2 = freeModule2 && freeModule2.exports === freeExports2; -var freeProcess = moduleExports2 && freeGlobal_default.process; -var nodeUtil = function() { - try { - var types = freeModule2 && freeModule2.require && freeModule2.require("util").types; - if (types) { - return types; - } - return freeProcess && freeProcess.binding && freeProcess.binding("util"); - } catch (e) { - } -}(); -var nodeUtil_default = nodeUtil; - -// node_modules/lodash-es/isTypedArray.js -var nodeIsTypedArray = nodeUtil_default && nodeUtil_default.isTypedArray; -var isTypedArray = nodeIsTypedArray ? baseUnary_default(nodeIsTypedArray) : baseIsTypedArray_default; -var isTypedArray_default = isTypedArray; - -// node_modules/lodash-es/_arrayLikeKeys.js -var objectProto7 = Object.prototype; -var hasOwnProperty5 = objectProto7.hasOwnProperty; -function arrayLikeKeys(value, inherited) { - var isArr = isArray_default(value), isArg = !isArr && isArguments_default(value), isBuff = !isArr && !isArg && isBuffer_default(value), isType = !isArr && !isArg && !isBuff && isTypedArray_default(value), skipIndexes = isArr || isArg || isBuff || isType, result = skipIndexes ? baseTimes_default(value.length, String) : [], length = result.length; - for (var key in value) { - if ((inherited || hasOwnProperty5.call(value, key)) && !(skipIndexes && // Safari 9 has enumerable `arguments.length` in strict mode. - (key == "length" || // Node.js 0.10 has enumerable non-index properties on buffers. - isBuff && (key == "offset" || key == "parent") || // PhantomJS 2 has enumerable non-index properties on typed arrays. - isType && (key == "buffer" || key == "byteLength" || key == "byteOffset") || // Skip index properties. - isIndex_default(key, length)))) { - result.push(key); - } - } - return result; -} -var arrayLikeKeys_default = arrayLikeKeys; - -// node_modules/lodash-es/_overArg.js -function overArg(func, transform) { - return function(arg) { - return func(transform(arg)); - }; -} -var overArg_default = overArg; - -// node_modules/lodash-es/_nativeKeysIn.js -function nativeKeysIn(object) { - var result = []; - if (object != null) { - for (var key in Object(object)) { - result.push(key); - } - } - return result; -} -var nativeKeysIn_default = nativeKeysIn; - -// node_modules/lodash-es/_baseKeysIn.js -var objectProto8 = Object.prototype; -var hasOwnProperty6 = objectProto8.hasOwnProperty; -function baseKeysIn(object) { - if (!isObject_default(object)) { - return nativeKeysIn_default(object); - } - var isProto = isPrototype_default(object), result = []; - for (var key in object) { - if (!(key == "constructor" && (isProto || !hasOwnProperty6.call(object, key)))) { - result.push(key); - } - } - return result; -} -var baseKeysIn_default = baseKeysIn; - -// node_modules/lodash-es/keysIn.js -function keysIn(object) { - return isArrayLike_default(object) ? arrayLikeKeys_default(object, true) : baseKeysIn_default(object); -} -var keysIn_default = keysIn; - -// node_modules/lodash-es/_nativeCreate.js -var nativeCreate = getNative_default(Object, "create"); -var nativeCreate_default = nativeCreate; - -// node_modules/lodash-es/_hashClear.js -function hashClear() { - this.__data__ = nativeCreate_default ? nativeCreate_default(null) : {}; - this.size = 0; -} -var hashClear_default = hashClear; - -// node_modules/lodash-es/_hashDelete.js -function hashDelete(key) { - var result = this.has(key) && delete this.__data__[key]; - this.size -= result ? 1 : 0; - return result; -} -var hashDelete_default = hashDelete; - -// node_modules/lodash-es/_hashGet.js -var HASH_UNDEFINED = "__lodash_hash_undefined__"; -var objectProto9 = Object.prototype; -var hasOwnProperty7 = objectProto9.hasOwnProperty; -function hashGet(key) { - var data = this.__data__; - if (nativeCreate_default) { - var result = data[key]; - return result === HASH_UNDEFINED ? void 0 : result; - } - return hasOwnProperty7.call(data, key) ? data[key] : void 0; -} -var hashGet_default = hashGet; - -// node_modules/lodash-es/_hashHas.js -var objectProto10 = Object.prototype; -var hasOwnProperty8 = objectProto10.hasOwnProperty; -function hashHas(key) { - var data = this.__data__; - return nativeCreate_default ? data[key] !== void 0 : hasOwnProperty8.call(data, key); -} -var hashHas_default = hashHas; - -// node_modules/lodash-es/_hashSet.js -var HASH_UNDEFINED2 = "__lodash_hash_undefined__"; -function hashSet(key, value) { - var data = this.__data__; - this.size += this.has(key) ? 0 : 1; - data[key] = nativeCreate_default && value === void 0 ? HASH_UNDEFINED2 : value; - return this; -} -var hashSet_default = hashSet; - -// node_modules/lodash-es/_Hash.js -function Hash(entries) { - var index = -1, length = entries == null ? 0 : entries.length; - this.clear(); - while (++index < length) { - var entry = entries[index]; - this.set(entry[0], entry[1]); - } -} -Hash.prototype.clear = hashClear_default; -Hash.prototype["delete"] = hashDelete_default; -Hash.prototype.get = hashGet_default; -Hash.prototype.has = hashHas_default; -Hash.prototype.set = hashSet_default; -var Hash_default = Hash; - -// node_modules/lodash-es/_listCacheClear.js -function listCacheClear() { - this.__data__ = []; - this.size = 0; -} -var listCacheClear_default = listCacheClear; - -// node_modules/lodash-es/_assocIndexOf.js -function assocIndexOf(array, key) { - var length = array.length; - while (length--) { - if (eq_default(array[length][0], key)) { - return length; - } - } - return -1; -} -var assocIndexOf_default = assocIndexOf; - -// node_modules/lodash-es/_listCacheDelete.js -var arrayProto = Array.prototype; -var splice = arrayProto.splice; -function listCacheDelete(key) { - var data = this.__data__, index = assocIndexOf_default(data, key); - if (index < 0) { - return false; - } - var lastIndex = data.length - 1; - if (index == lastIndex) { - data.pop(); - } else { - splice.call(data, index, 1); - } - --this.size; - return true; -} -var listCacheDelete_default = listCacheDelete; - -// node_modules/lodash-es/_listCacheGet.js -function listCacheGet(key) { - var data = this.__data__, index = assocIndexOf_default(data, key); - return index < 0 ? void 0 : data[index][1]; -} -var listCacheGet_default = listCacheGet; - -// node_modules/lodash-es/_listCacheHas.js -function listCacheHas(key) { - return assocIndexOf_default(this.__data__, key) > -1; -} -var listCacheHas_default = listCacheHas; - -// node_modules/lodash-es/_listCacheSet.js -function listCacheSet(key, value) { - var data = this.__data__, index = assocIndexOf_default(data, key); - if (index < 0) { - ++this.size; - data.push([key, value]); - } else { - data[index][1] = value; - } - return this; -} -var listCacheSet_default = listCacheSet; - -// node_modules/lodash-es/_ListCache.js -function ListCache(entries) { - var index = -1, length = entries == null ? 0 : entries.length; - this.clear(); - while (++index < length) { - var entry = entries[index]; - this.set(entry[0], entry[1]); - } -} -ListCache.prototype.clear = listCacheClear_default; -ListCache.prototype["delete"] = listCacheDelete_default; -ListCache.prototype.get = listCacheGet_default; -ListCache.prototype.has = listCacheHas_default; -ListCache.prototype.set = listCacheSet_default; -var ListCache_default = ListCache; - -// node_modules/lodash-es/_Map.js -var Map = getNative_default(root_default, "Map"); -var Map_default = Map; - -// node_modules/lodash-es/_mapCacheClear.js -function mapCacheClear() { - this.size = 0; - this.__data__ = { - "hash": new Hash_default(), - "map": new (Map_default || ListCache_default)(), - "string": new Hash_default() - }; -} -var mapCacheClear_default = mapCacheClear; - -// node_modules/lodash-es/_isKeyable.js -function isKeyable(value) { - var type = typeof value; - return type == "string" || type == "number" || type == "symbol" || type == "boolean" ? value !== "__proto__" : value === null; -} -var isKeyable_default = isKeyable; - -// node_modules/lodash-es/_getMapData.js -function getMapData(map, key) { - var data = map.__data__; - return isKeyable_default(key) ? data[typeof key == "string" ? "string" : "hash"] : data.map; -} -var getMapData_default = getMapData; - -// node_modules/lodash-es/_mapCacheDelete.js -function mapCacheDelete(key) { - var result = getMapData_default(this, key)["delete"](key); - this.size -= result ? 1 : 0; - return result; -} -var mapCacheDelete_default = mapCacheDelete; - -// node_modules/lodash-es/_mapCacheGet.js -function mapCacheGet(key) { - return getMapData_default(this, key).get(key); -} -var mapCacheGet_default = mapCacheGet; - -// node_modules/lodash-es/_mapCacheHas.js -function mapCacheHas(key) { - return getMapData_default(this, key).has(key); -} -var mapCacheHas_default = mapCacheHas; - -// node_modules/lodash-es/_mapCacheSet.js -function mapCacheSet(key, value) { - var data = getMapData_default(this, key), size = data.size; - data.set(key, value); - this.size += data.size == size ? 0 : 1; - return this; -} -var mapCacheSet_default = mapCacheSet; - -// node_modules/lodash-es/_MapCache.js -function MapCache(entries) { - var index = -1, length = entries == null ? 0 : entries.length; - this.clear(); - while (++index < length) { - var entry = entries[index]; - this.set(entry[0], entry[1]); - } -} -MapCache.prototype.clear = mapCacheClear_default; -MapCache.prototype["delete"] = mapCacheDelete_default; -MapCache.prototype.get = mapCacheGet_default; -MapCache.prototype.has = mapCacheHas_default; -MapCache.prototype.set = mapCacheSet_default; -var MapCache_default = MapCache; - -// node_modules/lodash-es/toString.js -function toString(value) { - return value == null ? "" : baseToString_default(value); -} -var toString_default = toString; - -// node_modules/lodash-es/_getPrototype.js -var getPrototype = overArg_default(Object.getPrototypeOf, Object); -var getPrototype_default = getPrototype; - -// node_modules/lodash-es/isPlainObject.js -var objectTag2 = "[object Object]"; -var funcProto3 = Function.prototype; -var objectProto11 = Object.prototype; -var funcToString3 = funcProto3.toString; -var hasOwnProperty9 = objectProto11.hasOwnProperty; -var objectCtorString = funcToString3.call(Object); -function isPlainObject(value) { - if (!isObjectLike_default(value) || baseGetTag_default(value) != objectTag2) { - return false; - } - var proto = getPrototype_default(value); - if (proto === null) { - return true; - } - var Ctor = hasOwnProperty9.call(proto, "constructor") && proto.constructor; - return typeof Ctor == "function" && Ctor instanceof Ctor && funcToString3.call(Ctor) == objectCtorString; -} -var isPlainObject_default = isPlainObject; - -// node_modules/lodash-es/_baseSlice.js -function baseSlice(array, start, end) { - var index = -1, length = array.length; - if (start < 0) { - start = -start > length ? 0 : length + start; - } - end = end > length ? length : end; - if (end < 0) { - end += length; - } - length = start > end ? 0 : end - start >>> 0; - start >>>= 0; - var result = Array(length); - while (++index < length) { - result[index] = array[index + start]; - } - return result; -} -var baseSlice_default = baseSlice; - -// node_modules/lodash-es/_castSlice.js -function castSlice(array, start, end) { - var length = array.length; - end = end === void 0 ? length : end; - return !start && end >= length ? array : baseSlice_default(array, start, end); -} -var castSlice_default = castSlice; - -// node_modules/lodash-es/_hasUnicode.js -var rsAstralRange = "\\ud800-\\udfff"; -var rsComboMarksRange = "\\u0300-\\u036f"; -var reComboHalfMarksRange = "\\ufe20-\\ufe2f"; -var rsComboSymbolsRange = "\\u20d0-\\u20ff"; -var rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange; -var rsVarRange = "\\ufe0e\\ufe0f"; -var rsZWJ = "\\u200d"; -var reHasUnicode = RegExp("[" + rsZWJ + rsAstralRange + rsComboRange + rsVarRange + "]"); -function hasUnicode(string) { - return reHasUnicode.test(string); -} -var hasUnicode_default = hasUnicode; - -// node_modules/lodash-es/_asciiToArray.js -function asciiToArray(string) { - return string.split(""); -} -var asciiToArray_default = asciiToArray; - -// node_modules/lodash-es/_unicodeToArray.js -var rsAstralRange2 = "\\ud800-\\udfff"; -var rsComboMarksRange2 = "\\u0300-\\u036f"; -var reComboHalfMarksRange2 = "\\ufe20-\\ufe2f"; -var rsComboSymbolsRange2 = "\\u20d0-\\u20ff"; -var rsComboRange2 = rsComboMarksRange2 + reComboHalfMarksRange2 + rsComboSymbolsRange2; -var rsVarRange2 = "\\ufe0e\\ufe0f"; -var rsAstral = "[" + rsAstralRange2 + "]"; -var rsCombo = "[" + rsComboRange2 + "]"; -var rsFitz = "\\ud83c[\\udffb-\\udfff]"; -var rsModifier = "(?:" + rsCombo + "|" + rsFitz + ")"; -var rsNonAstral = "[^" + rsAstralRange2 + "]"; -var rsRegional = "(?:\\ud83c[\\udde6-\\uddff]){2}"; -var rsSurrPair = "[\\ud800-\\udbff][\\udc00-\\udfff]"; -var rsZWJ2 = "\\u200d"; -var reOptMod = rsModifier + "?"; -var rsOptVar = "[" + rsVarRange2 + "]?"; -var rsOptJoin = "(?:" + rsZWJ2 + "(?:" + [rsNonAstral, rsRegional, rsSurrPair].join("|") + ")" + rsOptVar + reOptMod + ")*"; -var rsSeq = rsOptVar + reOptMod + rsOptJoin; -var rsSymbol = "(?:" + [rsNonAstral + rsCombo + "?", rsCombo, rsRegional, rsSurrPair, rsAstral].join("|") + ")"; -var reUnicode = RegExp(rsFitz + "(?=" + rsFitz + ")|" + rsSymbol + rsSeq, "g"); -function unicodeToArray(string) { - return string.match(reUnicode) || []; -} -var unicodeToArray_default = unicodeToArray; - -// node_modules/lodash-es/_stringToArray.js -function stringToArray(string) { - return hasUnicode_default(string) ? unicodeToArray_default(string) : asciiToArray_default(string); -} -var stringToArray_default = stringToArray; - -// node_modules/lodash-es/_createCaseFirst.js -function createCaseFirst(methodName) { - return function(string) { - string = toString_default(string); - var strSymbols = hasUnicode_default(string) ? stringToArray_default(string) : void 0; - var chr = strSymbols ? strSymbols[0] : string.charAt(0); - var trailing = strSymbols ? castSlice_default(strSymbols, 1).join("") : string.slice(1); - return chr[methodName]() + trailing; - }; -} -var createCaseFirst_default = createCaseFirst; - -// node_modules/lodash-es/upperFirst.js -var upperFirst = createCaseFirst_default("toUpperCase"); -var upperFirst_default = upperFirst; - -// node_modules/lodash-es/capitalize.js -function capitalize(string) { - return upperFirst_default(toString_default(string).toLowerCase()); -} -var capitalize_default = capitalize; - -// node_modules/lodash-es/_arrayReduce.js -function arrayReduce(array, iteratee, accumulator, initAccum) { - var index = -1, length = array == null ? 0 : array.length; - if (initAccum && length) { - accumulator = array[++index]; - } - while (++index < length) { - accumulator = iteratee(accumulator, array[index], index, array); - } - return accumulator; -} -var arrayReduce_default = arrayReduce; - -// node_modules/lodash-es/_basePropertyOf.js -function basePropertyOf(object) { - return function(key) { - return object == null ? void 0 : object[key]; - }; -} -var basePropertyOf_default = basePropertyOf; - -// node_modules/lodash-es/_deburrLetter.js -var deburredLetters = { - // Latin-1 Supplement block. - "\xC0": "A", - "\xC1": "A", - "\xC2": "A", - "\xC3": "A", - "\xC4": "A", - "\xC5": "A", - "\xE0": "a", - "\xE1": "a", - "\xE2": "a", - "\xE3": "a", - "\xE4": "a", - "\xE5": "a", - "\xC7": "C", - "\xE7": "c", - "\xD0": "D", - "\xF0": "d", - "\xC8": "E", - "\xC9": "E", - "\xCA": "E", - "\xCB": "E", - "\xE8": "e", - "\xE9": "e", - "\xEA": "e", - "\xEB": "e", - "\xCC": "I", - "\xCD": "I", - "\xCE": "I", - "\xCF": "I", - "\xEC": "i", - "\xED": "i", - "\xEE": "i", - "\xEF": "i", - "\xD1": "N", - "\xF1": "n", - "\xD2": "O", - "\xD3": "O", - "\xD4": "O", - "\xD5": "O", - "\xD6": "O", - "\xD8": "O", - "\xF2": "o", - "\xF3": "o", - "\xF4": "o", - "\xF5": "o", - "\xF6": "o", - "\xF8": "o", - "\xD9": "U", - "\xDA": "U", - "\xDB": "U", - "\xDC": "U", - "\xF9": "u", - "\xFA": "u", - "\xFB": "u", - "\xFC": "u", - "\xDD": "Y", - "\xFD": "y", - "\xFF": "y", - "\xC6": "Ae", - "\xE6": "ae", - "\xDE": "Th", - "\xFE": "th", - "\xDF": "ss", - // Latin Extended-A block. - "\u0100": "A", - "\u0102": "A", - "\u0104": "A", - "\u0101": "a", - "\u0103": "a", - "\u0105": "a", - "\u0106": "C", - "\u0108": "C", - "\u010A": "C", - "\u010C": "C", - "\u0107": "c", - "\u0109": "c", - "\u010B": "c", - "\u010D": "c", - "\u010E": "D", - "\u0110": "D", - "\u010F": "d", - "\u0111": "d", - "\u0112": "E", - "\u0114": "E", - "\u0116": "E", - "\u0118": "E", - "\u011A": "E", - "\u0113": "e", - "\u0115": "e", - "\u0117": "e", - "\u0119": "e", - "\u011B": "e", - "\u011C": "G", - "\u011E": "G", - "\u0120": "G", - "\u0122": "G", - "\u011D": "g", - "\u011F": "g", - "\u0121": "g", - "\u0123": "g", - "\u0124": "H", - "\u0126": "H", - "\u0125": "h", - "\u0127": "h", - "\u0128": "I", - "\u012A": "I", - "\u012C": "I", - "\u012E": "I", - "\u0130": "I", - "\u0129": "i", - "\u012B": "i", - "\u012D": "i", - "\u012F": "i", - "\u0131": "i", - "\u0134": "J", - "\u0135": "j", - "\u0136": "K", - "\u0137": "k", - "\u0138": "k", - "\u0139": "L", - "\u013B": "L", - "\u013D": "L", - "\u013F": "L", - "\u0141": "L", - "\u013A": "l", - "\u013C": "l", - "\u013E": "l", - "\u0140": "l", - "\u0142": "l", - "\u0143": "N", - "\u0145": "N", - "\u0147": "N", - "\u014A": "N", - "\u0144": "n", - "\u0146": "n", - "\u0148": "n", - "\u014B": "n", - "\u014C": "O", - "\u014E": "O", - "\u0150": "O", - "\u014D": "o", - "\u014F": "o", - "\u0151": "o", - "\u0154": "R", - "\u0156": "R", - "\u0158": "R", - "\u0155": "r", - "\u0157": "r", - "\u0159": "r", - "\u015A": "S", - "\u015C": "S", - "\u015E": "S", - "\u0160": "S", - "\u015B": "s", - "\u015D": "s", - "\u015F": "s", - "\u0161": "s", - "\u0162": "T", - "\u0164": "T", - "\u0166": "T", - "\u0163": "t", - "\u0165": "t", - "\u0167": "t", - "\u0168": "U", - "\u016A": "U", - "\u016C": "U", - "\u016E": "U", - "\u0170": "U", - "\u0172": "U", - "\u0169": "u", - "\u016B": "u", - "\u016D": "u", - "\u016F": "u", - "\u0171": "u", - "\u0173": "u", - "\u0174": "W", - "\u0175": "w", - "\u0176": "Y", - "\u0177": "y", - "\u0178": "Y", - "\u0179": "Z", - "\u017B": "Z", - "\u017D": "Z", - "\u017A": "z", - "\u017C": "z", - "\u017E": "z", - "\u0132": "IJ", - "\u0133": "ij", - "\u0152": "Oe", - "\u0153": "oe", - "\u0149": "'n", - "\u017F": "s" -}; -var deburrLetter = basePropertyOf_default(deburredLetters); -var deburrLetter_default = deburrLetter; - -// node_modules/lodash-es/deburr.js -var reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g; -var rsComboMarksRange3 = "\\u0300-\\u036f"; -var reComboHalfMarksRange3 = "\\ufe20-\\ufe2f"; -var rsComboSymbolsRange3 = "\\u20d0-\\u20ff"; -var rsComboRange3 = rsComboMarksRange3 + reComboHalfMarksRange3 + rsComboSymbolsRange3; -var rsCombo2 = "[" + rsComboRange3 + "]"; -var reComboMark = RegExp(rsCombo2, "g"); -function deburr(string) { - string = toString_default(string); - return string && string.replace(reLatin, deburrLetter_default).replace(reComboMark, ""); -} -var deburr_default = deburr; - -// node_modules/lodash-es/_asciiWords.js -var reAsciiWord = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g; -function asciiWords(string) { - return string.match(reAsciiWord) || []; -} -var asciiWords_default = asciiWords; - -// node_modules/lodash-es/_hasUnicodeWord.js -var reHasUnicodeWord = /[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/; -function hasUnicodeWord(string) { - return reHasUnicodeWord.test(string); -} -var hasUnicodeWord_default = hasUnicodeWord; - -// node_modules/lodash-es/_unicodeWords.js -var rsAstralRange3 = "\\ud800-\\udfff"; -var rsComboMarksRange4 = "\\u0300-\\u036f"; -var reComboHalfMarksRange4 = "\\ufe20-\\ufe2f"; -var rsComboSymbolsRange4 = "\\u20d0-\\u20ff"; -var rsComboRange4 = rsComboMarksRange4 + reComboHalfMarksRange4 + rsComboSymbolsRange4; -var rsDingbatRange = "\\u2700-\\u27bf"; -var rsLowerRange = "a-z\\xdf-\\xf6\\xf8-\\xff"; -var rsMathOpRange = "\\xac\\xb1\\xd7\\xf7"; -var rsNonCharRange = "\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf"; -var rsPunctuationRange = "\\u2000-\\u206f"; -var rsSpaceRange = " \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000"; -var rsUpperRange = "A-Z\\xc0-\\xd6\\xd8-\\xde"; -var rsVarRange3 = "\\ufe0e\\ufe0f"; -var rsBreakRange = rsMathOpRange + rsNonCharRange + rsPunctuationRange + rsSpaceRange; -var rsApos = "['\u2019]"; -var rsBreak = "[" + rsBreakRange + "]"; -var rsCombo3 = "[" + rsComboRange4 + "]"; -var rsDigits = "\\d+"; -var rsDingbat = "[" + rsDingbatRange + "]"; -var rsLower = "[" + rsLowerRange + "]"; -var rsMisc = "[^" + rsAstralRange3 + rsBreakRange + rsDigits + rsDingbatRange + rsLowerRange + rsUpperRange + "]"; -var rsFitz2 = "\\ud83c[\\udffb-\\udfff]"; -var rsModifier2 = "(?:" + rsCombo3 + "|" + rsFitz2 + ")"; -var rsNonAstral2 = "[^" + rsAstralRange3 + "]"; -var rsRegional2 = "(?:\\ud83c[\\udde6-\\uddff]){2}"; -var rsSurrPair2 = "[\\ud800-\\udbff][\\udc00-\\udfff]"; -var rsUpper = "[" + rsUpperRange + "]"; -var rsZWJ3 = "\\u200d"; -var rsMiscLower = "(?:" + rsLower + "|" + rsMisc + ")"; -var rsMiscUpper = "(?:" + rsUpper + "|" + rsMisc + ")"; -var rsOptContrLower = "(?:" + rsApos + "(?:d|ll|m|re|s|t|ve))?"; -var rsOptContrUpper = "(?:" + rsApos + "(?:D|LL|M|RE|S|T|VE))?"; -var reOptMod2 = rsModifier2 + "?"; -var rsOptVar2 = "[" + rsVarRange3 + "]?"; -var rsOptJoin2 = "(?:" + rsZWJ3 + "(?:" + [rsNonAstral2, rsRegional2, rsSurrPair2].join("|") + ")" + rsOptVar2 + reOptMod2 + ")*"; -var rsOrdLower = "\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])"; -var rsOrdUpper = "\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])"; -var rsSeq2 = rsOptVar2 + reOptMod2 + rsOptJoin2; -var rsEmoji = "(?:" + [rsDingbat, rsRegional2, rsSurrPair2].join("|") + ")" + rsSeq2; -var reUnicodeWord = RegExp([ - rsUpper + "?" + rsLower + "+" + rsOptContrLower + "(?=" + [rsBreak, rsUpper, "$"].join("|") + ")", - rsMiscUpper + "+" + rsOptContrUpper + "(?=" + [rsBreak, rsUpper + rsMiscLower, "$"].join("|") + ")", - rsUpper + "?" + rsMiscLower + "+" + rsOptContrLower, - rsUpper + "+" + rsOptContrUpper, - rsOrdUpper, - rsOrdLower, - rsDigits, - rsEmoji -].join("|"), "g"); -function unicodeWords(string) { - return string.match(reUnicodeWord) || []; -} -var unicodeWords_default = unicodeWords; - -// node_modules/lodash-es/words.js -function words(string, pattern, guard) { - string = toString_default(string); - pattern = guard ? void 0 : pattern; - if (pattern === void 0) { - return hasUnicodeWord_default(string) ? unicodeWords_default(string) : asciiWords_default(string); - } - return string.match(pattern) || []; -} -var words_default = words; - -// node_modules/lodash-es/_createCompounder.js -var rsApos2 = "['\u2019]"; -var reApos = RegExp(rsApos2, "g"); -function createCompounder(callback) { - return function(string) { - return arrayReduce_default(words_default(deburr_default(string).replace(reApos, "")), callback, ""); - }; -} -var createCompounder_default = createCompounder; - -// node_modules/lodash-es/camelCase.js -var camelCase = createCompounder_default(function(result, word, index) { - word = word.toLowerCase(); - return result + (index ? capitalize_default(word) : word); -}); -var camelCase_default = camelCase; - -// node_modules/lodash-es/_stackClear.js -function stackClear() { - this.__data__ = new ListCache_default(); - this.size = 0; -} -var stackClear_default = stackClear; - -// node_modules/lodash-es/_stackDelete.js -function stackDelete(key) { - var data = this.__data__, result = data["delete"](key); - this.size = data.size; - return result; -} -var stackDelete_default = stackDelete; - -// node_modules/lodash-es/_stackGet.js -function stackGet(key) { - return this.__data__.get(key); -} -var stackGet_default = stackGet; - -// node_modules/lodash-es/_stackHas.js -function stackHas(key) { - return this.__data__.has(key); -} -var stackHas_default = stackHas; - -// node_modules/lodash-es/_stackSet.js -var LARGE_ARRAY_SIZE = 200; -function stackSet(key, value) { - var data = this.__data__; - if (data instanceof ListCache_default) { - var pairs = data.__data__; - if (!Map_default || pairs.length < LARGE_ARRAY_SIZE - 1) { - pairs.push([key, value]); - this.size = ++data.size; - return this; - } - data = this.__data__ = new MapCache_default(pairs); - } - data.set(key, value); - this.size = data.size; - return this; -} -var stackSet_default = stackSet; - -// node_modules/lodash-es/_Stack.js -function Stack(entries) { - var data = this.__data__ = new ListCache_default(entries); - this.size = data.size; -} -Stack.prototype.clear = stackClear_default; -Stack.prototype["delete"] = stackDelete_default; -Stack.prototype.get = stackGet_default; -Stack.prototype.has = stackHas_default; -Stack.prototype.set = stackSet_default; -var Stack_default = Stack; - -// node_modules/lodash-es/_cloneBuffer.js -var freeExports3 = typeof exports == "object" && exports && !exports.nodeType && exports; -var freeModule3 = freeExports3 && typeof module == "object" && module && !module.nodeType && module; -var moduleExports3 = freeModule3 && freeModule3.exports === freeExports3; -var Buffer2 = moduleExports3 ? root_default.Buffer : void 0; -var allocUnsafe = Buffer2 ? Buffer2.allocUnsafe : void 0; -function cloneBuffer(buffer, isDeep) { - if (isDeep) { - return buffer.slice(); - } - var length = buffer.length, result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length); - buffer.copy(result); - return result; -} -var cloneBuffer_default = cloneBuffer; - -// node_modules/lodash-es/_Uint8Array.js -var Uint8Array = root_default.Uint8Array; -var Uint8Array_default = Uint8Array; - -// node_modules/lodash-es/_cloneArrayBuffer.js -function cloneArrayBuffer(arrayBuffer) { - var result = new arrayBuffer.constructor(arrayBuffer.byteLength); - new Uint8Array_default(result).set(new Uint8Array_default(arrayBuffer)); - return result; -} -var cloneArrayBuffer_default = cloneArrayBuffer; - -// node_modules/lodash-es/_cloneTypedArray.js -function cloneTypedArray(typedArray, isDeep) { - var buffer = isDeep ? cloneArrayBuffer_default(typedArray.buffer) : typedArray.buffer; - return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length); -} -var cloneTypedArray_default = cloneTypedArray; - -// node_modules/lodash-es/_initCloneObject.js -function initCloneObject(object) { - return typeof object.constructor == "function" && !isPrototype_default(object) ? baseCreate_default(getPrototype_default(object)) : {}; -} -var initCloneObject_default = initCloneObject; - -// node_modules/lodash-es/_createBaseFor.js -function createBaseFor(fromRight) { - return function(object, iteratee, keysFunc) { - var index = -1, iterable = Object(object), props = keysFunc(object), length = props.length; - while (length--) { - var key = props[fromRight ? length : ++index]; - if (iteratee(iterable[key], key, iterable) === false) { - break; - } - } - return object; - }; -} -var createBaseFor_default = createBaseFor; - -// node_modules/lodash-es/_baseFor.js -var baseFor = createBaseFor_default(); -var baseFor_default = baseFor; - -// node_modules/lodash-es/_assignMergeValue.js -function assignMergeValue(object, key, value) { - if (value !== void 0 && !eq_default(object[key], value) || value === void 0 && !(key in object)) { - baseAssignValue_default(object, key, value); - } -} -var assignMergeValue_default = assignMergeValue; - -// node_modules/lodash-es/isArrayLikeObject.js -function isArrayLikeObject(value) { - return isObjectLike_default(value) && isArrayLike_default(value); -} -var isArrayLikeObject_default = isArrayLikeObject; - -// node_modules/lodash-es/_safeGet.js -function safeGet(object, key) { - if (key === "constructor" && typeof object[key] === "function") { - return; - } - if (key == "__proto__") { - return; - } - return object[key]; -} -var safeGet_default = safeGet; - -// node_modules/lodash-es/toPlainObject.js -function toPlainObject(value) { - return copyObject_default(value, keysIn_default(value)); -} -var toPlainObject_default = toPlainObject; - -// node_modules/lodash-es/_baseMergeDeep.js -function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) { - var objValue = safeGet_default(object, key), srcValue = safeGet_default(source, key), stacked = stack.get(srcValue); - if (stacked) { - assignMergeValue_default(object, key, stacked); - return; - } - var newValue = customizer ? customizer(objValue, srcValue, key + "", object, source, stack) : void 0; - var isCommon = newValue === void 0; - if (isCommon) { - var isArr = isArray_default(srcValue), isBuff = !isArr && isBuffer_default(srcValue), isTyped = !isArr && !isBuff && isTypedArray_default(srcValue); - newValue = srcValue; - if (isArr || isBuff || isTyped) { - if (isArray_default(objValue)) { - newValue = objValue; - } else if (isArrayLikeObject_default(objValue)) { - newValue = copyArray_default(objValue); - } else if (isBuff) { - isCommon = false; - newValue = cloneBuffer_default(srcValue, true); - } else if (isTyped) { - isCommon = false; - newValue = cloneTypedArray_default(srcValue, true); - } else { - newValue = []; - } - } else if (isPlainObject_default(srcValue) || isArguments_default(srcValue)) { - newValue = objValue; - if (isArguments_default(objValue)) { - newValue = toPlainObject_default(objValue); - } else if (!isObject_default(objValue) || isFunction_default(objValue)) { - newValue = initCloneObject_default(srcValue); - } - } else { - isCommon = false; - } - } - if (isCommon) { - stack.set(srcValue, newValue); - mergeFunc(newValue, srcValue, srcIndex, customizer, stack); - stack["delete"](srcValue); - } - assignMergeValue_default(object, key, newValue); -} -var baseMergeDeep_default = baseMergeDeep; - -// node_modules/lodash-es/_baseMerge.js -function baseMerge(object, source, srcIndex, customizer, stack) { - if (object === source) { - return; - } - baseFor_default(source, function(srcValue, key) { - stack || (stack = new Stack_default()); - if (isObject_default(srcValue)) { - baseMergeDeep_default(object, source, key, srcIndex, baseMerge, customizer, stack); - } else { - var newValue = customizer ? customizer(safeGet_default(object, key), srcValue, key + "", object, source, stack) : void 0; - if (newValue === void 0) { - newValue = srcValue; - } - assignMergeValue_default(object, key, newValue); - } - }, keysIn_default); -} -var baseMerge_default = baseMerge; - -// node_modules/lodash-es/_customDefaultsMerge.js -function customDefaultsMerge(objValue, srcValue, key, object, source, stack) { - if (isObject_default(objValue) && isObject_default(srcValue)) { - stack.set(srcValue, objValue); - baseMerge_default(objValue, srcValue, void 0, customDefaultsMerge, stack); - stack["delete"](srcValue); - } - return objValue; -} -var customDefaultsMerge_default = customDefaultsMerge; - -// node_modules/lodash-es/mergeWith.js -var mergeWith = createAssigner_default(function(object, source, srcIndex, customizer) { - baseMerge_default(object, source, srcIndex, customizer); -}); -var mergeWith_default = mergeWith; - -// node_modules/lodash-es/defaultsDeep.js -var defaultsDeep = baseRest_default(function(args) { - args.push(void 0, customDefaultsMerge_default); - return apply_default(mergeWith_default, void 0, args); -}); -var defaultsDeep_default = defaultsDeep; - -// node_modules/lodash-es/kebabCase.js -var kebabCase = createCompounder_default(function(result, word, index) { - return result + (index ? "-" : "") + word.toLowerCase(); -}); -var kebabCase_default = kebabCase; - -// node_modules/lodash-es/lowerFirst.js -var lowerFirst = createCaseFirst_default("toLowerCase"); -var lowerFirst_default = lowerFirst; - -// node_modules/lodash-es/snakeCase.js -var snakeCase = createCompounder_default(function(result, word, index) { - return result + (index ? "_" : "") + word.toLowerCase(); -}); -var snakeCase_default = snakeCase; -export { - camelCase_default as camelCase, - defaultsDeep_default as defaultsDeep, - kebabCase_default as kebabCase, - lowerFirst_default as lowerFirst, - snakeCase_default as snakeCase, - upperFirst_default as upperFirst -}; -/*! Bundled license information: - -lodash-es/lodash.js: - (** - * @license - * Lodash (Custom Build) - * Build: `lodash modularize exports="es" -o ./` - * Copyright OpenJS Foundation and other contributors - * Released under MIT license - * Based on Underscore.js 1.8.3 - * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors - *) -*/ diff --git a/rules/utils/needs-semicolon.js b/rules/utils/needs-semicolon.js index a687b718c3..184ffcdbb0 100644 --- a/rules/utils/needs-semicolon.js +++ b/rules/utils/needs-semicolon.js @@ -1,3 +1,4 @@ +/* eslint-disable complexity */ // https://github.com/eslint/espree/blob/6b7d0b8100537dcd5c84a7fb17bbe28edcabe05d/lib/token-translator.js#L20 const tokenTypesNeedsSemicolon = new Set([ 'String', diff --git a/rules/utils/rule.js b/rules/utils/rule.js index 18523e28a0..c726c03cd0 100644 --- a/rules/utils/rule.js +++ b/rules/utils/rule.js @@ -3,10 +3,7 @@ import getDocumentationUrl from './get-documentation-url.js'; const isIterable = object => typeof object?.[Symbol.iterator] === 'function'; class FixAbortError extends Error { - constructor() { - super(); - this.name = 'FixAbortError'; - } + name = 'FixAbortError'; } const fixOptions = { abort() { @@ -155,19 +152,24 @@ export function checkVueTemplate(create, options) { return wrapped; } -/** @returns {import('eslint').Rule.RuleModule} */ -export function createRule(rule, ruleId) { - return { - meta: { - // If there is are, options add `[]` so ESLint can validate that no data is passed to the rule. - // https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/require-meta-schema.md - schema: [], - ...rule.meta, - docs: { - ...rule.meta.docs, - url: getDocumentationUrl(ruleId), +/** @returns {Record} */ +export function createRules(rules) { + return Object.fromEntries( + Object.entries(rules).map(([ruleId, rule]) => [ + ruleId, + { + meta: { + // If there is are, options add `[]` so ESLint can validate that no data is passed to the rule. + // https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/require-meta-schema.md + schema: [], + ...rule.meta, + docs: { + ...rule.meta.docs, + url: getDocumentationUrl(ruleId), + }, + }, + create: reportProblems(rule.create), }, - }, - create: reportProblems(rule.create), - }; + ]), + ); } diff --git a/rules/utils/string-cases.js b/rules/utils/string-cases.js new file mode 100644 index 0000000000..59e6c2b276 --- /dev/null +++ b/rules/utils/string-cases.js @@ -0,0 +1,2 @@ +export const upperFirst = string => string.charAt(0).toUpperCase() + string.slice(1); +export const lowerFirst = string => string.charAt(0).toLowerCase() + string.slice(1); diff --git a/scripts/create-rules-index-file.js b/scripts/create-rules-index-file.js index eb80e85f95..95055afd90 100644 --- a/scripts/create-rules-index-file.js +++ b/scripts/create-rules-index-file.js @@ -1,35 +1,18 @@ import fs from 'node:fs'; import path from 'node:path'; -import {camelCase} from 'lodash-es'; -import {outdent} from 'outdent'; const DIRECTORY = new URL('../rules/', import.meta.url); -const rules = fs.readdirSync(DIRECTORY, {withFileTypes: true}) - .filter(file => file.isFile() && file.name !== '.DS_Store' && file.name !== 'index.js') - .map(file => { - const filename = file.name; - const id = path.basename(filename, '.js'); - const specifier = camelCase(id); +const files = fs.readdirSync(DIRECTORY, {withFileTypes: true}) + .filter(file => file.isFile() && file.name.endsWith('.js') && file.name !== 'index.js') + .map(file => file.name) + .sort(); - return {id, specifier, filename}; - }); - -const toObjectKey = name => name.includes('-') ? `'${name}'` : name; -const content = outdent` - // Generated file, DO NOT edit - import {createRule} from './utils/rule.js'; - - ${rules.map(({filename, specifier}) => `import ${specifier} from './${filename}';`).join('\n')} - - const rules = { - ${rules.map(({id, specifier}) => `\t${toObjectKey(id)}: createRule(${specifier}, '${id}'),`).join('\n')} - }; - - export default rules; -`; +const content = files + .map(file => `export {default as '${path.basename(file, '.js')}'} from './${file}';`) + .join('\n'); fs.writeFileSync( new URL('index.js', DIRECTORY), - content + '\n', + '// Generated file, DO NOT edit\n\n' + content + '\n', ); diff --git a/test/error-message.js b/test/error-message.js index 02934f6c02..91fb7a6294 100644 --- a/test/error-message.js +++ b/test/error-message.js @@ -104,3 +104,39 @@ test.snapshot({ 'const error = new AggregateError;', ], }); + +// `SuppressedError` +test.snapshot({ + valid: [ + 'new SuppressedError(error, suppressed, "message")', + 'new NotSuppressedError(error, suppressed)', + 'new SuppressedError(...foo)', + 'new SuppressedError(...foo, "")', + 'new SuppressedError(error, suppressed, ...foo)', + 'new SuppressedError(error, suppressed, message, "")', + 'new SuppressedError("", "", message, "")', + ], + invalid: [ + 'new SuppressedError(error, suppressed,)', + 'new SuppressedError(error,)', + 'new SuppressedError()', + 'SuppressedError(error, suppressed,)', + 'SuppressedError(error,)', + 'SuppressedError()', + 'new SuppressedError(error, suppressed, "")', + 'new SuppressedError(error, suppressed, ``)', + 'new SuppressedError(error, suppressed, "", options)', + outdent` + const errorMessage = Object.freeze({errorMessage: 1}).errorMessage; + throw new SuppressedError(error, suppressed, errorMessage) + `, + 'new SuppressedError(error, suppressed, [])', + 'new SuppressedError(error, suppressed, [foo])', + 'new SuppressedError(error, suppressed, [0][0])', + 'new SuppressedError(error, suppressed, {})', + 'new SuppressedError(error, suppressed, {foo})', + 'new SuppressedError(error, suppressed, {foo: 0}.foo)', + 'new SuppressedError(error, suppressed, lineNumber=2)', + 'const error = new SuppressedError;', + ], +}); diff --git a/test/import-style.js b/test/import-style.js index 62db07bc98..4e2d5d4334 100644 --- a/test/import-style.js +++ b/test/import-style.js @@ -213,6 +213,21 @@ test({ const {red} = await import(variable); } `, + // `node:util` only allow `named`, set to `false` should allow any style + { + code: ` + import util from "node:util"; + import * as util2 from "node:util"; + import {foo} from "node:util"; + `, + options: [ + { + styles: { + 'node:util': false, + }, + }, + ], + }, ].map(test => addDefaultOptions(test)), invalid: [ @@ -626,6 +641,51 @@ test({ }, }], }, + // `node:util` only allow `named`, add `default` should keep `named` allowed ... (see next test) + { + code: ` + import * as util from "node:util"; + `, + options: [ + { + styles: { + 'node:util': { + default: true, + }, + }, + }, + ], + errors: [{ + messageId: 'importStyle', + data: { + allowedStyles: 'named or default', + moduleName: 'node:util', + }, + }], + }, + // ...(see previous test), unless we disable `named` explicitly + { + code: ` + import * as util from "node:util"; + `, + options: [ + { + styles: { + 'node:util': { + default: true, + named: false, + }, + }, + }, + ], + errors: [{ + messageId: 'importStyle', + data: { + allowedStyles: 'default', + moduleName: 'node:util', + }, + }], + }, ].map(test => addDefaultOptions(test)), }); diff --git a/test/integration/run-eslint.js b/test/integration/run-eslint.js index 96c11ea755..a6ef3f25ee 100644 --- a/test/integration/run-eslint.js +++ b/test/integration/run-eslint.js @@ -155,3 +155,4 @@ async function runEslint(project) { } export default runEslint; +export {UnicornEslintFatalError, UnicornIntegrationTestError}; diff --git a/test/integration/test.js b/test/integration/test.js index 938a540c50..409a81ff3b 100644 --- a/test/integration/test.js +++ b/test/integration/test.js @@ -8,10 +8,9 @@ import spawn from 'nano-spawn'; import styleText from 'node-style-text'; import {outdent} from 'outdent'; import {isCI} from 'ci-info'; -import memoize from 'memoize'; import YAML from 'yaml'; import allProjects from './projects.js'; -import runEslint from './run-eslint.js'; +import runEslint, {UnicornIntegrationTestError} from './run-eslint.js'; if (isCI) { const CI_CONFIG_FILE = new URL('../../.github/workflows/main.yml', import.meta.url); @@ -61,39 +60,23 @@ if (projects.length === 0) { process.exit(0); } -const getBranch = memoize(async dirname => { - const {stdout} = await spawn('git', ['branch', '--show-current'], {cwd: dirname}); - return stdout; -}); +const execute = async project => { + if (!fs.existsSync(project.location)) { + await spawn('git', [ + 'clone', + project.repository, + '--single-branch', + '--depth', + '1', + project.location, + ], {stdout: 'inherit', stderr: 'inherit'}); + } -const execute = project => new Listr( - [ - { - title: 'Cloning', - skip: () => fs.existsSync(project.location) ? 'Project already downloaded.' : false, - task: () => spawn('git', [ - 'clone', - project.repository, - '--single-branch', - '--depth', - '1', - project.location, - ], {stdout: 'inherit', stderr: 'inherit'}), - }, - { - title: 'Running eslint', - task: () => runEslint(project), - }, - ].map(({title, task, skip}) => ({ - title: `${project.name} / ${title}`, - skip, - task, - })), - {exitOnError: false}, -); + await runEslint(project); +}; -async function printEslintError(eslintError) { - const {message, project} = eslintError; +function printEslintError(error) { + const {message, project, errors} = error; console.log(); console.error( @@ -101,11 +84,10 @@ async function printEslintError(eslintError) { message, ); - project.branch ??= await getBranch(project.location); - for (const error of eslintError.errors) { - let file = path.relative(project.location, error.eslintFile.filePath); + for (const error of errors) { + let file = path.relative(project.location, error.eslintFile.filePath).replaceAll('\\', '/'); if (project.repository) { - file = `${project.repository}/blob/${project.branch}/${file}`; + file = `${project.repository}/blob/HEAD/${file}`; } if (typeof error.eslintMessage.line === 'number') { @@ -119,33 +101,30 @@ async function printEslintError(eslintError) { } } -async function printListrError(listrError) { - process.exitCode = 1; +async function printTestError(error) { + process.exitCode ||= 1; - if (!listrError.errors) { - console.error(listrError); + if (!(error instanceof UnicornIntegrationTestError)) { + console.error(error); return; } - for (const error of listrError.errors) { - if (error.name !== 'UnicornIntegrationTestError') { - console.error(error); - continue; - } - - // eslint-disable-next-line no-await-in-loop - await printEslintError(error); - } + printEslintError(error); } -try { - await new Listr( - projects.map(project => ({title: project.name, task: () => execute(project)})), - { - renderer: isCI ? 'verbose' : 'default', - concurrent: true, +await new Listr( + projects.map(project => ({ + title: project.name, + async task() { + try { + await execute(project); + } catch (error) { + await printTestError(error); + } }, - ).run(); -} catch (error) { - await printListrError(error); -} + })), + { + renderer: isCI ? 'verbose' : 'default', + concurrent: true, + }, +).run(); diff --git a/test/no-array-callback-reference.js b/test/no-array-callback-reference.js index 765032ba92..77c262135e 100644 --- a/test/no-array-callback-reference.js +++ b/test/no-array-callback-reference.js @@ -57,6 +57,10 @@ test({ ...simpleMethods.map(method => `foo.${method}(element => fn(element))`), ...reduceLikeMethods.map(method => `foo.${method}((accumulator, element) => fn(element))`), + // Optional chaining + ...simpleMethods.map(method => `foo?.${method}(element => fn(element))`), + ...reduceLikeMethods.map(method => `foo?.${method}((accumulator, element) => fn(element))`), + // `this.{map, filter, …}` ...simpleMethods.map(method => `this.${method}(fn)`), ...reduceLikeMethods.map(method => `this.${method}(fn)`), @@ -160,6 +164,18 @@ test({ ], }), ), + ...simpleMethodsExceptForEach.map( + method => invalidTestCase({ + code: `foo?.${method}(fn)`, + method, + name: 'fn', + suggestions: [ + `foo?.${method}((element) => fn(element))`, + `foo?.${method}((element, index) => fn(element, index))`, + `foo?.${method}((element, index, array) => fn(element, index, array))`, + ], + }), + ), invalidTestCase({ code: 'foo.forEach(fn)', method: 'forEach', diff --git a/test/no-array-reverse.js b/test/no-array-reverse.js new file mode 100644 index 0000000000..4cc68ab6f0 --- /dev/null +++ b/test/no-array-reverse.js @@ -0,0 +1,39 @@ +import {getTester} from './utils/test.js'; + +const {test} = getTester(import.meta); + +const FORBID_EXPRESSION_OPTIONS = [{allowExpressionStatement: false}]; + +test.snapshot({ + valid: [ + 'reversed =[...array].toReversed()', + 'reversed =array.toReversed()', + 'reversed =[...array].reverse', + 'reversed =[...array].reverse?.()', + 'array.reverse()', + 'array.reverse?.()', + 'array?.reverse()', + 'if (true) array.reverse()', + 'reversed = array.reverse(extraArgument)', + ], + invalid: [ + 'reversed = [...array].reverse()', + 'reversed = [...array]?.reverse()', + 'reversed = array.reverse()', + 'reversed = array?.reverse()', + { + code: 'array.reverse()', + options: FORBID_EXPRESSION_OPTIONS, + }, + { + code: 'array?.reverse()', + options: FORBID_EXPRESSION_OPTIONS, + }, + // Don't care about `allowExpression` + { + code: '[...array].reverse()', + options: FORBID_EXPRESSION_OPTIONS, + }, + 'reversed = [...(0, array)].reverse()', + ], +}); diff --git a/test/no-instanceof-builtins.js b/test/no-instanceof-builtins.js index e528da2ff0..d1c1399aa2 100644 --- a/test/no-instanceof-builtins.js +++ b/test/no-instanceof-builtins.js @@ -23,8 +23,8 @@ const strictStrategyInvalid = [ 'foo instanceof SyntaxError', 'foo instanceof TypeError', 'foo instanceof URIError', - 'foo instanceof InternalError', 'foo instanceof AggregateError', + 'foo instanceof SuppressedError', // Collection types 'foo instanceof Map', @@ -100,8 +100,8 @@ test.snapshot({ 'err instanceof SyntaxError', 'err instanceof TypeError', 'err instanceof URIError', - 'err instanceof InternalError', 'err instanceof AggregateError', + 'err instanceof SuppressedError', ].map(code => ({code, options: [{useErrorIsError: true, strategy: 'strict'}]})), }); diff --git a/test/no-typeof-undefined.js b/test/no-typeof-undefined.js index 15ff01504e..6095813667 100644 --- a/test/no-typeof-undefined.js +++ b/test/no-typeof-undefined.js @@ -18,6 +18,17 @@ test.snapshot({ 'foo = 2; typeof foo === "undefined"', '/* globals foo: readonly */ typeof foo === "undefined"', '/* globals globalThis: readonly */ typeof globalThis === "undefined"', + outdent` + function parse() { + switch (typeof value === 'undefined') {} + } + `, + outdent` + /* globals value: readonly */ + function parse() { + switch (typeof value === 'undefined') {} + } + `, // Cases we are not checking '"undefined" === typeof a.b', 'const UNDEFINED = "undefined"; typeof a.b === UNDEFINED', @@ -76,6 +87,11 @@ test.snapshot({ a.b) === 'undefined'; } `, + outdent` + function parse(value) { + switch (typeof value === 'undefined') {} + } + `, ], }); @@ -86,5 +102,16 @@ test.snapshot({ invalid: [ 'typeof undefinedVariableIdentifier === "undefined"', 'typeof Array !== "undefined"', + outdent` + function parse() { + switch (typeof value === 'undefined') {} + } + `, + outdent` + /* globals value: readonly */ + function parse() { + switch (typeof value === 'undefined') {} + } + `, ].map(code => ({code, options: [{checkGlobalVariables: true}]})), }); diff --git a/test/no-unnecessary-await.js b/test/no-unnecessary-await.js index 49ffc3d7fb..1dec6ac7df 100644 --- a/test/no-unnecessary-await.js +++ b/test/no-unnecessary-await.js @@ -113,3 +113,33 @@ test.snapshot({ 'async function foo() {+await -1}', ], }); + +test.babel({ + valid: [ + { + code: 'Promise.resolve() |> await', + languageOptions: { + parserOptions: { + babelOptions: { + parserOpts: { + plugins: [['pipelineOperator', {proposal: 'fsharp'}]], + }, + }, + }, + }, + }, + { + code: 'Promise.resolve() |> await %', + languageOptions: { + parserOptions: { + babelOptions: { + parserOpts: { + plugins: [['pipelineOperator', {proposal: 'hack', topicToken: '%'}]], + }, + }, + }, + }, + }, + ], + invalid: [], +}); diff --git a/test/no-useless-error-capture-stack-trace.js b/test/no-useless-error-capture-stack-trace.js new file mode 100644 index 0000000000..af6c730517 --- /dev/null +++ b/test/no-useless-error-capture-stack-trace.js @@ -0,0 +1,195 @@ +import outdent from 'outdent'; +import {getTester, parsers} from './utils/test.js'; +import builtinErrors from '../rules/shared/builtin-errors.js'; + +const {test} = getTester(import.meta); + +test.snapshot({ + valid: [ + 'class MyError {constructor() {Error.captureStackTrace(this, MyError)}}', + 'class MyError extends NotABuiltinError {constructor() {Error.captureStackTrace(this, MyError)}}', + ...[ + '', + 'Error.captureStackTrace(not_this, MyError)', + 'Error.captureStackTrace(this, NotClassName)', + 'Error.captureStackTrace(this, MyError, ...extraArguments)', + 'Error.captureStackTrace(this)', + 'Error.captureStackTrace(..._, MyError)', + 'Error.captureStackTrace(this, ..._)', + 'Error.captureStackTrace(...[this, MyError])', + 'NotError.captureStackTrace(this, MyError)', + 'Error.not_captureStackTrace(this, MyError)', + 'Error.captureStackTrace', + 'new Error.captureStackTrace(this, MyError)', + 'Error?.captureStackTrace(this, MyError)', + 'Error.captureStackTrace(this, this?.constructor)', + 'Error.captureStackTrace(this, this.notConstructor)', + // `MetaProperty`, but not `new.target` + 'Error.captureStackTrace(this, import.meta)', + outdent` + function foo() { + Error.captureStackTrace(this, MyError) + } + `, + ].map(code => outdent` + class MyError extends Error { + constructor() { + ${code} + } + } + `), + outdent` + class MyError extends Error { + notConstructor() { + Error.captureStackTrace(this, MyError) + } + } + `, + outdent` + class MyError extends Error { + constructor() { + function foo() { + Error.captureStackTrace(this, MyError) + } + } + } + `, + outdent` + class MyError extends Error { + constructor(MyError) { + Error.captureStackTrace(this, MyError) + } + } + `, + outdent` + class MyError extends Error { + static { + Error.captureStackTrace(this, MyError) + + function foo() { + Error.captureStackTrace(this, MyError) + } + } + } + `, + outdent` + class MyError extends Error { + constructor() { + class NotAErrorSubclass { + constructor() { + Error.captureStackTrace(this, new.target) + } + } + } + } + `, + outdent` + class Error {} + class MyError extends Error { + constructor() { + Error.captureStackTrace(this, MyError) + } + } + `, + outdent` + class Error {} + class MyError extends RangeError { + constructor() { + Error.captureStackTrace(this, MyError) + } + } + `, + ], + invalid: [ + ...[ + 'Error.captureStackTrace(this, MyError)', + 'Error.captureStackTrace?.(this, MyError)', + 'Error.captureStackTrace(this, this.constructor)', + 'Error.captureStackTrace(this, this.constructor)', + 'Error.captureStackTrace?.(this, this.constructor)', + 'Error.captureStackTrace(this, new.target)', + 'Error.captureStackTrace?.(this, new.target)', + ].map(code => outdent` + class MyError extends Error { + constructor() { + ${code}; + } + } + `), + ...builtinErrors.map(builtinError => outdent` + class MyError extends ${builtinError} { + constructor() { + Error.captureStackTrace(this, MyError) + } + } + `), + outdent` + class MyError extends Error { + constructor() { + const foo = () => { + Error.captureStackTrace(this, MyError) + } + } + } + `, + outdent` + class MyError extends Error { + constructor() { + if (a) Error.captureStackTrace(this, MyError) + } + } + `, + outdent` + class MyError extends Error { + constructor() { + const x = () => Error.captureStackTrace(this, MyError) + } + } + `, + outdent` + class MyError extends Error { + constructor() { + void Error.captureStackTrace(this, MyError) + } + } + `, + outdent` + export default class extends Error { + constructor() { + Error.captureStackTrace(this, new.target) + } + } + `, + // ClassExpression + outdent` + export default ( + class extends Error { + constructor() { + Error.captureStackTrace(this, new.target) + } + } + ) + `, + ], +}); + +test.snapshot({ + testerOptions: { + languageOptions: {parser: parsers.typescript}, + }, + valid: [ + outdent` + class MyError extends Error { + constructor(): void; + static { + Error.captureStackTrace(this, MyError) + + function foo() { + Error.captureStackTrace(this, MyError) + } + } + } + `, + ], + invalid: [], +}); diff --git a/test/package.js b/test/package.js index f05f9bf6ba..f555d81483 100644 --- a/test/package.js +++ b/test/package.js @@ -31,6 +31,7 @@ const RULES_WITHOUT_EXAMPLES_SECTION = new Set([ 'prefer-modern-math-apis', 'prefer-math-min-max', 'consistent-existence-index-check', + 'prefer-class-fields', 'prefer-global-this', 'no-instanceof-builtins', 'no-named-default', diff --git a/test/prefer-class-fields.js b/test/prefer-class-fields.js new file mode 100644 index 0000000000..2fc41395db --- /dev/null +++ b/test/prefer-class-fields.js @@ -0,0 +1,200 @@ +import outdent from 'outdent'; +import {getTester, parsers} from './utils/test.js'; + +const {test} = getTester(import.meta); + +test.snapshot({ + valid: [ + 'class Foo {bar = 1}', + 'class Foo {static bar = 1}', + 'class Foo {#bar = 1}', + 'class Foo {static #bar = 1}', + // Not `=` assign + 'class Foo {constructor() {this.bar += 1}}', + // Computed + 'class Foo {constructor() {this[bar] = 1}}', + // Not `this` + 'class Foo {constructor() {notThis.bar = 1}}', + // Not `Literal` + 'class Foo {constructor() {notThis.bar = 1 + 2}}', + outdent` + class Foo { + constructor() { + if (something) { return; } + this.bar = 1; + } + } + `, + ], + invalid: [ + outdent` + class Foo { + constructor() { + this.bar = 1; + } + } + `, + outdent` + class Foo { + constructor() { + ; + this.bar = 1; + } + } + `, + outdent` + class Foo { + constructor() { + this.bar = 1; + this.baz = 2; + } + } + `, + outdent` + class Foo { + constructor() { + this.bar = 1; + this.bar = 2; + } + } + `, + outdent` + class Foo { + bar; + constructor() { + this.bar = 1; + } + } + `, + outdent` + class Foo { + #bar; + constructor() { + this.#bar = 1; + } + } + `, + outdent` + class Foo { + bar = 0; + constructor() { + this.bar = 1; + } + } + `, + outdent` + class Foo { + #bar = 0; + constructor() { + this.#bar = 1; + } + } + `, + outdent` + class Foo { + [bar]; + constructor() { + this.bar = 1; + } + } + `, + outdent` + class Foo { + [bar] = 0; + constructor() { + this.bar = 1; + } + } + `, + outdent` + class Foo { + static bar; + constructor() { + this.bar = 1; + } + } + `, + outdent` + class Foo { + static bar = 0; + constructor() { + this.bar = 1; + } + } + `, + outdent` + class Foo { + static [bar]; + constructor() { + this.bar = 1; + } + } + `, + outdent` + class Foo { + static [bar] = 1; + constructor() { + this.bar = 1; + } + } + `, + outdent` + class Foo { + constructor() { + this.bar = 1; + }} + `, + outdent` + class Foo { + constructor() { + this.bar = 1; + } + static} + `, + outdent` + class Foo { + constructor() { + this.bar = 1; + } + static// comment; + } + `, + ], +}); + +test.snapshot({ + testerOptions: { + languageOptions: { + parser: parsers.typescript, + }, + }, + valid: [ + outdent` + class Foo { + foo: string = 'foo'; + } + `, + outdent` + declare class Foo { + constructor(foo?: string); + } + `, + ], + invalid: [ + outdent` + class MyError extends Error { + constructor(message: string) { + this.name = "MyError"; + } + } + `, + outdent` + class MyError extends Error { + name: string; + constructor(message: string) { + this.name = "MyError"; + } + } + `, + ], +}); diff --git a/test/prefer-string-raw.js b/test/prefer-string-raw.js index 5d3acf25c4..b0c463b0f9 100644 --- a/test/prefer-string-raw.js +++ b/test/prefer-string-raw.js @@ -34,12 +34,16 @@ test.snapshot({ String.raw`import {} from "foo" with {"key\\key": "value"}`, String.raw`export {} from "foo" with {key: "value\\value"}`, String.raw`export {} from "foo" with {"key\\key": "value"}`, + String.raw`a = '\\'`, + String.raw`a = 'a\\b\"'`, ], invalid: [ String.raw`a = 'a\\b'`, String.raw`a = {['a\\b']: b}`, String.raw`function a() {return'a\\b'}`, String.raw`const foo = "foo \\x46";`, + String.raw`a = 'a\\b\''`, + String.raw`a = "a\\b\""`, ], }); diff --git a/test/prefer-string-replace-all.js b/test/prefer-string-replace-all.js index 9f8d8b4d66..8f57645cbe 100644 --- a/test/prefer-string-replace-all.js +++ b/test/prefer-string-replace-all.js @@ -78,6 +78,8 @@ test.snapshot({ String.raw`foo.replace(/\u{61}/g, bar)`, String.raw`foo.replace(/\u{61}/gu, bar)`, String.raw`foo.replace(/\u{61}/gv, bar)`, + String.raw`str.replace(/\u200B/g, '')`, + String.raw`str.replace(/\x20/g, '')`, 'foo.replace(/]/g, "bar")', // Extra flag 'foo.replace(/a/gi, bar)', @@ -94,7 +96,12 @@ test.snapshot({ 'foo.replace(/a{1}/g, _)', String.raw`foo.replace(/\u0022/g, _)`, String.raw`foo.replace(/\u0027/g, _)`, - String.raw`foo.replace(/\cM\cj/g, _)`, + + // `\r\n\t` + String.raw`foo.replace(/\cM\cj\cI/g, _)`, + String.raw`foo.replace(/\x0d\x0a\x09/g, _)`, + String.raw`foo.replace(/\u000d\u000a\u0009/g, _)`, + String.raw`foo.replace(/\x22/g, _)`, String.raw`foo.replace(/\x27/g, _)`, String.raw`foo.replace(/\uD83D\ude00/g, _)`, @@ -102,6 +109,7 @@ test.snapshot({ String.raw`foo.replace(/\n/g, _)`, String.raw`foo.replace(/\u{20}/gu, _)`, String.raw`foo.replace(/\u{20}/gv, _)`, + String.raw`foo.replace(/\1/g, _)`, 'foo.replaceAll(/a]/g, _)', String.raw`foo.replaceAll(/\r\n\u{1f600}/gu, _)`, diff --git a/test/prevent-abbreviations.js b/test/prevent-abbreviations.js index be45528510..7da7d903f2 100644 --- a/test/prevent-abbreviations.js +++ b/test/prevent-abbreviations.js @@ -1,3 +1,4 @@ +/* eslint-disable @stylistic/max-len, max-lines */ import outdent from 'outdent'; import {getTester, avoidTestTitleConflict} from './utils/test.js'; @@ -238,6 +239,20 @@ const tests = { code: 'const propTypes = 2;const err = 2;', options: extendDefaultAllowListOptions, }, + + // `arr` has only one replacement, disable it makes `arr` valid + { + code: 'let arr', + options: [ + { + replacements: { + arr: { + array: false, + }, + }, + }, + ], + }, ], invalid: [ @@ -272,7 +287,9 @@ const tests = { { code: 'this.eResDir = 1', options: checkPropertiesOptions, - errors: createErrors('Please rename the property `eResDir`. Suggested names are: `errorResourceDirection`, `errorResourceDirectory`, `errorResponseDirection`, ... (9 more omitted). A more descriptive name will do too.'), + errors: createErrors( + 'Please rename the property `eResDir`. Suggested names are: `errorResourceDirection`, `errorResourceDirectory`, `errorResponseDirection`, ... (9 more omitted). A more descriptive name will do too.', + ), }, // All suggested names should avoid capture @@ -1257,6 +1274,21 @@ const tests = { output: 'const returnValue_value = "that should be ok";', errors: 1, }, + // `docs` has two replacements, disable one should autofixable + { + code: 'let docs', + output: 'let documents', + options: [ + { + replacements: { + docs: { + documentation: false, + }, + }, + }, + ], + errors: 1, + }, ], }; diff --git a/test/require-module-specifiers.js b/test/require-module-specifiers.js new file mode 100644 index 0000000000..2b321a2cac --- /dev/null +++ b/test/require-module-specifiers.js @@ -0,0 +1,77 @@ +import outdent from 'outdent'; +import {getTester, parsers} from './utils/test.js'; + +const {test} = getTester(import.meta); + +const typescriptCode = code => ({ + code, + languageOptions: {parser: parsers.typescript}, +}); + +// `import` +test.snapshot({ + valid: [ + 'import "foo"', + 'import foo from "foo"', + 'import * as foo from "foo"', + 'import {foo} from "foo"', + 'import foo,{bar} from "foo"', + typescriptCode('import type foo from "foo"'), + typescriptCode('import * as foo from "foo"'), + typescriptCode('import type foo,{bar} from "foo"'), + typescriptCode('import foo,{type bar} from "foo"'), + ], + invalid: [ + 'import {} from "foo";', + 'import{}from"foo";', + outdent` + import { + } from "foo"; + `, + 'import foo, {} from "foo";', + 'import foo,{}from "foo";', + outdent` + import foo, { + } from "foo"; + `, + 'import foo,{}/* comment */from "foo";', + typescriptCode('import type {} from "foo";'), + typescriptCode('import type{}from"foo";'), + typescriptCode('import type foo, {} from "foo";'), + typescriptCode('import type foo,{}from "foo";'), + ], +}); + +// `export` +test.snapshot({ + valid: [ + outdent` + const foo = 1; + export {foo}; + `, + 'export {foo} from "foo"', + 'export * as foo from "foo"', + typescriptCode('export type {Foo}'), + typescriptCode('export type {foo} from "foo"'), + typescriptCode('export type * as foo from "foo"'), + 'export const foo = 1', + 'export function foo() {}', + 'export class foo {}', + typescriptCode('export type foo = Foo'), + 'export const {} = foo', + 'export const [] = foo', + ], + invalid: [ + 'export {}', + typescriptCode('export type{}'), + typescriptCode('export type {} from "foo";'), + typescriptCode('declare export type {} from "foo";'), + 'export {} from "foo";', + 'export{}from"foo";', + outdent` + export { + } from "foo"; + `, + 'export {} from "foo" with {type: "json"};', + ], +}); diff --git a/test/snapshots/error-message.js.md b/test/snapshots/error-message.js.md index 5a2647958b..66c10a4a67 100644 --- a/test/snapshots/error-message.js.md +++ b/test/snapshots/error-message.js.md @@ -512,3 +512,275 @@ Generated by [AVA](https://avajs.dev). > 1 | const error = new AggregateError;␊ | ^^^^^^^^^^^^^^^^^^ Pass a message to the \`AggregateError\` constructor.␊ ` + +## invalid(1): new SuppressedError(error, suppressed,) + +> Input + + `␊ + 1 | new SuppressedError(error, suppressed,)␊ + ` + +> Error 1/1 + + `␊ + > 1 | new SuppressedError(error, suppressed,)␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Pass a message to the \`SuppressedError\` constructor.␊ + ` + +## invalid(2): new SuppressedError(error,) + +> Input + + `␊ + 1 | new SuppressedError(error,)␊ + ` + +> Error 1/1 + + `␊ + > 1 | new SuppressedError(error,)␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Pass a message to the \`SuppressedError\` constructor.␊ + ` + +## invalid(3): new SuppressedError() + +> Input + + `␊ + 1 | new SuppressedError()␊ + ` + +> Error 1/1 + + `␊ + > 1 | new SuppressedError()␊ + | ^^^^^^^^^^^^^^^^^^^^^ Pass a message to the \`SuppressedError\` constructor.␊ + ` + +## invalid(4): SuppressedError(error, suppressed,) + +> Input + + `␊ + 1 | SuppressedError(error, suppressed,)␊ + ` + +> Error 1/1 + + `␊ + > 1 | SuppressedError(error, suppressed,)␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Pass a message to the \`SuppressedError\` constructor.␊ + ` + +## invalid(5): SuppressedError(error,) + +> Input + + `␊ + 1 | SuppressedError(error,)␊ + ` + +> Error 1/1 + + `␊ + > 1 | SuppressedError(error,)␊ + | ^^^^^^^^^^^^^^^^^^^^^^^ Pass a message to the \`SuppressedError\` constructor.␊ + ` + +## invalid(6): SuppressedError() + +> Input + + `␊ + 1 | SuppressedError()␊ + ` + +> Error 1/1 + + `␊ + > 1 | SuppressedError()␊ + | ^^^^^^^^^^^^^^^^^ Pass a message to the \`SuppressedError\` constructor.␊ + ` + +## invalid(7): new SuppressedError(error, suppressed, "") + +> Input + + `␊ + 1 | new SuppressedError(error, suppressed, "")␊ + ` + +> Error 1/1 + + `␊ + > 1 | new SuppressedError(error, suppressed, "")␊ + | ^^ Error message should not be an empty string.␊ + ` + +## invalid(8): new SuppressedError(error, suppressed, ``) + +> Input + + `␊ + 1 | new SuppressedError(error, suppressed, \`\`)␊ + ` + +> Error 1/1 + + `␊ + > 1 | new SuppressedError(error, suppressed, \`\`)␊ + | ^^ Error message should not be an empty string.␊ + ` + +## invalid(9): new SuppressedError(error, suppressed, "", options) + +> Input + + `␊ + 1 | new SuppressedError(error, suppressed, "", options)␊ + ` + +> Error 1/1 + + `␊ + > 1 | new SuppressedError(error, suppressed, "", options)␊ + | ^^ Error message should not be an empty string.␊ + ` + +## invalid(10): const errorMessage = Object.freeze({errorMessage: 1}).errorMessage; throw new SuppressedError(error, suppressed, errorMessage) + +> Input + + `␊ + 1 | const errorMessage = Object.freeze({errorMessage: 1}).errorMessage;␊ + 2 | throw new SuppressedError(error, suppressed, errorMessage)␊ + ` + +> Error 1/1 + + `␊ + 1 | const errorMessage = Object.freeze({errorMessage: 1}).errorMessage;␊ + > 2 | throw new SuppressedError(error, suppressed, errorMessage)␊ + | ^^^^^^^^^^^^ Error message should be a string.␊ + ` + +## invalid(11): new SuppressedError(error, suppressed, []) + +> Input + + `␊ + 1 | new SuppressedError(error, suppressed, [])␊ + ` + +> Error 1/1 + + `␊ + > 1 | new SuppressedError(error, suppressed, [])␊ + | ^^ Error message should be a string.␊ + ` + +## invalid(12): new SuppressedError(error, suppressed, [foo]) + +> Input + + `␊ + 1 | new SuppressedError(error, suppressed, [foo])␊ + ` + +> Error 1/1 + + `␊ + > 1 | new SuppressedError(error, suppressed, [foo])␊ + | ^^^^^ Error message should be a string.␊ + ` + +## invalid(13): new SuppressedError(error, suppressed, [0][0]) + +> Input + + `␊ + 1 | new SuppressedError(error, suppressed, [0][0])␊ + ` + +> Error 1/1 + + `␊ + > 1 | new SuppressedError(error, suppressed, [0][0])␊ + | ^^^^^^ Error message should be a string.␊ + ` + +## invalid(14): new SuppressedError(error, suppressed, {}) + +> Input + + `␊ + 1 | new SuppressedError(error, suppressed, {})␊ + ` + +> Error 1/1 + + `␊ + > 1 | new SuppressedError(error, suppressed, {})␊ + | ^^ Error message should be a string.␊ + ` + +## invalid(15): new SuppressedError(error, suppressed, {foo}) + +> Input + + `␊ + 1 | new SuppressedError(error, suppressed, {foo})␊ + ` + +> Error 1/1 + + `␊ + > 1 | new SuppressedError(error, suppressed, {foo})␊ + | ^^^^^ Error message should be a string.␊ + ` + +## invalid(16): new SuppressedError(error, suppressed, {foo: 0}.foo) + +> Input + + `␊ + 1 | new SuppressedError(error, suppressed, {foo: 0}.foo)␊ + ` + +> Error 1/1 + + `␊ + > 1 | new SuppressedError(error, suppressed, {foo: 0}.foo)␊ + | ^^^^^^^^^^^^ Error message should be a string.␊ + ` + +## invalid(17): new SuppressedError(error, suppressed, lineNumber=2) + +> Input + + `␊ + 1 | new SuppressedError(error, suppressed, lineNumber=2)␊ + ` + +> Error 1/1 + + `␊ + > 1 | new SuppressedError(error, suppressed, lineNumber=2)␊ + | ^^^^^^^^^^^^ Error message should be a string.␊ + ` + +## invalid(18): const error = new SuppressedError; + +> Input + + `␊ + 1 | const error = new SuppressedError;␊ + ` + +> Error 1/1 + + `␊ + > 1 | const error = new SuppressedError;␊ + | ^^^^^^^^^^^^^^^^^^^ Pass a message to the \`SuppressedError\` constructor.␊ + ` diff --git a/test/snapshots/error-message.js.snap b/test/snapshots/error-message.js.snap index 0345896652..b7b6c42328 100644 Binary files a/test/snapshots/error-message.js.snap and b/test/snapshots/error-message.js.snap differ diff --git a/test/snapshots/no-array-reverse.js.md b/test/snapshots/no-array-reverse.js.md new file mode 100644 index 0000000000..fd7c824165 --- /dev/null +++ b/test/snapshots/no-array-reverse.js.md @@ -0,0 +1,203 @@ +# Snapshot report for `test/no-array-reverse.js` + +The actual snapshot is saved in `no-array-reverse.js.snap`. + +Generated by [AVA](https://avajs.dev). + +## invalid(1): reversed = [...array].reverse() + +> Input + + `␊ + 1 | reversed = [...array].reverse()␊ + ` + +> Error 1/1 + + `␊ + > 1 | reversed = [...array].reverse()␊ + | ^^^^^^^ Use \`Array#toReversed()\` instead of \`Array#reverse()\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: The spreading object is an array␊ + 1 | reversed = array.toReversed()␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: The spreading object is NOT an array␊ + 1 | reversed = [...array].toReversed()␊ + ` + +## invalid(2): reversed = [...array]?.reverse() + +> Input + + `␊ + 1 | reversed = [...array]?.reverse()␊ + ` + +> Error 1/1 + + `␊ + > 1 | reversed = [...array]?.reverse()␊ + | ^^^^^^^ Use \`Array#toReversed()\` instead of \`Array#reverse()\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: The spreading object is an array␊ + 1 | reversed = array?.toReversed()␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: The spreading object is NOT an array␊ + 1 | reversed = [...array]?.toReversed()␊ + ` + +## invalid(3): reversed = array.reverse() + +> Input + + `␊ + 1 | reversed = array.reverse()␊ + ` + +> Error 1/1 + + `␊ + > 1 | reversed = array.reverse()␊ + | ^^^^^^^ Use \`Array#toReversed()\` instead of \`Array#reverse()\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to \`.toReversed()\`.␊ + 1 | reversed = array.toReversed()␊ + ` + +## invalid(4): reversed = array?.reverse() + +> Input + + `␊ + 1 | reversed = array?.reverse()␊ + ` + +> Error 1/1 + + `␊ + > 1 | reversed = array?.reverse()␊ + | ^^^^^^^ Use \`Array#toReversed()\` instead of \`Array#reverse()\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to \`.toReversed()\`.␊ + 1 | reversed = array?.toReversed()␊ + ` + +## invalid(5): array.reverse() + +> Input + + `␊ + 1 | array.reverse()␊ + ` + +> Options + + `␊ + [␊ + {␊ + "allowExpressionStatement": false␊ + }␊ + ]␊ + ` + +> Error 1/1 + + `␊ + > 1 | array.reverse()␊ + | ^^^^^^^ Use \`Array#toReversed()\` instead of \`Array#reverse()\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to \`.toReversed()\`.␊ + 1 | array.toReversed()␊ + ` + +## invalid(6): array?.reverse() + +> Input + + `␊ + 1 | array?.reverse()␊ + ` + +> Options + + `␊ + [␊ + {␊ + "allowExpressionStatement": false␊ + }␊ + ]␊ + ` + +> Error 1/1 + + `␊ + > 1 | array?.reverse()␊ + | ^^^^^^^ Use \`Array#toReversed()\` instead of \`Array#reverse()\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to \`.toReversed()\`.␊ + 1 | array?.toReversed()␊ + ` + +## invalid(7): [...array].reverse() + +> Input + + `␊ + 1 | [...array].reverse()␊ + ` + +> Options + + `␊ + [␊ + {␊ + "allowExpressionStatement": false␊ + }␊ + ]␊ + ` + +> Error 1/1 + + `␊ + > 1 | [...array].reverse()␊ + | ^^^^^^^ Use \`Array#toReversed()\` instead of \`Array#reverse()\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: The spreading object is an array␊ + 1 | array.toReversed()␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: The spreading object is NOT an array␊ + 1 | [...array].toReversed()␊ + ` + +## invalid(8): reversed = [...(0, array)].reverse() + +> Input + + `␊ + 1 | reversed = [...(0, array)].reverse()␊ + ` + +> Error 1/1 + + `␊ + > 1 | reversed = [...(0, array)].reverse()␊ + | ^^^^^^^ Use \`Array#toReversed()\` instead of \`Array#reverse()\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: The spreading object is an array␊ + 1 | reversed = (0, array).toReversed()␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: The spreading object is NOT an array␊ + 1 | reversed = [...(0, array)].toReversed()␊ + ` diff --git a/test/snapshots/no-array-reverse.js.snap b/test/snapshots/no-array-reverse.js.snap new file mode 100644 index 0000000000..16914b2d82 Binary files /dev/null and b/test/snapshots/no-array-reverse.js.snap differ diff --git a/test/snapshots/no-instanceof-builtins.js.md b/test/snapshots/no-instanceof-builtins.js.md index bcda4481dc..0facbaebd2 100644 --- a/test/snapshots/no-instanceof-builtins.js.md +++ b/test/snapshots/no-instanceof-builtins.js.md @@ -523,12 +523,12 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid using \`instanceof\` for type checking as it can lead to unreliable results.␊ ` -## invalid(15): fooStrict instanceof InternalError +## invalid(15): fooStrict instanceof AggregateError > Input `␊ - 1 | fooStrict instanceof InternalError␊ + 1 | fooStrict instanceof AggregateError␊ ` > Options @@ -544,16 +544,16 @@ Generated by [AVA](https://avajs.dev). > Error 1/1 `␊ - > 1 | fooStrict instanceof InternalError␊ - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid using \`instanceof\` for type checking as it can lead to unreliable results.␊ + > 1 | fooStrict instanceof AggregateError␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid using \`instanceof\` for type checking as it can lead to unreliable results.␊ ` -## invalid(16): fooStrict instanceof AggregateError +## invalid(16): fooStrict instanceof SuppressedError > Input `␊ - 1 | fooStrict instanceof AggregateError␊ + 1 | fooStrict instanceof SuppressedError␊ ` > Options @@ -569,8 +569,8 @@ Generated by [AVA](https://avajs.dev). > Error 1/1 `␊ - > 1 | fooStrict instanceof AggregateError␊ - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid using \`instanceof\` for type checking as it can lead to unreliable results.␊ + > 1 | fooStrict instanceof SuppressedError␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid using \`instanceof\` for type checking as it can lead to unreliable results.␊ ` ## invalid(17): fooStrict instanceof Map @@ -1475,12 +1475,12 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^ Avoid using \`instanceof\` for type checking as it can lead to unreliable results.␊ ` -## invalid(8): err instanceof InternalError +## invalid(8): err instanceof AggregateError > Input `␊ - 1 | err instanceof InternalError␊ + 1 | err instanceof AggregateError␊ ` > Options @@ -1497,16 +1497,16 @@ Generated by [AVA](https://avajs.dev). > Error 1/1 `␊ - > 1 | err instanceof InternalError␊ - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid using \`instanceof\` for type checking as it can lead to unreliable results.␊ + > 1 | err instanceof AggregateError␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid using \`instanceof\` for type checking as it can lead to unreliable results.␊ ` -## invalid(9): err instanceof AggregateError +## invalid(9): err instanceof SuppressedError > Input `␊ - 1 | err instanceof AggregateError␊ + 1 | err instanceof SuppressedError␊ ` > Options @@ -1523,8 +1523,8 @@ Generated by [AVA](https://avajs.dev). > Error 1/1 `␊ - > 1 | err instanceof AggregateError␊ - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid using \`instanceof\` for type checking as it can lead to unreliable results.␊ + > 1 | err instanceof SuppressedError␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid using \`instanceof\` for type checking as it can lead to unreliable results.␊ ` ## invalid(1): fooInclude instanceof WebWorker diff --git a/test/snapshots/no-instanceof-builtins.js.snap b/test/snapshots/no-instanceof-builtins.js.snap index 115df964e8..c5f27ada70 100644 Binary files a/test/snapshots/no-instanceof-builtins.js.snap and b/test/snapshots/no-instanceof-builtins.js.snap differ diff --git a/test/snapshots/no-nested-ternary.js.md b/test/snapshots/no-nested-ternary.js.md index c9d9854562..54ef81339b 100644 --- a/test/snapshots/no-nested-ternary.js.md +++ b/test/snapshots/no-nested-ternary.js.md @@ -22,14 +22,14 @@ Generated by [AVA](https://avajs.dev). `␊ > 1 | const foo = i > 5 ? i < 100 ? true : false : i < 100 ? true : false;␊ - | ^^^^^^^^^^^^^^^^^^^^^^ Nest ternary expression should be parenthesized.␊ + | ^^^^^^^^^^^^^^^^^^^^^^ Nested ternary expression should be parenthesized.␊ ` > Error 2/2 `␊ > 1 | const foo = i > 5 ? i < 100 ? true : false : i < 100 ? true : false;␊ - | ^^^^^^^^^^^^^^^^^^^^^^ Nest ternary expression should be parenthesized.␊ + | ^^^^^^^^^^^^^^^^^^^^^^ Nested ternary expression should be parenthesized.␊ ` ## invalid(2): const foo = i > 5 ? true : (i < 100 ? true : (i < 1000 ? true : false)); diff --git a/test/snapshots/no-nested-ternary.js.snap b/test/snapshots/no-nested-ternary.js.snap index fd1daf5af7..c101128ba0 100644 Binary files a/test/snapshots/no-nested-ternary.js.snap and b/test/snapshots/no-nested-ternary.js.snap differ diff --git a/test/snapshots/no-typeof-undefined.js.md b/test/snapshots/no-typeof-undefined.js.md index 3312d4fc3c..97140b4e59 100644 --- a/test/snapshots/no-typeof-undefined.js.md +++ b/test/snapshots/no-typeof-undefined.js.md @@ -511,6 +511,33 @@ Generated by [AVA](https://avajs.dev). 4 | }␊ ` +## invalid(23): function parse(value) { switch (typeof value === 'undefined') {} } + +> Input + + `␊ + 1 | function parse(value) {␊ + 2 | switch (typeof value === 'undefined') {}␊ + 3 | }␊ + ` + +> Output + + `␊ + 1 | function parse(value) {␊ + 2 | switch (value === undefined) {}␊ + 3 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | function parse(value) {␊ + > 2 | switch (typeof value === 'undefined') {}␊ + | ^^^^^^ Compare with \`undefined\` directly instead of using \`typeof\`.␊ + 3 | }␊ + ` + ## invalid(1): typeof undefinedVariableIdentifier === "undefined" > Input @@ -568,3 +595,76 @@ Generated by [AVA](https://avajs.dev). Suggestion 1/1: Switch to \`… !== undefined\`.␊ 1 | Array !== undefined␊ ` + +## invalid(3): function parse() { switch (typeof value === 'undefined') {} } + +> Input + + `␊ + 1 | function parse() {␊ + 2 | switch (typeof value === 'undefined') {}␊ + 3 | }␊ + ` + +> Options + + `␊ + [␊ + {␊ + "checkGlobalVariables": true␊ + }␊ + ]␊ + ` + +> Error 1/1 + + `␊ + 1 | function parse() {␊ + > 2 | switch (typeof value === 'undefined') {}␊ + | ^^^^^^ Compare with \`undefined\` directly instead of using \`typeof\`.␊ + 3 | }␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to \`… === undefined\`.␊ + 1 | function parse() {␊ + 2 | switch (value === undefined) {}␊ + 3 | }␊ + ` + +## invalid(4): /* globals value: readonly */ function parse() { switch (typeof value === 'undefined') {} } + +> Input + + `␊ + 1 | /* globals value: readonly */␊ + 2 | function parse() {␊ + 3 | switch (typeof value === 'undefined') {}␊ + 4 | }␊ + ` + +> Options + + `␊ + [␊ + {␊ + "checkGlobalVariables": true␊ + }␊ + ]␊ + ` + +> Error 1/1 + + `␊ + 1 | /* globals value: readonly */␊ + 2 | function parse() {␊ + > 3 | switch (typeof value === 'undefined') {}␊ + | ^^^^^^ Compare with \`undefined\` directly instead of using \`typeof\`.␊ + 4 | }␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to \`… === undefined\`.␊ + 1 | /* globals value: readonly */␊ + 2 | function parse() {␊ + 3 | switch (value === undefined) {}␊ + 4 | }␊ + ` diff --git a/test/snapshots/no-typeof-undefined.js.snap b/test/snapshots/no-typeof-undefined.js.snap index 8dad76954c..d9f56fdbe1 100644 Binary files a/test/snapshots/no-typeof-undefined.js.snap and b/test/snapshots/no-typeof-undefined.js.snap differ diff --git a/test/snapshots/no-useless-error-capture-stack-trace.js.md b/test/snapshots/no-useless-error-capture-stack-trace.js.md new file mode 100644 index 0000000000..4ef25eeb78 --- /dev/null +++ b/test/snapshots/no-useless-error-capture-stack-trace.js.md @@ -0,0 +1,683 @@ +# Snapshot report for `test/no-useless-error-capture-stack-trace.js` + +The actual snapshot is saved in `no-useless-error-capture-stack-trace.js.snap`. + +Generated by [AVA](https://avajs.dev). + +## invalid(1): class MyError extends Error { constructor() { Error.captureStackTrace(this, MyError); } } + +> Input + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + 3 | Error.captureStackTrace(this, MyError);␊ + 4 | }␊ + 5 | }␊ + ` + +> Output + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + 3 | ␊ + 4 | }␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + > 3 | Error.captureStackTrace(this, MyError);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unnecessary \`Error.captureStackTrace(…)\` call.␊ + 4 | }␊ + 5 | }␊ + ` + +## invalid(2): class MyError extends Error { constructor() { Error.captureStackTrace?.(this, MyError); } } + +> Input + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + 3 | Error.captureStackTrace?.(this, MyError);␊ + 4 | }␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + > 3 | Error.captureStackTrace?.(this, MyError);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unnecessary \`Error.captureStackTrace(…)\` call.␊ + 4 | }␊ + 5 | }␊ + ` + +## invalid(3): class MyError extends Error { constructor() { Error.captureStackTrace(this, this.constructor); } } + +> Input + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + 3 | Error.captureStackTrace(this, this.constructor);␊ + 4 | }␊ + 5 | }␊ + ` + +> Output + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + 3 | ␊ + 4 | }␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + > 3 | Error.captureStackTrace(this, this.constructor);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unnecessary \`Error.captureStackTrace(…)\` call.␊ + 4 | }␊ + 5 | }␊ + ` + +## invalid(4): class MyError extends Error { constructor() { Error.captureStackTrace(this, this.constructor); } } + +> Input + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + 3 | Error.captureStackTrace(this, this.constructor);␊ + 4 | }␊ + 5 | }␊ + ` + +> Output + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + 3 | ␊ + 4 | }␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + > 3 | Error.captureStackTrace(this, this.constructor);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unnecessary \`Error.captureStackTrace(…)\` call.␊ + 4 | }␊ + 5 | }␊ + ` + +## invalid(5): class MyError extends Error { constructor() { Error.captureStackTrace?.(this, this.constructor); } } + +> Input + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + 3 | Error.captureStackTrace?.(this, this.constructor);␊ + 4 | }␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + > 3 | Error.captureStackTrace?.(this, this.constructor);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unnecessary \`Error.captureStackTrace(…)\` call.␊ + 4 | }␊ + 5 | }␊ + ` + +## invalid(6): class MyError extends Error { constructor() { Error.captureStackTrace(this, new.target); } } + +> Input + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + 3 | Error.captureStackTrace(this, new.target);␊ + 4 | }␊ + 5 | }␊ + ` + +> Output + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + 3 | ␊ + 4 | }␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + > 3 | Error.captureStackTrace(this, new.target);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unnecessary \`Error.captureStackTrace(…)\` call.␊ + 4 | }␊ + 5 | }␊ + ` + +## invalid(7): class MyError extends Error { constructor() { Error.captureStackTrace?.(this, new.target); } } + +> Input + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + 3 | Error.captureStackTrace?.(this, new.target);␊ + 4 | }␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + > 3 | Error.captureStackTrace?.(this, new.target);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unnecessary \`Error.captureStackTrace(…)\` call.␊ + 4 | }␊ + 5 | }␊ + ` + +## invalid(8): class MyError extends Error { constructor() { Error.captureStackTrace(this, MyError) } } + +> Input + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + 3 | Error.captureStackTrace(this, MyError)␊ + 4 | }␊ + 5 | }␊ + ` + +> Output + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + 3 | ␊ + 4 | }␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + > 3 | Error.captureStackTrace(this, MyError)␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unnecessary \`Error.captureStackTrace(…)\` call.␊ + 4 | }␊ + 5 | }␊ + ` + +## invalid(9): class MyError extends EvalError { constructor() { Error.captureStackTrace(this, MyError) } } + +> Input + + `␊ + 1 | class MyError extends EvalError {␊ + 2 | constructor() {␊ + 3 | Error.captureStackTrace(this, MyError)␊ + 4 | }␊ + 5 | }␊ + ` + +> Output + + `␊ + 1 | class MyError extends EvalError {␊ + 2 | constructor() {␊ + 3 | ␊ + 4 | }␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class MyError extends EvalError {␊ + 2 | constructor() {␊ + > 3 | Error.captureStackTrace(this, MyError)␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unnecessary \`Error.captureStackTrace(…)\` call.␊ + 4 | }␊ + 5 | }␊ + ` + +## invalid(10): class MyError extends RangeError { constructor() { Error.captureStackTrace(this, MyError) } } + +> Input + + `␊ + 1 | class MyError extends RangeError {␊ + 2 | constructor() {␊ + 3 | Error.captureStackTrace(this, MyError)␊ + 4 | }␊ + 5 | }␊ + ` + +> Output + + `␊ + 1 | class MyError extends RangeError {␊ + 2 | constructor() {␊ + 3 | ␊ + 4 | }␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class MyError extends RangeError {␊ + 2 | constructor() {␊ + > 3 | Error.captureStackTrace(this, MyError)␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unnecessary \`Error.captureStackTrace(…)\` call.␊ + 4 | }␊ + 5 | }␊ + ` + +## invalid(11): class MyError extends ReferenceError { constructor() { Error.captureStackTrace(this, MyError) } } + +> Input + + `␊ + 1 | class MyError extends ReferenceError {␊ + 2 | constructor() {␊ + 3 | Error.captureStackTrace(this, MyError)␊ + 4 | }␊ + 5 | }␊ + ` + +> Output + + `␊ + 1 | class MyError extends ReferenceError {␊ + 2 | constructor() {␊ + 3 | ␊ + 4 | }␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class MyError extends ReferenceError {␊ + 2 | constructor() {␊ + > 3 | Error.captureStackTrace(this, MyError)␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unnecessary \`Error.captureStackTrace(…)\` call.␊ + 4 | }␊ + 5 | }␊ + ` + +## invalid(12): class MyError extends SyntaxError { constructor() { Error.captureStackTrace(this, MyError) } } + +> Input + + `␊ + 1 | class MyError extends SyntaxError {␊ + 2 | constructor() {␊ + 3 | Error.captureStackTrace(this, MyError)␊ + 4 | }␊ + 5 | }␊ + ` + +> Output + + `␊ + 1 | class MyError extends SyntaxError {␊ + 2 | constructor() {␊ + 3 | ␊ + 4 | }␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class MyError extends SyntaxError {␊ + 2 | constructor() {␊ + > 3 | Error.captureStackTrace(this, MyError)␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unnecessary \`Error.captureStackTrace(…)\` call.␊ + 4 | }␊ + 5 | }␊ + ` + +## invalid(13): class MyError extends TypeError { constructor() { Error.captureStackTrace(this, MyError) } } + +> Input + + `␊ + 1 | class MyError extends TypeError {␊ + 2 | constructor() {␊ + 3 | Error.captureStackTrace(this, MyError)␊ + 4 | }␊ + 5 | }␊ + ` + +> Output + + `␊ + 1 | class MyError extends TypeError {␊ + 2 | constructor() {␊ + 3 | ␊ + 4 | }␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class MyError extends TypeError {␊ + 2 | constructor() {␊ + > 3 | Error.captureStackTrace(this, MyError)␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unnecessary \`Error.captureStackTrace(…)\` call.␊ + 4 | }␊ + 5 | }␊ + ` + +## invalid(14): class MyError extends URIError { constructor() { Error.captureStackTrace(this, MyError) } } + +> Input + + `␊ + 1 | class MyError extends URIError {␊ + 2 | constructor() {␊ + 3 | Error.captureStackTrace(this, MyError)␊ + 4 | }␊ + 5 | }␊ + ` + +> Output + + `␊ + 1 | class MyError extends URIError {␊ + 2 | constructor() {␊ + 3 | ␊ + 4 | }␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class MyError extends URIError {␊ + 2 | constructor() {␊ + > 3 | Error.captureStackTrace(this, MyError)␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unnecessary \`Error.captureStackTrace(…)\` call.␊ + 4 | }␊ + 5 | }␊ + ` + +## invalid(15): class MyError extends AggregateError { constructor() { Error.captureStackTrace(this, MyError) } } + +> Input + + `␊ + 1 | class MyError extends AggregateError {␊ + 2 | constructor() {␊ + 3 | Error.captureStackTrace(this, MyError)␊ + 4 | }␊ + 5 | }␊ + ` + +> Output + + `␊ + 1 | class MyError extends AggregateError {␊ + 2 | constructor() {␊ + 3 | ␊ + 4 | }␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class MyError extends AggregateError {␊ + 2 | constructor() {␊ + > 3 | Error.captureStackTrace(this, MyError)␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unnecessary \`Error.captureStackTrace(…)\` call.␊ + 4 | }␊ + 5 | }␊ + ` + +## invalid(16): class MyError extends SuppressedError { constructor() { Error.captureStackTrace(this, MyError) } } + +> Input + + `␊ + 1 | class MyError extends SuppressedError {␊ + 2 | constructor() {␊ + 3 | Error.captureStackTrace(this, MyError)␊ + 4 | }␊ + 5 | }␊ + ` + +> Output + + `␊ + 1 | class MyError extends SuppressedError {␊ + 2 | constructor() {␊ + 3 | ␊ + 4 | }␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class MyError extends SuppressedError {␊ + 2 | constructor() {␊ + > 3 | Error.captureStackTrace(this, MyError)␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unnecessary \`Error.captureStackTrace(…)\` call.␊ + 4 | }␊ + 5 | }␊ + ` + +## invalid(17): class MyError extends Error { constructor() { const foo = () => { Error.captureStackTrace(this, MyError) } } } + +> Input + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + 3 | const foo = () => {␊ + 4 | Error.captureStackTrace(this, MyError)␊ + 5 | }␊ + 6 | }␊ + 7 | }␊ + ` + +> Output + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + 3 | const foo = () => {␊ + 4 | ␊ + 5 | }␊ + 6 | }␊ + 7 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + 3 | const foo = () => {␊ + > 4 | Error.captureStackTrace(this, MyError)␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unnecessary \`Error.captureStackTrace(…)\` call.␊ + 5 | }␊ + 6 | }␊ + 7 | }␊ + ` + +## invalid(18): class MyError extends Error { constructor() { if (a) Error.captureStackTrace(this, MyError) } } + +> Input + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + 3 | if (a) Error.captureStackTrace(this, MyError)␊ + 4 | }␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + > 3 | if (a) Error.captureStackTrace(this, MyError)␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unnecessary \`Error.captureStackTrace(…)\` call.␊ + 4 | }␊ + 5 | }␊ + ` + +## invalid(19): class MyError extends Error { constructor() { const x = () => Error.captureStackTrace(this, MyError) } } + +> Input + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + 3 | const x = () => Error.captureStackTrace(this, MyError)␊ + 4 | }␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + > 3 | const x = () => Error.captureStackTrace(this, MyError)␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unnecessary \`Error.captureStackTrace(…)\` call.␊ + 4 | }␊ + 5 | }␊ + ` + +## invalid(20): class MyError extends Error { constructor() { void Error.captureStackTrace(this, MyError) } } + +> Input + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + 3 | void Error.captureStackTrace(this, MyError)␊ + 4 | }␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor() {␊ + > 3 | void Error.captureStackTrace(this, MyError)␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unnecessary \`Error.captureStackTrace(…)\` call.␊ + 4 | }␊ + 5 | }␊ + ` + +## invalid(21): export default class extends Error { constructor() { Error.captureStackTrace(this, new.target) } } + +> Input + + `␊ + 1 | export default class extends Error {␊ + 2 | constructor() {␊ + 3 | Error.captureStackTrace(this, new.target)␊ + 4 | }␊ + 5 | }␊ + ` + +> Output + + `␊ + 1 | export default class extends Error {␊ + 2 | constructor() {␊ + 3 | ␊ + 4 | }␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | export default class extends Error {␊ + 2 | constructor() {␊ + > 3 | Error.captureStackTrace(this, new.target)␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unnecessary \`Error.captureStackTrace(…)\` call.␊ + 4 | }␊ + 5 | }␊ + ` + +## invalid(22): export default ( class extends Error { constructor() { Error.captureStackTrace(this, new.target) } } ) + +> Input + + `␊ + 1 | export default (␊ + 2 | class extends Error {␊ + 3 | constructor() {␊ + 4 | Error.captureStackTrace(this, new.target)␊ + 5 | }␊ + 6 | }␊ + 7 | )␊ + ` + +> Output + + `␊ + 1 | export default (␊ + 2 | class extends Error {␊ + 3 | constructor() {␊ + 4 | ␊ + 5 | }␊ + 6 | }␊ + 7 | )␊ + ` + +> Error 1/1 + + `␊ + 1 | export default (␊ + 2 | class extends Error {␊ + 3 | constructor() {␊ + > 4 | Error.captureStackTrace(this, new.target)␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unnecessary \`Error.captureStackTrace(…)\` call.␊ + 5 | }␊ + 6 | }␊ + 7 | )␊ + ` diff --git a/test/snapshots/no-useless-error-capture-stack-trace.js.snap b/test/snapshots/no-useless-error-capture-stack-trace.js.snap new file mode 100644 index 0000000000..c5aa84921d Binary files /dev/null and b/test/snapshots/no-useless-error-capture-stack-trace.js.snap differ diff --git a/test/snapshots/prefer-class-fields.js.md b/test/snapshots/prefer-class-fields.js.md new file mode 100644 index 0000000000..5c9a98b549 --- /dev/null +++ b/test/snapshots/prefer-class-fields.js.md @@ -0,0 +1,667 @@ +# Snapshot report for `test/prefer-class-fields.js` + +The actual snapshot is saved in `prefer-class-fields.js.snap`. + +Generated by [AVA](https://avajs.dev). + +## invalid(1): class Foo { constructor() { this.bar = 1; } } + +> Input + + `␊ + 1 | class Foo {␊ + 2 | constructor() {␊ + 3 | this.bar = 1;␊ + 4 | }␊ + 5 | }␊ + ` + +> Output + + `␊ + 1 | class Foo {␊ + 2 | constructor() {␊ + 3 | }␊ + 4 | bar = 1;␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class Foo {␊ + 2 | constructor() {␊ + > 3 | this.bar = 1;␊ + | ^^^^^^^^^^^^^ Prefer class field declaration over \`this\` assignment in constructor for static values.␊ + 4 | }␊ + 5 | }␊ + ` + +## invalid(2): class Foo { constructor() { ; this.bar = 1; } } + +> Input + + `␊ + 1 | class Foo {␊ + 2 | constructor() {␊ + 3 | ;␊ + 4 | this.bar = 1;␊ + 5 | }␊ + 6 | }␊ + ` + +> Output + + `␊ + 1 | class Foo {␊ + 2 | constructor() {␊ + 3 | ;␊ + 4 | }␊ + 5 | bar = 1;␊ + 6 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class Foo {␊ + 2 | constructor() {␊ + 3 | ;␊ + > 4 | this.bar = 1;␊ + | ^^^^^^^^^^^^^ Prefer class field declaration over \`this\` assignment in constructor for static values.␊ + 5 | }␊ + 6 | }␊ + ` + +## invalid(3): class Foo { constructor() { this.bar = 1; this.baz = 2; } } + +> Input + + `␊ + 1 | class Foo {␊ + 2 | constructor() {␊ + 3 | this.bar = 1;␊ + 4 | this.baz = 2;␊ + 5 | }␊ + 6 | }␊ + ` + +> Output + + `␊ + 1 | class Foo {␊ + 2 | constructor() {␊ + 3 | }␊ + 4 | bar = 1;␊ + 5 | baz = 2;␊ + 6 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class Foo {␊ + 2 | constructor() {␊ + > 3 | this.bar = 1;␊ + | ^^^^^^^^^^^^^ Prefer class field declaration over \`this\` assignment in constructor for static values.␊ + 4 | this.baz = 2;␊ + 5 | }␊ + 6 | }␊ + ` + +## invalid(4): class Foo { constructor() { this.bar = 1; this.bar = 2; } } + +> Input + + `␊ + 1 | class Foo {␊ + 2 | constructor() {␊ + 3 | this.bar = 1;␊ + 4 | this.bar = 2;␊ + 5 | }␊ + 6 | }␊ + ` + +> Output + + `␊ + 1 | class Foo {␊ + 2 | constructor() {␊ + 3 | this.bar = 2;␊ + 4 | }␊ + 5 | bar = 1;␊ + 6 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class Foo {␊ + 2 | constructor() {␊ + > 3 | this.bar = 1;␊ + | ^^^^^^^^^^^^^ Prefer class field declaration over \`this\` assignment in constructor for static values.␊ + 4 | this.bar = 2;␊ + 5 | }␊ + 6 | }␊ + ` + +## invalid(5): class Foo { bar; constructor() { this.bar = 1; } } + +> Input + + `␊ + 1 | class Foo {␊ + 2 | bar;␊ + 3 | constructor() {␊ + 4 | this.bar = 1;␊ + 5 | }␊ + 6 | }␊ + ` + +> Output + + `␊ + 1 | class Foo {␊ + 2 | bar = 1;␊ + 3 | constructor() {␊ + 4 | }␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class Foo {␊ + 2 | bar;␊ + 3 | constructor() {␊ + > 4 | this.bar = 1;␊ + | ^^^^^^^^^^^^^ Prefer class field declaration over \`this\` assignment in constructor for static values.␊ + 5 | }␊ + 6 | }␊ + ` + +## invalid(6): class Foo { #bar; constructor() { this.#bar = 1; } } + +> Input + + `␊ + 1 | class Foo {␊ + 2 | #bar;␊ + 3 | constructor() {␊ + 4 | this.#bar = 1;␊ + 5 | }␊ + 6 | }␊ + ` + +> Output + + `␊ + 1 | class Foo {␊ + 2 | #bar = 1;␊ + 3 | constructor() {␊ + 4 | }␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class Foo {␊ + 2 | #bar;␊ + 3 | constructor() {␊ + > 4 | this.#bar = 1;␊ + | ^^^^^^^^^^^^^^ Prefer class field declaration over \`this\` assignment in constructor for static values.␊ + 5 | }␊ + 6 | }␊ + ` + +## invalid(7): class Foo { bar = 0; constructor() { this.bar = 1; } } + +> Input + + `␊ + 1 | class Foo {␊ + 2 | bar = 0;␊ + 3 | constructor() {␊ + 4 | this.bar = 1;␊ + 5 | }␊ + 6 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class Foo {␊ + 2 | bar = 0;␊ + 3 | constructor() {␊ + > 4 | this.bar = 1;␊ + | ^^^^^^^^^^^^^ Prefer class field declaration over \`this\` assignment in constructor for static values.␊ + 5 | }␊ + 6 | }␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Encountered same-named class field declaration and \`this\` assignment in constructor. Replace the class field declaration with the value from \`this\` assignment.␊ + 1 | class Foo {␊ + 2 | bar = 1;␊ + 3 | constructor() {␊ + 4 | }␊ + 5 | }␊ + ` + +## invalid(8): class Foo { #bar = 0; constructor() { this.#bar = 1; } } + +> Input + + `␊ + 1 | class Foo {␊ + 2 | #bar = 0;␊ + 3 | constructor() {␊ + 4 | this.#bar = 1;␊ + 5 | }␊ + 6 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class Foo {␊ + 2 | #bar = 0;␊ + 3 | constructor() {␊ + > 4 | this.#bar = 1;␊ + | ^^^^^^^^^^^^^^ Prefer class field declaration over \`this\` assignment in constructor for static values.␊ + 5 | }␊ + 6 | }␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Encountered same-named class field declaration and \`this\` assignment in constructor. Replace the class field declaration with the value from \`this\` assignment.␊ + 1 | class Foo {␊ + 2 | #bar = 1;␊ + 3 | constructor() {␊ + 4 | }␊ + 5 | }␊ + ` + +## invalid(9): class Foo { [bar]; constructor() { this.bar = 1; } } + +> Input + + `␊ + 1 | class Foo {␊ + 2 | [bar];␊ + 3 | constructor() {␊ + 4 | this.bar = 1;␊ + 5 | }␊ + 6 | }␊ + ` + +> Output + + `␊ + 1 | class Foo {␊ + 2 | [bar];␊ + 3 | constructor() {␊ + 4 | }␊ + 5 | bar = 1;␊ + 6 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class Foo {␊ + 2 | [bar];␊ + 3 | constructor() {␊ + > 4 | this.bar = 1;␊ + | ^^^^^^^^^^^^^ Prefer class field declaration over \`this\` assignment in constructor for static values.␊ + 5 | }␊ + 6 | }␊ + ` + +## invalid(10): class Foo { [bar] = 0; constructor() { this.bar = 1; } } + +> Input + + `␊ + 1 | class Foo {␊ + 2 | [bar] = 0;␊ + 3 | constructor() {␊ + 4 | this.bar = 1;␊ + 5 | }␊ + 6 | }␊ + ` + +> Output + + `␊ + 1 | class Foo {␊ + 2 | [bar] = 0;␊ + 3 | constructor() {␊ + 4 | }␊ + 5 | bar = 1;␊ + 6 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class Foo {␊ + 2 | [bar] = 0;␊ + 3 | constructor() {␊ + > 4 | this.bar = 1;␊ + | ^^^^^^^^^^^^^ Prefer class field declaration over \`this\` assignment in constructor for static values.␊ + 5 | }␊ + 6 | }␊ + ` + +## invalid(11): class Foo { static bar; constructor() { this.bar = 1; } } + +> Input + + `␊ + 1 | class Foo {␊ + 2 | static bar;␊ + 3 | constructor() {␊ + 4 | this.bar = 1;␊ + 5 | }␊ + 6 | }␊ + ` + +> Output + + `␊ + 1 | class Foo {␊ + 2 | static bar;␊ + 3 | constructor() {␊ + 4 | }␊ + 5 | bar = 1;␊ + 6 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class Foo {␊ + 2 | static bar;␊ + 3 | constructor() {␊ + > 4 | this.bar = 1;␊ + | ^^^^^^^^^^^^^ Prefer class field declaration over \`this\` assignment in constructor for static values.␊ + 5 | }␊ + 6 | }␊ + ` + +## invalid(12): class Foo { static bar = 0; constructor() { this.bar = 1; } } + +> Input + + `␊ + 1 | class Foo {␊ + 2 | static bar = 0;␊ + 3 | constructor() {␊ + 4 | this.bar = 1;␊ + 5 | }␊ + 6 | }␊ + ` + +> Output + + `␊ + 1 | class Foo {␊ + 2 | static bar = 0;␊ + 3 | constructor() {␊ + 4 | }␊ + 5 | bar = 1;␊ + 6 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class Foo {␊ + 2 | static bar = 0;␊ + 3 | constructor() {␊ + > 4 | this.bar = 1;␊ + | ^^^^^^^^^^^^^ Prefer class field declaration over \`this\` assignment in constructor for static values.␊ + 5 | }␊ + 6 | }␊ + ` + +## invalid(13): class Foo { static [bar]; constructor() { this.bar = 1; } } + +> Input + + `␊ + 1 | class Foo {␊ + 2 | static [bar];␊ + 3 | constructor() {␊ + 4 | this.bar = 1;␊ + 5 | }␊ + 6 | }␊ + ` + +> Output + + `␊ + 1 | class Foo {␊ + 2 | static [bar];␊ + 3 | constructor() {␊ + 4 | }␊ + 5 | bar = 1;␊ + 6 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class Foo {␊ + 2 | static [bar];␊ + 3 | constructor() {␊ + > 4 | this.bar = 1;␊ + | ^^^^^^^^^^^^^ Prefer class field declaration over \`this\` assignment in constructor for static values.␊ + 5 | }␊ + 6 | }␊ + ` + +## invalid(14): class Foo { static [bar] = 1; constructor() { this.bar = 1; } } + +> Input + + `␊ + 1 | class Foo {␊ + 2 | static [bar] = 1;␊ + 3 | constructor() {␊ + 4 | this.bar = 1;␊ + 5 | }␊ + 6 | }␊ + ` + +> Output + + `␊ + 1 | class Foo {␊ + 2 | static [bar] = 1;␊ + 3 | constructor() {␊ + 4 | }␊ + 5 | bar = 1;␊ + 6 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class Foo {␊ + 2 | static [bar] = 1;␊ + 3 | constructor() {␊ + > 4 | this.bar = 1;␊ + | ^^^^^^^^^^^^^ Prefer class field declaration over \`this\` assignment in constructor for static values.␊ + 5 | }␊ + 6 | }␊ + ` + +## invalid(15): class Foo { constructor() { this.bar = 1; }} + +> Input + + `␊ + 1 | class Foo {␊ + 2 | constructor() {␊ + 3 | this.bar = 1;␊ + 4 | }}␊ + ` + +> Output + + `␊ + 1 | class Foo {␊ + 2 | constructor() {␊ + 3 | }␊ + 4 | bar = 1;␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class Foo {␊ + 2 | constructor() {␊ + > 3 | this.bar = 1;␊ + | ^^^^^^^^^^^^^ Prefer class field declaration over \`this\` assignment in constructor for static values.␊ + 4 | }}␊ + ` + +## invalid(16): class Foo { constructor() { this.bar = 1; } static} + +> Input + + `␊ + 1 | class Foo {␊ + 2 | constructor() {␊ + 3 | this.bar = 1;␊ + 4 | }␊ + 5 | static}␊ + ` + +> Output + + `␊ + 1 | class Foo {␊ + 2 | constructor() {␊ + 3 | }␊ + 4 | static;␊ + 5 | bar = 1;␊ + 6 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class Foo {␊ + 2 | constructor() {␊ + > 3 | this.bar = 1;␊ + | ^^^^^^^^^^^^^ Prefer class field declaration over \`this\` assignment in constructor for static values.␊ + 4 | }␊ + 5 | static}␊ + ` + +## invalid(17): class Foo { constructor() { this.bar = 1; } static// comment; } + +> Input + + `␊ + 1 | class Foo {␊ + 2 | constructor() {␊ + 3 | this.bar = 1;␊ + 4 | }␊ + 5 | static// comment;␊ + 6 | }␊ + ` + +> Output + + `␊ + 1 | class Foo {␊ + 2 | constructor() {␊ + 3 | }␊ + 4 | static// comment;␊ + 5 | ;bar = 1;␊ + 6 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class Foo {␊ + 2 | constructor() {␊ + > 3 | this.bar = 1;␊ + | ^^^^^^^^^^^^^ Prefer class field declaration over \`this\` assignment in constructor for static values.␊ + 4 | }␊ + 5 | static// comment;␊ + 6 | }␊ + ` + +## invalid(1): class MyError extends Error { constructor(message: string) { this.name = "MyError"; } } + +> Input + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor(message: string) {␊ + 3 | this.name = "MyError";␊ + 4 | }␊ + 5 | }␊ + ` + +> Output + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor(message: string) {␊ + 3 | }␊ + 4 | name = "MyError";␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class MyError extends Error {␊ + 2 | constructor(message: string) {␊ + > 3 | this.name = "MyError";␊ + | ^^^^^^^^^^^^^^^^^^^^^^ Prefer class field declaration over \`this\` assignment in constructor for static values.␊ + 4 | }␊ + 5 | }␊ + ` + +## invalid(2): class MyError extends Error { name: string; constructor(message: string) { this.name = "MyError"; } } + +> Input + + `␊ + 1 | class MyError extends Error {␊ + 2 | name: string;␊ + 3 | constructor(message: string) {␊ + 4 | this.name = "MyError";␊ + 5 | }␊ + 6 | }␊ + ` + +> Output + + `␊ + 1 | class MyError extends Error {␊ + 2 | name: string = "MyError";␊ + 3 | constructor(message: string) {␊ + 4 | }␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | class MyError extends Error {␊ + 2 | name: string;␊ + 3 | constructor(message: string) {␊ + > 4 | this.name = "MyError";␊ + | ^^^^^^^^^^^^^^^^^^^^^^ Prefer class field declaration over \`this\` assignment in constructor for static values.␊ + 5 | }␊ + 6 | }␊ + ` diff --git a/test/snapshots/prefer-class-fields.js.snap b/test/snapshots/prefer-class-fields.js.snap new file mode 100644 index 0000000000..bd393c134b Binary files /dev/null and b/test/snapshots/prefer-class-fields.js.snap differ diff --git a/test/snapshots/prefer-string-raw.js.md b/test/snapshots/prefer-string-raw.js.md index 44a6d762d6..cd8cc4004b 100644 --- a/test/snapshots/prefer-string-raw.js.md +++ b/test/snapshots/prefer-string-raw.js.md @@ -87,3 +87,45 @@ Generated by [AVA](https://avajs.dev). > 1 | const foo = "foo \\\\x46";␊ | ^^^^^^^^^^^ \`String.raw\` should be used to avoid escaping \`\\\`.␊ ` + +## invalid(5): a = 'a\\b\'' + +> Input + + `␊ + 1 | a = 'a\\\\b\\''␊ + ` + +> Output + + `␊ + 1 | a = String.raw\`a\\b'\`␊ + ` + +> Error 1/1 + + `␊ + > 1 | a = 'a\\\\b\\''␊ + | ^^^^^^^^ \`String.raw\` should be used to avoid escaping \`\\\`.␊ + ` + +## invalid(6): a = "a\\b\"" + +> Input + + `␊ + 1 | a = "a\\\\b\\""␊ + ` + +> Output + + `␊ + 1 | a = String.raw\`a\\b"\`␊ + ` + +> Error 1/1 + + `␊ + > 1 | a = "a\\\\b\\""␊ + | ^^^^^^^^ \`String.raw\` should be used to avoid escaping \`\\\`.␊ + ` diff --git a/test/snapshots/prefer-string-raw.js.snap b/test/snapshots/prefer-string-raw.js.snap index 93f30083b5..2afb8c0aa6 100644 Binary files a/test/snapshots/prefer-string-raw.js.snap and b/test/snapshots/prefer-string-raw.js.snap differ diff --git a/test/snapshots/prefer-string-replace-all.js.md b/test/snapshots/prefer-string-replace-all.js.md index d4affe838a..7cb8ce44cd 100644 --- a/test/snapshots/prefer-string-replace-all.js.md +++ b/test/snapshots/prefer-string-replace-all.js.md @@ -327,7 +327,7 @@ Generated by [AVA](https://avajs.dev). > Output `␊ - 1 | foo.replaceAll('a', bar)␊ + 1 | foo.replaceAll('\\u{61}', bar)␊ ` > Error 1/1 @@ -348,7 +348,7 @@ Generated by [AVA](https://avajs.dev). > Output `␊ - 1 | foo.replaceAll('a', bar)␊ + 1 | foo.replaceAll('\\u{61}', bar)␊ ` > Error 1/1 @@ -358,7 +358,49 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊ ` -## invalid(17): foo.replace(/]/g, "bar") +## invalid(17): str.replace(/\u200B/g, '') + +> Input + + `␊ + 1 | str.replace(/\\u200B/g, '')␊ + ` + +> Output + + `␊ + 1 | str.replaceAll('\\u200B', '')␊ + ` + +> Error 1/1 + + `␊ + > 1 | str.replace(/\\u200B/g, '')␊ + | ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊ + ` + +## invalid(18): str.replace(/\x20/g, '') + +> Input + + `␊ + 1 | str.replace(/\\x20/g, '')␊ + ` + +> Output + + `␊ + 1 | str.replaceAll('\\x20', '')␊ + ` + +> Error 1/1 + + `␊ + > 1 | str.replace(/\\x20/g, '')␊ + | ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊ + ` + +## invalid(19): foo.replace(/]/g, "bar") > Input @@ -379,7 +421,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊ ` -## invalid(18): foo.replace(/a/gi, bar) +## invalid(20): foo.replace(/a/gi, bar) > Input @@ -400,7 +442,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊ ` -## invalid(19): foo.replace(/a/gui, bar) +## invalid(21): foo.replace(/a/gui, bar) > Input @@ -421,7 +463,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊ ` -## invalid(20): foo.replace(/a/uig, bar) +## invalid(22): foo.replace(/a/uig, bar) > Input @@ -442,7 +484,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊ ` -## invalid(21): foo.replace(/a/vig, bar) +## invalid(23): foo.replace(/a/vig, bar) > Input @@ -463,7 +505,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊ ` -## invalid(22): const pattern = new RegExp("foo", "g"); foo.replace(pattern, bar) +## invalid(24): const pattern = new RegExp("foo", "g"); foo.replace(pattern, bar) > Input @@ -484,7 +526,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊ ` -## invalid(23): foo.replace(new RegExp("foo", "g"), bar) +## invalid(25): foo.replace(new RegExp("foo", "g"), bar) > Input @@ -505,7 +547,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊ ` -## invalid(24): foo.replace(/a]/g, _) +## invalid(26): foo.replace(/a]/g, _) > Input @@ -526,7 +568,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊ ` -## invalid(25): foo.replace(/[a]/g, _) +## invalid(27): foo.replace(/[a]/g, _) > Input @@ -547,7 +589,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊ ` -## invalid(26): foo.replace(/a{1/g, _) +## invalid(28): foo.replace(/a{1/g, _) > Input @@ -568,7 +610,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊ ` -## invalid(27): foo.replace(/a{1}/g, _) +## invalid(29): foo.replace(/a{1}/g, _) > Input @@ -589,7 +631,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊ ` -## invalid(28): foo.replace(/\u0022/g, _) +## invalid(30): foo.replace(/\u0022/g, _) > Input @@ -600,7 +642,7 @@ Generated by [AVA](https://avajs.dev). > Output `␊ - 1 | foo.replaceAll('"', _)␊ + 1 | foo.replaceAll('\\u0022', _)␊ ` > Error 1/1 @@ -610,7 +652,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊ ` -## invalid(29): foo.replace(/\u0027/g, _) +## invalid(31): foo.replace(/\u0027/g, _) > Input @@ -621,7 +663,7 @@ Generated by [AVA](https://avajs.dev). > Output `␊ - 1 | foo.replaceAll('\\'', _)␊ + 1 | foo.replaceAll('\\u0027', _)␊ ` > Error 1/1 @@ -631,28 +673,70 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊ ` -## invalid(30): foo.replace(/\cM\cj/g, _) +## invalid(32): foo.replace(/\cM\cj\cI/g, _) > Input `␊ - 1 | foo.replace(/\\cM\\cj/g, _)␊ + 1 | foo.replace(/\\cM\\cj\\cI/g, _)␊ ` > Output `␊ - 1 | foo.replaceAll('\\r\\n', _)␊ + 1 | foo.replaceAll('\\r\\n\\t', _)␊ ` > Error 1/1 `␊ - > 1 | foo.replace(/\\cM\\cj/g, _)␊ + > 1 | foo.replace(/\\cM\\cj\\cI/g, _)␊ | ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊ ` -## invalid(31): foo.replace(/\x22/g, _) +## invalid(33): foo.replace(/\x0d\x0a\x09/g, _) + +> Input + + `␊ + 1 | foo.replace(/\\x0d\\x0a\\x09/g, _)␊ + ` + +> Output + + `␊ + 1 | foo.replaceAll('\\x0d\\x0a\\x09', _)␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.replace(/\\x0d\\x0a\\x09/g, _)␊ + | ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊ + ` + +## invalid(34): foo.replace(/\u000d\u000a\u0009/g, _) + +> Input + + `␊ + 1 | foo.replace(/\\u000d\\u000a\\u0009/g, _)␊ + ` + +> Output + + `␊ + 1 | foo.replaceAll('\\u000d\\u000a\\u0009', _)␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.replace(/\\u000d\\u000a\\u0009/g, _)␊ + | ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊ + ` + +## invalid(35): foo.replace(/\x22/g, _) > Input @@ -663,7 +747,7 @@ Generated by [AVA](https://avajs.dev). > Output `␊ - 1 | foo.replaceAll('"', _)␊ + 1 | foo.replaceAll('\\x22', _)␊ ` > Error 1/1 @@ -673,7 +757,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊ ` -## invalid(32): foo.replace(/\x27/g, _) +## invalid(36): foo.replace(/\x27/g, _) > Input @@ -684,7 +768,7 @@ Generated by [AVA](https://avajs.dev). > Output `␊ - 1 | foo.replaceAll('\\'', _)␊ + 1 | foo.replaceAll('\\x27', _)␊ ` > Error 1/1 @@ -694,7 +778,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊ ` -## invalid(33): foo.replace(/\uD83D\ude00/g, _) +## invalid(37): foo.replace(/\uD83D\ude00/g, _) > Input @@ -705,7 +789,7 @@ Generated by [AVA](https://avajs.dev). > Output `␊ - 1 | foo.replaceAll('πŸ˜€', _)␊ + 1 | foo.replaceAll('\\uD83D\\ude00', _)␊ ` > Error 1/1 @@ -715,7 +799,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊ ` -## invalid(34): foo.replace(/\u{1f600}/gu, _) +## invalid(38): foo.replace(/\u{1f600}/gu, _) > Input @@ -726,7 +810,7 @@ Generated by [AVA](https://avajs.dev). > Output `␊ - 1 | foo.replaceAll('πŸ˜€', _)␊ + 1 | foo.replaceAll('\\u{1f600}', _)␊ ` > Error 1/1 @@ -736,7 +820,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊ ` -## invalid(35): foo.replace(/\n/g, _) +## invalid(39): foo.replace(/\n/g, _) > Input @@ -757,7 +841,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊ ` -## invalid(36): foo.replace(/\u{20}/gu, _) +## invalid(40): foo.replace(/\u{20}/gu, _) > Input @@ -768,7 +852,7 @@ Generated by [AVA](https://avajs.dev). > Output `␊ - 1 | foo.replaceAll(' ', _)␊ + 1 | foo.replaceAll('\\u{20}', _)␊ ` > Error 1/1 @@ -778,7 +862,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊ ` -## invalid(37): foo.replace(/\u{20}/gv, _) +## invalid(41): foo.replace(/\u{20}/gv, _) > Input @@ -789,7 +873,7 @@ Generated by [AVA](https://avajs.dev). > Output `␊ - 1 | foo.replaceAll(' ', _)␊ + 1 | foo.replaceAll('\\u{20}', _)␊ ` > Error 1/1 @@ -799,7 +883,28 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊ ` -## invalid(38): foo.replaceAll(/a]/g, _) +## invalid(42): foo.replace(/\1/g, _) + +> Input + + `␊ + 1 | foo.replace(/\\1/g, _)␊ + ` + +> Output + + `␊ + 1 | foo.replaceAll('\\u{1}', _)␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.replace(/\\1/g, _)␊ + | ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊ + ` + +## invalid(43): foo.replaceAll(/a]/g, _) > Input @@ -820,7 +925,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^ This pattern can be replaced with 'a]'.␊ ` -## invalid(39): foo.replaceAll(/\r\n\u{1f600}/gu, _) +## invalid(44): foo.replaceAll(/\r\n\u{1f600}/gu, _) > Input @@ -831,17 +936,17 @@ Generated by [AVA](https://avajs.dev). > Output `␊ - 1 | foo.replaceAll('\\r\\nπŸ˜€', _)␊ + 1 | foo.replaceAll('\\r\\n\\u{1f600}', _)␊ ` > Error 1/1 `␊ > 1 | foo.replaceAll(/\\r\\n\\u{1f600}/gu, _)␊ - | ^^^^^^^^^^^^^^^^^ This pattern can be replaced with '\\r\\nπŸ˜€'.␊ + | ^^^^^^^^^^^^^^^^^ This pattern can be replaced with '\\r\\n\\u{1f600}'.␊ ` -## invalid(40): foo.replaceAll(/\r\n\u{1f600}/gv, _) +## invalid(45): foo.replaceAll(/\r\n\u{1f600}/gv, _) > Input @@ -852,17 +957,17 @@ Generated by [AVA](https://avajs.dev). > Output `␊ - 1 | foo.replaceAll('\\r\\nπŸ˜€', _)␊ + 1 | foo.replaceAll('\\r\\n\\u{1f600}', _)␊ ` > Error 1/1 `␊ > 1 | foo.replaceAll(/\\r\\n\\u{1f600}/gv, _)␊ - | ^^^^^^^^^^^^^^^^^ This pattern can be replaced with '\\r\\nπŸ˜€'.␊ + | ^^^^^^^^^^^^^^^^^ This pattern can be replaced with '\\r\\n\\u{1f600}'.␊ ` -## invalid(41): foo.replaceAll(/a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long string/g, _) +## invalid(46): foo.replaceAll(/a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long string/g, _) > Input @@ -883,7 +988,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This pattern can be replaced with a string literal.␊ ` -## invalid(42): foo.replace(/(?!a)+/g, "") +## invalid(47): foo.replace(/(?!a)+/g, "") > Input diff --git a/test/snapshots/prefer-string-replace-all.js.snap b/test/snapshots/prefer-string-replace-all.js.snap index ab9ccae832..239107a53e 100644 Binary files a/test/snapshots/prefer-string-replace-all.js.snap and b/test/snapshots/prefer-string-replace-all.js.snap differ diff --git a/test/snapshots/require-module-specifiers.js.md b/test/snapshots/require-module-specifiers.js.md new file mode 100644 index 0000000000..1462ece12d --- /dev/null +++ b/test/snapshots/require-module-specifiers.js.md @@ -0,0 +1,430 @@ +# Snapshot report for `test/require-module-specifiers.js` + +The actual snapshot is saved in `require-module-specifiers.js.snap`. + +Generated by [AVA](https://avajs.dev). + +## invalid(1): import {} from "foo"; + +> Input + + `␊ + 1 | import {} from "foo";␊ + ` + +> Error 1/1 + + `␊ + > 1 | import {} from "foo";␊ + | ^^ import statement without specifiers is not allowed.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Remove this import statement.␊ + 1 |␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to side effect import.␊ + 1 | import "foo";␊ + ` + +## invalid(2): import{}from"foo"; + +> Input + + `␊ + 1 | import{}from"foo";␊ + ` + +> Error 1/1 + + `␊ + > 1 | import{}from"foo";␊ + | ^^ import statement without specifiers is not allowed.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Remove this import statement.␊ + 1 |␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to side effect import.␊ + 1 | import"foo";␊ + ` + +## invalid(3): import { } from "foo"; + +> Input + + `␊ + 1 | import {␊ + 2 | } from "foo";␊ + ` + +> Error 1/1 + + `␊ + > 1 | import {␊ + | ^␊ + > 2 | } from "foo";␊ + | ^^ import statement without specifiers is not allowed.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Remove this import statement.␊ + 1 |␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to side effect import.␊ + 1 | import ␊ + 2 | "foo";␊ + ` + +## invalid(4): import foo, {} from "foo"; + +> Input + + `␊ + 1 | import foo, {} from "foo";␊ + ` + +> Output + + `␊ + 1 | import foo from "foo";␊ + ` + +> Error 1/1 + + `␊ + > 1 | import foo, {} from "foo";␊ + | ^^ import statement without specifiers is not allowed.␊ + ` + +## invalid(5): import foo,{}from "foo"; + +> Input + + `␊ + 1 | import foo,{}from "foo";␊ + ` + +> Output + + `␊ + 1 | import foo from "foo";␊ + ` + +> Error 1/1 + + `␊ + > 1 | import foo,{}from "foo";␊ + | ^^ import statement without specifiers is not allowed.␊ + ` + +## invalid(6): import foo, { } from "foo"; + +> Input + + `␊ + 1 | import foo, {␊ + 2 | } from "foo";␊ + ` + +> Output + + `␊ + 1 | import foo ␊ + 2 | from "foo";␊ + ` + +> Error 1/1 + + `␊ + > 1 | import foo, {␊ + | ^␊ + > 2 | } from "foo";␊ + | ^^ import statement without specifiers is not allowed.␊ + ` + +## invalid(7): import foo,{}/* comment */from "foo"; + +> Input + + `␊ + 1 | import foo,{}/* comment */from "foo";␊ + ` + +> Output + + `␊ + 1 | import foo/* comment */from "foo";␊ + ` + +> Error 1/1 + + `␊ + > 1 | import foo,{}/* comment */from "foo";␊ + | ^^ import statement without specifiers is not allowed.␊ + ` + +## invalid(8): import type {} from "foo"; + +> Input + + `␊ + 1 | import type {} from "foo";␊ + ` + +> Output + + `␊ + 1 |␊ + ` + +> Error 1/1 + + `␊ + > 1 | import type {} from "foo";␊ + | ^^ import statement without specifiers is not allowed.␊ + ` + +## invalid(9): import type{}from"foo"; + +> Input + + `␊ + 1 | import type{}from"foo";␊ + ` + +> Output + + `␊ + 1 |␊ + ` + +> Error 1/1 + + `␊ + > 1 | import type{}from"foo";␊ + | ^^ import statement without specifiers is not allowed.␊ + ` + +## invalid(10): import type foo, {} from "foo"; + +> Input + + `␊ + 1 | import type foo, {} from "foo";␊ + ` + +> Output + + `␊ + 1 | import type foo from "foo";␊ + ` + +> Error 1/1 + + `␊ + > 1 | import type foo, {} from "foo";␊ + | ^^ import statement without specifiers is not allowed.␊ + ` + +## invalid(11): import type foo,{}from "foo"; + +> Input + + `␊ + 1 | import type foo,{}from "foo";␊ + ` + +> Output + + `␊ + 1 | import type foo from "foo";␊ + ` + +> Error 1/1 + + `␊ + > 1 | import type foo,{}from "foo";␊ + | ^^ import statement without specifiers is not allowed.␊ + ` + +## invalid(1): export {} + +> Input + + `␊ + 1 | export {}␊ + ` + +> Output + + `␊ + 1 |␊ + ` + +> Error 1/1 + + `␊ + > 1 | export {}␊ + | ^^ export statement without specifiers is not allowed.␊ + ` + +## invalid(2): export type{} + +> Input + + `␊ + 1 | export type{}␊ + ` + +> Output + + `␊ + 1 |␊ + ` + +> Error 1/1 + + `␊ + > 1 | export type{}␊ + | ^^ export statement without specifiers is not allowed.␊ + ` + +## invalid(3): export type {} from "foo"; + +> Input + + `␊ + 1 | export type {} from "foo";␊ + ` + +> Output + + `␊ + 1 |␊ + ` + +> Error 1/1 + + `␊ + > 1 | export type {} from "foo";␊ + | ^^ export statement without specifiers is not allowed.␊ + ` + +## invalid(4): declare export type {} from "foo"; + +> Input + + `␊ + 1 | declare export type {} from "foo";␊ + ` + +> Output + + `␊ + 1 |␊ + ` + +> Error 1/1 + + `␊ + > 1 | declare export type {} from "foo";␊ + | ^^ export statement without specifiers is not allowed.␊ + ` + +## invalid(5): export {} from "foo"; + +> Input + + `␊ + 1 | export {} from "foo";␊ + ` + +> Error 1/1 + + `␊ + > 1 | export {} from "foo";␊ + | ^^ export statement without specifiers is not allowed.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Remove this export statement.␊ + 1 |␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to side effect import.␊ + 1 | import "foo";␊ + ` + +## invalid(6): export{}from"foo"; + +> Input + + `␊ + 1 | export{}from"foo";␊ + ` + +> Error 1/1 + + `␊ + > 1 | export{}from"foo";␊ + | ^^ export statement without specifiers is not allowed.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Remove this export statement.␊ + 1 |␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to side effect import.␊ + 1 | import"foo";␊ + ` + +## invalid(7): export { } from "foo"; + +> Input + + `␊ + 1 | export {␊ + 2 | } from "foo";␊ + ` + +> Error 1/1 + + `␊ + > 1 | export {␊ + | ^␊ + > 2 | } from "foo";␊ + | ^^ export statement without specifiers is not allowed.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Remove this export statement.␊ + 1 |␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to side effect import.␊ + 1 | import ␊ + 2 | "foo";␊ + ` + +## invalid(8): export {} from "foo" with {type: "json"}; + +> Input + + `␊ + 1 | export {} from "foo" with {type: "json"};␊ + ` + +> Error 1/1 + + `␊ + > 1 | export {} from "foo" with {type: "json"};␊ + | ^^ export statement without specifiers is not allowed.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/2: Remove this export statement.␊ + 1 |␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/2: Switch to side effect import.␊ + 1 | import "foo" with {type: "json"};␊ + ` diff --git a/test/snapshots/require-module-specifiers.js.snap b/test/snapshots/require-module-specifiers.js.snap new file mode 100644 index 0000000000..a88d0603ec Binary files /dev/null and b/test/snapshots/require-module-specifiers.js.snap differ diff --git a/test/utils/test.js b/test/utils/test.js index f30a593263..e558564776 100644 --- a/test/utils/test.js +++ b/test/utils/test.js @@ -5,7 +5,7 @@ import AvaRuleTester from 'eslint-ava-rule-tester'; import SnapshotRuleTester from './snapshot-rule-tester.js'; import parsers from './parsers.js'; import {DEFAULT_LANGUAGE_OPTIONS, normalizeLanguageOptions, mergeLanguageOptions} from './language-options.js'; -import rules from '../../rules/index.js'; +import plugin from '../../index.js'; function normalizeTestCase(testCase, shouldNormalizeLanguageOptions = true) { if (typeof testCase === 'string') { @@ -67,7 +67,7 @@ function only(...arguments_) { class Tester { constructor(ruleId) { this.ruleId = ruleId; - this.rule = rules[ruleId]; + this.rule = plugin.rules[ruleId]; } runTest(tests) { diff --git a/workaround-for-eslint-doc-generator/index.js b/workaround-for-eslint-doc-generator/index.js deleted file mode 100644 index 76cfc204b7..0000000000 --- a/workaround-for-eslint-doc-generator/index.js +++ /dev/null @@ -1,5 +0,0 @@ -'use strict'; - -// Remove this once https://github.com/bmish/eslint-doc-generator/issues/615 get fixed - -module.exports = require('../index.js').default; diff --git a/workaround-for-eslint-doc-generator/package.json b/workaround-for-eslint-doc-generator/package.json deleted file mode 100644 index 88ee844d9d..0000000000 --- a/workaround-for-eslint-doc-generator/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "// Remove this once https://github.com/bmish/eslint-doc-generator/issues/615 get fixed": "", - "name":"eslint-plugin-unicorn", - "type": "commonjs" -}