├── .editorconfig ├── .eslint-doc-generatorrc.js ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── rule_proposal.yml ├── contributing.md ├── pull_request_template.md └── workflows │ ├── main.yml │ └── smoke-test.yml ├── .gitignore ├── .markdownlint.json ├── .markdownlintignore ├── .npmpackagejsonlintrc.json ├── .npmrc ├── codecov.yml ├── configs ├── all.js └── recommended.js ├── docs ├── deprecated-rules.md ├── new-rule.md └── rules │ ├── better-regex.md │ ├── catch-error-name.md │ ├── consistent-destructuring.md │ ├── consistent-function-scoping.md │ ├── custom-error-definition.md │ ├── empty-brace-spaces.md │ ├── error-message.md │ ├── escape-case.md │ ├── expiring-todo-comments.md │ ├── explicit-length-check.md │ ├── filename-case.md │ ├── import-style.md │ ├── new-for-builtins.md │ ├── no-abusive-eslint-disable.md │ ├── no-array-callback-reference.md │ ├── no-array-for-each.md │ ├── no-array-method-this-argument.md │ ├── no-array-push-push.md │ ├── no-array-reduce.md │ ├── no-await-expression-member.md │ ├── no-console-spaces.md │ ├── no-document-cookie.md │ ├── no-empty-file.md │ ├── no-for-loop.md │ ├── no-hex-escape.md │ ├── no-instanceof-array.md │ ├── no-invalid-remove-event-listener.md │ ├── no-keyword-prefix.md │ ├── no-lonely-if.md │ ├── no-negated-condition.md │ ├── no-nested-ternary.md │ ├── no-new-array.md │ ├── no-new-buffer.md │ ├── no-null.md │ ├── no-object-as-default-parameter.md │ ├── no-process-exit.md │ ├── no-static-only-class.md │ ├── no-thenable.md │ ├── no-this-assignment.md │ ├── no-typeof-undefined.md │ ├── no-unnecessary-await.md │ ├── no-unreadable-array-destructuring.md │ ├── no-unreadable-iife.md │ ├── no-unsafe-regex.md │ ├── no-unused-properties.md │ ├── no-useless-fallback-in-spread.md │ ├── no-useless-length-check.md │ ├── no-useless-promise-resolve-reject.md │ ├── no-useless-spread.md │ ├── no-useless-switch-case.md │ ├── no-useless-undefined.md │ ├── no-zero-fractions.md │ ├── number-literal-case.md │ ├── numeric-separators-style.md │ ├── prefer-add-event-listener.md │ ├── prefer-array-find.md │ ├── prefer-array-flat-map.md │ ├── prefer-array-flat.md │ ├── prefer-array-index-of.md │ ├── prefer-array-some.md │ ├── prefer-at.md │ ├── prefer-code-point.md │ ├── prefer-date-now.md │ ├── prefer-default-parameters.md │ ├── prefer-dom-node-append.md │ ├── prefer-dom-node-dataset.md │ ├── prefer-dom-node-remove.md │ ├── prefer-dom-node-text-content.md │ ├── prefer-event-target.md │ ├── prefer-export-from.md │ ├── prefer-includes.md │ ├── prefer-json-parse-buffer.md │ ├── prefer-keyboard-event-key.md │ ├── prefer-logical-operator-over-ternary.md │ ├── prefer-math-trunc.md │ ├── prefer-modern-dom-apis.md │ ├── prefer-modern-math-apis.md │ ├── prefer-module.md │ ├── prefer-native-coercion-functions.md │ ├── prefer-negative-index.md │ ├── prefer-node-protocol.md │ ├── prefer-number-properties.md │ ├── prefer-object-from-entries.md │ ├── prefer-optional-catch-binding.md │ ├── prefer-prototype-methods.md │ ├── prefer-query-selector.md │ ├── prefer-reflect-apply.md │ ├── prefer-regexp-test.md │ ├── prefer-set-has.md │ ├── prefer-set-size.md │ ├── prefer-spread.md │ ├── prefer-string-replace-all.md │ ├── prefer-string-slice.md │ ├── prefer-string-starts-ends-with.md │ ├── prefer-string-trim-start-end.md │ ├── prefer-switch.md │ ├── prefer-ternary.md │ ├── prefer-top-level-await.md │ ├── prefer-type-error.md │ ├── prevent-abbreviations.md │ ├── relative-url-style.md │ ├── require-array-join-separator.md │ ├── require-number-to-fixed-digits-argument.md │ ├── require-post-message-target-origin.md │ ├── string-content.md │ ├── switch-case-braces.md │ ├── template-indent.md │ ├── text-encoding-identifier-case.md │ └── throw-new-error.md ├── index.js ├── license ├── package.json ├── readme.md ├── rules ├── ast │ ├── index.js │ ├── is-arrow-function-body.js │ ├── is-empty-node.js │ ├── is-new-expression.js │ ├── is-static-require.js │ ├── is-undefined.js │ └── literal.js ├── better-regex.js ├── catch-error-name.js ├── consistent-destructuring.js ├── consistent-function-scoping.js ├── custom-error-definition.js ├── empty-brace-spaces.js ├── error-message.js ├── escape-case.js ├── expiring-todo-comments.js ├── explicit-length-check.js ├── filename-case.js ├── fix │ ├── add-parenthesizes-to-return-or-throw-expression.js │ ├── append-argument.js │ ├── extend-fix-range.js │ ├── fix-space-around-keywords.js │ ├── index.js │ ├── remove-argument.js │ ├── remove-member-expression-property.js │ ├── remove-method-call.js │ ├── remove-parentheses.js │ ├── remove-spaces-after.js │ ├── rename-variable.js │ ├── replace-argument.js │ ├── replace-node-or-token-and-spaces-before.js │ ├── replace-reference-identifier.js │ ├── replace-string-literal.js │ ├── replace-string-raw.js │ ├── replace-template-element.js │ ├── switch-call-expression-to-new-expression.js │ └── switch-new-expression-to-call-expression.js ├── import-style.js ├── new-for-builtins.js ├── no-abusive-eslint-disable.js ├── no-array-callback-reference.js ├── no-array-for-each.js ├── no-array-method-this-argument.js ├── no-array-push-push.js ├── no-array-reduce.js ├── no-await-expression-member.js ├── no-console-spaces.js ├── no-document-cookie.js ├── no-empty-file.js ├── no-for-loop.js ├── no-hex-escape.js ├── no-instanceof-array.js ├── no-invalid-remove-event-listener.js ├── no-keyword-prefix.js ├── no-lonely-if.js ├── no-negated-condition.js ├── no-nested-ternary.js ├── no-new-array.js ├── no-new-buffer.js ├── no-null.js ├── no-object-as-default-parameter.js ├── no-process-exit.js ├── no-static-only-class.js ├── no-thenable.js ├── no-this-assignment.js ├── no-typeof-undefined.js ├── no-unnecessary-await.js ├── no-unreadable-array-destructuring.js ├── no-unreadable-iife.js ├── no-unsafe-regex.js ├── no-unused-properties.js ├── no-useless-fallback-in-spread.js ├── no-useless-length-check.js ├── no-useless-promise-resolve-reject.js ├── no-useless-spread.js ├── no-useless-switch-case.js ├── no-useless-undefined.js ├── no-zero-fractions.js ├── number-literal-case.js ├── numeric-separators-style.js ├── prefer-add-event-listener.js ├── prefer-array-find.js ├── prefer-array-flat-map.js ├── prefer-array-flat.js ├── prefer-array-index-of.js ├── prefer-array-some.js ├── prefer-at.js ├── prefer-code-point.js ├── prefer-date-now.js ├── prefer-default-parameters.js ├── prefer-dom-node-append.js ├── prefer-dom-node-dataset.js ├── prefer-dom-node-remove.js ├── prefer-dom-node-text-content.js ├── prefer-event-target.js ├── prefer-export-from.js ├── prefer-includes.js ├── prefer-json-parse-buffer.js ├── prefer-keyboard-event-key.js ├── prefer-logical-operator-over-ternary.js ├── prefer-math-trunc.js ├── prefer-modern-dom-apis.js ├── prefer-modern-math-apis.js ├── prefer-module.js ├── prefer-native-coercion-functions.js ├── prefer-negative-index.js ├── prefer-node-protocol.js ├── prefer-number-properties.js ├── prefer-object-from-entries.js ├── prefer-optional-catch-binding.js ├── prefer-prototype-methods.js ├── prefer-query-selector.js ├── prefer-reflect-apply.js ├── prefer-regexp-test.js ├── prefer-set-has.js ├── prefer-set-size.js ├── prefer-spread.js ├── prefer-string-replace-all.js ├── prefer-string-slice.js ├── prefer-string-starts-ends-with.js ├── prefer-string-trim-start-end.js ├── prefer-switch.js ├── prefer-ternary.js ├── prefer-top-level-await.js ├── prefer-type-error.js ├── prevent-abbreviations.js ├── relative-url-style.js ├── require-array-join-separator.js ├── require-number-to-fixed-digits-argument.js ├── require-post-message-target-origin.js ├── selectors │ ├── call-or-new-expression-selector.js │ ├── empty-array-selector.js │ ├── empty-object-selector.js │ ├── index.js │ ├── matches-any.js │ ├── member-expression-selector.js │ ├── method-call-selector.js │ ├── negation.js │ ├── not-dom-node.js │ ├── not-function.js │ ├── not-left-hand-side.js │ ├── prototype-method-selector.js │ ├── reference-identifier-selector.js │ └── require-selector.js ├── shared │ ├── abbreviations.js │ ├── dom-events.js │ ├── event-keys.js │ ├── negative-index.js │ ├── simple-array-search-rule.js │ └── typed-array.js ├── string-content.js ├── switch-case-braces.js ├── template-indent.js ├── text-encoding-identifier-case.js ├── throw-new-error.js └── utils │ ├── assert-token.js │ ├── avoid-capture.js │ ├── boolean.js │ ├── builtins.js │ ├── cartesian-product-samples.js │ ├── create-deprecated-rules.js │ ├── escape-string.js │ ├── escape-template-element-raw.js │ ├── get-builtin-rule.js │ ├── get-call-expression-arguments-text.js │ ├── get-class-head-location.js │ ├── get-documentation-url.js │ ├── get-indent-string.js │ ├── get-references.js │ ├── get-scopes.js │ ├── get-switch-case-head-location.js │ ├── get-variable-identifiers.js │ ├── global-reference-tracker.js │ ├── has-same-range.js │ ├── is-function-self-used-inside.js │ ├── is-left-hand-side.js │ ├── is-logical-expression.js │ ├── is-method-named.js │ ├── is-new-expression-with-parentheses.js │ ├── is-node-matches.js │ ├── is-number.js │ ├── is-object-method.js │ ├── is-on-same-line.js │ ├── is-same-reference.js │ ├── is-shadowed.js │ ├── is-shorthand-export-local.js │ ├── is-shorthand-import-local.js │ ├── is-shorthand-property-assignment-pattern-left.js │ ├── is-shorthand-property-value.js │ ├── is-value-not-usable.js │ ├── needs-semicolon.js │ ├── numeric.js │ ├── parentheses.js │ ├── resolve-variable-name.js │ ├── rule.js │ ├── should-add-parentheses-to-conditional-expression-child.js │ ├── should-add-parentheses-to-expression-statement-expression.js │ ├── should-add-parentheses-to-logical-expression-child.js │ ├── should-add-parentheses-to-member-expression-object.js │ ├── should-add-parentheses-to-new-expression-callee.js │ ├── should-add-parentheses-to-spread-element-argument.js │ ├── singular.js │ └── to-location.js ├── scripts ├── create-rule.mjs ├── internal-rules │ ├── fix-snapshot-test.js │ ├── index.js │ ├── package.json │ ├── prefer-disallow-over-forbid.js │ └── prefer-negative-boolean-attribute.js ├── rename-rule.mjs └── template │ ├── documentation.md.jst │ ├── rule.js.jst │ └── test.mjs.jst └── test ├── better-regex.mjs ├── catch-error-name.mjs ├── consistent-destructuring.mjs ├── consistent-function-scoping.mjs ├── custom-error-definition.mjs ├── empty-brace-spaces.mjs ├── error-message.mjs ├── escape-case.mjs ├── expiring-todo-comments.mjs ├── explicit-length-check.mjs ├── filename-case.mjs ├── import-style.mjs ├── integration ├── fixtures-local │ ├── conflicts-no-array-for-each-and-prevent-abbreviations.js │ └── error-name-conflicts.js ├── projects.mjs ├── readme.md ├── run-eslint.mjs └── test.mjs ├── new-for-builtins.mjs ├── no-abusive-eslint-disable.mjs ├── no-array-callback-reference.mjs ├── no-array-for-each.mjs ├── no-array-method-this-argument.mjs ├── no-array-push-push.mjs ├── no-array-reduce.mjs ├── no-await-expression-member.mjs ├── no-console-spaces.mjs ├── no-document-cookie.mjs ├── no-empty-file.mjs ├── no-for-loop.mjs ├── no-hex-escape.mjs ├── no-instanceof-array.mjs ├── no-invalid-remove-event-listener.mjs ├── no-keyword-prefix.mjs ├── no-lonely-if.mjs ├── no-negated-condition.mjs ├── no-nested-ternary.mjs ├── no-new-array.mjs ├── no-new-buffer.mjs ├── no-null.mjs ├── no-object-as-default-parameter.mjs ├── no-process-exit.mjs ├── no-static-only-class.mjs ├── no-thenable.mjs ├── no-this-assignment.mjs ├── no-typeof-undefined.mjs ├── no-unnecessary-await.mjs ├── no-unreadable-array-destructuring.mjs ├── no-unreadable-iife.mjs ├── no-unsafe-regex.mjs ├── no-unused-properties.mjs ├── no-useless-fallback-in-spread.mjs ├── no-useless-length-check.mjs ├── no-useless-promise-resolve-reject.mjs ├── no-useless-spread.mjs ├── no-useless-switch-case.mjs ├── no-useless-undefined.mjs ├── no-zero-fractions.mjs ├── number-literal-case.mjs ├── numeric-separators-style.mjs ├── package.mjs ├── prefer-add-event-listener.mjs ├── prefer-array-find.mjs ├── prefer-array-flat-map.mjs ├── prefer-array-flat.mjs ├── prefer-array-index-of.mjs ├── prefer-array-some.mjs ├── prefer-at.mjs ├── prefer-code-point.mjs ├── prefer-date-now.mjs ├── prefer-default-parameters.mjs ├── prefer-dom-node-append.mjs ├── prefer-dom-node-dataset.mjs ├── prefer-dom-node-remove.mjs ├── prefer-dom-node-text-content.mjs ├── prefer-event-target.mjs ├── prefer-export-from.mjs ├── prefer-includes.mjs ├── prefer-json-parse-buffer.mjs ├── prefer-keyboard-event-key.mjs ├── prefer-logical-operator-over-ternary.mjs ├── prefer-math-trunc.mjs ├── prefer-modern-dom-apis.mjs ├── prefer-modern-math-apis.mjs ├── prefer-module.mjs ├── prefer-native-coercion-functions.mjs ├── prefer-negative-index.mjs ├── prefer-node-protocol.mjs ├── prefer-number-properties.mjs ├── prefer-object-from-entries.mjs ├── prefer-optional-catch-binding.mjs ├── prefer-prototype-methods.mjs ├── prefer-query-selector.mjs ├── prefer-reflect-apply.mjs ├── prefer-regexp-test.mjs ├── prefer-set-has.mjs ├── prefer-set-size.mjs ├── prefer-spread.mjs ├── prefer-string-replace-all.mjs ├── prefer-string-slice.mjs ├── prefer-string-starts-ends-with.mjs ├── prefer-string-trim-start-end.mjs ├── prefer-switch.mjs ├── prefer-ternary.mjs ├── prefer-top-level-await.mjs ├── prefer-type-error.mjs ├── prevent-abbreviations.mjs ├── relative-url-style.mjs ├── require-array-join-separator.mjs ├── require-number-to-fixed-digits-argument.mjs ├── require-post-message-target-origin.mjs ├── run-rules-on-codebase └── lint.mjs ├── shared └── simple-array-search-rule-tests.mjs ├── smoke └── eslint-remote-tester.config.js ├── snapshots ├── better-regex.mjs.md ├── better-regex.mjs.snap ├── consistent-function-scoping.mjs.md ├── consistent-function-scoping.mjs.snap ├── empty-brace-spaces.mjs.md ├── empty-brace-spaces.mjs.snap ├── error-message.mjs.md ├── error-message.mjs.snap ├── explicit-length-check.mjs.md ├── explicit-length-check.mjs.snap ├── filename-case.mjs.md ├── filename-case.mjs.snap ├── import-style.mjs.md ├── import-style.mjs.snap ├── new-for-builtins.mjs.md ├── new-for-builtins.mjs.snap ├── no-abusive-eslint-disable.mjs.md ├── no-abusive-eslint-disable.mjs.snap ├── no-array-for-each.mjs.md ├── no-array-for-each.mjs.snap ├── no-array-method-this-argument.mjs.md ├── no-array-method-this-argument.mjs.snap ├── no-array-push-push.mjs.md ├── no-array-push-push.mjs.snap ├── no-await-expression-member.mjs.md ├── no-await-expression-member.mjs.snap ├── no-console-spaces.mjs.md ├── no-console-spaces.mjs.snap ├── no-document-cookie.mjs.md ├── no-document-cookie.mjs.snap ├── no-empty-file.mjs.md ├── no-empty-file.mjs.snap ├── no-for-loop.mjs.md ├── no-for-loop.mjs.snap ├── no-hex-escape.mjs.md ├── no-hex-escape.mjs.snap ├── no-instanceof-array.mjs.md ├── no-instanceof-array.mjs.snap ├── no-invalid-remove-event-listener.mjs.md ├── no-invalid-remove-event-listener.mjs.snap ├── no-lonely-if.mjs.md ├── no-lonely-if.mjs.snap ├── no-negated-condition.mjs.md ├── no-negated-condition.mjs.snap ├── no-nested-ternary.mjs.md ├── no-nested-ternary.mjs.snap ├── no-new-array.mjs.md ├── no-new-array.mjs.snap ├── no-new-buffer.mjs.md ├── no-new-buffer.mjs.snap ├── no-object-as-default-parameter.mjs.md ├── no-object-as-default-parameter.mjs.snap ├── no-process-exit.mjs.md ├── no-process-exit.mjs.snap ├── no-static-only-class.mjs.md ├── no-static-only-class.mjs.snap ├── no-thenable.mjs.md ├── no-thenable.mjs.snap ├── no-this-assignment.mjs.md ├── no-this-assignment.mjs.snap ├── no-typeof-undefined.mjs.md ├── no-typeof-undefined.mjs.snap ├── no-unnecessary-await.mjs.md ├── no-unnecessary-await.mjs.snap ├── no-unreadable-array-destructuring.mjs.md ├── no-unreadable-array-destructuring.mjs.snap ├── no-unreadable-iife.mjs.md ├── no-unreadable-iife.mjs.snap ├── no-unsafe-regex.mjs.md ├── no-unsafe-regex.mjs.snap ├── no-unused-properties.mjs.md ├── no-unused-properties.mjs.snap ├── no-useless-fallback-in-spread.mjs.md ├── no-useless-fallback-in-spread.mjs.snap ├── no-useless-length-check.mjs.md ├── no-useless-length-check.mjs.snap ├── no-useless-spread.mjs.md ├── no-useless-spread.mjs.snap ├── no-useless-switch-case.mjs.md ├── no-useless-switch-case.mjs.snap ├── no-useless-undefined.mjs.md ├── no-useless-undefined.mjs.snap ├── no-zero-fractions.mjs.md ├── no-zero-fractions.mjs.snap ├── number-literal-case.mjs.md ├── number-literal-case.mjs.snap ├── numeric-separators-style.mjs.md ├── numeric-separators-style.mjs.snap ├── prefer-add-event-listener.mjs.md ├── prefer-add-event-listener.mjs.snap ├── prefer-array-flat-map.mjs.md ├── prefer-array-flat-map.mjs.snap ├── prefer-array-flat.mjs.md ├── prefer-array-flat.mjs.snap ├── prefer-array-index-of.mjs.md ├── prefer-array-index-of.mjs.snap ├── prefer-array-some.mjs.md ├── prefer-array-some.mjs.snap ├── prefer-at.mjs.md ├── prefer-at.mjs.snap ├── prefer-code-point.mjs.md ├── prefer-code-point.mjs.snap ├── prefer-date-now.mjs.md ├── prefer-date-now.mjs.snap ├── prefer-dom-node-dataset.mjs.md ├── prefer-dom-node-dataset.mjs.snap ├── prefer-dom-node-text-content.mjs.md ├── prefer-dom-node-text-content.mjs.snap ├── prefer-event-target.mjs.md ├── prefer-event-target.mjs.snap ├── prefer-export-from.mjs.md ├── prefer-export-from.mjs.snap ├── prefer-includes.mjs.md ├── prefer-includes.mjs.snap ├── prefer-json-parse-buffer.mjs.md ├── prefer-json-parse-buffer.mjs.snap ├── prefer-keyboard-event-key.mjs.md ├── prefer-keyboard-event-key.mjs.snap ├── prefer-logical-operator-over-ternary.mjs.md ├── prefer-logical-operator-over-ternary.mjs.snap ├── prefer-math-trunc.mjs.md ├── prefer-math-trunc.mjs.snap ├── prefer-modern-math-apis.mjs.md ├── prefer-modern-math-apis.mjs.snap ├── prefer-module.mjs.md ├── prefer-module.mjs.snap ├── prefer-native-coercion-functions.mjs.md ├── prefer-native-coercion-functions.mjs.snap ├── prefer-negative-index.mjs.md ├── prefer-negative-index.mjs.snap ├── prefer-node-protocol.mjs.md ├── prefer-node-protocol.mjs.snap ├── prefer-number-properties.mjs.md ├── prefer-number-properties.mjs.snap ├── prefer-object-from-entries.mjs.md ├── prefer-object-from-entries.mjs.snap ├── prefer-optional-catch-binding.mjs.md ├── prefer-optional-catch-binding.mjs.snap ├── prefer-prototype-methods.mjs.md ├── prefer-prototype-methods.mjs.snap ├── prefer-query-selector.mjs.md ├── prefer-query-selector.mjs.snap ├── prefer-regexp-test.mjs.md ├── prefer-regexp-test.mjs.snap ├── prefer-set-has.mjs.md ├── prefer-set-has.mjs.snap ├── prefer-set-size.mjs.md ├── prefer-set-size.mjs.snap ├── prefer-spread.mjs.md ├── prefer-spread.mjs.snap ├── prefer-string-replace-all.mjs.md ├── prefer-string-replace-all.mjs.snap ├── prefer-string-slice.mjs.md ├── prefer-string-slice.mjs.snap ├── prefer-string-starts-ends-with.mjs.md ├── prefer-string-starts-ends-with.mjs.snap ├── prefer-string-trim-start-end.mjs.md ├── prefer-string-trim-start-end.mjs.snap ├── prefer-switch.mjs.md ├── prefer-switch.mjs.snap ├── prefer-top-level-await.mjs.md ├── prefer-top-level-await.mjs.snap ├── prefer-type-error.mjs.md ├── prefer-type-error.mjs.snap ├── relative-url-style.mjs.md ├── relative-url-style.mjs.snap ├── require-array-join-separator.mjs.md ├── require-array-join-separator.mjs.snap ├── require-number-to-fixed-digits-argument.mjs.md ├── require-number-to-fixed-digits-argument.mjs.snap ├── require-post-message-target-origin.mjs.md ├── require-post-message-target-origin.mjs.snap ├── switch-case-braces.mjs.md ├── switch-case-braces.mjs.snap ├── template-indent.mjs.md ├── template-indent.mjs.snap ├── text-encoding-identifier-case.mjs.md └── text-encoding-identifier-case.mjs.snap ├── string-content.mjs ├── switch-case-braces.mjs ├── template-indent.mjs ├── text-encoding-identifier-case.mjs ├── throw-new-error.mjs ├── unit ├── assert-token.mjs ├── get-documentation-url.mjs └── snapshots │ ├── assert-token.mjs.md │ └── assert-token.mjs.snap └── utils ├── default-options.mjs ├── not-dom-node-types.mjs ├── not-function-types.mjs ├── parsers.mjs ├── snapshot-rule-tester.mjs └── test.mjs /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.yml] 11 | indent_style = space 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /.eslint-doc-generatorrc.js: -------------------------------------------------------------------------------- 1 | /* eslint unicorn/prevent-abbreviations:"off" -- https://github.com/sindresorhus/eslint-plugin-unicorn/issues/2015 */ 2 | 3 | /** @type {import('eslint-doc-generator').GenerateOptions} */ 4 | const config = { 5 | ignoreConfig: ['all'], 6 | ignoreDeprecatedRules: true, 7 | ruleDocTitleFormat: 'desc', 8 | ruleListColumns: [ 9 | 'name', 10 | 'description', 11 | 'configsError', 12 | // Omit `configsOff` since we don't intend to convey meaning by setting rules to `off` in the `recommended` config. 13 | 'configsWarn', 14 | 'fixable', 15 | 'hasSuggestions', 16 | 'requiresTypeChecking', 17 | ], 18 | urlConfigs: 'https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs', 19 | }; 20 | 21 | module.exports = config; 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: A rule isn't working as it should? 4 | --- 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | ```js 15 | const code = 'that should be ok'; 16 | ``` 17 | -------------------------------------------------------------------------------- /.github/contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing guidelines 2 | 3 | ## I have an idea for a new rule 4 | 5 | Open an issue with your proposal. Make sure you elaborate on what problem it solves and include fail/pass examples. [(Example)](https://github.com/sindresorhus/eslint-plugin-unicorn/issues/166) 6 | 7 | ## I have an idea for a new rule and I also want to implement it 8 | 9 | First open an issue with your proposal. When the rule is accepted, see the [docs on creating and submitting a new rule](../docs/new-rule.md). 10 | 11 | ## I want to implement a rule from an open issue 12 | 13 | See the [docs on creating and submitting a new rule](../docs/new-rule.md). 14 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /.github/workflows/smoke-test.yml: -------------------------------------------------------------------------------- 1 | name: Smoke test 2 | 3 | on: 4 | schedule: 5 | - cron: "0 0 * * SUN" 6 | workflow_dispatch: 7 | 8 | jobs: 9 | lint: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - uses: actions/setup-node@v3 14 | - run: | 15 | npm install 16 | npm link 17 | npm link eslint-plugin-unicorn 18 | - uses: AriPerkkio/eslint-remote-tester-run-action@v2 19 | with: 20 | issue-title: "Results of weekly scheduled smoke test" 21 | eslint-remote-tester-config: test/smoke/eslint-remote-tester.config.js 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | yarn.lock 3 | coverage 4 | package-lock.json 5 | /test/integration/fixtures 6 | .cache-eslint-remote-tester 7 | eslint-remote-tester-results 8 | *.log 9 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "line-length": false, 3 | "no-duplicate-heading": false, 4 | "no-hard-tabs": false, 5 | "ul-style": { 6 | "style": "dash" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.markdownlintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | test/snapshots 3 | test/integration/fixtures 4 | -------------------------------------------------------------------------------- /.npmpackagejsonlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-duplicate-properties": "error", 4 | "no-repeated-dependencies": "error", 5 | "prefer-alphabetical-dependencies": "error", 6 | "prefer-alphabetical-devDependencies": "error", 7 | "prefer-alphabetical-optionalDependencies": "error", 8 | "prefer-alphabetical-bundledDependencies": "error", 9 | "prefer-alphabetical-scripts": "error", 10 | "prefer-caret-version-dependencies": [ 11 | "error", 12 | { 13 | "exceptions": [ 14 | "eslint-plugin-unicorn" 15 | ] 16 | } 17 | ], 18 | "prefer-caret-version-devDependencies": "error", 19 | "prefer-scripts": [ 20 | "error", 21 | [ 22 | "lint", 23 | "test" 24 | ] 25 | ] 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: 5 | target: 99% 6 | threshold: 1% 7 | -------------------------------------------------------------------------------- /configs/all.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {rules, ...baseConfigs} = require('./recommended.js'); 3 | 4 | module.exports = { 5 | ...baseConfigs, 6 | rules: Object.fromEntries(Object.entries(rules).map( 7 | ([ruleId, severity]) => [ruleId, ruleId.startsWith('unicorn/') ? 'error' : severity], 8 | )), 9 | }; 10 | -------------------------------------------------------------------------------- /docs/rules/better-regex.md: -------------------------------------------------------------------------------- 1 | # Improve regexes by making them shorter, consistent, and safer 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 6 | 7 | 8 | 9 | 10 | Note: This rule uses [`regexp-tree`](https://github.com/DmitrySoshnikov/regexp-tree) and [`clean-regexp`](https://github.com/samverschueren/clean-regexp) under the hood. 11 | 12 | ## Fail 13 | 14 | ```js 15 | const regex = /[0-9]/; 16 | const regex = /[^0-9]/; 17 | const regex = /[a-zA-Z0-9_]/; 18 | const regex = /[a-z0-9_]/i; 19 | const regex = /[^a-zA-Z0-9_]/; 20 | const regex = /[^a-z0-9_]/i; 21 | const regex = /[0-9]\.[a-zA-Z0-9_]\-[^0-9]/i; 22 | ``` 23 | 24 | ## Pass 25 | 26 | ```js 27 | const regex = /\d/; 28 | const regex = /\D/; 29 | const regex = /\w/; 30 | const regex = /\w/i; 31 | const regex = /\W/; 32 | const regex = /\W/i; 33 | const regex = /\d\.\w\-\D/i; 34 | ``` 35 | 36 | ## Options 37 | 38 | ### sortCharacterClasses 39 | 40 | Type: `boolean`\ 41 | Default: `true` 42 | 43 | Disables optimizations that affect the sorting of character classes. For example, preserves the order of the characters in `[AaQqTt]` rather than sorting it to `[AQTaqt]`. 44 | -------------------------------------------------------------------------------- /docs/rules/empty-brace-spaces.md: -------------------------------------------------------------------------------- 1 | # Enforce no spaces between braces 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 6 | 7 | 8 | 9 | 10 | ## Fail 11 | 12 | ```js 13 | class Unicorn { 14 | } 15 | ``` 16 | 17 | ```js 18 | try { 19 | foo(); 20 | } catch { } 21 | ``` 22 | 23 | ## Pass 24 | 25 | ```js 26 | class Unicorn {} 27 | ``` 28 | 29 | ```js 30 | try { 31 | foo(); 32 | } catch {} 33 | ``` 34 | -------------------------------------------------------------------------------- /docs/rules/error-message.md: -------------------------------------------------------------------------------- 1 | # Enforce passing a `message` value when creating a built-in error 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 6 | 7 | 8 | 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. 9 | 10 | ## Fail 11 | 12 | ```js 13 | throw Error(); 14 | ``` 15 | 16 | ```js 17 | throw Error(''); 18 | ``` 19 | 20 | ```js 21 | throw new TypeError(); 22 | ``` 23 | 24 | ```js 25 | const error = new AggregateError(errors); 26 | ``` 27 | 28 | ## Pass 29 | 30 | ```js 31 | throw Error('Unexpected property.'); 32 | ``` 33 | 34 | ```js 35 | throw new TypeError('Array expected.'); 36 | ``` 37 | 38 | ```js 39 | const error = new AggregateError(errors, 'Promises rejected.'); 40 | ``` 41 | -------------------------------------------------------------------------------- /docs/rules/escape-case.md: -------------------------------------------------------------------------------- 1 | # Require escape sequences to use uppercase values 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 6 | 7 | 8 | 9 | 10 | Enforces defining escape sequence values with uppercase characters rather than lowercase ones. This promotes readability by making the escaped value more distinguishable from the identifier. 11 | 12 | ## Fail 13 | 14 | ```js 15 | const foo = '\xa9'; 16 | const foo = '\ud834'; 17 | const foo = '\u{1d306}'; 18 | const foo = '\ca'; 19 | ``` 20 | 21 | ## Pass 22 | 23 | ```js 24 | const foo = '\xA9'; 25 | const foo = '\uD834'; 26 | const foo = '\u{1D306}'; 27 | const foo = '\cA'; 28 | ``` 29 | -------------------------------------------------------------------------------- /docs/rules/no-await-expression-member.md: -------------------------------------------------------------------------------- 1 | # Disallow member access from await expression 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 6 | 7 | 8 | 9 | 10 | When accessing a member from an await expression, the await expression has to be parenthesized, which is not readable. 11 | 12 | This rule is fixable for simple member access. 13 | 14 | ## Fail 15 | 16 | ```js 17 | const foo = (await import('./foo.js')).default; 18 | ``` 19 | 20 | ```js 21 | const secondElement = (await getArray())[1]; 22 | ``` 23 | 24 | ```js 25 | const property = (await getObject()).property; 26 | ``` 27 | 28 | ```js 29 | const data = await (await fetch('/foo')).json(); 30 | ``` 31 | 32 | ## Pass 33 | 34 | ```js 35 | const {default: foo} = await import('./foo.js'); 36 | ``` 37 | 38 | ```js 39 | const [, secondElement] = await getArray(); 40 | ``` 41 | 42 | ```js 43 | const {property} = await getObject(); 44 | ``` 45 | 46 | ```js 47 | const response = await fetch('/foo'); 48 | const data = await response.json(); 49 | ``` 50 | -------------------------------------------------------------------------------- /docs/rules/no-console-spaces.md: -------------------------------------------------------------------------------- 1 | # Do not use leading/trailing space between `console.log` parameters 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 6 | 7 | 8 | 9 | 10 | The [`console.log()` method](https://developer.mozilla.org/en-US/docs/Web/API/Console/log) and similar methods joins the parameters with a space, so adding a leading/trailing space to a parameter, results in two spaces being added. 11 | 12 | ## Fail 13 | 14 | ```js 15 | console.log('abc ', 'def'); 16 | console.log('abc', ' def'); 17 | 18 | console.log("abc ", " def"); 19 | console.log(`abc `, ` def`); 20 | 21 | console.debug('abc ', 'def'); 22 | console.info('abc ', 'def'); 23 | console.warn('abc ', 'def'); 24 | console.error('abc ', 'def'); 25 | ``` 26 | 27 | ## Pass 28 | 29 | ```js 30 | console.log('abc'); 31 | console.log('abc', 'def'); 32 | 33 | console.log('abc '); 34 | console.log(' abc'); 35 | 36 | console.log('abc ', 'def'); 37 | console.log('abc\t', 'def'); 38 | console.log('abc\n', 'def'); 39 | 40 | console.log(` 41 | abc 42 | `); 43 | ``` 44 | -------------------------------------------------------------------------------- /docs/rules/no-document-cookie.md: -------------------------------------------------------------------------------- 1 | # Do not use `document.cookie` directly 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 6 | 7 | 8 | It's not recommended to use [`document.cookie`](https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie) directly as it's easy to get the string wrong. Instead, you should use the [Cookie Store API](https://developer.mozilla.org/en-US/docs/Web/API/Cookie_Store_API) or a [cookie library](https://www.npmjs.com/search?q=cookie). 9 | 10 | ## Fail 11 | 12 | ```js 13 | document.cookie = 14 | 'foo=bar' + 15 | '; Path=/' + 16 | '; Domain=example.com' + 17 | '; expires=Fri, 31 Dec 9999 23:59:59 GMT' + 18 | '; Secure'; 19 | ``` 20 | 21 | ```js 22 | document.cookie += '; foo=bar'; 23 | ``` 24 | 25 | ## Pass 26 | 27 | ```js 28 | await cookieStore.set({ 29 | name: 'foo', 30 | value: 'bar', 31 | expires: Date.now() + 24 * 60 * 60 * 1000, 32 | domain: 'example.com' 33 | }); 34 | ``` 35 | 36 | ```js 37 | const array = document.cookie.split('; '); 38 | ``` 39 | 40 | ```js 41 | import Cookies from 'js-cookie'; 42 | 43 | Cookies.set('foo', 'bar'); 44 | ``` 45 | -------------------------------------------------------------------------------- /docs/rules/no-empty-file.md: -------------------------------------------------------------------------------- 1 | # Disallow empty files 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 6 | 7 | 8 | Meaningless files clutter a codebase. 9 | 10 | Disallow any files only containing the following: 11 | 12 | - Whitespace 13 | - Comments 14 | - Directives 15 | - Empty statements 16 | - Empty block statements 17 | - Hashbang 18 | 19 | ## Fail 20 | 21 | ```js 22 | 23 | ``` 24 | 25 | ```js 26 | // Comment 27 | ``` 28 | 29 | ```js 30 | /* Comment */ 31 | ``` 32 | 33 | ```js 34 | 'use strict'; 35 | ``` 36 | 37 | ```js 38 | ; 39 | ``` 40 | 41 | ```js 42 | { 43 | } 44 | ``` 45 | 46 | ```js 47 | #!/usr/bin/env node 48 | ``` 49 | 50 | ## Pass 51 | 52 | ```js 53 | const x = 0; 54 | ``` 55 | 56 | ```js 57 | 'use strict'; 58 | const x = 0; 59 | ``` 60 | 61 | ```js 62 | ;; 63 | const x = 0; 64 | ``` 65 | 66 | ```js 67 | { 68 | const x = 0; 69 | } 70 | ``` 71 | -------------------------------------------------------------------------------- /docs/rules/no-for-loop.md: -------------------------------------------------------------------------------- 1 | # Do not use a `for` loop that can be replaced with a `for-of` loop 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 6 | 7 | 8 | 9 | 10 | There's no reason to use old school for loops anymore for the common case. You can instead use for-of loop (with `.entries()` if you need to access the index). 11 | 12 | Off-by-one errors are one of the most common bugs in software. [Swift actually removed this completely from the language.](https://github.com/apple/swift-evolution/blob/master/proposals/0007-remove-c-style-for-loops.md). 13 | 14 | This rule is fixable unless index or element variables were used outside of the loop. 15 | 16 | ## Fail 17 | 18 | ```js 19 | for (let index = 0; index < array.length; index++) { 20 | const element = array[index]; 21 | console.log(index, element); 22 | } 23 | ``` 24 | 25 | ## Pass 26 | 27 | ```js 28 | for (const [index, element] of array.entries()) { 29 | console.log(index, element); 30 | } 31 | 32 | for (const element of array) { 33 | console.log(element); 34 | } 35 | ``` 36 | -------------------------------------------------------------------------------- /docs/rules/no-hex-escape.md: -------------------------------------------------------------------------------- 1 | # Enforce the use of Unicode escapes instead of hexadecimal escapes 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 6 | 7 | 8 | 9 | 10 | Enforces a convention of using [Unicode escapes](https://mathiasbynens.be/notes/javascript-escapes#unicode) instead of [hexadecimal escapes](https://mathiasbynens.be/notes/javascript-escapes#hexadecimal) for consistency and clarity. 11 | 12 | ## Fail 13 | 14 | ```js 15 | const foo = '\x1B'; 16 | const foo = `\x1B${bar}`; 17 | ``` 18 | 19 | ## Pass 20 | 21 | ```js 22 | const foo = '\u001B'; 23 | const foo = `\u001B${bar}`; 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/rules/no-instanceof-array.md: -------------------------------------------------------------------------------- 1 | # Require `Array.isArray()` instead of `instanceof Array` 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 6 | 7 | 8 | 9 | 10 | The `instanceof Array` check doesn't work across realms/contexts, for example, frames/windows in browsers or the `vm` module in Node.js. 11 | 12 | ## Fail 13 | 14 | ```js 15 | array instanceof Array; 16 | [1,2,3] instanceof Array; 17 | ``` 18 | 19 | ## Pass 20 | 21 | ```js 22 | Array.isArray(array); 23 | Array.isArray([1,2,3]); 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/rules/no-lonely-if.md: -------------------------------------------------------------------------------- 1 | # Disallow `if` statements as the only statement in `if` blocks without `else` 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 6 | 7 | 8 | 9 | 10 | This rule adds onto the built-in [`no-lonely-if`](https://eslint.org/docs/rules/no-lonely-if) rule, which only disallows `if` statements in `else`, not in `if`. It is recommended to use `unicorn/no-lonely-if` together with the core ESLint `no-lonely-if` rule. 11 | 12 | ## Fail 13 | 14 | ```js 15 | if (foo) { 16 | if (bar) { 17 | // … 18 | } 19 | } 20 | ``` 21 | 22 | ```js 23 | if (foo) { 24 | // … 25 | } else if (bar) { 26 | if (baz) { 27 | // … 28 | } 29 | } 30 | ``` 31 | 32 | ## Pass 33 | 34 | ```js 35 | if (foo && bar) { 36 | // … 37 | } 38 | ``` 39 | 40 | ```js 41 | if (foo) { 42 | // … 43 | } else if (bar && baz) { 44 | // … 45 | } 46 | ``` 47 | 48 | ```js 49 | if (foo) { 50 | // … 51 | } else if (bar) { 52 | if (baz) { 53 | // … 54 | } 55 | } else { 56 | // … 57 | } 58 | ``` 59 | 60 | ```js 61 | // Built-in rule `no-lonely-if` case https://eslint.org/docs/rules/no-lonely-if 62 | if (foo) { 63 | // … 64 | } else { 65 | if (bar) { 66 | // … 67 | } 68 | } 69 | ``` 70 | -------------------------------------------------------------------------------- /docs/rules/no-new-buffer.md: -------------------------------------------------------------------------------- 1 | # Enforce the use of `Buffer.from()` and `Buffer.alloc()` instead of the deprecated `new Buffer()` 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧💡 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/developer-guide/working-with-rules#providing-suggestions). 6 | 7 | 8 | 9 | 10 | Enforces the use of [Buffer.from](https://nodejs.org/api/buffer.html#static-method-bufferfromarray) and [Buffer.alloc()](https://nodejs.org/api/buffer.html#static-method-bufferallocsize-fill-encoding) instead of [new Buffer()](https://nodejs.org/api/buffer.html#new-bufferarray), which has been deprecated since Node.js 4. 11 | 12 | ## Fail 13 | 14 | ```js 15 | const buffer = new Buffer('7468697320697320612074c3a97374', 'hex'); 16 | ``` 17 | 18 | ```js 19 | const buffer = new Buffer([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]); 20 | ``` 21 | 22 | ```js 23 | const buffer = new Buffer(10); 24 | ``` 25 | 26 | ## Pass 27 | 28 | ```js 29 | const buffer = Buffer.from('7468697320697320612074c3a97374', 'hex'); 30 | ``` 31 | 32 | ```js 33 | const buffer = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]) 34 | ``` 35 | 36 | ```js 37 | const buffer = Buffer.alloc(10); 38 | ``` 39 | -------------------------------------------------------------------------------- /docs/rules/no-object-as-default-parameter.md: -------------------------------------------------------------------------------- 1 | # Disallow the use of objects as default parameters 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 6 | 7 | 8 | Default parameters should not be passed to a function through an object literal. The `foo = {a: false}` parameter works fine if only used with one option. As soon as additional options are added, you risk replacing the whole `foo = {a: false, b: true}` object when passing only one option: `{a: true}`. For this reason, object destructuring should be used instead. 9 | 10 | ## Fail 11 | 12 | ```js 13 | const abc = (foo = {a: false}) => {}; 14 | ``` 15 | 16 | ```js 17 | function foo({a} = {a: false}) {} 18 | ``` 19 | 20 | ```js 21 | const abc = (foo = {a: false, b: 123}) => {}; 22 | ``` 23 | 24 | ## Pass 25 | 26 | ```js 27 | const abc = (foo = {}) => {}; 28 | ``` 29 | 30 | ```js 31 | function foo(options) { 32 | const {a} = {a: false, ...options}; 33 | } 34 | ``` 35 | 36 | ```js 37 | const abc = (foo = false) => {}; 38 | ``` 39 | 40 | ```js 41 | const foo = ({a = false, b = 123}) => {}; 42 | ``` 43 | -------------------------------------------------------------------------------- /docs/rules/no-process-exit.md: -------------------------------------------------------------------------------- 1 | # Disallow `process.exit()` 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 6 | 7 | 8 | This rule is an extension to ESLint's [`no-process-exit` rule](https://eslint.org/docs/rules/no-process-exit), that allows `process.exit()` to be called in files that start with a [hashbang](https://en.wikipedia.org/wiki/Shebang_(Unix)) → `#!/usr/bin/env node`. It also allows `process.exit()` to be called in `process.on('', func)` event handlers and in files that imports `worker_threads`. 9 | 10 | ## Fail 11 | 12 | ```js 13 | process.exit(0); 14 | ``` 15 | 16 | ## Pass 17 | 18 | ```js 19 | #!/usr/bin/env node 20 | process.exit(0); 21 | ``` 22 | 23 | ```js 24 | process.on('SIGINT', () => { 25 | console.log('Got SIGINT'); 26 | process.exit(1); 27 | }); 28 | ``` 29 | 30 | ```js 31 | import workerThreads from 'worker_threads'; 32 | 33 | try { 34 | // Do something… 35 | process.exit(0); 36 | } catch (_) { 37 | process.exit(1); 38 | } 39 | ``` 40 | -------------------------------------------------------------------------------- /docs/rules/no-static-only-class.md: -------------------------------------------------------------------------------- 1 | # Disallow classes that only have static members 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 6 | 7 | 8 | 9 | 10 | A class with only static members could just be an object instead. 11 | 12 | ## Fail 13 | 14 | ```js 15 | class X { 16 | static foo = false; 17 | static bar() {}; 18 | } 19 | ``` 20 | 21 | ## Pass 22 | 23 | ```js 24 | const X = { 25 | foo: false, 26 | bar() {} 27 | }; 28 | ``` 29 | 30 | ```js 31 | class X { 32 | static foo = false; 33 | static bar() {}; 34 | 35 | constructor() {} 36 | } 37 | ``` 38 | 39 | ```js 40 | class X { 41 | static foo = false; 42 | static bar() {}; 43 | 44 | unicorn() {} 45 | } 46 | ``` 47 | 48 | ```js 49 | class X { 50 | static #foo = false; 51 | static bar() {} 52 | } 53 | ``` 54 | -------------------------------------------------------------------------------- /docs/rules/no-this-assignment.md: -------------------------------------------------------------------------------- 1 | # Disallow assigning `this` to a variable 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 6 | 7 | 8 | `this` should be used directly. If you want a reference to `this` from a higher scope, consider using [arrow function expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) or [`Function#bind()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind). 9 | 10 | ## Fail 11 | 12 | ```js 13 | const foo = this; 14 | 15 | setTimeout(function () { 16 | foo.bar(); 17 | }, 1000); 18 | ``` 19 | 20 | ```js 21 | const foo = this; 22 | 23 | class Bar { 24 | method() { 25 | foo.baz(); 26 | } 27 | } 28 | 29 | new Bar().method(); 30 | ``` 31 | 32 | ## Pass 33 | 34 | ```js 35 | setTimeout(() => { 36 | this.bar(); 37 | }, 1000); 38 | ``` 39 | 40 | ```js 41 | setTimeout(function () { 42 | this.bar(); 43 | }.bind(this), 1000); 44 | ``` 45 | 46 | ```js 47 | class Bar { 48 | constructor(fooInstance) { 49 | this.fooInstance = fooInstance; 50 | } 51 | method() { 52 | this.fooInstance.baz(); 53 | } 54 | } 55 | 56 | new Bar(this).method(); 57 | ``` 58 | -------------------------------------------------------------------------------- /docs/rules/no-unnecessary-await.md: -------------------------------------------------------------------------------- 1 | # Disallow awaiting non-promise values 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 6 | 7 | 8 | 9 | 10 | The [`await` operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) should only be used on [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) values. 11 | 12 | ## Fail 13 | 14 | ```js 15 | await await promise; 16 | ``` 17 | 18 | ```js 19 | await [promise1, promise2]; 20 | ``` 21 | 22 | ## Pass 23 | 24 | ```js 25 | await promise; 26 | ``` 27 | 28 | ```js 29 | await Promise.allSettled([promise1, promise2]); 30 | ``` 31 | -------------------------------------------------------------------------------- /docs/rules/no-unreadable-iife.md: -------------------------------------------------------------------------------- 1 | # Disallow unreadable IIFEs 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 6 | 7 | 8 | [IIFE](https://en.wikipedia.org/wiki/Immediately_invoked_function_expression) with parenthesized arrow function body is considered unreadable. 9 | 10 | ## Fail 11 | 12 | ```js 13 | const foo = (bar => (bar ? bar.baz : baz))(getBar()); 14 | ``` 15 | 16 | ```js 17 | const foo = ((bar, baz) => ({bar, baz}))(bar, baz); 18 | ``` 19 | 20 | ## Pass 21 | 22 | ```js 23 | const bar = getBar(); 24 | const foo = bar ? bar.baz : baz; 25 | ``` 26 | 27 | ```js 28 | const getBaz = bar => (bar ? bar.baz : baz); 29 | const foo = getBaz(getBar()); 30 | ``` 31 | 32 | ```js 33 | const foo = (bar => { 34 | return bar ? bar.baz : baz; 35 | })(getBar()); 36 | ``` 37 | -------------------------------------------------------------------------------- /docs/rules/no-unsafe-regex.md: -------------------------------------------------------------------------------- 1 | # Disallow unsafe regular expressions 2 | 3 | 🚫 This rule is _disabled_ in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 6 | 7 | 8 | Uses [safe-regex](https://github.com/substack/safe-regex) to disallow potentially [catastrophic](https://regular-expressions.info/catastrophic.html) [exponential-time](https://perlgeek.de/blog-en/perl-tips/in-search-of-an-exponetial-regexp.html) regular expressions. 9 | 10 | ## Fail 11 | 12 | ```js 13 | const regex = /^(a?){25}(a){25}$/; 14 | ``` 15 | 16 | ```js 17 | const regex = /(x+x+)+y/; 18 | ``` 19 | 20 | ```js 21 | const regex = /foo|(x+x+)+y/; 22 | ``` 23 | 24 | ```js 25 | const regex = /(a+){10}y/; 26 | ``` 27 | 28 | ```js 29 | const regex = /(a+){2}y/; 30 | ``` 31 | 32 | ```js 33 | const regex = /(.*){1,32000}[bc]/; 34 | ``` 35 | 36 | ## Pass 37 | 38 | ```js 39 | const regex = /\bOakland\b/; 40 | ``` 41 | 42 | ```js 43 | const regex = /\b(Oakland|San Francisco)\b/i; 44 | ``` 45 | 46 | ```js 47 | const regex = /^\d+1337\d+$/i; 48 | ``` 49 | 50 | ```js 51 | const regex = /^\d+(1337|404)\d+$/i; 52 | ``` 53 | 54 | ```js 55 | const regex = /^\d+(1337|404)*\d+$/i; 56 | ``` 57 | 58 | ```js 59 | const regex = new RegExp('a?'.repeat(25) + 'a'.repeat(25)); 60 | ``` 61 | -------------------------------------------------------------------------------- /docs/rules/no-useless-fallback-in-spread.md: -------------------------------------------------------------------------------- 1 | # Disallow useless fallback when spreading in object literals 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 6 | 7 | 8 | 9 | 10 | Spreading [falsy values](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) in object literals won't add any unexpected properties, so it's unnecessary to add an empty object as fallback. 11 | 12 | ## Fail 13 | 14 | ```js 15 | const object = {...(foo || {})}; 16 | ``` 17 | 18 | ```js 19 | const object = {...(foo ?? {})}; 20 | ``` 21 | 22 | ## Pass 23 | 24 | ```js 25 | const object = {...foo}; 26 | ``` 27 | 28 | ```js 29 | const object = {...(foo && {})}; 30 | ``` 31 | 32 | ```js 33 | const array = [...(foo || [])]; 34 | ``` 35 | -------------------------------------------------------------------------------- /docs/rules/no-useless-switch-case.md: -------------------------------------------------------------------------------- 1 | # Disallow useless case in switch statements 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 💡 This rule is manually fixable by [editor suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions). 6 | 7 | 8 | 9 | 10 | An empty case before the last default case is useless. 11 | 12 | ## Fail 13 | 14 | ```js 15 | switch (foo) { 16 | case 1: 17 | default: 18 | handleDefaultCase(); 19 | break; 20 | } 21 | ``` 22 | 23 | ## Pass 24 | 25 | ```js 26 | switch (foo) { 27 | case 1: 28 | case 2: 29 | handleCase1And2(); 30 | break; 31 | } 32 | ``` 33 | 34 | ```js 35 | switch (foo) { 36 | case 1: 37 | handleCase1(); 38 | break; 39 | default: 40 | handleDefaultCase(); 41 | break; 42 | } 43 | ``` 44 | 45 | ```js 46 | switch (foo) { 47 | case 1: 48 | handleCase1(); 49 | // Fallthrough 50 | default: 51 | handleDefaultCase(); 52 | break; 53 | } 54 | ``` 55 | 56 | ```js 57 | switch (foo) { 58 | // This is actually useless, but we only check cases where the last case is the `default` case 59 | case 1: 60 | default: 61 | handleDefaultCase(); 62 | break; 63 | case 2: 64 | handleCase2(); 65 | break; 66 | } 67 | ``` 68 | -------------------------------------------------------------------------------- /docs/rules/no-zero-fractions.md: -------------------------------------------------------------------------------- 1 | # Disallow number literals with zero fractions or dangling dots 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 6 | 7 | 8 | 9 | 10 | There is no difference in JavaScript between, for example, `1`, `1.0` and `1.`, so prefer the former for consistency. 11 | 12 | ## Fail 13 | 14 | ```js 15 | const foo = 1.0; 16 | ``` 17 | 18 | ```js 19 | const foo = -1.0; 20 | ``` 21 | 22 | ```js 23 | const foo = 123_456.000_000; 24 | ``` 25 | 26 | ```js 27 | const foo = 1.; 28 | ``` 29 | 30 | ```js 31 | const foo = 123.111000000; 32 | ``` 33 | 34 | ```js 35 | const foo = 123.00e20; 36 | ``` 37 | 38 | ## Pass 39 | 40 | ```js 41 | const foo = 1; 42 | ``` 43 | 44 | ```js 45 | const foo = -1; 46 | ``` 47 | 48 | ```js 49 | const foo = 123456; 50 | ``` 51 | 52 | ```js 53 | const foo = 1.1; 54 | ``` 55 | 56 | ```js 57 | const foo = -1.1; 58 | ``` 59 | 60 | ```js 61 | const foo = 123.456; 62 | ``` 63 | 64 | ```js 65 | const foo = 1e3; 66 | ``` 67 | -------------------------------------------------------------------------------- /docs/rules/prefer-array-flat-map.md: -------------------------------------------------------------------------------- 1 | # Prefer `.flatMap(…)` over `.map(…).flat()` 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 6 | 7 | 8 | 9 | 10 | [`Array#flatMap`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap) performs [`Array#map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) and [`Array#flat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat) in one step. 11 | 12 | ## Fail 13 | 14 | ```js 15 | const foo = bar.map(element => unicorn(element)).flat(); 16 | ``` 17 | 18 | ```js 19 | const foo = bar.map(element => unicorn(element)).flat(1); 20 | ``` 21 | 22 | ## Pass 23 | 24 | ```js 25 | const foo = bar.flatMap(element => unicorn(element)); 26 | ``` 27 | 28 | ```js 29 | const foo = bar.map(element => unicorn(element)).flat(2); 30 | ``` 31 | 32 | ```js 33 | const foo = bar.map(element => unicorn(element)).foo().flat(); 34 | ``` 35 | 36 | ```js 37 | const foo = bar.flat().map(element => unicorn(element)); 38 | ``` 39 | 40 | ## Related rules 41 | 42 | - [unicorn/prefer-array-flat](./prefer-array-flat.md) 43 | -------------------------------------------------------------------------------- /docs/rules/prefer-code-point.md: -------------------------------------------------------------------------------- 1 | # Prefer `String#codePointAt(…)` over `String#charCodeAt(…)` and `String.fromCodePoint(…)` over `String.fromCharCode(…)` 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 💡 This rule is manually fixable by [editor suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions). 6 | 7 | 8 | 9 | 10 | Unicode is better supported in [`String#codePointAt()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/codePointAt) and [`String.fromCodePoint()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint). 11 | 12 | - [Difference between `String.fromCodePoint()` and `String.fromCharCode()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint#compared_to_fromcharcode) 13 | 14 | ## Fail 15 | 16 | ```js 17 | const unicorn = '🦄'.charCodeAt(0).toString(16); 18 | ``` 19 | 20 | ```js 21 | const unicorn = String.fromCharCode(0x1f984); 22 | ``` 23 | 24 | ## Pass 25 | 26 | ```js 27 | const unicorn = '🦄'.codePointAt(0).toString(16); 28 | ``` 29 | 30 | ```js 31 | const unicorn = String.fromCodePoint(0x1f984); 32 | ``` 33 | -------------------------------------------------------------------------------- /docs/rules/prefer-date-now.md: -------------------------------------------------------------------------------- 1 | # Prefer `Date.now()` to get the number of milliseconds since the Unix Epoch 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 6 | 7 | 8 | 9 | 10 | [`Date.now()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now) is shorter and nicer than [`new Date().getTime()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTime), and avoids unnecessary instantiation of `Date` objects. 11 | 12 | ## Fail 13 | 14 | ```js 15 | const foo = new Date().getTime(); 16 | ``` 17 | 18 | ```js 19 | const foo = new Date().valueOf(); 20 | ``` 21 | 22 | ```js 23 | const foo = +new Date; 24 | ``` 25 | 26 | ```js 27 | const foo = Number(new Date()); 28 | ``` 29 | 30 | ```js 31 | const foo = new Date() * 2; 32 | ``` 33 | 34 | ## Pass 35 | 36 | ```js 37 | const foo = Date.now(); 38 | ``` 39 | 40 | ```js 41 | const foo = Date.now() * 2; 42 | ``` 43 | -------------------------------------------------------------------------------- /docs/rules/prefer-default-parameters.md: -------------------------------------------------------------------------------- 1 | # Prefer default parameters over reassignment 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧💡 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/developer-guide/working-with-rules#providing-suggestions). 6 | 7 | 8 | 9 | 10 | Instead of reassigning a function parameter, default parameters should be used. The `foo = foo || 123` statement evaluates to `123` when `foo` is falsy, possibly leading to confusing behavior, whereas default parameters only apply when passed an `undefined` value. This rule only reports reassignments to literal values. 11 | 12 | ## Fail 13 | 14 | ```js 15 | function abc(foo) { 16 | foo = foo || 'bar'; 17 | } 18 | ``` 19 | 20 | ```js 21 | function abc(foo) { 22 | const bar = foo || 'bar'; 23 | } 24 | ``` 25 | 26 | ## Pass 27 | 28 | ```js 29 | function abc(foo = 'bar') {} 30 | ``` 31 | 32 | ```js 33 | function abc(foo) { 34 | foo = foo || bar(); 35 | } 36 | ``` 37 | -------------------------------------------------------------------------------- /docs/rules/prefer-dom-node-append.md: -------------------------------------------------------------------------------- 1 | # Prefer `Node#append()` over `Node#appendChild()` 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 6 | 7 | 8 | 9 | 10 | Enforces the use of, for example, `document.body.append(div);` over `document.body.appendChild(div);` for DOM nodes. There are [some advantages of using `Node#append()`](https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/append), like the ability to append multiple nodes and to append both [`DOMString`](https://developer.mozilla.org/en-US/docs/Web/API/DOMString) and DOM node objects. 11 | 12 | ## Fail 13 | 14 | ```js 15 | foo.appendChild(bar); 16 | ``` 17 | 18 | ## Pass 19 | 20 | ```js 21 | foo.append(bar); 22 | foo.append('bar'); 23 | foo.append(bar, 'baz'); 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/rules/prefer-dom-node-remove.md: -------------------------------------------------------------------------------- 1 | # Prefer `childNode.remove()` over `parentNode.removeChild(childNode)` 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧💡 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/developer-guide/working-with-rules#providing-suggestions). 6 | 7 | 8 | 9 | 10 | Enforces the use of, for example, `child.remove();` over `child.parentNode.removeChild(child);`. The DOM function [`Node#remove()`](https://developer.mozilla.org/en-US/docs/Web/API/ChildNode/remove) is preferred over the indirect removal of an object with [`Node#removeChild()`](https://developer.mozilla.org/en-US/docs/Web/API/Node/removeChild). 11 | 12 | ## Fail 13 | 14 | ```js 15 | parentNode.removeChild(foo); 16 | parentNode.removeChild(this); 17 | ``` 18 | 19 | ## Pass 20 | 21 | ```js 22 | foo.remove(); 23 | this.remove(); 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/rules/prefer-dom-node-text-content.md: -------------------------------------------------------------------------------- 1 | # Prefer `.textContent` over `.innerText` 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 💡 This rule is manually fixable by [editor suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions). 6 | 7 | 8 | 9 | 10 | Enforces the use of `.textContent` over `.innerText` for DOM nodes. 11 | 12 | There are [some advantages of using `.textContent`](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent), like performance and more predictable behavior when updating it. 13 | 14 | Note that there are [differences](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent#differences_from_innertext) between them. 15 | 16 | ## Fail 17 | 18 | ```js 19 | const text = foo.innerText; 20 | ``` 21 | 22 | ```js 23 | const {innerText} = foo; 24 | ``` 25 | 26 | ```js 27 | foo.innerText = '🦄'; 28 | ``` 29 | 30 | ## Pass 31 | 32 | ```js 33 | const text = foo.textContent; 34 | ``` 35 | 36 | ```js 37 | const {textContent} = foo; 38 | ``` 39 | 40 | ```js 41 | foo.textContent = '🦄'; 42 | ``` 43 | -------------------------------------------------------------------------------- /docs/rules/prefer-event-target.md: -------------------------------------------------------------------------------- 1 | # Prefer `EventTarget` over `EventEmitter` 2 | 3 | 🚫 This rule is _disabled_ in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 6 | 7 | 8 | While [`EventEmitter`](https://nodejs.org/api/events.html#class-eventemitter) is only available in Node.js, [`EventTarget`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget) is also available in _Deno_ and browsers. 9 | 10 | This rule reduces the bundle size and makes your code more cross-platform friendly. 11 | 12 | See the [differences](https://nodejs.org/api/events.html#eventtarget-and-event-api) between `EventEmitter` and `EventTarget`. 13 | 14 | ## Fail 15 | 16 | ```js 17 | import {EventEmitter} from 'node:event'; 18 | 19 | class Foo extends EventEmitter {} 20 | ``` 21 | 22 | ```js 23 | const emitter = new EventEmitter(); 24 | ``` 25 | 26 | ## Pass 27 | 28 | ```js 29 | class Foo extends EventTarget {} 30 | ``` 31 | 32 | ```js 33 | const target = new EventTarget(); 34 | ``` 35 | -------------------------------------------------------------------------------- /docs/rules/prefer-keyboard-event-key.md: -------------------------------------------------------------------------------- 1 | # Prefer `KeyboardEvent#key` over `KeyboardEvent#keyCode` 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 6 | 7 | 8 | 9 | 10 | Enforces the use of [`KeyboardEvent#key`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) over [`KeyboardEvent#keyCode`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode) which is deprecated. The `.key` property is also more semantic and readable. 11 | 12 | This rule is partly fixable. It can only fix direct property access. 13 | 14 | ## Fail 15 | 16 | ```js 17 | window.addEventListener('keydown', event => { 18 | console.log(event.keyCode); 19 | }); 20 | ``` 21 | 22 | ```js 23 | window.addEventListener('keydown', event => { 24 | if (event.keyCode === 8) { 25 | console.log('Backspace was pressed'); 26 | } 27 | }); 28 | ``` 29 | 30 | ## Pass 31 | 32 | ```js 33 | window.addEventListener('click', event => { 34 | console.log(event.key); 35 | }); 36 | ``` 37 | 38 | ```js 39 | window.addEventListener('keydown', event => { 40 | if (event.key === 'Backspace') { 41 | console.log('Backspace was pressed'); 42 | } 43 | }); 44 | ``` 45 | -------------------------------------------------------------------------------- /docs/rules/prefer-logical-operator-over-ternary.md: -------------------------------------------------------------------------------- 1 | # Prefer using a logical operator over a ternary 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 💡 This rule is manually fixable by [editor suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions). 6 | 7 | 8 | 9 | 10 | Disallow ternary operators when simpler logical operator alternatives exist. 11 | 12 | Ideally, most reported cases have an equivalent [`Logical OR(||)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_OR) expression. The rule intentionally provides suggestions instead of auto-fixes, because in many cases, the [nullish coalescing operator (`??`)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator) should be preferred. 13 | 14 | ## Fail 15 | 16 | ```js 17 | foo ? foo : bar; 18 | ``` 19 | 20 | ```js 21 | foo.bar ? foo.bar : foo.baz 22 | ``` 23 | 24 | ```js 25 | foo?.bar ? foo.bar : baz 26 | ``` 27 | 28 | ```js 29 | !bar ? foo : bar; 30 | ``` 31 | 32 | ## Pass 33 | 34 | ```js 35 | foo ?? bar; 36 | ``` 37 | 38 | ```js 39 | foo || bar; 40 | ``` 41 | 42 | ```js 43 | foo ? bar : baz; 44 | ``` 45 | 46 | ```js 47 | foo.bar ?? foo.baz 48 | ``` 49 | 50 | ```js 51 | foo?.bar ?? baz 52 | ``` 53 | -------------------------------------------------------------------------------- /docs/rules/prefer-optional-catch-binding.md: -------------------------------------------------------------------------------- 1 | # Prefer omitting the `catch` binding parameter 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 6 | 7 | 8 | 9 | 10 | If the `catch` binding parameter is not used, it should be omitted. 11 | 12 | ## Fail 13 | 14 | ```js 15 | try {} catch (notUsedError) {} 16 | ``` 17 | 18 | ```js 19 | try {} catch ({message}) {} 20 | ``` 21 | 22 | ## Pass 23 | 24 | ```js 25 | try {} catch {} 26 | ``` 27 | 28 | ```js 29 | try {} catch (error) { 30 | console.error(error); 31 | } 32 | ``` 33 | -------------------------------------------------------------------------------- /docs/rules/prefer-prototype-methods.md: -------------------------------------------------------------------------------- 1 | # Prefer borrowing methods from the prototype instead of the instance 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 6 | 7 | 8 | 9 | 10 | When “borrowing” a method from `Array` or `Object`, it's clearer to get it from the prototype than from an instance. 11 | 12 | ## Fail 13 | 14 | ```js 15 | const array = [].slice.apply(bar); 16 | ``` 17 | 18 | ```js 19 | const hasProperty = {}.hasOwnProperty.call(foo, 'property'); 20 | ``` 21 | 22 | ```js 23 | Reflect.apply([].forEach, arrayLike, [callback]); 24 | ``` 25 | 26 | ## Pass 27 | 28 | ```js 29 | const array = Array.prototype.slice.apply(bar); 30 | ``` 31 | 32 | ```js 33 | const hasProperty = Object.prototype.hasOwnProperty.call(foo, 'property'); 34 | ``` 35 | 36 | ```js 37 | Reflect.apply(Array.prototype.forEach, arrayLike, [callback]); 38 | ``` 39 | 40 | ```js 41 | const maxValue = Math.max.apply(Math, numbers); 42 | ``` 43 | -------------------------------------------------------------------------------- /docs/rules/prefer-query-selector.md: -------------------------------------------------------------------------------- 1 | # Prefer `.querySelector()` over `.getElementById()`, `.querySelectorAll()` over `.getElementsByClassName()` and `.getElementsByTagName()` 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 6 | 7 | 8 | 9 | 10 | It's better to use the same method to query DOM elements. 11 | 12 | ## Fail 13 | 14 | ```js 15 | document.getElementById('foo'); 16 | document.getElementsByClassName('foo bar'); 17 | document.getElementsByTagName('main'); 18 | document.getElementsByClassName(fn()); 19 | ``` 20 | 21 | ## Pass 22 | 23 | ```js 24 | document.querySelector('#foo'); 25 | document.querySelector('.bar'); 26 | document.querySelector('main #foo .bar'); 27 | document.querySelectorAll('.foo .bar'); 28 | document.querySelectorAll('li a'); 29 | document.querySelector('li').querySelectorAll('a'); 30 | ``` 31 | -------------------------------------------------------------------------------- /docs/rules/prefer-reflect-apply.md: -------------------------------------------------------------------------------- 1 | # Prefer `Reflect.apply()` over `Function#apply()` 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 6 | 7 | 8 | 9 | 10 | [`Reflect.apply()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/apply) is arguably less verbose and easier to understand. In addition, when you accept arbitrary methods, it's not safe to assume `.apply()` exists or is not overridden. 11 | 12 | ## Fail 13 | 14 | ```js 15 | function foo() {} 16 | 17 | foo.apply(null, [42]); 18 | Function.prototype.apply.call(foo, null, [42]); 19 | foo.apply(this, [42]); 20 | Function.prototype.apply.call(foo, this, [42]); 21 | foo.apply(null, arguments); 22 | Function.prototype.apply.call(foo, null, arguments); 23 | foo.apply(this, arguments); 24 | Function.prototype.apply.call(foo, this, arguments); 25 | ``` 26 | 27 | ## Pass 28 | 29 | ```js 30 | function foo() {} 31 | 32 | Reflect.apply(foo, null, [42]); 33 | ``` 34 | -------------------------------------------------------------------------------- /docs/rules/prefer-regexp-test.md: -------------------------------------------------------------------------------- 1 | # Prefer `RegExp#test()` over `String#match()` and `RegExp#exec()` 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧💡 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/developer-guide/working-with-rules#providing-suggestions). 6 | 7 | 8 | 9 | 10 | When you want to know whether a pattern is found in a string, use [`RegExp#test()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test) instead of [`String#match()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match) and [`RegExp#exec()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec). 11 | 12 | ## Fail 13 | 14 | ```js 15 | if (string.match(/unicorn/)) {} 16 | ``` 17 | 18 | ```js 19 | if (/unicorn/.exec(string)) {} 20 | ``` 21 | 22 | ```vue 23 | 26 | ``` 27 | 28 | ## Pass 29 | 30 | ```js 31 | if (/unicorn/.test(string)) {} 32 | ``` 33 | 34 | ```vue 35 | 38 | ``` 39 | -------------------------------------------------------------------------------- /docs/rules/prefer-set-has.md: -------------------------------------------------------------------------------- 1 | # Prefer `Set#has()` over `Array#includes()` when checking for existence or non-existence 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧💡 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/developer-guide/working-with-rules#providing-suggestions). 6 | 7 | 8 | 9 | 10 | [`Set#has()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/has) is faster than [`Array#includes()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes). 11 | 12 | ## Fail 13 | 14 | ```js 15 | const array = [1, 2, 3]; 16 | const hasValue = value => array.includes(value); 17 | ``` 18 | 19 | ## Pass 20 | 21 | ```js 22 | const set = new Set([1, 2, 3]); 23 | const hasValue = value => set.has(value); 24 | ``` 25 | 26 | ```js 27 | // This array is not only checking existence. 28 | const array = [1, 2]; 29 | const hasValue = value => array.includes(value); 30 | array.push(3); 31 | ``` 32 | 33 | ```js 34 | // This array is only checked once. 35 | const array = [1, 2, 3]; 36 | const hasOne = array.includes(1); 37 | ``` 38 | -------------------------------------------------------------------------------- /docs/rules/prefer-set-size.md: -------------------------------------------------------------------------------- 1 | # Prefer using `Set#size` instead of `Array#length` 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 6 | 7 | 8 | 9 | 10 | Prefer using `Set#size` directly instead of first converting it to an array and then using its `.length` property. 11 | 12 | ## Fail 13 | 14 | ```js 15 | function isUnique(array) { 16 | return [...new Set(array)].length === array.length; 17 | } 18 | ``` 19 | 20 | ## Pass 21 | 22 | ```js 23 | function isUnique(array) { 24 | return new Set(array).size === array.length; 25 | } 26 | ``` 27 | -------------------------------------------------------------------------------- /docs/rules/prefer-string-slice.md: -------------------------------------------------------------------------------- 1 | # Prefer `String#slice()` over `String#substr()` and `String#substring()` 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 6 | 7 | 8 | 9 | 10 | [`String#substr()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substr) and [`String#substring()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substring) are the two lesser known legacy ways to slice a string. It's better to use [`String#slice()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice) as it's a more popular option with clearer behavior that has a consistent [`Array` counterpart](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice). 11 | 12 | ## Fail 13 | 14 | ```js 15 | foo.substr(start, length); 16 | foo.substring(indexStart, indexEnd); 17 | ``` 18 | 19 | ## Pass 20 | 21 | ```js 22 | foo.slice(beginIndex, endIndex); 23 | ``` 24 | -------------------------------------------------------------------------------- /docs/rules/prefer-string-trim-start-end.md: -------------------------------------------------------------------------------- 1 | # Prefer `String#trimStart()` / `String#trimEnd()` over `String#trimLeft()` / `String#trimRight()` 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 6 | 7 | 8 | 9 | 10 | [`String#trimLeft()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimLeft) and [`String#trimRight()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimRight) are aliases of [`String#trimStart()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimStart) and [`String#trimEnd()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimEnd) 11 | 12 | ## Fail 13 | 14 | ```js 15 | const foo = bar.trimLeft(); 16 | const foo = bar.trimRight(); 17 | ``` 18 | 19 | ## Pass 20 | 21 | ```js 22 | const foo = bar.trimStart(); 23 | const foo = bar.trimEnd(); 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/rules/prefer-top-level-await.md: -------------------------------------------------------------------------------- 1 | # Prefer top-level await over top-level promises and async function calls 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 💡 This rule is manually fixable by [editor suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions). 6 | 7 | 8 | 9 | 10 | [Top-level await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await#top_level_await) is more readable and can prevent unhandled rejections. 11 | 12 | ## Fail 13 | 14 | ```js 15 | (async () => { 16 | try { 17 | await run(); 18 | } catch (error) { 19 | console.error(error); 20 | process.exit(1); 21 | } 22 | })(); 23 | ``` 24 | 25 | ```js 26 | run().catch(error => { 27 | console.error(error); 28 | process.exit(1); 29 | }); 30 | ``` 31 | 32 | ```js 33 | async function main() { 34 | try { 35 | await run(); 36 | } catch (error) { 37 | console.error(error); 38 | process.exit(1); 39 | } 40 | } 41 | 42 | main(); 43 | ``` 44 | 45 | ## Pass 46 | 47 | ```js 48 | await run(); 49 | ``` 50 | -------------------------------------------------------------------------------- /docs/rules/relative-url-style.md: -------------------------------------------------------------------------------- 1 | # Enforce consistent relative URL style 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧💡 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/developer-guide/working-with-rules#providing-suggestions). 6 | 7 | 8 | 9 | 10 | When using a relative URL in [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL), the URL should either never or always use the `./` prefix consistently. 11 | 12 | ## Fail 13 | 14 | ```js 15 | const url = new URL('./foo', base); 16 | ``` 17 | 18 | ## Pass 19 | 20 | ```js 21 | const url = new URL('foo', base); 22 | ``` 23 | 24 | ## Options 25 | 26 | Type: `string`\ 27 | Default: `'never'` 28 | 29 | - `'never'` (default) 30 | - Never use a `./` prefix. 31 | - `'always'` 32 | - Always add a `./` prefix to the relative URL when possible. 33 | 34 | ```js 35 | // eslint unicorn/relative-url-style: ["error", "always"] 36 | const url = new URL('foo', base); // Fail 37 | const url = new URL('./foo', base); // Pass 38 | ``` 39 | -------------------------------------------------------------------------------- /docs/rules/require-array-join-separator.md: -------------------------------------------------------------------------------- 1 | # Enforce using the separator argument with `Array#join()` 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 6 | 7 | 8 | 9 | 10 | It's better to make it clear what the separator is when calling [Array#join()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join), instead of relying on the default comma (`','`) separator. 11 | 12 | ## Fail 13 | 14 | ```js 15 | const string = array.join(); 16 | ``` 17 | 18 | ```js 19 | const string = Array.prototype.join.call(arrayLike); 20 | ``` 21 | 22 | ```js 23 | const string = [].join.call(arrayLike); 24 | ``` 25 | 26 | ## Pass 27 | 28 | ```js 29 | const string = array.join(','); 30 | ``` 31 | 32 | ```js 33 | const string = array.join('|'); 34 | ``` 35 | 36 | ```js 37 | const string = Array.prototype.join.call(arrayLike, ''); 38 | ``` 39 | 40 | ```js 41 | const string = [].join.call(arrayLike, '\n'); 42 | ``` 43 | -------------------------------------------------------------------------------- /docs/rules/require-number-to-fixed-digits-argument.md: -------------------------------------------------------------------------------- 1 | # Enforce using the digits argument with `Number#toFixed()` 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 6 | 7 | 8 | 9 | 10 | It's better to make it clear what the value of the `digits` argument is when calling [Number#toFixed()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed), instead of relying on the default value of `0`. 11 | 12 | ## Fail 13 | 14 | ```js 15 | const string = number.toFixed(); 16 | ``` 17 | 18 | ## Pass 19 | 20 | ```js 21 | const string = foo.toFixed(0); 22 | ``` 23 | 24 | ```js 25 | const string = foo.toFixed(2); 26 | ``` 27 | 28 | ```js 29 | const integer = Math.floor(foo); 30 | ``` 31 | 32 | ```js 33 | const integer = Math.ceil(foo); 34 | ``` 35 | 36 | ```js 37 | const integer = Math.round(foo); 38 | ``` 39 | 40 | ```js 41 | const integer = Math.trunc(foo); 42 | ``` 43 | -------------------------------------------------------------------------------- /docs/rules/require-post-message-target-origin.md: -------------------------------------------------------------------------------- 1 | # Enforce using the `targetOrigin` argument with `window.postMessage()` 2 | 3 | 🚫 This rule is _disabled_ in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 💡 This rule is manually fixable by [editor suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions). 6 | 7 | 8 | 9 | 10 | When calling [`window.postMessage()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) without the `targetOrigin` argument, the message cannot be received by any window. 11 | 12 | This rule cannot distinguish between `window.postMessage()` and other calls like [`Worker#postMessage()`](https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage), [`MessagePort#postMessage()`](https://developer.mozilla.org/en-US/docs/Web/API/MessagePort/postMessage), [`Client#postMessage()`](https://developer.mozilla.org/en-US/docs/Web/API/Client/postMessage), and [`BroadcastChannel#postMessage()`](https://developer.mozilla.org/en-US/docs/Web/API/BroadcastChannel/postMessage). Use on your own risk. 13 | 14 | ## Fail 15 | 16 | ```js 17 | window.postMessage(message); 18 | ``` 19 | 20 | ## Pass 21 | 22 | ```js 23 | window.postMessage(message, 'https://example.com'); 24 | ``` 25 | 26 | ```js 27 | window.postMessage(message, '*'); 28 | ``` 29 | -------------------------------------------------------------------------------- /docs/rules/text-encoding-identifier-case.md: -------------------------------------------------------------------------------- 1 | # Enforce consistent case for text encoding identifiers 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧💡 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/developer-guide/working-with-rules#providing-suggestions). 6 | 7 | 8 | 9 | 10 | - Enforce `'utf8'` for [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoding. 11 | - Enforce `'ascii'` for [ASCII](https://en.wikipedia.org/wiki/ASCII) encoding. 12 | 13 | This rule only auto-fix encoding in `fs.readFile()` and `fs.readFileSync()`. 14 | 15 | ## Fail 16 | 17 | ```js 18 | await fs.readFile(file, 'UTF-8'); 19 | ``` 20 | 21 | ```js 22 | await fs.readFile(file, 'ASCII'); 23 | ``` 24 | 25 | ```js 26 | const string = buffer.toString('utf-8'); 27 | ``` 28 | 29 | ## Pass 30 | 31 | ```js 32 | await fs.readFile(file, 'utf8'); 33 | ``` 34 | 35 | ```js 36 | await fs.readFile(file, 'ascii'); 37 | ``` 38 | 39 | ```js 40 | const string = buffer.toString('utf8'); 41 | ``` 42 | -------------------------------------------------------------------------------- /docs/rules/throw-new-error.md: -------------------------------------------------------------------------------- 1 | # Require `new` when throwing an error 2 | 3 | 💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). 4 | 5 | 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). 6 | 7 | 8 | 9 | 10 | While it's possible to create a new error without using the `new` keyword, it's better to be explicit. 11 | 12 | ## Fail 13 | 14 | ```js 15 | throw Error(); 16 | ``` 17 | 18 | ```js 19 | throw TypeError('unicorn'); 20 | ``` 21 | 22 | ```js 23 | throw lib.TypeError(); 24 | ``` 25 | 26 | ## Pass 27 | 28 | ```js 29 | throw new Error(); 30 | ``` 31 | 32 | ```js 33 | throw new TypeError('unicorn'); 34 | ``` 35 | 36 | ```js 37 | throw new lib.TypeError(); 38 | ``` 39 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Sindre Sorhus (https://sindresorhus.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /rules/ast/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { 4 | isLiteral, 5 | isStringLiteral, 6 | isNumberLiteral, 7 | isBigIntLiteral, 8 | isNullLiteral, 9 | isRegexLiteral, 10 | } = require('./literal.js'); 11 | 12 | module.exports = { 13 | isLiteral, 14 | isStringLiteral, 15 | isNumberLiteral, 16 | isBigIntLiteral, 17 | isNullLiteral, 18 | isRegexLiteral, 19 | 20 | isArrowFunctionBody: require('./is-arrow-function-body.js'), 21 | isEmptyNode: require('./is-empty-node.js'), 22 | isStaticRequire: require('./is-static-require.js'), 23 | isUndefined: require('./is-undefined.js'), 24 | isNewExpression: require('./is-new-expression.js'), 25 | }; 26 | -------------------------------------------------------------------------------- /rules/ast/is-arrow-function-body.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function isArrowFunctionBody(node) { 4 | return node.parent.type === 'ArrowFunctionExpression' && node.parent.body === node; 5 | } 6 | 7 | module.exports = isArrowFunctionBody; 8 | -------------------------------------------------------------------------------- /rules/ast/is-empty-node.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | function isEmptyNode(node, additionalEmpty) { 3 | const {type} = node; 4 | 5 | if (type === 'BlockStatement') { 6 | return node.body.every(currentNode => isEmptyNode(currentNode, additionalEmpty)); 7 | } 8 | 9 | if (type === 'EmptyStatement') { 10 | return true; 11 | } 12 | 13 | if (additionalEmpty?.(node)) { 14 | return true; 15 | } 16 | 17 | return false; 18 | } 19 | 20 | module.exports = isEmptyNode; 21 | -------------------------------------------------------------------------------- /rules/ast/is-new-expression.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function isNewExpression(node, options) { 4 | if (node?.type !== 'NewExpression') { 5 | return false; 6 | } 7 | 8 | const { 9 | name, 10 | } = { 11 | ...options, 12 | }; 13 | 14 | if (name) { 15 | return node.callee.type === 'Identifier' && node.callee.name === name; 16 | } 17 | 18 | /* c8 ignore next */ 19 | return true; 20 | } 21 | 22 | module.exports = isNewExpression; 23 | -------------------------------------------------------------------------------- /rules/ast/is-static-require.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {isStringLiteral} = require('./literal.js'); 4 | 5 | const isStaticRequire = node => Boolean( 6 | node?.type === 'CallExpression' 7 | && node.callee.type === 'Identifier' 8 | && node.callee.name === 'require' 9 | && !node.optional 10 | && node.arguments.length === 1 11 | && isStringLiteral(node.arguments[0]), 12 | ); 13 | 14 | module.exports = isStaticRequire; 15 | -------------------------------------------------------------------------------- /rules/ast/is-undefined.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function isUndefined(node) { 4 | return node.type === 'Identifier' && node.name === 'undefined'; 5 | } 6 | 7 | module.exports = isUndefined; 8 | -------------------------------------------------------------------------------- /rules/ast/literal.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function isLiteral(node, value) { 4 | if (node?.type !== 'Literal') { 5 | return false; 6 | } 7 | 8 | if (value === null) { 9 | return node.raw === 'null'; 10 | } 11 | 12 | return node.value === value; 13 | } 14 | 15 | const isStringLiteral = node => node?.type === 'Literal' && typeof node.value === 'string'; 16 | const isNumberLiteral = node => node.type === 'Literal' && typeof node.value === 'number'; 17 | const isRegexLiteral = node => node.type === 'Literal' && Boolean(node.regex); 18 | // eslint-disable-next-line unicorn/no-null 19 | const isNullLiteral = node => isLiteral(node, null); 20 | const isBigIntLiteral = node => node.type === 'Literal' && Boolean(node.bigint); 21 | 22 | module.exports = { 23 | isLiteral, 24 | isStringLiteral, 25 | isNumberLiteral, 26 | isBigIntLiteral, 27 | isNullLiteral, 28 | isRegexLiteral, 29 | }; 30 | -------------------------------------------------------------------------------- /rules/fix/add-parenthesizes-to-return-or-throw-expression.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {isSemicolonToken} = require('@eslint-community/eslint-utils'); 3 | 4 | function * addParenthesizesToReturnOrThrowExpression(fixer, node, sourceCode) { 5 | if (node.type !== 'ReturnStatement' && node.type !== 'ThrowStatement') { 6 | return; 7 | } 8 | 9 | const returnOrThrowToken = sourceCode.getFirstToken(node); 10 | yield fixer.insertTextAfter(returnOrThrowToken, ' ('); 11 | 12 | const lastToken = sourceCode.getLastToken(node); 13 | if (!isSemicolonToken(lastToken)) { 14 | yield fixer.insertTextAfter(node, ')'); 15 | return; 16 | } 17 | 18 | yield fixer.insertTextBefore(lastToken, ')'); 19 | } 20 | 21 | module.exports = addParenthesizesToReturnOrThrowExpression; 22 | -------------------------------------------------------------------------------- /rules/fix/append-argument.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {isCommaToken} = require('@eslint-community/eslint-utils'); 3 | 4 | function appendArgument(fixer, node, text, sourceCode) { 5 | // This function should also work for `NewExpression` 6 | // But parentheses of `NewExpression` could be omitted, add this check to prevent accident use on it 7 | /* c8 ignore next 3 */ 8 | if (node.type !== 'CallExpression') { 9 | throw new Error(`Unexpected node "${node.type}".`); 10 | } 11 | 12 | const [penultimateToken, lastToken] = sourceCode.getLastTokens(node, 2); 13 | if (node.arguments.length > 0) { 14 | text = isCommaToken(penultimateToken) ? ` ${text},` : `, ${text}`; 15 | } 16 | 17 | return fixer.insertTextBefore(lastToken, text); 18 | } 19 | 20 | module.exports = appendArgument; 21 | -------------------------------------------------------------------------------- /rules/fix/extend-fix-range.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | Extend fix range to prevent changes from other rules. 5 | https://github.com/eslint/eslint/pull/13748/files#diff-c692f3fde09eda7c89f1802c908511a3fb59f5d207fe95eb009cb52e46a99e84R348 6 | 7 | @param {ruleFixer} fixer - The fixer to fix. 8 | @param {int[]} range - The extended range node. 9 | */ 10 | function * extendFixRange(fixer, range) { 11 | yield fixer.insertTextBeforeRange(range, ''); 12 | yield fixer.insertTextAfterRange(range, ''); 13 | } 14 | 15 | module.exports = extendFixRange; 16 | -------------------------------------------------------------------------------- /rules/fix/fix-space-around-keywords.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {getParenthesizedRange} = require('../utils/parentheses.js'); 3 | 4 | const isProblematicToken = ({type, value}) => ( 5 | (type === 'Keyword' && /^[a-z]*$/.test(value)) 6 | // ForOfStatement 7 | || (type === 'Identifier' && value === 'of') 8 | // AwaitExpression 9 | || (type === 'Identifier' && value === 'await') 10 | ); 11 | 12 | function * fixSpaceAroundKeyword(fixer, node, sourceCode) { 13 | const range = getParenthesizedRange(node, sourceCode); 14 | const tokenBefore = sourceCode.getTokenBefore({range}, {includeComments: true}); 15 | 16 | if ( 17 | tokenBefore 18 | && range[0] === tokenBefore.range[1] 19 | && isProblematicToken(tokenBefore) 20 | ) { 21 | yield fixer.insertTextAfter(tokenBefore, ' '); 22 | } 23 | 24 | const tokenAfter = sourceCode.getTokenAfter({range}, {includeComments: true}); 25 | 26 | if ( 27 | tokenAfter 28 | && range[1] === tokenAfter.range[0] 29 | && isProblematicToken(tokenAfter) 30 | ) { 31 | yield fixer.insertTextBefore(tokenAfter, ' '); 32 | } 33 | } 34 | 35 | module.exports = fixSpaceAroundKeyword; 36 | -------------------------------------------------------------------------------- /rules/fix/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | // Utilities 5 | extendFixRange: require('./extend-fix-range.js'), 6 | removeParentheses: require('./remove-parentheses.js'), 7 | 8 | appendArgument: require('./append-argument.js'), 9 | removeArgument: require('./remove-argument.js'), 10 | replaceArgument: require('./replace-argument.js'), 11 | switchNewExpressionToCallExpression: require('./switch-new-expression-to-call-expression.js'), 12 | switchCallExpressionToNewExpression: require('./switch-call-expression-to-new-expression.js'), 13 | removeMemberExpressionProperty: require('./remove-member-expression-property.js'), 14 | removeMethodCall: require('./remove-method-call.js'), 15 | replaceTemplateElement: require('./replace-template-element.js'), 16 | replaceReferenceIdentifier: require('./replace-reference-identifier.js'), 17 | renameVariable: require('./rename-variable.js'), 18 | replaceNodeOrTokenAndSpacesBefore: require('./replace-node-or-token-and-spaces-before.js'), 19 | removeSpacesAfter: require('./remove-spaces-after.js'), 20 | fixSpaceAroundKeyword: require('./fix-space-around-keywords.js'), 21 | replaceStringLiteral: require('./replace-string-literal.js'), 22 | addParenthesizesToReturnOrThrowExpression: require('./add-parenthesizes-to-return-or-throw-expression.js'), 23 | }; 24 | -------------------------------------------------------------------------------- /rules/fix/remove-argument.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {isCommaToken} = require('@eslint-community/eslint-utils'); 3 | const {getParentheses} = require('../utils/parentheses.js'); 4 | 5 | function removeArgument(fixer, node, sourceCode) { 6 | const callExpression = node.parent; 7 | const index = callExpression.arguments.indexOf(node); 8 | const parentheses = getParentheses(node, sourceCode); 9 | const firstToken = parentheses[0] || node; 10 | const lastToken = parentheses[parentheses.length - 1] || node; 11 | 12 | let [start] = firstToken.range; 13 | let [, end] = lastToken.range; 14 | 15 | if (index !== 0) { 16 | start = sourceCode.getTokenBefore(firstToken).range[0]; 17 | } 18 | 19 | // If the removed argument is the only argument, the trailing comma must be removed too 20 | /* c8 ignore start */ 21 | if (callExpression.arguments.length === 1) { 22 | const tokenAfter = sourceCode.getTokenBefore(lastToken); 23 | if (isCommaToken(tokenAfter)) { 24 | end = tokenAfter[1]; 25 | } 26 | } 27 | /* c8 ignore end */ 28 | 29 | return fixer.replaceTextRange([start, end], ''); 30 | } 31 | 32 | module.exports = removeArgument; 33 | -------------------------------------------------------------------------------- /rules/fix/remove-member-expression-property.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {getParenthesizedRange} = require('../utils/parentheses.js'); 3 | 4 | function removeMemberExpressionProperty(fixer, memberExpression, sourceCode) { 5 | const [, start] = getParenthesizedRange(memberExpression.object, sourceCode); 6 | const [, end] = memberExpression.range; 7 | 8 | return fixer.removeRange([start, end]); 9 | } 10 | 11 | module.exports = removeMemberExpressionProperty; 12 | -------------------------------------------------------------------------------- /rules/fix/remove-method-call.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {getParenthesizedRange} = require('../utils/parentheses.js'); 3 | const removeMemberExpressionProperty = require('./remove-member-expression-property.js'); 4 | 5 | function * removeMethodCall(fixer, callExpression, sourceCode) { 6 | const memberExpression = callExpression.callee; 7 | 8 | // `(( (( foo )).bar ))()` 9 | // ^^^^ 10 | yield removeMemberExpressionProperty(fixer, memberExpression, sourceCode); 11 | 12 | // `(( (( foo )).bar ))()` 13 | // ^^ 14 | const [, start] = getParenthesizedRange(memberExpression, sourceCode); 15 | const [, end] = callExpression.range; 16 | 17 | yield fixer.removeRange([start, end]); 18 | } 19 | 20 | module.exports = removeMethodCall; 21 | -------------------------------------------------------------------------------- /rules/fix/remove-parentheses.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {getParentheses} = require('../utils/parentheses.js'); 3 | 4 | function * removeParentheses(node, fixer, sourceCode) { 5 | const parentheses = getParentheses(node, sourceCode); 6 | for (const token of parentheses) { 7 | yield fixer.remove(token); 8 | } 9 | } 10 | 11 | module.exports = removeParentheses; 12 | -------------------------------------------------------------------------------- /rules/fix/remove-spaces-after.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function removeSpacesAfter(indexOrNodeOrToken, sourceCode, fixer) { 4 | let index = indexOrNodeOrToken; 5 | if (typeof indexOrNodeOrToken === 'object' && Array.isArray(indexOrNodeOrToken.range)) { 6 | index = indexOrNodeOrToken.range[1]; 7 | } 8 | 9 | const textAfter = sourceCode.text.slice(index); 10 | const [leadingSpaces] = textAfter.match(/^\s*/); 11 | return fixer.removeRange([index, index + leadingSpaces.length]); 12 | } 13 | 14 | module.exports = removeSpacesAfter; 15 | -------------------------------------------------------------------------------- /rules/fix/rename-variable.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const getVariableIdentifiers = require('../utils/get-variable-identifiers.js'); 3 | const replaceReferenceIdentifier = require('./replace-reference-identifier.js'); 4 | 5 | const renameVariable = (variable, name, fixer) => 6 | getVariableIdentifiers(variable) 7 | .map(identifier => replaceReferenceIdentifier(identifier, name, fixer)); 8 | 9 | module.exports = renameVariable; 10 | -------------------------------------------------------------------------------- /rules/fix/replace-argument.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {getParenthesizedRange} = require('../utils/parentheses.js'); 3 | 4 | function replaceArgument(fixer, node, text, sourceCode) { 5 | return fixer.replaceTextRange(getParenthesizedRange(node, sourceCode), text); 6 | } 7 | 8 | module.exports = replaceArgument; 9 | -------------------------------------------------------------------------------- /rules/fix/replace-node-or-token-and-spaces-before.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {getParentheses} = require('../utils/parentheses.js'); 3 | 4 | function * replaceNodeOrTokenAndSpacesBefore(nodeOrToken, replacement, fixer, sourceCode, tokenStore = sourceCode) { 5 | const tokens = getParentheses(nodeOrToken, tokenStore); 6 | 7 | for (const token of tokens) { 8 | yield * replaceNodeOrTokenAndSpacesBefore(token, '', fixer, sourceCode, tokenStore); 9 | } 10 | 11 | let [start, end] = nodeOrToken.range; 12 | 13 | const textBefore = sourceCode.text.slice(0, start); 14 | const [trailingSpaces] = textBefore.match(/\s*$/); 15 | const [lineBreak] = trailingSpaces.match(/(?:\r?\n|\r){0,1}/); 16 | start -= trailingSpaces.length; 17 | 18 | yield fixer.replaceTextRange([start, end], `${lineBreak}${replacement}`); 19 | } 20 | 21 | module.exports = replaceNodeOrTokenAndSpacesBefore; 22 | -------------------------------------------------------------------------------- /rules/fix/replace-reference-identifier.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const isShorthandPropertyValue = require('../utils/is-shorthand-property-value.js'); 4 | const isShorthandPropertyAssignmentPatternLeft = require('../utils/is-shorthand-property-assignment-pattern-left.js'); 5 | const isShorthandImportLocal = require('../utils/is-shorthand-import-local.js'); 6 | const isShorthandExportLocal = require('../utils/is-shorthand-export-local.js'); 7 | 8 | function replaceReferenceIdentifier(identifier, replacement, fixer) { 9 | if ( 10 | isShorthandPropertyValue(identifier) 11 | || isShorthandPropertyAssignmentPatternLeft(identifier) 12 | ) { 13 | return fixer.replaceText(identifier, `${identifier.name}: ${replacement}`); 14 | } 15 | 16 | if (isShorthandImportLocal(identifier)) { 17 | return fixer.replaceText(identifier, `${identifier.name} as ${replacement}`); 18 | } 19 | 20 | if (isShorthandExportLocal(identifier)) { 21 | return fixer.replaceText(identifier, `${replacement} as ${identifier.name}`); 22 | } 23 | 24 | // `typeAnnotation` 25 | if (identifier.typeAnnotation) { 26 | return fixer.replaceTextRange( 27 | [identifier.range[0], identifier.typeAnnotation.range[0]], 28 | `${replacement}${identifier.optional ? '?' : ''}`, 29 | ); 30 | } 31 | 32 | return fixer.replaceText(identifier, replacement); 33 | } 34 | 35 | module.exports = replaceReferenceIdentifier; 36 | -------------------------------------------------------------------------------- /rules/fix/replace-string-literal.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function replaceStringLiteral(fixer, node, text, relativeRangeStart, relativeRangeEnd) { 4 | const firstCharacterIndex = node.range[0] + 1; 5 | const start = Number.isInteger(relativeRangeEnd) ? relativeRangeStart + firstCharacterIndex : firstCharacterIndex; 6 | const end = Number.isInteger(relativeRangeEnd) ? relativeRangeEnd + firstCharacterIndex : node.range[1] - 1; 7 | 8 | return fixer.replaceTextRange([start, end], text); 9 | } 10 | 11 | module.exports = replaceStringLiteral; 12 | -------------------------------------------------------------------------------- /rules/fix/replace-string-raw.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Replace `StringLiteral` or `TemplateLiteral` node with raw text 4 | const replaceStringRaw = (fixer, node, raw) => 5 | fixer.replaceTextRange( 6 | // Ignore quotes and backticks 7 | [ 8 | node.range[0] + 1, 9 | node.range[1] - 1, 10 | ], 11 | raw, 12 | ); 13 | 14 | module.exports = replaceStringRaw; 15 | -------------------------------------------------------------------------------- /rules/fix/replace-template-element.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const replaceTemplateElement = (fixer, node, replacement) => { 4 | const {range: [start, end], tail} = node; 5 | return fixer.replaceTextRange( 6 | [start + 1, end - (tail ? 1 : 2)], 7 | replacement, 8 | ); 9 | }; 10 | 11 | module.exports = replaceTemplateElement; 12 | -------------------------------------------------------------------------------- /rules/fix/switch-call-expression-to-new-expression.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {isParenthesized} = require('../utils/parentheses.js'); 3 | const shouldAddParenthesesToNewExpressionCallee = require('../utils/should-add-parentheses-to-new-expression-callee.js'); 4 | 5 | function * switchCallExpressionToNewExpression(node, sourceCode, fixer) { 6 | yield fixer.insertTextBefore(node, 'new '); 7 | 8 | const {callee} = node; 9 | if ( 10 | !isParenthesized(callee, sourceCode) 11 | && shouldAddParenthesesToNewExpressionCallee(callee) 12 | ) { 13 | yield fixer.insertTextBefore(callee, '('); 14 | yield fixer.insertTextAfter(callee, ')'); 15 | } 16 | } 17 | 18 | module.exports = switchCallExpressionToNewExpression; 19 | -------------------------------------------------------------------------------- /rules/fix/switch-new-expression-to-call-expression.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const isNewExpressionWithParentheses = require('../utils/is-new-expression-with-parentheses.js'); 3 | const {isParenthesized} = require('../utils/parentheses.js'); 4 | const isOnSameLine = require('../utils/is-on-same-line.js'); 5 | const addParenthesizesToReturnOrThrowExpression = require('./add-parenthesizes-to-return-or-throw-expression.js'); 6 | const removeSpaceAfter = require('./remove-spaces-after.js'); 7 | 8 | function * switchNewExpressionToCallExpression(newExpression, sourceCode, fixer) { 9 | const newToken = sourceCode.getFirstToken(newExpression); 10 | yield fixer.remove(newToken); 11 | yield removeSpaceAfter(newToken, sourceCode, fixer); 12 | 13 | if (!isNewExpressionWithParentheses(newExpression, sourceCode)) { 14 | yield fixer.insertTextAfter(newExpression, '()'); 15 | } 16 | 17 | /* 18 | Remove `new` from this code will makes the function return `undefined` 19 | 20 | ```js 21 | () => { 22 | return new // comment 23 | Foo() 24 | } 25 | ``` 26 | */ 27 | if (!isOnSameLine(newToken, newExpression.callee) && !isParenthesized(newExpression, sourceCode)) { 28 | // Ideally, we should use first parenthesis of the `callee`, and should check spaces after the `new` token 29 | // But adding extra parentheses is harmless, no need to be too complicated 30 | yield * addParenthesizesToReturnOrThrowExpression(fixer, newExpression.parent, sourceCode); 31 | } 32 | } 33 | 34 | module.exports = switchNewExpressionToCallExpression; 35 | -------------------------------------------------------------------------------- /rules/no-abusive-eslint-disable.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const MESSAGE_ID = 'no-abusive-eslint-disable'; 4 | const messages = { 5 | [MESSAGE_ID]: 'Specify the rules you want to disable.', 6 | }; 7 | 8 | const disableRegex = /^eslint-disable(?:-next-line|-line)?(?$|(?:\s+(?:@(?:[\w-]+\/){1,2})?[\w-]+)?)/; 9 | 10 | /** @param {import('eslint').Rule.RuleContext} context */ 11 | const create = () => ({ 12 | * Program(node) { 13 | for (const comment of node.comments) { 14 | const value = comment.value.trim(); 15 | const result = disableRegex.exec(value); 16 | 17 | if ( 18 | result // It's a eslint-disable comment 19 | && !result.groups.ruleId // But it did not specify any rules 20 | ) { 21 | yield { 22 | // Can't set it at the given location as the warning 23 | // will be ignored due to the disable comment 24 | loc: { 25 | start: { 26 | ...comment.loc.start, 27 | column: -1, 28 | }, 29 | end: comment.loc.end, 30 | }, 31 | messageId: MESSAGE_ID, 32 | }; 33 | } 34 | } 35 | }, 36 | }); 37 | 38 | /** @type {import('eslint').Rule.RuleModule} */ 39 | module.exports = { 40 | create, 41 | meta: { 42 | type: 'suggestion', 43 | docs: { 44 | description: 'Enforce specifying rules to disable in `eslint-disable` comments.', 45 | }, 46 | messages, 47 | }, 48 | }; 49 | -------------------------------------------------------------------------------- /rules/no-document-cookie.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {GlobalReferenceTracker} = require('./utils/global-reference-tracker.js'); 3 | 4 | const MESSAGE_ID = 'no-document-cookie'; 5 | const messages = { 6 | [MESSAGE_ID]: 'Do not use `document.cookie` directly.', 7 | }; 8 | 9 | const tracker = new GlobalReferenceTracker({ 10 | object: 'document.cookie', 11 | filter: ({node}) => node.parent.type === 'AssignmentExpression' && node.parent.left === node, 12 | handle: ({node}) => ({node, messageId: MESSAGE_ID}), 13 | }); 14 | 15 | /** @type {import('eslint').Rule.RuleModule} */ 16 | module.exports = { 17 | create: context => tracker.createListeners(context), 18 | meta: { 19 | type: 'problem', 20 | docs: { 21 | description: 'Do not use `document.cookie` directly.', 22 | }, 23 | messages, 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /rules/no-empty-file.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {isEmptyNode} = require('./ast/index.js'); 3 | 4 | const MESSAGE_ID = 'no-empty-file'; 5 | const messages = { 6 | [MESSAGE_ID]: 'Empty files are not allowed.', 7 | }; 8 | 9 | const isDirective = node => node.type === 'ExpressionStatement' && 'directive' in node; 10 | const isEmpty = node => isEmptyNode(node, isDirective); 11 | 12 | const isTripleSlashDirective = node => 13 | node.type === 'Line' && node.value.startsWith('/'); 14 | 15 | const hasTripeSlashDirectives = comments => 16 | comments.some(currentNode => isTripleSlashDirective(currentNode)); 17 | 18 | /** @param {import('eslint').Rule.RuleContext} context */ 19 | const create = context => { 20 | const filename = context.getPhysicalFilename(); 21 | 22 | if (!/\.(?:js|mjs|cjs|jsx|ts|mts|cts|tsx)$/i.test(filename)) { 23 | return; 24 | } 25 | 26 | return { 27 | Program(node) { 28 | if (node.body.some(node => !isEmpty(node))) { 29 | return; 30 | } 31 | 32 | const sourceCode = context.getSourceCode(); 33 | const comments = sourceCode.getAllComments(); 34 | 35 | if (hasTripeSlashDirectives(comments)) { 36 | return; 37 | } 38 | 39 | return { 40 | node, 41 | messageId: MESSAGE_ID, 42 | }; 43 | }, 44 | }; 45 | }; 46 | 47 | /** @type {import('eslint').Rule.RuleModule} */ 48 | module.exports = { 49 | create, 50 | meta: { 51 | type: 'suggestion', 52 | docs: { 53 | description: 'Disallow empty files.', 54 | }, 55 | messages, 56 | }, 57 | }; 58 | -------------------------------------------------------------------------------- /rules/no-hex-escape.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {replaceTemplateElement} = require('./fix/index.js'); 3 | 4 | const MESSAGE_ID = 'no-hex-escape'; 5 | const messages = { 6 | [MESSAGE_ID]: 'Use Unicode escapes instead of hexadecimal escapes.', 7 | }; 8 | 9 | function checkEscape(context, node, value) { 10 | const fixedValue = value.replace(/(?<=(?:^|[^\\])(?:\\\\)*\\)x/g, 'u00'); 11 | 12 | if (value !== fixedValue) { 13 | return { 14 | node, 15 | messageId: MESSAGE_ID, 16 | fix: fixer => 17 | node.type === 'TemplateElement' 18 | ? replaceTemplateElement(fixer, node, fixedValue) 19 | : fixer.replaceText(node, fixedValue), 20 | }; 21 | } 22 | } 23 | 24 | /** @param {import('eslint').Rule.RuleContext} context */ 25 | const create = context => ({ 26 | Literal(node) { 27 | if (node.regex || typeof node.value === 'string') { 28 | return checkEscape(context, node, node.raw); 29 | } 30 | }, 31 | TemplateElement: node => checkEscape(context, node, node.value.raw), 32 | }); 33 | 34 | /** @type {import('eslint').Rule.RuleModule} */ 35 | module.exports = { 36 | create, 37 | meta: { 38 | type: 'suggestion', 39 | docs: { 40 | description: 'Enforce the use of Unicode escapes instead of hexadecimal escapes.', 41 | }, 42 | fixable: 'code', 43 | messages, 44 | }, 45 | }; 46 | -------------------------------------------------------------------------------- /rules/no-nested-ternary.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {isParenthesized} = require('@eslint-community/eslint-utils'); 3 | 4 | const MESSAGE_ID_TOO_DEEP = 'too-deep'; 5 | const MESSAGE_ID_SHOULD_PARENTHESIZED = 'should-parenthesized'; 6 | const messages = { 7 | [MESSAGE_ID_TOO_DEEP]: 'Do not nest ternary expressions.', 8 | [MESSAGE_ID_SHOULD_PARENTHESIZED]: 'Nest ternary expression should be parenthesized.', 9 | }; 10 | 11 | const nestTernarySelector = level => `:not(ConditionalExpression)${' > ConditionalExpression'.repeat(level)}`; 12 | 13 | /** @param {import('eslint').Rule.RuleContext} context */ 14 | const create = context => { 15 | const sourceCode = context.getSourceCode(); 16 | 17 | return { 18 | [nestTernarySelector(3)]: node => 19 | // Nesting more than one level not allowed. 20 | ({node, messageId: MESSAGE_ID_TOO_DEEP}), 21 | [nestTernarySelector(2)](node) { 22 | if (!isParenthesized(node, sourceCode)) { 23 | return { 24 | node, 25 | messageId: MESSAGE_ID_SHOULD_PARENTHESIZED, 26 | fix: fixer => [ 27 | fixer.insertTextBefore(node, '('), 28 | fixer.insertTextAfter(node, ')'), 29 | ], 30 | }; 31 | } 32 | }, 33 | }; 34 | }; 35 | 36 | /** @type {import('eslint').Rule.RuleModule} */ 37 | module.exports = { 38 | create, 39 | meta: { 40 | type: 'suggestion', 41 | docs: { 42 | description: 'Disallow nested ternary expressions.', 43 | }, 44 | fixable: 'code', 45 | messages, 46 | }, 47 | }; 48 | -------------------------------------------------------------------------------- /rules/no-object-as-default-parameter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const MESSAGE_ID_IDENTIFIER = 'identifier'; 4 | const MESSAGE_ID_NON_IDENTIFIER = 'non-identifier'; 5 | const messages = { 6 | [MESSAGE_ID_IDENTIFIER]: 'Do not use an object literal as default for parameter `{{parameter}}`.', 7 | [MESSAGE_ID_NON_IDENTIFIER]: 'Do not use an object literal as default.', 8 | }; 9 | 10 | const objectParameterSelector = [ 11 | ':function > AssignmentPattern.params', 12 | '[right.type="ObjectExpression"]', 13 | '[right.properties.length>0]', 14 | ].join(''); 15 | 16 | /** @param {import('eslint').Rule.RuleContext} context */ 17 | const create = () => ({ 18 | [objectParameterSelector](node) { 19 | const {left, right} = node; 20 | 21 | if (left.type === 'Identifier') { 22 | return { 23 | node: left, 24 | messageId: MESSAGE_ID_IDENTIFIER, 25 | data: {parameter: left.name}, 26 | }; 27 | } 28 | 29 | return { 30 | node: right, 31 | messageId: MESSAGE_ID_NON_IDENTIFIER, 32 | }; 33 | }, 34 | }); 35 | 36 | /** @type {import('eslint').Rule.RuleModule} */ 37 | module.exports = { 38 | create, 39 | meta: { 40 | type: 'problem', 41 | docs: { 42 | description: 'Disallow the use of objects as default parameters.', 43 | }, 44 | messages, 45 | }, 46 | }; 47 | -------------------------------------------------------------------------------- /rules/no-this-assignment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {matches} = require('./selectors/index.js'); 3 | 4 | const MESSAGE_ID = 'no-this-assignment'; 5 | const messages = { 6 | [MESSAGE_ID]: 'Do not assign `this` to `{{name}}`.', 7 | }; 8 | 9 | const variableDeclaratorSelector = [ 10 | 'VariableDeclarator', 11 | '[init.type="ThisExpression"]', 12 | '[id.type="Identifier"]', 13 | ].join(''); 14 | 15 | const assignmentExpressionSelector = [ 16 | 'AssignmentExpression', 17 | '[right.type="ThisExpression"]', 18 | '[left.type="Identifier"]', 19 | ].join(''); 20 | 21 | const selector = matches([variableDeclaratorSelector, assignmentExpressionSelector]); 22 | 23 | /** @param {import('eslint').Rule.RuleContext} context */ 24 | const create = () => ({ 25 | [selector](node) { 26 | const variable = node.type === 'AssignmentExpression' ? node.left : node.id; 27 | return { 28 | node, 29 | data: {name: variable.name}, 30 | messageId: MESSAGE_ID, 31 | }; 32 | }, 33 | }); 34 | 35 | /** @type {import('eslint').Rule.RuleModule} */ 36 | module.exports = { 37 | create, 38 | meta: { 39 | type: 'suggestion', 40 | docs: { 41 | description: 'Disallow assigning `this` to a variable.', 42 | }, 43 | messages, 44 | }, 45 | }; 46 | -------------------------------------------------------------------------------- /rules/no-unreadable-iife.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const { 3 | isParenthesized, 4 | getParenthesizedRange, 5 | } = require('./utils/parentheses.js'); 6 | const toLocation = require('./utils/to-location.js'); 7 | 8 | const MESSAGE_ID_ERROR = 'no-unreadable-iife'; 9 | const messages = { 10 | [MESSAGE_ID_ERROR]: 'IIFE with parenthesized arrow function body is considered unreadable.', 11 | }; 12 | 13 | const selector = [ 14 | 'CallExpression', 15 | ' > ', 16 | 'ArrowFunctionExpression.callee', 17 | ' > ', 18 | ':not(BlockStatement).body', 19 | ].join(''); 20 | 21 | /** @param {import('eslint').Rule.RuleContext} context */ 22 | const create = context => ({ 23 | [selector](node) { 24 | const sourceCode = context.getSourceCode(); 25 | if (!isParenthesized(node, sourceCode)) { 26 | return; 27 | } 28 | 29 | return { 30 | node, 31 | loc: toLocation(getParenthesizedRange(node, sourceCode), sourceCode), 32 | messageId: MESSAGE_ID_ERROR, 33 | }; 34 | }, 35 | }); 36 | 37 | /** @type {import('eslint').Rule.RuleModule} */ 38 | module.exports = { 39 | create, 40 | meta: { 41 | type: 'suggestion', 42 | docs: { 43 | description: 'Disallow unreadable IIFEs.', 44 | }, 45 | hasSuggestions: false, 46 | messages, 47 | }, 48 | }; 49 | -------------------------------------------------------------------------------- /rules/number-literal-case.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {checkVueTemplate} = require('./utils/rule.js'); 3 | const {isNumberLiteral, isBigIntLiteral} = require('./ast/index.js'); 4 | 5 | const MESSAGE_ID = 'number-literal-case'; 6 | const messages = { 7 | [MESSAGE_ID]: 'Invalid number literal casing.', 8 | }; 9 | 10 | const fix = raw => { 11 | let fixed = raw.toLowerCase(); 12 | if (fixed.startsWith('0x')) { 13 | fixed = '0x' + fixed.slice(2).toUpperCase(); 14 | } 15 | 16 | return fixed; 17 | }; 18 | 19 | /** @param {import('eslint').Rule.RuleContext} context */ 20 | const create = () => ({ 21 | Literal(node) { 22 | const {raw} = node; 23 | 24 | let fixed = raw; 25 | if (isNumberLiteral(node)) { 26 | fixed = fix(raw); 27 | } else if (isBigIntLiteral(node)) { 28 | fixed = fix(raw.slice(0, -1)) + 'n'; 29 | } 30 | 31 | if (raw !== fixed) { 32 | return { 33 | node, 34 | messageId: MESSAGE_ID, 35 | fix: fixer => fixer.replaceText(node, fixed), 36 | }; 37 | } 38 | }, 39 | }); 40 | 41 | /** @type {import('eslint').Rule.RuleModule} */ 42 | module.exports = { 43 | create: checkVueTemplate(create), 44 | meta: { 45 | type: 'suggestion', 46 | docs: { 47 | description: 'Enforce proper case for numeric literals.', 48 | }, 49 | fixable: 'code', 50 | messages, 51 | }, 52 | }; 53 | -------------------------------------------------------------------------------- /rules/prefer-array-index-of.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const simpleArraySearchRule = require('./shared/simple-array-search-rule.js'); 3 | 4 | const indexOfOverFindIndexRule = simpleArraySearchRule({ 5 | method: 'findIndex', 6 | replacement: 'indexOf', 7 | }); 8 | 9 | const lastIndexOfOverFindLastIndexRule = simpleArraySearchRule({ 10 | method: 'findLastIndex', 11 | replacement: 'lastIndexOf', 12 | }); 13 | 14 | /** @type {import('eslint').Rule.RuleModule} */ 15 | module.exports = { 16 | create: context => ({ 17 | ...indexOfOverFindIndexRule.createListeners(context), 18 | ...lastIndexOfOverFindLastIndexRule.createListeners(context), 19 | }), 20 | meta: { 21 | type: 'suggestion', 22 | docs: { 23 | description: 'Prefer `Array#{indexOf,lastIndexOf}()` over `Array#{findIndex,findLastIndex}()` when looking for the index of an item.', 24 | }, 25 | fixable: 'code', 26 | hasSuggestions: true, 27 | messages: { 28 | ...indexOfOverFindIndexRule.messages, 29 | ...lastIndexOfOverFindLastIndexRule.messages, 30 | }, 31 | }, 32 | }; 33 | -------------------------------------------------------------------------------- /rules/prefer-dom-node-append.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const isValueNotUsable = require('./utils/is-value-not-usable.js'); 3 | const {methodCallSelector, notDomNodeSelector} = require('./selectors/index.js'); 4 | 5 | const MESSAGE_ID = 'prefer-dom-node-append'; 6 | const messages = { 7 | [MESSAGE_ID]: 'Prefer `Node#append()` over `Node#appendChild()`.', 8 | }; 9 | const selector = [ 10 | methodCallSelector({ 11 | method: 'appendChild', 12 | argumentsLength: 1, 13 | }), 14 | notDomNodeSelector('callee.object'), 15 | notDomNodeSelector('arguments.0'), 16 | ].join(''); 17 | 18 | /** @param {import('eslint').Rule.RuleContext} context */ 19 | const create = () => ({ 20 | [selector](node) { 21 | const fix = isValueNotUsable(node) 22 | ? fixer => fixer.replaceText(node.callee.property, 'append') 23 | : undefined; 24 | 25 | return { 26 | node, 27 | messageId: MESSAGE_ID, 28 | fix, 29 | }; 30 | }, 31 | }); 32 | 33 | /** @type {import('eslint').Rule.RuleModule} */ 34 | module.exports = { 35 | create, 36 | meta: { 37 | type: 'suggestion', 38 | docs: { 39 | description: 'Prefer `Node#append()` over `Node#appendChild()`.', 40 | }, 41 | fixable: 'code', 42 | messages, 43 | }, 44 | }; 45 | -------------------------------------------------------------------------------- /rules/prefer-event-target.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {matches} = require('./selectors/index.js'); 3 | 4 | const MESSAGE_ID = 'prefer-event-target'; 5 | const messages = { 6 | [MESSAGE_ID]: 'Prefer `EventTarget` over `EventEmitter`.', 7 | }; 8 | 9 | const selector = [ 10 | 'Identifier', 11 | '[name="EventEmitter"]', 12 | matches([ 13 | 'ClassDeclaration > .superClass', 14 | 'ClassExpression > .superClass', 15 | 'NewExpression > .callee', 16 | ]), 17 | ].join(''); 18 | 19 | /** @param {import('eslint').Rule.RuleContext} context */ 20 | const create = () => ({ 21 | [selector](node) { 22 | return { 23 | node, 24 | messageId: MESSAGE_ID, 25 | }; 26 | }, 27 | }); 28 | 29 | /** @type {import('eslint').Rule.RuleModule} */ 30 | module.exports = { 31 | create, 32 | meta: { 33 | type: 'suggestion', 34 | docs: { 35 | description: 'Prefer `EventTarget` over `EventEmitter`.', 36 | }, 37 | messages, 38 | }, 39 | }; 40 | -------------------------------------------------------------------------------- /rules/prefer-node-protocol.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const isBuiltinModule = require('is-builtin-module'); 3 | const {matches, STATIC_REQUIRE_SOURCE_SELECTOR} = require('./selectors/index.js'); 4 | const {replaceStringLiteral} = require('./fix/index.js'); 5 | 6 | const MESSAGE_ID = 'prefer-node-protocol'; 7 | const messages = { 8 | [MESSAGE_ID]: 'Prefer `node:{{moduleName}}` over `{{moduleName}}`.', 9 | }; 10 | 11 | const importExportSourceSelector = [ 12 | ':matches(ImportDeclaration, ExportNamedDeclaration, ImportExpression)', 13 | ' > ', 14 | 'Literal.source', 15 | ].join(''); 16 | 17 | const selector = matches([ 18 | importExportSourceSelector, 19 | STATIC_REQUIRE_SOURCE_SELECTOR, 20 | ]); 21 | 22 | const create = () => ({ 23 | [selector](node) { 24 | const {value} = node; 25 | if ( 26 | typeof value !== 'string' 27 | || value.startsWith('node:') 28 | || !isBuiltinModule(value) 29 | ) { 30 | return; 31 | } 32 | 33 | return { 34 | node, 35 | messageId: MESSAGE_ID, 36 | data: {moduleName: value}, 37 | /** @param {import('eslint').Rule.RuleFixer} fixer */ 38 | fix: fixer => replaceStringLiteral(fixer, node, 'node:', 0, 0), 39 | }; 40 | }, 41 | }); 42 | 43 | /** @type {import('eslint').Rule.RuleModule} */ 44 | module.exports = { 45 | create, 46 | meta: { 47 | type: 'suggestion', 48 | docs: { 49 | description: 'Prefer using the `node:` protocol when importing Node.js builtin modules.', 50 | }, 51 | fixable: 'code', 52 | messages, 53 | }, 54 | }; 55 | -------------------------------------------------------------------------------- /rules/prefer-string-trim-start-end.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {methodCallSelector} = require('./selectors/index.js'); 3 | 4 | const MESSAGE_ID = 'prefer-string-trim-start-end'; 5 | const messages = { 6 | [MESSAGE_ID]: 'Prefer `String#{{replacement}}()` over `String#{{method}}()`.', 7 | }; 8 | 9 | const selector = [ 10 | methodCallSelector({ 11 | methods: ['trimLeft', 'trimRight'], 12 | argumentsLength: 0, 13 | includeOptionalMember: true, 14 | }), 15 | ' > .callee', 16 | ' > .property', 17 | ].join(' '); 18 | 19 | /** @param {import('eslint').Rule.RuleContext} context */ 20 | const create = () => ({ 21 | [selector](node) { 22 | const method = node.name; 23 | const replacement = method === 'trimLeft' ? 'trimStart' : 'trimEnd'; 24 | 25 | return { 26 | node, 27 | messageId: MESSAGE_ID, 28 | data: {method, replacement}, 29 | fix: fixer => fixer.replaceText(node, replacement), 30 | }; 31 | }, 32 | }); 33 | 34 | /** @type {import('eslint').Rule.RuleModule} */ 35 | module.exports = { 36 | create, 37 | meta: { 38 | type: 'suggestion', 39 | docs: { 40 | description: 'Prefer `String#trimStart()` / `String#trimEnd()` over `String#trimLeft()` / `String#trimRight()`.', 41 | }, 42 | fixable: 'code', 43 | messages, 44 | }, 45 | }; 46 | -------------------------------------------------------------------------------- /rules/require-number-to-fixed-digits-argument.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {methodCallSelector, not} = require('./selectors/index.js'); 3 | const {appendArgument} = require('./fix/index.js'); 4 | 5 | const MESSAGE_ID = 'require-number-to-fixed-digits-argument'; 6 | const messages = { 7 | [MESSAGE_ID]: 'Missing the digits argument.', 8 | }; 9 | 10 | const mathToFixed = [ 11 | methodCallSelector({ 12 | method: 'toFixed', 13 | argumentsLength: 0, 14 | }), 15 | not('[callee.object.type="NewExpression"]'), 16 | ].join(''); 17 | 18 | /** @param {import('eslint').Rule.RuleContext} context */ 19 | const create = context => { 20 | const sourceCode = context.getSourceCode(); 21 | return { 22 | [mathToFixed](node) { 23 | const [ 24 | openingParenthesis, 25 | closingParenthesis, 26 | ] = sourceCode.getLastTokens(node, 2); 27 | 28 | return { 29 | loc: { 30 | start: openingParenthesis.loc.start, 31 | end: closingParenthesis.loc.end, 32 | }, 33 | messageId: MESSAGE_ID, 34 | /** @param {import('eslint').Rule.RuleFixer} fixer */ 35 | fix: fixer => appendArgument(fixer, node, '0', sourceCode), 36 | }; 37 | }, 38 | }; 39 | }; 40 | 41 | /** @type {import('eslint').Rule.RuleModule} */ 42 | module.exports = { 43 | create, 44 | meta: { 45 | type: 'suggestion', 46 | docs: { 47 | description: 'Enforce using the digits argument with `Number#toFixed()`.', 48 | }, 49 | fixable: 'code', 50 | messages, 51 | }, 52 | }; 53 | -------------------------------------------------------------------------------- /rules/selectors/empty-array-selector.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function emptyArraySelector(path) { 4 | const prefix = path ? `${path}.` : ''; 5 | return [ 6 | `[${prefix}type="ArrayExpression"]`, 7 | `[${prefix}elements.length=0]`, 8 | ].join(''); 9 | } 10 | 11 | module.exports = emptyArraySelector; 12 | -------------------------------------------------------------------------------- /rules/selectors/empty-object-selector.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function emptyObjectSelector(path) { 4 | const prefix = path ? `${path}.` : ''; 5 | return [ 6 | `[${prefix}type="ObjectExpression"]`, 7 | `[${prefix}properties.length=0]`, 8 | ].join(''); 9 | } 10 | 11 | module.exports = emptyObjectSelector; 12 | -------------------------------------------------------------------------------- /rules/selectors/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | // Utilities 5 | matches: require('./matches-any.js'), 6 | not: require('./negation.js'), 7 | 8 | arrayPrototypeMethodSelector: require('./prototype-method-selector.js').arrayPrototypeMethodSelector, 9 | objectPrototypeMethodSelector: require('./prototype-method-selector.js').objectPrototypeMethodSelector, 10 | emptyArraySelector: require('./empty-array-selector.js'), 11 | emptyObjectSelector: require('./empty-object-selector.js'), 12 | memberExpressionSelector: require('./member-expression-selector.js'), 13 | methodCallSelector: require('./method-call-selector.js'), 14 | notDomNodeSelector: require('./not-dom-node.js').notDomNodeSelector, 15 | notFunctionSelector: require('./not-function.js').notFunctionSelector, 16 | referenceIdentifierSelector: require('./reference-identifier-selector.js'), 17 | callExpressionSelector: require('./call-or-new-expression-selector.js').callExpressionSelector, 18 | newExpressionSelector: require('./call-or-new-expression-selector.js').newExpressionSelector, 19 | callOrNewExpressionSelector: require('./call-or-new-expression-selector.js').callOrNewExpressionSelector, 20 | STATIC_REQUIRE_SELECTOR: require('./require-selector.js').STATIC_REQUIRE_SELECTOR, 21 | STATIC_REQUIRE_SOURCE_SELECTOR: require('./require-selector.js').STATIC_REQUIRE_SOURCE_SELECTOR, 22 | notLeftHandSideSelector: require('./not-left-hand-side.js'), 23 | }; 24 | -------------------------------------------------------------------------------- /rules/selectors/matches-any.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function matches(selectors) { 4 | switch (selectors.length) { 5 | case 0: { 6 | return ''; 7 | } 8 | 9 | case 1: { 10 | // Make selectors more readable 11 | return selectors[0]; 12 | } 13 | 14 | default: { 15 | return `:matches(${selectors.join(', ')})`; 16 | } 17 | } 18 | } 19 | 20 | module.exports = matches; 21 | -------------------------------------------------------------------------------- /rules/selectors/negation.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function not(selectors) { 4 | selectors = Array.isArray(selectors) ? selectors : [selectors]; 5 | return `:not(${selectors.join(', ')})`; 6 | } 7 | 8 | module.exports = not; 9 | -------------------------------------------------------------------------------- /rules/selectors/not-dom-node.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {isUndefined} = require('../ast/index.js'); 4 | 5 | // AST Types: 6 | // https://github.com/eslint/espree/blob/master/lib/ast-node-types.js#L18 7 | // Only types possible to be `callee` or `argument` are listed 8 | const impossibleNodeTypes = [ 9 | 'ArrayExpression', 10 | 'ArrowFunctionExpression', 11 | 'ClassExpression', 12 | 'FunctionExpression', 13 | 'Literal', 14 | 'ObjectExpression', 15 | 'TemplateLiteral', 16 | ]; 17 | 18 | // We might need this later 19 | /* c8 ignore start */ 20 | const isNotDomNode = node => 21 | impossibleNodeTypes.includes(node.type) 22 | || isUndefined(node); 23 | /* c8 ignore end */ 24 | 25 | const notDomNodeSelector = node => [ 26 | ...impossibleNodeTypes.map(type => `[${node}.type!="${type}"]`), 27 | `:not([${node}.type="Identifier"][${node}.name="undefined"])`, 28 | ].join(''); 29 | 30 | module.exports = { 31 | isNotDomNode, 32 | notDomNodeSelector, 33 | }; 34 | -------------------------------------------------------------------------------- /rules/selectors/not-function.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const not = require('./negation.js'); 3 | 4 | // AST Types: 5 | // https://github.com/eslint/espree/blob/master/lib/ast-node-types.js#L18 6 | // Only types possible to be `argument` are listed 7 | const impossibleNodeTypes = [ 8 | 'ArrayExpression', 9 | 'BinaryExpression', 10 | 'ClassExpression', 11 | 'Literal', 12 | 'ObjectExpression', 13 | 'TemplateLiteral', 14 | 'UnaryExpression', 15 | 'UpdateExpression', 16 | ]; 17 | 18 | // Technically these nodes could be a function, but most likely not 19 | const mostLikelyNotNodeTypes = [ 20 | 'AssignmentExpression', 21 | 'AwaitExpression', 22 | 'LogicalExpression', 23 | 'NewExpression', 24 | 'TaggedTemplateExpression', 25 | 'ThisExpression', 26 | ]; 27 | 28 | const notFunctionSelector = node => not([ 29 | [...impossibleNodeTypes, ...mostLikelyNotNodeTypes].map(type => `[${node}.type="${type}"]`), 30 | `[${node}.type="Identifier"][${node}.name="undefined"]`, 31 | [ 32 | `[${node}.type="CallExpression"]`, 33 | not([ 34 | `[${node}.callee.type="MemberExpression"]`, 35 | `[${node}.callee.optional!=true]`, 36 | `[${node}.callee.computed!=true]`, 37 | `[${node}.callee.property.type="Identifier"]`, 38 | `[${node}.callee.property.name="bind"]`, 39 | ].join('')), 40 | ].join(''), 41 | ]); 42 | 43 | module.exports = { 44 | notFunctionSelector, 45 | }; 46 | -------------------------------------------------------------------------------- /rules/selectors/not-left-hand-side.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const not = require('./negation.js'); 3 | 4 | function notLeftHandSideSelector(path) { 5 | const prefix = path ? `${path}.` : ''; 6 | 7 | // Keep logic sync with `../utils/is-left-hand-side.js` 8 | return not([ 9 | `[${prefix}type="AssignmentExpression"] > .left`, 10 | `[${prefix}type="UpdateExpression"] > .argument`, 11 | `[${prefix}type="UnaryExpression"][${prefix}operator="delete"] > .argument`, 12 | ]); 13 | } 14 | 15 | module.exports = notLeftHandSideSelector; 16 | -------------------------------------------------------------------------------- /rules/selectors/require-selector.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {callExpressionSelector} = require('./call-or-new-expression-selector.js'); 3 | 4 | const requireCallSelector = callExpressionSelector({ 5 | name: 'require', 6 | argumentsLength: 1, 7 | // Do not add check on first argument 8 | allowSpreadElement: true, 9 | }); 10 | 11 | module.exports = { 12 | STATIC_REQUIRE_SELECTOR: `${requireCallSelector}[arguments.0.type="Literal"]`, 13 | STATIC_REQUIRE_SOURCE_SELECTOR: `${requireCallSelector} > Literal.arguments`, 14 | }; 15 | -------------------------------------------------------------------------------- /rules/shared/event-keys.js: -------------------------------------------------------------------------------- 1 | /* eslint sort-keys: ["error", "asc", {natural: true}] */ 2 | 'use strict'; 3 | // https://github.com/facebook/react/blob/b87aabd/packages/react-dom/src/events/getEventKey.js#L36 4 | // Only meta characters which can't be deciphered from `String.fromCharCode()` 5 | module.exports = { 6 | 8: 'Backspace', 7 | 9: 'Tab', 8 | 12: 'Clear', 9 | 13: 'Enter', 10 | 16: 'Shift', 11 | 17: 'Control', 12 | 18: 'Alt', 13 | 19: 'Pause', 14 | 20: 'CapsLock', 15 | 27: 'Escape', 16 | 32: ' ', 17 | 33: 'PageUp', 18 | 34: 'PageDown', 19 | 35: 'End', 20 | 36: 'Home', 21 | 37: 'ArrowLeft', 22 | 38: 'ArrowUp', 23 | 39: 'ArrowRight', 24 | 40: 'ArrowDown', 25 | 45: 'Insert', 26 | 46: 'Delete', 27 | 112: 'F1', 28 | 113: 'F2', 29 | 114: 'F3', 30 | 115: 'F4', 31 | 116: 'F5', 32 | 117: 'F6', 33 | 118: 'F7', 34 | 119: 'F8', 35 | 120: 'F9', 36 | 121: 'F10', 37 | 122: 'F11', 38 | 123: 'F12', 39 | 144: 'NumLock', 40 | 145: 'ScrollLock', 41 | 186: ';', 42 | 187: '=', 43 | 188: ',', 44 | 189: '-', 45 | 190: '.', 46 | 191: '/', 47 | 219: '[', 48 | 220: '\\', 49 | 221: ']', 50 | 222: '\'', 51 | 224: 'Meta', 52 | }; 53 | -------------------------------------------------------------------------------- /rules/shared/negative-index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const isSameReference = require('../utils/is-same-reference.js'); 3 | const {getParenthesizedRange} = require('../utils/parentheses.js'); 4 | const {isNumberLiteral} = require('../ast/index.js'); 5 | 6 | const isLengthMemberExpression = node => 7 | node.type === 'MemberExpression' 8 | && !node.computed 9 | && !node.optional 10 | && node.property.type === 'Identifier' 11 | && node.property.name === 'length'; 12 | const isLiteralPositiveNumber = node => 13 | isNumberLiteral(node) 14 | && node.value > 0; 15 | 16 | function getNegativeIndexLengthNode(node, objectNode) { 17 | if (!node) { 18 | return; 19 | } 20 | 21 | const {type, operator, left, right} = node; 22 | 23 | if (type !== 'BinaryExpression' || operator !== '-' || !isLiteralPositiveNumber(right)) { 24 | return; 25 | } 26 | 27 | if (isLengthMemberExpression(left) && isSameReference(left.object, objectNode)) { 28 | return left; 29 | } 30 | 31 | // Nested BinaryExpression 32 | return getNegativeIndexLengthNode(left, objectNode); 33 | } 34 | 35 | function removeLengthNode(node, fixer, sourceCode) { 36 | const [start, end] = getParenthesizedRange(node, sourceCode); 37 | return fixer.removeRange([ 38 | start, 39 | end + sourceCode.text.slice(end).match(/\S|$/).index, 40 | ]); 41 | } 42 | 43 | module.exports = { 44 | getNegativeIndexLengthNode, 45 | removeLengthNode, 46 | }; 47 | -------------------------------------------------------------------------------- /rules/shared/typed-array.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray#description 4 | module.exports = [ 5 | 'Int8Array', 6 | 'Uint8Array', 7 | 'Uint8ClampedArray', 8 | 'Int16Array', 9 | 'Uint16Array', 10 | 'Int32Array', 11 | 'Uint32Array', 12 | 'Float32Array', 13 | 'Float64Array', 14 | 'BigInt64Array', 15 | 'BigUint64Array', 16 | ]; 17 | -------------------------------------------------------------------------------- /rules/throw-new-error.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {matches} = require('./selectors/index.js'); 3 | const {switchCallExpressionToNewExpression} = require('./fix/index.js'); 4 | 5 | const messageId = 'throw-new-error'; 6 | const messages = { 7 | [messageId]: 'Use `new` when throwing an error.', 8 | }; 9 | 10 | const customError = /^(?:[A-Z][\da-z]*)*Error$/; 11 | 12 | const selector = [ 13 | 'ThrowStatement', 14 | ' > ', 15 | 'CallExpression.argument', 16 | matches([ 17 | // `throw FooError()` 18 | [ 19 | '[callee.type="Identifier"]', 20 | `[callee.name=/${customError.source}/]`, 21 | ].join(''), 22 | // `throw lib.FooError()` 23 | [ 24 | '[callee.type="MemberExpression"]', 25 | '[callee.computed!=true]', 26 | '[callee.property.type="Identifier"]', 27 | `[callee.property.name=/${customError.source}/]`, 28 | ].join(''), 29 | ]), 30 | ].join(''); 31 | 32 | /** @param {import('eslint').Rule.RuleContext} context */ 33 | const create = context => ({ 34 | [selector]: node => ({ 35 | node, 36 | messageId, 37 | fix: fixer => switchCallExpressionToNewExpression(node, context.getSourceCode(), fixer), 38 | }), 39 | }); 40 | 41 | /** @type {import('eslint').Rule.RuleModule} */ 42 | module.exports = { 43 | create, 44 | meta: { 45 | type: 'suggestion', 46 | docs: { 47 | description: 'Require `new` when throwing an error.', 48 | }, 49 | fixable: 'code', 50 | messages, 51 | }, 52 | }; 53 | -------------------------------------------------------------------------------- /rules/utils/assert-token.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const ISSUE_LINK_PREFIX = 'https://github.com/sindresorhus/eslint-plugin-unicorn/issues/new?'; 4 | function assertToken(token, {test, expected, ruleId}) { 5 | if (test?.(token)) { 6 | return; 7 | } 8 | 9 | expected = Array.isArray(expected) ? expected : [expected]; 10 | expected = expected.map(expectedToken => typeof expectedToken === 'string' ? {value: expectedToken} : expectedToken); 11 | 12 | if ( 13 | !test 14 | && expected.some( 15 | expectedToken => 16 | Object.entries(expectedToken) 17 | .every(([key, value]) => token[key] === value), 18 | ) 19 | ) { 20 | return; 21 | } 22 | 23 | const actual = `'${JSON.stringify({value: token.value, type: token.type})}'`; 24 | expected = expected.map(expectedToken => `'${JSON.stringify(expectedToken)}'`).join(' or '); 25 | const title = `\`${ruleId}\`: Unexpected token ${actual}`; 26 | const issueLink = `${ISSUE_LINK_PREFIX}title=${encodeURIComponent(title)}`; 27 | const message = `Expected token ${expected}, got ${actual}.\nPlease open an issue at ${issueLink}.`; 28 | 29 | throw new Error(message); 30 | } 31 | 32 | module.exports = assertToken; 33 | -------------------------------------------------------------------------------- /rules/utils/builtins.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const typedArray = require('../shared/typed-array.js'); 3 | 4 | const enforceNew = [ 5 | 'Object', 6 | 'Array', 7 | 'ArrayBuffer', 8 | 'DataView', 9 | 'Date', 10 | 'Error', 11 | 'Function', 12 | 'Map', 13 | 'WeakMap', 14 | 'Set', 15 | 'WeakSet', 16 | 'Promise', 17 | 'RegExp', 18 | 'SharedArrayBuffer', 19 | 'Proxy', 20 | 'WeakRef', 21 | 'FinalizationRegistry', 22 | ...typedArray, 23 | ]; 24 | 25 | const disallowNew = [ 26 | 'BigInt', 27 | 'Boolean', 28 | 'Number', 29 | 'String', 30 | 'Symbol', 31 | ]; 32 | 33 | module.exports = { 34 | enforceNew, 35 | disallowNew, 36 | }; 37 | -------------------------------------------------------------------------------- /rules/utils/cartesian-product-samples.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = (combinations, length = Number.POSITIVE_INFINITY) => { 4 | const total = combinations.reduce((total, {length}) => total * length, 1); 5 | 6 | const samples = Array.from({length: Math.min(total, length)}, (_, sampleIndex) => { 7 | let indexRemaining = sampleIndex; 8 | const combination = []; 9 | for (let combinationIndex = combinations.length - 1; combinationIndex >= 0; combinationIndex--) { 10 | const items = combinations[combinationIndex]; 11 | const {length} = items; 12 | const index = indexRemaining % length; 13 | indexRemaining = (indexRemaining - index) / length; 14 | combination.unshift(items[index]); 15 | } 16 | 17 | return combination; 18 | }); 19 | 20 | return { 21 | total, 22 | samples, 23 | }; 24 | }; 25 | -------------------------------------------------------------------------------- /rules/utils/create-deprecated-rules.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const packageJson = require('../../package.json'); 3 | 4 | const repoUrl = 'https://github.com/sindresorhus/eslint-plugin-unicorn'; 5 | 6 | /** @returns {{ [ruleName: string]: import('eslint').Rule.RuleModule }} */ 7 | function createDeprecatedRules(data) { 8 | return Object.fromEntries( 9 | Object.entries(data).map(([ruleId, replacedBy = []]) => [ 10 | ruleId, 11 | { 12 | create: () => ({}), 13 | meta: { 14 | docs: { 15 | url: `${repoUrl}/blob/v${packageJson.version}/docs/deprecated-rules.md#${ruleId}`, 16 | }, 17 | deprecated: true, 18 | replacedBy: Array.isArray(replacedBy) ? replacedBy : [replacedBy], 19 | }, 20 | }, 21 | ]), 22 | ); 23 | } 24 | 25 | module.exports = createDeprecatedRules; 26 | -------------------------------------------------------------------------------- /rules/utils/escape-string.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const jsesc = require('jsesc'); 4 | 5 | /** 6 | Escape string and wrap the result in quotes. 7 | 8 | @param {string} string - The string to be quoted. 9 | @param {string} [quote] - The quote character. 10 | @returns {string} - The quoted and escaped string. 11 | */ 12 | module.exports = (string, quote = '\'') => { 13 | /* c8 ignore start */ 14 | if (typeof string !== 'string') { 15 | throw new TypeError('Unexpected string.'); 16 | } 17 | /* c8 ignore end */ 18 | 19 | return jsesc(string, { 20 | quotes: quote === '"' ? 'double' : 'single', 21 | wrap: true, 22 | es6: true, 23 | minimal: true, 24 | lowercaseHex: false, 25 | }); 26 | }; 27 | -------------------------------------------------------------------------------- /rules/utils/escape-template-element-raw.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = string => string.replace( 4 | /(?<=(?:^|[^\\])(?:\\\\)*)(?(?:`|\$(?={)))/g, 5 | '\\$', 6 | ); 7 | -------------------------------------------------------------------------------- /rules/utils/get-builtin-rule.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function getBuiltinRule(id) { 4 | return require('eslint/use-at-your-own-risk').builtinRules.get(id); 5 | } 6 | 7 | module.exports = getBuiltinRule; 8 | -------------------------------------------------------------------------------- /rules/utils/get-call-expression-arguments-text.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {isOpeningParenToken} = require('@eslint-community/eslint-utils'); 3 | 4 | /** 5 | Get the text of the arguments list of `CallExpression`. 6 | 7 | @param {Node} node - The `CallExpression` node. 8 | @param {SourceCode} sourceCode - The source code object. 9 | @returns {string} 10 | */ 11 | const getCallExpressionArgumentsText = (node, sourceCode) => { 12 | const openingParenthesisToken = sourceCode.getTokenAfter(node.callee, isOpeningParenToken); 13 | const closingParenthesisToken = sourceCode.getLastToken(node); 14 | 15 | return sourceCode.text.slice( 16 | openingParenthesisToken.range[1], 17 | closingParenthesisToken.range[0], 18 | ); 19 | }; 20 | 21 | module.exports = getCallExpressionArgumentsText; 22 | -------------------------------------------------------------------------------- /rules/utils/get-class-head-location.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | @typedef {line: number, column: number} Position 5 | 6 | Get the location of the given class node for reporting. 7 | 8 | @param {Node} node - The class node to get. 9 | @param {SourceCode} sourceCode - The source code object to get tokens. 10 | @returns {{start: Position, end: Position}} The location of the class node for reporting. 11 | */ 12 | function getClassHeadLocation(node, sourceCode) { 13 | const {loc, body} = node; 14 | const tokenBeforeBody = sourceCode.getTokenBefore(body); 15 | 16 | const {start} = loc; 17 | const {end} = tokenBeforeBody.loc; 18 | 19 | return {start, end}; 20 | } 21 | 22 | module.exports = getClassHeadLocation; 23 | -------------------------------------------------------------------------------- /rules/utils/get-documentation-url.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const path = require('node:path'); 3 | const packageJson = require('../../package.json'); 4 | 5 | const repoUrl = 'https://github.com/sindresorhus/eslint-plugin-unicorn'; 6 | 7 | module.exports = filename => { 8 | const ruleName = path.basename(filename, '.js'); 9 | return `${repoUrl}/blob/v${packageJson.version}/docs/rules/${ruleName}.md`; 10 | }; 11 | -------------------------------------------------------------------------------- /rules/utils/get-indent-string.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function getIndentString(node, sourceCode) { 4 | const {line, column} = sourceCode.getLocFromIndex(node.range[0]); 5 | const lines = sourceCode.getLines(); 6 | const before = lines[line - 1].slice(0, column); 7 | 8 | return before.match(/\s*$/)[0]; 9 | } 10 | 11 | module.exports = getIndentString; 12 | -------------------------------------------------------------------------------- /rules/utils/get-references.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const getScopes = require('./get-scopes.js'); 4 | 5 | const getReferences = scope => [...new Set( 6 | getScopes(scope).flatMap(({references}) => references), 7 | )]; 8 | 9 | module.exports = getReferences; 10 | -------------------------------------------------------------------------------- /rules/utils/get-scopes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | Gather a list of all Scopes starting recursively from the input Scope. 5 | 6 | @param {Scope} scope - The Scope to start checking from. 7 | @returns {Scope[]} - The resulting Scopes. 8 | */ 9 | const getScopes = scope => [ 10 | scope, 11 | ...scope.childScopes.flatMap(scope => getScopes(scope)), 12 | ]; 13 | 14 | module.exports = getScopes; 15 | -------------------------------------------------------------------------------- /rules/utils/get-switch-case-head-location.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {isColonToken} = require('@eslint-community/eslint-utils'); 4 | 5 | /** 6 | @typedef {line: number, column: number} Position 7 | 8 | Get the location of the given `SwitchCase` node for reporting. 9 | 10 | @param {Node} node - The `SwitchCase` node to get. 11 | @param {SourceCode} sourceCode - The source code object to get tokens from. 12 | @returns {{start: Position, end: Position}} The location of the class node for reporting. 13 | */ 14 | function getSwitchCaseHeadLocation(node, sourceCode) { 15 | const startToken = node.test || sourceCode.getFirstToken(node); 16 | const colonToken = sourceCode.getTokenAfter(startToken, isColonToken); 17 | 18 | return {start: node.loc.start, end: colonToken.loc.end}; 19 | } 20 | 21 | module.exports = getSwitchCaseHeadLocation; 22 | -------------------------------------------------------------------------------- /rules/utils/get-variable-identifiers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Get identifiers of given variable 4 | module.exports = ({identifiers, references}) => [...new Set([ 5 | ...identifiers, 6 | ...references.map(({identifier}) => identifier), 7 | ])]; 8 | -------------------------------------------------------------------------------- /rules/utils/has-same-range.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = (node1, node2) => 4 | node1 5 | && node2 6 | && node1.range[0] === node2.range[0] 7 | && node1.range[1] === node2.range[1]; 8 | -------------------------------------------------------------------------------- /rules/utils/is-function-self-used-inside.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {findVariable} = require('@eslint-community/eslint-utils'); 3 | 4 | const getReferences = (scope, nodeOrName) => { 5 | const {references = []} = findVariable(scope, nodeOrName) || {}; 6 | return references; 7 | }; 8 | 9 | /** 10 | Check if `this`, `arguments`, or the function name is used inside of itself. 11 | 12 | @param {Node} functionNode - The function node. 13 | @param {Scope} functionScope - The scope of the function node. 14 | @returns {boolean} 15 | */ 16 | function isFunctionSelfUsedInside(functionNode, functionScope) { 17 | /* c8 ignore next 3 */ 18 | if (functionScope.block !== functionNode) { 19 | throw new Error('"functionScope" should be the scope of "functionNode".'); 20 | } 21 | 22 | const {type, id} = functionNode; 23 | 24 | if (type === 'ArrowFunctionExpression') { 25 | return false; 26 | } 27 | 28 | if (functionScope.thisFound) { 29 | return true; 30 | } 31 | 32 | if (getReferences(functionScope, 'arguments').some(({from}) => from === functionScope)) { 33 | return true; 34 | } 35 | 36 | if (id && getReferences(functionScope, id).length > 0) { 37 | return true; 38 | } 39 | 40 | return false; 41 | } 42 | 43 | module.exports = isFunctionSelfUsedInside; 44 | -------------------------------------------------------------------------------- /rules/utils/is-left-hand-side.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Keep logic sync with `../selector/not-left-hand-side.js` 4 | const isLeftHandSide = node => 5 | (node.parent.type === 'AssignmentExpression' && node.parent.left === node) 6 | || (node.parent.type === 'UpdateExpression' && node.parent.argument === node) 7 | || ( 8 | node.parent.type === 'UnaryExpression' 9 | && node.parent.operator === 'delete' 10 | && node.parent.argument === node 11 | ); 12 | 13 | module.exports = isLeftHandSide; 14 | -------------------------------------------------------------------------------- /rules/utils/is-logical-expression.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | Check if the given node is a true logical expression or not. 5 | 6 | The three binary expressions logical-or (`||`), logical-and (`&&`), and coalesce (`??`) are known as `ShortCircuitExpression`, but ESTree represents these by the `LogicalExpression` node type. This function rejects coalesce expressions of `LogicalExpression` node type. 7 | 8 | @param {Node} node - The node to check. 9 | @returns {boolean} `true` if the node is `&&` or `||`. 10 | @see https://tc39.es/ecma262/#prod-ShortCircuitExpression 11 | */ 12 | const isLogicalExpression = node => 13 | node?.type === 'LogicalExpression' 14 | && (node.operator === '&&' || node.operator === '||'); 15 | 16 | module.exports = isLogicalExpression; 17 | -------------------------------------------------------------------------------- /rules/utils/is-method-named.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const isMethodNamed = (node, name) => 4 | node.type === 'CallExpression' 5 | && node.callee.type === 'MemberExpression' 6 | && node.callee.property.type === 'Identifier' 7 | && node.callee.property.name === name; 8 | 9 | module.exports = isMethodNamed; 10 | -------------------------------------------------------------------------------- /rules/utils/is-new-expression-with-parentheses.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {isOpeningParenToken, isClosingParenToken} = require('@eslint-community/eslint-utils'); 4 | 5 | /** 6 | Determine if a constructor function is newed-up with parens. 7 | 8 | @param {Node} node - The `NewExpression` node to be checked. 9 | @param {SourceCode} sourceCode - The source code object. 10 | @returns {boolean} True if the constructor is called with parens. 11 | 12 | Copied from https://github.com/eslint/eslint/blob/cc4871369645c3409dc56ded7a555af8a9f63d51/lib/rules/no-extra-parens.js#L252 13 | */ 14 | function isNewExpressionWithParentheses(node, sourceCode) { 15 | if (node.arguments.length > 0) { 16 | return true; 17 | } 18 | 19 | const [penultimateToken, lastToken] = sourceCode.getLastTokens(node, 2); 20 | // The expression should end with its own parens, for example, `new new Foo()` is not a new expression with parens. 21 | return isOpeningParenToken(penultimateToken) 22 | && isClosingParenToken(lastToken) 23 | && node.callee.range[1] < node.range[1]; 24 | } 25 | 26 | module.exports = isNewExpressionWithParentheses; 27 | -------------------------------------------------------------------------------- /rules/utils/is-node-matches.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | Check if node matches object name or key path. 5 | 6 | @param {Node} node - The AST node to check. 7 | @param {string} nameOrPath - The object name or key path. 8 | @returns {boolean} 9 | */ 10 | function isNodeMatchesNameOrPath(node, nameOrPath) { 11 | const names = nameOrPath.trim().split('.'); 12 | for (let index = names.length - 1; index >= 0; index--) { 13 | const name = names[index]; 14 | if (!name) { 15 | return false; 16 | } 17 | 18 | if (index === 0) { 19 | return ( 20 | (node.type === 'Identifier' && node.name === name) 21 | || (name === 'this' && node.type === 'ThisExpression') 22 | ); 23 | } 24 | 25 | if ( 26 | node.type !== 'MemberExpression' 27 | || node.optional 28 | || node.computed 29 | || node.property.type !== 'Identifier' 30 | || node.property.name !== name 31 | ) { 32 | return false; 33 | } 34 | 35 | node = node.object; 36 | } 37 | } 38 | 39 | /** 40 | Check if node matches any object name or key path. 41 | 42 | @param {Node} node - The AST node to check. 43 | @param {string[]} nameOrPaths - The object name or key paths. 44 | @returns {boolean} 45 | */ 46 | function isNodeMatches(node, nameOrPaths) { 47 | return nameOrPaths.some(nameOrPath => isNodeMatchesNameOrPath(node, nameOrPath)); 48 | } 49 | 50 | module.exports = { 51 | isNodeMatchesNameOrPath, 52 | isNodeMatches, 53 | }; 54 | -------------------------------------------------------------------------------- /rules/utils/is-object-method.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = (node, object, method) => { 3 | const {callee} = node; 4 | return ( 5 | callee.type === 'MemberExpression' 6 | && callee.object.type === 'Identifier' 7 | && callee.object.name === object 8 | && callee.property.type === 'Identifier' 9 | && callee.property.name === method 10 | ); 11 | }; 12 | -------------------------------------------------------------------------------- /rules/utils/is-on-same-line.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function isOnSameLine(nodeOrTokenA, nodeOrTokenB) { 4 | return nodeOrTokenA.loc.start.line === nodeOrTokenB.loc.start.line; 5 | } 6 | 7 | module.exports = isOnSameLine; 8 | -------------------------------------------------------------------------------- /rules/utils/is-shadowed.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Finds the eslint-scope reference in the given scope. 5 | * @param {Object} scope The scope to search. 6 | * @param {ASTNode} node The identifier node. 7 | * @returns {Reference|undefined} Returns the found reference or null if none were found. 8 | */ 9 | function findReference(scope, node) { 10 | const references = scope.references 11 | .filter(reference => reference.identifier === node); 12 | 13 | if (references.length === 1) { 14 | return references[0]; 15 | } 16 | } 17 | 18 | /** 19 | * Checks if the given identifier node is shadowed in the given scope. 20 | * @param {Object} scope The current scope. 21 | * @param {string} node The identifier node to check 22 | * @returns {boolean} Whether or not the name is shadowed. 23 | */ 24 | function isShadowed(scope, node) { 25 | const reference = findReference(scope, node); 26 | 27 | return ( 28 | reference?.resolved 29 | && reference.resolved.defs.length > 0 30 | ); 31 | } 32 | 33 | module.exports = isShadowed; 34 | -------------------------------------------------------------------------------- /rules/utils/is-shorthand-export-local.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const hasSameRange = require('./has-same-range.js'); 3 | 4 | const isShorthandExportLocal = node => { 5 | const {type, local, exported} = node.parent; 6 | return type === 'ExportSpecifier' && hasSameRange(local, exported) && local === node; 7 | }; 8 | 9 | module.exports = isShorthandExportLocal; 10 | -------------------------------------------------------------------------------- /rules/utils/is-shorthand-import-local.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const hasSameRange = require('./has-same-range.js'); 3 | 4 | const isShorthandImportLocal = node => { 5 | const {type, local, imported} = node.parent; 6 | return type === 'ImportSpecifier' && hasSameRange(local, imported) && local === node; 7 | }; 8 | 9 | module.exports = isShorthandImportLocal; 10 | -------------------------------------------------------------------------------- /rules/utils/is-shorthand-property-assignment-pattern-left.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const isShorthandPropertyValue = require('./is-shorthand-property-value.js'); 4 | 5 | const isShorthandPropertyAssignmentPatternLeft = identifier => 6 | identifier.parent.type === 'AssignmentPattern' 7 | && identifier.parent.left === identifier 8 | && isShorthandPropertyValue(identifier.parent); 9 | 10 | module.exports = isShorthandPropertyAssignmentPatternLeft; 11 | -------------------------------------------------------------------------------- /rules/utils/is-shorthand-property-value.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const isShorthandPropertyValue = identifier => 4 | identifier.parent.type === 'Property' 5 | && identifier.parent.shorthand 6 | && identifier === identifier.parent.value; 7 | 8 | module.exports = isShorthandPropertyValue; 9 | -------------------------------------------------------------------------------- /rules/utils/is-value-not-usable.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = ({parent}) => !parent || parent.type === 'ExpressionStatement'; 4 | -------------------------------------------------------------------------------- /rules/utils/resolve-variable-name.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | Finds a variable named `name` in the scope `scope` (or it's parents). 5 | 6 | @param {string} name - The variable name to be resolve. 7 | @param {Scope} scope - The scope to look for the variable in. 8 | @returns {Variable?} - The found variable, if any. 9 | */ 10 | module.exports = (name, scope) => { 11 | while (scope) { 12 | const variable = scope.set.get(name); 13 | 14 | if (variable) { 15 | return variable; 16 | } 17 | 18 | scope = scope.upper; 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /rules/utils/should-add-parentheses-to-conditional-expression-child.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | Check if parentheses should be added to a `node` when it's used as child of `ConditionalExpression`. 5 | 6 | @param {Node} node - The AST node to check. 7 | @returns {boolean} 8 | */ 9 | function shouldAddParenthesesToConditionalExpressionChild(node) { 10 | return node.type === 'AwaitExpression' 11 | // Lower precedence, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#Table 12 | || node.type === 'AssignmentExpression' 13 | || node.type === 'YieldExpression' 14 | || node.type === 'SequenceExpression'; 15 | } 16 | 17 | module.exports = shouldAddParenthesesToConditionalExpressionChild; 18 | -------------------------------------------------------------------------------- /rules/utils/should-add-parentheses-to-expression-statement-expression.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | Check if parentheses should to be added to a `node` when it's used as an `expression` of `ExpressionStatement`. 5 | 6 | @param {Node} node - The AST node to check. 7 | @param {SourceCode} sourceCode - The source code object. 8 | @returns {boolean} 9 | */ 10 | function shouldAddParenthesesToExpressionStatementExpression(node) { 11 | switch (node.type) { 12 | case 'ObjectExpression': { 13 | return true; 14 | } 15 | 16 | case 'AssignmentExpression': { 17 | return node.left.type === 'ObjectPattern' || node.left.type === 'ArrayPattern'; 18 | } 19 | 20 | default: { 21 | return false; 22 | } 23 | } 24 | } 25 | 26 | module.exports = shouldAddParenthesesToExpressionStatementExpression; 27 | -------------------------------------------------------------------------------- /rules/utils/should-add-parentheses-to-logical-expression-child.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | Check if parentheses should be added to a `node` when it's used as child of `LogicalExpression`. 5 | @param {Node} node - The AST node to check. 6 | @param {{operator: string, property: string}} options - Options 7 | @returns {boolean} 8 | */ 9 | function shouldAddParenthesesToLogicalExpressionChild(node, {operator, property}) { 10 | // We are not using this, but we can improve this function with it 11 | /* c8 ignore next 3 */ 12 | if (!property) { 13 | throw new Error('`property` is required.'); 14 | } 15 | 16 | if ( 17 | node.type === 'LogicalExpression' 18 | && node.operator === operator 19 | ) { 20 | return false; 21 | } 22 | 23 | // Not really needed, but more readable 24 | if ( 25 | node.type === 'AwaitExpression' 26 | || node.type === 'BinaryExpression' 27 | ) { 28 | return true; 29 | } 30 | 31 | // Lower precedence than `LogicalExpression` 32 | // see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#Table 33 | if ( 34 | node.type === 'LogicalExpression' 35 | || node.type === 'ConditionalExpression' 36 | || node.type === 'AssignmentExpression' 37 | || node.type === 'ArrowFunctionExpression' 38 | || node.type === 'YieldExpression' 39 | || node.type === 'SequenceExpression' 40 | ) { 41 | return true; 42 | } 43 | 44 | return false; 45 | } 46 | 47 | module.exports = shouldAddParenthesesToLogicalExpressionChild; 48 | -------------------------------------------------------------------------------- /rules/utils/should-add-parentheses-to-member-expression-object.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const isNewExpressionWithParentheses = require('./is-new-expression-with-parentheses.js'); 4 | const {isDecimalIntegerNode} = require('./numeric.js'); 5 | 6 | /** 7 | Check if parentheses should to be added to a `node` when it's used as an `object` of `MemberExpression`. 8 | 9 | @param {Node} node - The AST node to check. 10 | @param {SourceCode} sourceCode - The source code object. 11 | @returns {boolean} 12 | */ 13 | function shouldAddParenthesesToMemberExpressionObject(node, sourceCode) { 14 | switch (node.type) { 15 | // This is not a full list. Some other nodes like `FunctionDeclaration` don't need parentheses, 16 | // but it's not possible to be in the place we are checking at this point. 17 | case 'Identifier': 18 | case 'MemberExpression': 19 | case 'CallExpression': 20 | case 'ChainExpression': 21 | case 'TemplateLiteral': 22 | case 'ThisExpression': 23 | case 'ArrayExpression': 24 | case 'FunctionExpression': { 25 | return false; 26 | } 27 | 28 | case 'NewExpression': { 29 | return !isNewExpressionWithParentheses(node, sourceCode); 30 | } 31 | 32 | case 'Literal': { 33 | /* c8 ignore next */ 34 | if (isDecimalIntegerNode(node)) { 35 | return true; 36 | } 37 | 38 | return false; 39 | } 40 | 41 | default: { 42 | return true; 43 | } 44 | } 45 | } 46 | 47 | module.exports = shouldAddParenthesesToMemberExpressionObject; 48 | -------------------------------------------------------------------------------- /rules/utils/should-add-parentheses-to-new-expression-callee.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Copied from https://github.com/eslint/eslint/blob/aa87329d919f569404ca573b439934552006572f/lib/rules/no-extra-parens.js#L448 4 | /** 5 | Check if a member expression contains a call expression. 6 | 7 | @param {ASTNode} node - The `MemberExpression` node to evaluate. 8 | @returns {boolean} true if found, false if not. 9 | */ 10 | function doesMemberExpressionContainCallExpression(node) { 11 | let currentNode = node.object; 12 | let currentNodeType = node.object.type; 13 | 14 | while (currentNodeType === 'MemberExpression') { 15 | currentNode = currentNode.object; 16 | currentNodeType = currentNode.type; 17 | } 18 | 19 | return currentNodeType === 'CallExpression'; 20 | } 21 | 22 | /** 23 | Check if parentheses should be added to a `node` when it's used as `callee` of `NewExpression`. 24 | 25 | @param {Node} node - The AST node to check. 26 | @returns {boolean} 27 | */ 28 | function shouldAddParenthesesToNewExpressionCallee(node) { 29 | return node.type === 'MemberExpression' && doesMemberExpressionContainCallExpression(node); 30 | } 31 | 32 | module.exports = shouldAddParenthesesToNewExpressionCallee; 33 | -------------------------------------------------------------------------------- /rules/utils/should-add-parentheses-to-spread-element-argument.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const nodeTypesDoNotNeedParentheses = new Set([ 4 | 'CallExpression', 5 | 'Identifier', 6 | 'Literal', 7 | 'MemberExpression', 8 | 'NewExpression', 9 | 'TemplateLiteral', 10 | 'ThisExpression', 11 | ]); 12 | 13 | /** 14 | Check if parentheses should be added to a `node` when it's used as `argument` of `SpreadElement`. 15 | 16 | @param {Node} node - The AST node to check. 17 | @returns {boolean} 18 | */ 19 | const shouldAddParenthesesToSpreadElementArgument = node => 20 | !nodeTypesDoNotNeedParentheses.has(node.type); 21 | 22 | module.exports = shouldAddParenthesesToSpreadElementArgument; 23 | -------------------------------------------------------------------------------- /rules/utils/singular.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {singular: pluralizeSingular} = require('pluralize'); 4 | 5 | /** 6 | Singularizes a word/name, i.e. `items` to `item`. 7 | 8 | @param {string} original - The word/name to singularize. 9 | @returns {string|undefined} - The singularized result, or `undefined` if attempting singularization resulted in no change. 10 | */ 11 | const singular = original => { 12 | const singularized = pluralizeSingular(original); 13 | if (singularized !== original) { 14 | return singularized; 15 | } 16 | }; 17 | 18 | module.exports = singular; 19 | -------------------------------------------------------------------------------- /rules/utils/to-location.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | Get location info for the given node or range. 5 | 6 | @param {import('estree').Node | number[]} nodeOrRange - The AST node or range to get the location for. 7 | @param {import('eslint').SourceCode} sourceCode - The source code object. 8 | @param {int} [startOffset] - Start position offset. 9 | @param {int} [endOffset] - End position offset. 10 | @returns {import('estree').SourceLocation} 11 | */ 12 | function toLocation(nodeOrRange, sourceCode, startOffset = 0, endOffset = 0) { 13 | const [start, end] = Array.isArray(nodeOrRange) ? nodeOrRange : nodeOrRange.range; 14 | 15 | return { 16 | start: sourceCode.getLocFromIndex(start + startOffset), 17 | end: sourceCode.getLocFromIndex(end + endOffset), 18 | }; 19 | } 20 | 21 | module.exports = toLocation; 22 | -------------------------------------------------------------------------------- /scripts/internal-rules/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('node:path'); 4 | 5 | const pluginName = 'internal-rules'; 6 | const TEST_DIRECTORIES = [ 7 | path.join(__dirname, '../../test'), 8 | ]; 9 | const RULES_DIRECTORIES = [ 10 | path.join(__dirname, '../../rules'), 11 | ]; 12 | 13 | const rules = [ 14 | {id: 'fix-snapshot-test', directories: TEST_DIRECTORIES}, 15 | {id: 'prefer-disallow-over-forbid', directories: RULES_DIRECTORIES}, 16 | {id: 'prefer-negative-boolean-attribute', directories: RULES_DIRECTORIES}, 17 | ]; 18 | 19 | const isFileInsideDirectory = (filename, directory) => filename.startsWith(directory + path.sep); 20 | 21 | module.exports = { 22 | rules: Object.fromEntries( 23 | rules.map(({id, directories}) => { 24 | const rule = require(`./${id}.js`); 25 | return [ 26 | id, 27 | { 28 | ...rule, 29 | create(context) { 30 | const filename = context.getPhysicalFilename(); 31 | if (directories.every(directory => !isFileInsideDirectory(filename, directory))) { 32 | return {}; 33 | } 34 | 35 | return rule.create(context); 36 | }, 37 | }, 38 | ]; 39 | }), 40 | ), 41 | configs: { 42 | all: { 43 | plugins: [pluginName], 44 | rules: Object.fromEntries(rules.map(({id}) => [`${pluginName}/${id}`, 'error'])), 45 | }, 46 | }, 47 | }; 48 | -------------------------------------------------------------------------------- /scripts/internal-rules/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "eslint-plugin-internal-rules", 4 | "version": "0.0.0", 5 | "description": "Internal rules", 6 | "license": "MIT" 7 | } 8 | -------------------------------------------------------------------------------- /scripts/internal-rules/prefer-negative-boolean-attribute.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const path = require('node:path'); 3 | 4 | const messageId = path.basename(__filename, '.js'); 5 | 6 | const shouldReport = (string, value) => { 7 | const index = string.indexOf(`=${value}]`); 8 | 9 | if (index === -1) { 10 | return false; 11 | } 12 | 13 | return string[index - 1] !== '!'; 14 | }; 15 | 16 | module.exports = { 17 | create(context) { 18 | return { 19 | 'TemplateElement, Literal'(node) { 20 | const string = node.value; 21 | if (typeof string !== 'string') { 22 | return; 23 | } 24 | 25 | for (const value of [true, false]) { 26 | if (shouldReport(string, value)) { 27 | context.report({ 28 | node, 29 | messageId, 30 | data: { 31 | preferred: String(!value), 32 | }, 33 | }); 34 | } 35 | } 36 | }, 37 | }; 38 | }, 39 | meta: { 40 | messages: { 41 | [messageId]: 'Prefer use `[…!={{preferred}}]` in esquery selector.', 42 | }, 43 | }, 44 | }; 45 | -------------------------------------------------------------------------------- /scripts/template/documentation.md.jst: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ## Fail 7 | 8 | ```js 9 | const foo = 'unicorn'; 10 | ``` 11 | 12 | ## Pass 13 | 14 | ```js 15 | const foo = '🦄'; 16 | ``` 17 | -------------------------------------------------------------------------------- /scripts/template/test.mjs.jst: -------------------------------------------------------------------------------- 1 | import outdent from 'outdent'; 2 | import {getTester} from './utils/test.mjs'; 3 | 4 | const {test} = getTester(import.meta); 5 | 6 | test.snapshot({ 7 | valid: [ 8 | 'const foo = "🦄";', 9 | ], 10 | invalid: [ 11 | 'const foo = "unicorn";', 12 | ], 13 | }); 14 | -------------------------------------------------------------------------------- /test/integration/fixtures-local/conflicts-no-array-for-each-and-prevent-abbreviations.js: -------------------------------------------------------------------------------- 1 | const btn = bar; 2 | foo.forEach(btn => click(btn)) 3 | -------------------------------------------------------------------------------- /test/integration/fixtures-local/error-name-conflicts.js: -------------------------------------------------------------------------------- 1 | function foo() { 2 | try { 3 | } catch (err) { 4 | console.log(err); 5 | 6 | if (test) { 7 | throw a; 8 | } else { 9 | throw b; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/integration/readme.md: -------------------------------------------------------------------------------- 1 | # Integration tests 2 | 3 | To run the integration tests, go to the project root, and run `$ npm run integration`. 4 | 5 | To run tests on specific projects, run `$ npm run integration projectName1 projectName2 … projectNameN`. The project names can be found in [`projects.mjs`](projects.mjs). 6 | -------------------------------------------------------------------------------- /test/no-document-cookie.mjs: -------------------------------------------------------------------------------- 1 | import outdent from 'outdent'; 2 | import {getTester} from './utils/test.mjs'; 3 | 4 | const {test} = getTester(import.meta); 5 | 6 | test.snapshot({ 7 | valid: [ 8 | 'document.cookie', 9 | 'const foo = document.cookie', 10 | 'foo = document.cookie', 11 | 'foo = document?.cookie', 12 | 'foo = document.cookie + ";foo=bar"', 13 | 'delete document.cookie', 14 | 'if (document.cookie.includes("foo")){}', 15 | 'Object.assign(document, {cookie: "foo=bar"})', 16 | 'document[CONSTANTS_COOKIE] = "foo=bar"', 17 | 'document[cookie] = "foo=bar"', 18 | outdent` 19 | const CONSTANTS_COOKIE = "cookie"; 20 | document[CONSTANTS_COOKIE] = "foo=bar"; 21 | `, 22 | ], 23 | invalid: [ 24 | 'document.cookie = "foo=bar"', 25 | 'document.cookie += ";foo=bar"', 26 | 'document.cookie = document.cookie + ";foo=bar"', 27 | 'document.cookie &&= true', 28 | 'document["coo" + "kie"] = "foo=bar"', 29 | 'foo = document.cookie = "foo=bar"', 30 | 'var doc = document; doc.cookie = "foo=bar"', 31 | 'let doc = document; doc.cookie = "foo=bar"', 32 | 'const doc = globalThis.document; doc.cookie = "foo=bar"', 33 | 'window.document.cookie = "foo=bar"', 34 | ], 35 | }); 36 | -------------------------------------------------------------------------------- /test/no-this-assignment.mjs: -------------------------------------------------------------------------------- 1 | import outdent from 'outdent'; 2 | import {getTester} from './utils/test.mjs'; 3 | 4 | const {test} = getTester(import.meta); 5 | 6 | test.snapshot({ 7 | valid: [ 8 | 'const {property} = this;', 9 | 'const property = this.property;', 10 | 'const [element] = this;', 11 | 'const element = this[0];', 12 | '([element] = this);', 13 | 'element = this[0];', 14 | 'property = this.property;', 15 | 'const [element] = [this];', 16 | '([element] = [this]);', 17 | 'const {property} = {property: this};', 18 | '({property} = {property: this});', 19 | 'const self = true && this;', 20 | 'const self = false || this;', 21 | 'const self = false ?? this;', 22 | 'foo.bar = this;', 23 | 'function foo(a = this) {}', 24 | 'function foo({a = this}) {}', 25 | 'function foo([a = this]) {}', 26 | ], 27 | invalid: [ 28 | 'const foo = this;', 29 | 'let foo;foo = this;', 30 | 'var foo = bar, baz = this;', 31 | ], 32 | }); 33 | 34 | test.babel({ 35 | valid: [ 36 | outdent` 37 | class A { 38 | foo = this; 39 | } 40 | `, 41 | outdent` 42 | class A { 43 | static foo = this; 44 | } 45 | `, 46 | ], 47 | invalid: [], 48 | }); 49 | -------------------------------------------------------------------------------- /test/no-unreadable-iife.mjs: -------------------------------------------------------------------------------- 1 | import outdent from 'outdent'; 2 | import {getTester} from './utils/test.mjs'; 3 | 4 | const {test} = getTester(import.meta); 5 | 6 | test.snapshot({ 7 | valid: [ 8 | 'const foo = (bar => bar)();', 9 | outdent` 10 | const foo = (() => { 11 | return a ? b : c 12 | })(); 13 | `, 14 | ], 15 | invalid: [ 16 | 'const foo = (() => (a ? b : c))();', 17 | outdent` 18 | const foo = (() => ( 19 | a ? b : c 20 | ))(); 21 | `, 22 | outdent` 23 | const foo = ( 24 | () => ( 25 | a ? b : c 26 | ) 27 | )(); 28 | `, 29 | outdent` 30 | const foo = (() => ( 31 | a, b 32 | ))(); 33 | `, 34 | outdent` 35 | const foo = (() => ({ 36 | a: b, 37 | }))(); 38 | `, 39 | 'const foo = (bar => (bar))();', 40 | outdent` 41 | (async () => ({ 42 | bar, 43 | }))(); 44 | `, 45 | outdent` 46 | const foo = (async (bar) => ({ 47 | bar: await baz(), 48 | }))(); 49 | `, 50 | '(async () => (( {bar} )))();', 51 | ], 52 | }); 53 | -------------------------------------------------------------------------------- /test/no-unsafe-regex.mjs: -------------------------------------------------------------------------------- 1 | import {getTester} from './utils/test.mjs'; 2 | 3 | const {test} = getTester(import.meta); 4 | 5 | const error = { 6 | messageId: 'no-unsafe-regex', 7 | }; 8 | 9 | test({ 10 | valid: [ 11 | 'const foo = /\bunicorn\b/', 12 | 'const foo = /\bunicorn\b/g', 13 | 'const foo = new RegExp(\'^\bunicorn\b\')', 14 | 'const foo = new RegExp(\'^\bunicorn\b\', \'i\')', 15 | 'const foo = new RegExp(/\bunicorn\b/)', 16 | 'const foo = new RegExp(/\bunicorn\b/g)', 17 | 'const foo = new RegExp()', 18 | ], 19 | invalid: [ 20 | { 21 | code: 'const foo = /(x+x+)+y/', 22 | errors: [error], 23 | }, 24 | { 25 | code: 'const foo = /(x+x+)+y/g', 26 | errors: [error], 27 | }, 28 | { 29 | code: 'const foo = new RegExp(\'(x+x+)+y\')', 30 | errors: [error], 31 | }, 32 | { 33 | code: 'const foo = new RegExp(\'(x+x+)+y\', \'g\')', 34 | errors: [error], 35 | }, 36 | { 37 | code: 'const foo = new RegExp(/(x+x+)+y/)', 38 | errors: [error], 39 | }, 40 | { 41 | code: 'const foo = new RegExp(/(x+x+)+y/g)', 42 | errors: [error], 43 | }, 44 | ], 45 | }); 46 | 47 | test.snapshot({ 48 | valid: [], 49 | invalid: [ 50 | 'const foo = /(x+x+)+y/g', 51 | ], 52 | }); 53 | -------------------------------------------------------------------------------- /test/prefer-array-index-of.mjs: -------------------------------------------------------------------------------- 1 | import {getTester} from './utils/test.mjs'; 2 | import createSimpleArraySearchRuleTestFixtures from './shared/simple-array-search-rule-tests.mjs'; 3 | 4 | const {test} = getTester(import.meta); 5 | 6 | const indexOfOverFindIndexFixtures = createSimpleArraySearchRuleTestFixtures({ 7 | method: 'findIndex', 8 | replacement: 'indexOf', 9 | }); 10 | 11 | test.snapshot(indexOfOverFindIndexFixtures.snapshot); 12 | test.typescript(indexOfOverFindIndexFixtures.typescript); 13 | 14 | const lastIndexOfOverFindLastIndexFixtures = createSimpleArraySearchRuleTestFixtures({ 15 | method: 'findLastIndex', 16 | replacement: 'lastIndexOf', 17 | }); 18 | 19 | test.snapshot(lastIndexOfOverFindLastIndexFixtures.snapshot); 20 | test.typescript(lastIndexOfOverFindLastIndexFixtures.typescript); 21 | -------------------------------------------------------------------------------- /test/prefer-code-point.mjs: -------------------------------------------------------------------------------- 1 | import {getTester} from './utils/test.mjs'; 2 | 3 | const {test} = getTester(import.meta); 4 | 5 | test.snapshot({ 6 | valid: [ 7 | '"🦄".codePointAt(0)', 8 | 'foo.charCodeAt', 9 | 'new foo.charCodeAt', 10 | 'charCodeAt(0)', 11 | 'foo.charCodeAt?.(0)', 12 | 'foo?.charCodeAt(0)', 13 | 'foo[charCodeAt](0)', 14 | 'foo["charCodeAt"](0)', 15 | 'foo.notCharCodeAt(0)', 16 | 17 | 'String.fromCodePoint(0x1f984)', 18 | 'String.fromCodePoint', 19 | 'new String.fromCodePoint', 20 | 'fromCodePoint(foo)', 21 | 'String.fromCodePoint?.(foo)', 22 | 'String?.fromCodePoint(foo)', 23 | 'window.String.fromCodePoint(foo)', 24 | 'String[fromCodePoint](foo)', 25 | 'String["fromCodePoint"](foo)', 26 | 'String.notFromCodePoint(foo)', 27 | 'NotString.fromCodePoint(foo)', 28 | ], 29 | invalid: [ 30 | 'string.charCodeAt(index)', 31 | '(( (( string )).charCodeAt( ((index)), )))', 32 | 'String.fromCharCode( code )', 33 | '(( (( String )).fromCharCode( ((code)), ) ))', 34 | ], 35 | }); 36 | -------------------------------------------------------------------------------- /test/prefer-dom-node-text-content.mjs: -------------------------------------------------------------------------------- 1 | import {getTester} from './utils/test.mjs'; 2 | 3 | const {test} = getTester(import.meta); 4 | 5 | test.snapshot({ 6 | valid: [ 7 | 'innerText;', 8 | 'node.textContent;', 9 | 'node[innerText];', 10 | 'innerText = true;', 11 | 'node[\'innerText\'];', 12 | 'innerText.textContent', 13 | 'const [innerText] = node;', 14 | '[innerText] = node;', 15 | 'const {[innerText]: text} = node;', 16 | '({[innerText]: text} = node);', 17 | 'const foo = {innerText}', 18 | 'const foo = {innerText: text}', 19 | ], 20 | invalid: [ 21 | 'node.innerText;', 22 | 'node.innerText = \'foo\';', 23 | 'innerText.innerText;', 24 | 'const {innerText} = node;', 25 | 'const {innerText,} = node;', 26 | 'const {innerText: text} = node;', 27 | 'const {innerText = "default text"} = node;', 28 | 'const {innerText: text = "default text"} = node;', 29 | '({innerText} = node);', 30 | '({innerText: text} = node);', 31 | '({innerText = "default text"} = node);', 32 | '({innerText: text = "default text"} = node);', 33 | 'function foo({innerText}) {return innerText}', 34 | 'for (const [{innerText}] of elements);', 35 | ], 36 | }); 37 | -------------------------------------------------------------------------------- /test/prefer-event-target.mjs: -------------------------------------------------------------------------------- 1 | import outdent from 'outdent'; 2 | import {getTester} from './utils/test.mjs'; 3 | 4 | const {test} = getTester(import.meta); 5 | 6 | test.snapshot({ 7 | valid: [ 8 | 'class Foo {}', 9 | 'class Foo extends OtherClass {}', 10 | 'class Foo extends EventTarget {}', 11 | 'const Foo = class extends EventTarget {}', 12 | 'const Foo = class extends foo.EventTarget {}', 13 | 'const Foo = class extends foo.bar.EventTarget {}', 14 | 'class Foo extends foo.EventEmitter {}', 15 | 'class Foo extends foo.bar.EventEmitter {}', 16 | 'class EventEmitter extends Foo {}', 17 | 'const Foo = class EventEmitter extends Foo {}', 18 | 'new Foo(EventEmitter)', 19 | 'new foo.EventEmitter()', 20 | ], 21 | invalid: [ 22 | 'class Foo extends EventEmitter {}', 23 | 'class Foo extends EventEmitter { someMethod() {} }', 24 | 'const Foo = class extends EventEmitter {}', 25 | outdent` 26 | class Foo extends EventEmitter { 27 | addListener() {} 28 | removeListener() {} 29 | } 30 | `, 31 | ], 32 | }); 33 | 34 | test.snapshot({ 35 | valid: [ 36 | 'EventTarget()', 37 | 'new EventTarget', 38 | 'const target = new EventTarget;', 39 | 'const target = EventTarget()', 40 | 'const target = new Foo(EventEmitter);', 41 | 'EventEmitter()', 42 | 'const emitter = EventEmitter()', 43 | ], 44 | invalid: [ 45 | 'new EventEmitter', 46 | 'const emitter = new EventEmitter;', 47 | ], 48 | }); 49 | -------------------------------------------------------------------------------- /test/prefer-includes.mjs: -------------------------------------------------------------------------------- 1 | import {getTester} from './utils/test.mjs'; 2 | import tests from './shared/simple-array-search-rule-tests.mjs'; 3 | 4 | const {test} = getTester(import.meta); 5 | 6 | test.snapshot({ 7 | valid: [ 8 | 'str.indexOf(\'foo\') !== -n', 9 | 'str.indexOf(\'foo\') !== 1', 10 | 'str.indexOf(\'foo\') === -2', 11 | '!str.indexOf(\'foo\') === 1', 12 | '!str.indexOf(\'foo\') === -n', 13 | 'str.includes(\'foo\')', 14 | '\'foobar\'.includes(\'foo\')', 15 | '[1,2,3].includes(4)', 16 | 'null.indexOf(\'foo\') !== 1', 17 | 'f(0) < 0', 18 | 'something.indexOf(foo, 0, another) !== -1', 19 | '_.indexOf(foo, bar) !== -1', 20 | 'lodash.indexOf(foo, bar) !== -1', 21 | 'underscore.indexOf(foo, bar) !== -1', 22 | ], 23 | invalid: [ 24 | '\'foobar\'.indexOf(\'foo\') !== -1', 25 | 'str.indexOf(\'foo\') != -1', 26 | 'str.indexOf(\'foo\') > -1', 27 | 'str.indexOf(\'foo\') == -1', 28 | '\'foobar\'.indexOf(\'foo\') >= 0', 29 | '[1,2,3].indexOf(4) !== -1', 30 | 'str.indexOf(\'foo\') < 0', 31 | '\'\'.indexOf(\'foo\') < 0', 32 | '(a || b).indexOf(\'foo\') === -1', 33 | 'foo.indexOf(bar, 0) !== -1', 34 | 'foo.indexOf(bar, 1) !== -1', 35 | ], 36 | }); 37 | 38 | const {snapshot, typescript} = tests({ 39 | method: 'some', 40 | replacement: 'includes', 41 | }); 42 | 43 | test.snapshot(snapshot); 44 | test.typescript(typescript); 45 | -------------------------------------------------------------------------------- /test/prefer-logical-operator-over-ternary.mjs: -------------------------------------------------------------------------------- 1 | import outdent from 'outdent'; 2 | import {getTester} from './utils/test.mjs'; 3 | 4 | const {test} = getTester(import.meta); 5 | 6 | test.snapshot({ 7 | valid: [ 8 | 'foo ? foo1 : bar;', 9 | 'foo.bar ? foo.bar1 : foo.baz', 10 | 'foo.bar ? foo1.bar : foo.baz', 11 | '++foo ? ++foo : bar;', 12 | 13 | // Not checking 14 | '!!bar ? foo : bar;', 15 | ], 16 | invalid: [ 17 | 'foo ? foo : bar;', 18 | 'foo.bar ? foo.bar : foo.baz', 19 | 'foo?.bar ? foo.bar : baz', 20 | '!bar ? foo : bar;', 21 | '!!bar ? foo : !bar;', 22 | 23 | 'foo() ? foo() : bar', 24 | 25 | // Children parentheses 26 | 'foo ? foo : a && b', 27 | 'foo ? foo : a || b', 28 | 'foo ? foo : a ?? b', 29 | 'a && b ? a && b : bar', 30 | 'a || b ? a || b : bar', 31 | 'a ?? b ? a ?? b : bar', 32 | 'foo ? foo : await a', 33 | 'await a ? await a : foo', 34 | 35 | // ASI 36 | outdent` 37 | const foo = [] 38 | !+a ? b : +a 39 | `, 40 | outdent` 41 | const foo = [] 42 | a && b ? a && b : 1 43 | `, 44 | ], 45 | }); 46 | -------------------------------------------------------------------------------- /test/prefer-set-size.mjs: -------------------------------------------------------------------------------- 1 | import outdent from 'outdent'; 2 | import {getTester} from './utils/test.mjs'; 3 | 4 | const {test} = getTester(import.meta); 5 | 6 | test.snapshot({ 7 | valid: [ 8 | 'new Set(foo).size', 9 | 'for (const foo of bar) console.log([...foo].length)', 10 | '[...new Set(array), foo].length', 11 | '[foo, ...new Set(array), ].length', 12 | '[...new Set(array)].notLength', 13 | '[...new Set(array)]?.length', 14 | '[...new Set(array)][length]', 15 | '[...new Set(array)]["length"]', 16 | '[...new NotSet(array)].length', 17 | '[...Set(array)].length', 18 | 'const foo = new NotSet([]);[...foo].length;', 19 | 'let foo = new Set([]);[...foo].length;', 20 | 'const {foo} = new Set([]);[...foo].length;', 21 | 'const [foo] = new Set([]);[...foo].length;', 22 | '[...foo].length', 23 | 'var foo = new Set(); var foo = new Set(); [...foo].length', 24 | ], 25 | invalid: [ 26 | '[...new Set(array)].length', 27 | outdent` 28 | const foo = new Set([]); 29 | console.log([...foo].length); 30 | `, 31 | outdent` 32 | function isUnique(array) { 33 | return[...new Set(array)].length === array.length 34 | } 35 | `, 36 | '[...new Set(array),].length', 37 | '[...(( new Set(array) ))].length', 38 | '(( [...new Set(array)] )).length', 39 | outdent` 40 | foo 41 | ;[...new Set(array)].length 42 | `, 43 | '[/* comment */...new Set(array)].length', 44 | '[...new /* comment */ Set(array)].length', 45 | ], 46 | }); 47 | -------------------------------------------------------------------------------- /test/prefer-string-trim-start-end.mjs: -------------------------------------------------------------------------------- 1 | import outdent from 'outdent'; 2 | import {getTester} from './utils/test.mjs'; 3 | 4 | const {test} = getTester(import.meta); 5 | 6 | test.snapshot({ 7 | valid: [ 8 | 'foo.trimStart()', 9 | 'foo.trimStart?.()', 10 | 'foo.trimEnd()', 11 | // Not `CallExpression` 12 | 'new foo.trimLeft();', 13 | // Not `MemberExpression` 14 | 'trimLeft();', 15 | // `callee.property` is not a `Identifier` 16 | 'foo[\'trimLeft\']();', 17 | // Computed 18 | 'foo[trimLeft]();', 19 | // Not `trimLeft`/`trimRight` 20 | 'foo.bar();', 21 | // More argument(s) 22 | 'foo.trimLeft(extra);', 23 | 'foo.trimLeft(...argumentsArray)', 24 | // `trimLeft` is in argument 25 | 'foo.bar(trimLeft)', 26 | 'foo.bar(foo.trimLeft)', 27 | // `trimLeft` is in `MemberExpression.object` 28 | 'trimLeft.foo()', 29 | 'foo.trimLeft.bar()', 30 | ], 31 | invalid: [ 32 | 'foo.trimLeft()', 33 | 'foo.trimRight()', 34 | 'trimLeft.trimRight()', 35 | 'foo.trimLeft.trimRight()', 36 | '"foo".trimLeft()', 37 | outdent` 38 | foo 39 | // comment 40 | .trimRight/* comment */( 41 | /* comment */ 42 | ) 43 | `, 44 | 'foo?.trimLeft()', 45 | ], 46 | }); 47 | -------------------------------------------------------------------------------- /test/require-number-to-fixed-digits-argument.mjs: -------------------------------------------------------------------------------- 1 | import {getTester} from './utils/test.mjs'; 2 | 3 | const {test} = getTester(import.meta); 4 | 5 | test.snapshot({ 6 | valid: [ 7 | 'number.toFixed(0)', 8 | 'number.toFixed(...[])', 9 | 'number.toFixed(2)', 10 | 'number.toFixed(1,2,3)', 11 | 'number[toFixed]()', 12 | 'number["toFixed"]()', 13 | 'number?.toFixed()', 14 | 'number.toFixed?.()', 15 | 'number.notToFixed();', 16 | 17 | // `callee` is a `NewExpression` 18 | 'new BigNumber(1).toFixed()', 19 | 'new Number(1).toFixed()', 20 | ], 21 | invalid: [ 22 | 'const string = number.toFixed();', 23 | 'const string = number.toFixed( /* comment */ );', 24 | 'Number(1).toFixed()', 25 | 26 | // False positive cases 27 | 'const bigNumber = new BigNumber(1); const string = bigNumber.toFixed();', 28 | ], 29 | }); 30 | -------------------------------------------------------------------------------- /test/require-post-message-target-origin.mjs: -------------------------------------------------------------------------------- 1 | import {getTester} from './utils/test.mjs'; 2 | 3 | const {test} = getTester(import.meta); 4 | 5 | test.snapshot({ 6 | valid: [ 7 | 'window.postMessage(message, targetOrigin)', 8 | 'postMessage(message)', 9 | 'window.postMessage', 10 | 'window.postMessage()', 11 | 'window.postMessage(message, targetOrigin, transfer)', 12 | 'window.postMessage(...message)', 13 | 'window[postMessage](message)', 14 | 'window["postMessage"](message)', 15 | 'window.notPostMessage(message)', 16 | 'window.postMessage?.(message)', 17 | 'window?.postMessage(message)', 18 | ], 19 | invalid: [ 20 | 'window.postMessage(message)', 21 | 'self.postMessage(message)', 22 | 'globalThis.postMessage(message)', 23 | 'foo.postMessage(message )', 24 | 'foo.postMessage( ((message)) )', 25 | 'foo.postMessage(message,)', 26 | 'foo.postMessage(message , )', 27 | 'foo.window.postMessage(message)', 28 | 'document.defaultView.postMessage(message)', 29 | 'getWindow().postMessage(message)', 30 | ], 31 | }); 32 | -------------------------------------------------------------------------------- /test/smoke/eslint-remote-tester.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {getRepositories, getPathIgnorePattern} = require('eslint-remote-tester-repositories'); 4 | 5 | module.exports = { 6 | /** Repositories to scan */ 7 | repositories: getRepositories({randomize: true}), 8 | 9 | /** Optional pattern used to exclude paths */ 10 | pathIgnorePattern: getPathIgnorePattern(), 11 | 12 | /** Extensions of files under scanning */ 13 | extensions: ['js', 'cjs', 'mjs', 'ts', 'cts', 'mts', 'jsx', 'tsx', 'vue'], 14 | 15 | /** Maximum amount of tasks ran concurrently */ 16 | concurrentTasks: 3, 17 | 18 | /** Optional boolean flag used to enable caching of cloned repositories. For CIs it's ideal to disable caching. Defaults to true. */ 19 | cache: false, 20 | 21 | /** Optional setting for log level. Valid values are verbose, info, warn, error. Defaults to verbose. */ 22 | logLevel: 'info', 23 | 24 | /** ESLint configuration */ 25 | eslintrc: { 26 | root: true, 27 | parser: '@typescript-eslint/parser', 28 | parserOptions: { 29 | ecmaFeatures: { 30 | jsx: true, 31 | }, 32 | project: [], 33 | }, 34 | extends: ['plugin:unicorn/all'], 35 | }, 36 | }; 37 | -------------------------------------------------------------------------------- /test/snapshots/better-regex.mjs.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `test/better-regex.mjs` 2 | 3 | The actual snapshot is saved in `better-regex.mjs.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## Invalid #1 8 | 1 | /(?!a)+/g 9 | 10 | > Error 1/1 11 | 12 | `␊ 13 | > 1 | /(?!a)+/g␊ 14 | | ^^^^^^^^^ Problem parsing /(?!a)+/g: ␊ 15 | ␊ 16 | /(?!a)+/g␊ 17 | ^␊ 18 | Unexpected token: "+" at 1:6.␊ 19 | ` 20 | -------------------------------------------------------------------------------- /test/snapshots/better-regex.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/better-regex.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/consistent-function-scoping.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/consistent-function-scoping.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/empty-brace-spaces.mjs.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `test/empty-brace-spaces.mjs` 2 | 3 | The actual snapshot is saved in `empty-brace-spaces.mjs.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## Invalid #1 8 | 1 | try { 9 | 2 | foo(); 10 | 3 | } catch (error) { 11 | 4 | 12 | 5 | } 13 | 14 | > Output 15 | 16 | `␊ 17 | 1 | try {␊ 18 | 2 | foo();␊ 19 | 3 | } catch (error) {}␊ 20 | ` 21 | 22 | > Error 1/1 23 | 24 | `␊ 25 | 1 | try {␊ 26 | 2 | foo();␊ 27 | > 3 | } catch (error) {␊ 28 | | ^␊ 29 | > 4 | ␊ 30 | | ^^^^^^^^␊ 31 | > 5 | }␊ 32 | | ^ Do not add spaces between braces.␊ 33 | ` 34 | -------------------------------------------------------------------------------- /test/snapshots/empty-brace-spaces.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/empty-brace-spaces.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/error-message.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/error-message.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/explicit-length-check.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/explicit-length-check.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/filename-case.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/filename-case.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/import-style.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/import-style.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/new-for-builtins.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/new-for-builtins.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-abusive-eslint-disable.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-abusive-eslint-disable.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-array-for-each.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-array-for-each.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-array-method-this-argument.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-array-method-this-argument.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-array-push-push.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-array-push-push.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-await-expression-member.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-await-expression-member.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-console-spaces.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-console-spaces.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-document-cookie.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-document-cookie.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-empty-file.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-empty-file.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-for-loop.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-for-loop.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-hex-escape.mjs.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `test/no-hex-escape.mjs` 2 | 3 | The actual snapshot is saved in `no-hex-escape.mjs.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## Invalid #1 8 | 1 | const foo = "\xb1" 9 | 10 | > Output 11 | 12 | `␊ 13 | 1 | const foo = "\\u00b1"␊ 14 | ` 15 | 16 | > Error 1/1 17 | 18 | `␊ 19 | > 1 | const foo = "\\xb1"␊ 20 | | ^^^^^^ Use Unicode escapes instead of hexadecimal escapes.␊ 21 | ` 22 | -------------------------------------------------------------------------------- /test/snapshots/no-hex-escape.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-hex-escape.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-instanceof-array.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-instanceof-array.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-invalid-remove-event-listener.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-invalid-remove-event-listener.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-lonely-if.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-lonely-if.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-negated-condition.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-negated-condition.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-nested-ternary.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-nested-ternary.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-new-array.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-new-array.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-new-buffer.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-new-buffer.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-object-as-default-parameter.mjs.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `test/no-object-as-default-parameter.mjs` 2 | 3 | The actual snapshot is saved in `no-object-as-default-parameter.mjs.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## Invalid #1 8 | 1 | function abc(foo = {a: 123}) {} 9 | 10 | > Error 1/1 11 | 12 | `␊ 13 | > 1 | function abc(foo = {a: 123}) {}␊ 14 | | ^^^ Do not use an object literal as default for parameter \`foo\`.␊ 15 | ` 16 | 17 | ## Invalid #2 18 | 1 | const abc = (foo = {a: false}) => {}; 19 | 20 | > Error 1/1 21 | 22 | `␊ 23 | > 1 | const abc = (foo = {a: false}) => {};␊ 24 | | ^^^ Do not use an object literal as default for parameter \`foo\`.␊ 25 | ` 26 | 27 | ## Invalid #3 28 | 1 | function abc({a} = {a: 123}) {} 29 | 30 | > Error 1/1 31 | 32 | `␊ 33 | > 1 | function abc({a} = {a: 123}) {}␊ 34 | | ^^^^^^^^ Do not use an object literal as default.␊ 35 | ` 36 | 37 | ## Invalid #4 38 | 1 | function abc([a] = {a: 123}) {} 39 | 40 | > Error 1/1 41 | 42 | `␊ 43 | > 1 | function abc([a] = {a: 123}) {}␊ 44 | | ^^^^^^^^ Do not use an object literal as default.␊ 45 | ` 46 | -------------------------------------------------------------------------------- /test/snapshots/no-object-as-default-parameter.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-object-as-default-parameter.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-process-exit.mjs.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `test/no-process-exit.mjs` 2 | 3 | The actual snapshot is saved in `no-process-exit.mjs.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## Invalid #1 8 | 1 | process.exit(1); 9 | 10 | > Error 1/1 11 | 12 | `␊ 13 | > 1 | process.exit(1);␊ 14 | | ^^^^^^^^^^^^^^^ Only use \`process.exit()\` in CLI apps. Throw an error instead.␊ 15 | ` 16 | -------------------------------------------------------------------------------- /test/snapshots/no-process-exit.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-process-exit.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-static-only-class.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-static-only-class.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-thenable.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-thenable.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-this-assignment.mjs.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `test/no-this-assignment.mjs` 2 | 3 | The actual snapshot is saved in `no-this-assignment.mjs.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## Invalid #1 8 | 1 | const foo = this; 9 | 10 | > Error 1/1 11 | 12 | `␊ 13 | > 1 | const foo = this;␊ 14 | | ^^^^^^^^^^ Do not assign \`this\` to \`foo\`.␊ 15 | ` 16 | 17 | ## Invalid #2 18 | 1 | let foo;foo = this; 19 | 20 | > Error 1/1 21 | 22 | `␊ 23 | > 1 | let foo;foo = this;␊ 24 | | ^^^^^^^^^^ Do not assign \`this\` to \`foo\`.␊ 25 | ` 26 | 27 | ## Invalid #3 28 | 1 | var foo = bar, baz = this; 29 | 30 | > Error 1/1 31 | 32 | `␊ 33 | > 1 | var foo = bar, baz = this;␊ 34 | | ^^^^^^^^^^ Do not assign \`this\` to \`baz\`.␊ 35 | ` 36 | -------------------------------------------------------------------------------- /test/snapshots/no-this-assignment.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-this-assignment.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-typeof-undefined.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-typeof-undefined.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-unnecessary-await.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-unnecessary-await.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-unreadable-array-destructuring.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-unreadable-array-destructuring.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-unreadable-iife.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-unreadable-iife.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-unsafe-regex.mjs.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `test/no-unsafe-regex.mjs` 2 | 3 | The actual snapshot is saved in `no-unsafe-regex.mjs.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## Invalid #1 8 | 1 | const foo = /(x+x+)+y/g 9 | 10 | > Error 1/1 11 | 12 | `␊ 13 | > 1 | const foo = /(x+x+)+y/g␊ 14 | | ^^^^^^^^^^^ Unsafe regular expression.␊ 15 | ` 16 | -------------------------------------------------------------------------------- /test/snapshots/no-unsafe-regex.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-unsafe-regex.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-unused-properties.mjs.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `test/no-unused-properties.mjs` 2 | 3 | The actual snapshot is saved in `no-unused-properties.mjs.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## Invalid #1 8 | 1 | function foo() { 9 | 2 | const bar = { 10 | 3 | b: 2, 11 | 4 | u: 3 12 | 5 | }; 13 | 6 | console.log(bar.b); 14 | 7 | } 15 | 16 | > Error 1/1 17 | 18 | `␊ 19 | 1 | function foo() {␊ 20 | 2 | const bar = {␊ 21 | 3 | b: 2,␊ 22 | > 4 | u: 3␊ 23 | | ^^^^ Property \`u\` is defined but never used.␊ 24 | 5 | };␊ 25 | 6 | console.log(bar.b);␊ 26 | 7 | }␊ 27 | ` 28 | -------------------------------------------------------------------------------- /test/snapshots/no-unused-properties.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-unused-properties.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-useless-fallback-in-spread.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-useless-fallback-in-spread.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-useless-length-check.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-useless-length-check.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-useless-spread.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-useless-spread.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-useless-switch-case.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-useless-switch-case.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-useless-undefined.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-useless-undefined.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/no-zero-fractions.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/no-zero-fractions.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/number-literal-case.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/number-literal-case.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/numeric-separators-style.mjs.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `test/numeric-separators-style.mjs` 2 | 3 | The actual snapshot is saved in `numeric-separators-style.mjs.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## Invalid #1 8 | 1 | console.log(0XdeEdBeeFn) 9 | 10 | > Output 11 | 12 | `␊ 13 | 1 | console.log(0Xde_Ed_Be_eFn)␊ 14 | ` 15 | 16 | > Error 1/1 17 | 18 | `␊ 19 | > 1 | console.log(0XdeEdBeeFn)␊ 20 | | ^^^^^^^^^^^ Invalid group length in numeric value.␊ 21 | ` 22 | 23 | ## Invalid #2 24 | 1 | const foo = 12345678..toString() 25 | 26 | > Output 27 | 28 | `␊ 29 | 1 | const foo = 12_345_678..toString()␊ 30 | ` 31 | 32 | > Error 1/1 33 | 34 | `␊ 35 | > 1 | const foo = 12345678..toString()␊ 36 | | ^^^^^^^^^ Invalid group length in numeric value.␊ 37 | ` 38 | -------------------------------------------------------------------------------- /test/snapshots/numeric-separators-style.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/numeric-separators-style.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-add-event-listener.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-add-event-listener.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-array-flat-map.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-array-flat-map.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-array-flat.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-array-flat.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-array-index-of.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-array-index-of.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-array-some.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-array-some.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-at.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-at.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-code-point.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-code-point.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-date-now.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-date-now.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-dom-node-dataset.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-dom-node-dataset.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-dom-node-text-content.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-dom-node-text-content.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-event-target.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-event-target.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-export-from.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-export-from.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-includes.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-includes.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-json-parse-buffer.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-json-parse-buffer.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-keyboard-event-key.mjs.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `test/prefer-keyboard-event-key.mjs` 2 | 3 | The actual snapshot is saved in `prefer-keyboard-event-key.mjs.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## Invalid #1 8 | 1 | window.addEventListener('click', ({which, another}) => { 9 | 2 | if (which === 23) { 10 | 3 | console.log('Wrong!') 11 | 4 | } 12 | 5 | }) 13 | 14 | > Error 1/1 15 | 16 | `␊ 17 | > 1 | window.addEventListener('click', ({which, another}) => {␊ 18 | | ^^^^^ Use \`.key\` instead of \`.which\`.␊ 19 | 2 | if (which === 23) {␊ 20 | 3 | console.log('Wrong!')␊ 21 | 4 | }␊ 22 | 5 | })␊ 23 | ` 24 | 25 | ## Invalid #2 26 | 1 | foo123.addEventListener('click', event => { 27 | 2 | if (event.keyCode === 27) { 28 | 3 | } 29 | 4 | }); 30 | 31 | > Output 32 | 33 | `␊ 34 | 1 | foo123.addEventListener('click', event => {␊ 35 | 2 | if (event.key === 'Escape') {␊ 36 | 3 | }␊ 37 | 4 | });␊ 38 | ` 39 | 40 | > Error 1/1 41 | 42 | `␊ 43 | 1 | foo123.addEventListener('click', event => {␊ 44 | > 2 | if (event.keyCode === 27) {␊ 45 | | ^^^^^^^ Use \`.key\` instead of \`.keyCode\`.␊ 46 | 3 | }␊ 47 | 4 | });␊ 48 | ` 49 | -------------------------------------------------------------------------------- /test/snapshots/prefer-keyboard-event-key.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-keyboard-event-key.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-logical-operator-over-ternary.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-logical-operator-over-ternary.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-math-trunc.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-math-trunc.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-modern-math-apis.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-modern-math-apis.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-module.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-module.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-native-coercion-functions.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-native-coercion-functions.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-negative-index.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-negative-index.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-node-protocol.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-node-protocol.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-number-properties.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-number-properties.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-object-from-entries.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-object-from-entries.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-optional-catch-binding.mjs.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `test/prefer-optional-catch-binding.mjs` 2 | 3 | The actual snapshot is saved in `prefer-optional-catch-binding.mjs.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## Invalid #1 8 | 1 | try {} catch ({}) {} 9 | 10 | > Output 11 | 12 | `␊ 13 | 1 | try {} catch {}␊ 14 | ` 15 | 16 | > Error 1/1 17 | 18 | `␊ 19 | > 1 | try {} catch ({}) {}␊ 20 | | ^^ Remove unused catch binding.␊ 21 | ` 22 | 23 | ## Invalid #2 24 | 1 | try {} catch ({message}) {} 25 | 26 | > Output 27 | 28 | `␊ 29 | 1 | try {} catch {}␊ 30 | ` 31 | 32 | > Error 1/1 33 | 34 | `␊ 35 | > 1 | try {} catch ({message}) {}␊ 36 | | ^^^^^^^^^ Remove unused catch binding.␊ 37 | ` 38 | 39 | ## Invalid #3 40 | 1 | try {} catch ({message: notUsedMessage}) {} 41 | 42 | > Output 43 | 44 | `␊ 45 | 1 | try {} catch {}␊ 46 | ` 47 | 48 | > Error 1/1 49 | 50 | `␊ 51 | > 1 | try {} catch ({message: notUsedMessage}) {}␊ 52 | | ^^^^^^^^^^^^^^^^^^^^^^^^^ Remove unused catch binding.␊ 53 | ` 54 | 55 | ## Invalid #4 56 | 1 | try {} catch ({cause: {message}}) {} 57 | 58 | > Output 59 | 60 | `␊ 61 | 1 | try {} catch {}␊ 62 | ` 63 | 64 | > Error 1/1 65 | 66 | `␊ 67 | > 1 | try {} catch ({cause: {message}}) {}␊ 68 | | ^^^^^^^^^^^^^^^^^^ Remove unused catch binding.␊ 69 | ` 70 | -------------------------------------------------------------------------------- /test/snapshots/prefer-optional-catch-binding.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-optional-catch-binding.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-prototype-methods.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-prototype-methods.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-query-selector.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-query-selector.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-regexp-test.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-regexp-test.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-set-has.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-set-has.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-set-size.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-set-size.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-spread.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-spread.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-string-replace-all.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-string-replace-all.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-string-slice.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-string-slice.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-string-starts-ends-with.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-string-starts-ends-with.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-string-trim-start-end.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-string-trim-start-end.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-switch.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-switch.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-top-level-await.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-top-level-await.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/prefer-type-error.mjs.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `test/prefer-type-error.mjs` 2 | 3 | The actual snapshot is saved in `prefer-type-error.mjs.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## Invalid #1 8 | 1 | if (!isFinite(foo)) { 9 | 2 | throw new Error(); 10 | 3 | } 11 | 12 | > Output 13 | 14 | `␊ 15 | 1 | if (!isFinite(foo)) {␊ 16 | 2 | throw new TypeError();␊ 17 | 3 | }␊ 18 | ` 19 | 20 | > Error 1/1 21 | 22 | `␊ 23 | 1 | if (!isFinite(foo)) {␊ 24 | > 2 | throw new Error();␊ 25 | | ^^^^^ \`new Error()\` is too unspecific for a type check. Use \`new TypeError()\` instead.␊ 26 | 3 | }␊ 27 | ` 28 | -------------------------------------------------------------------------------- /test/snapshots/prefer-type-error.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/prefer-type-error.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/relative-url-style.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/relative-url-style.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/require-array-join-separator.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/require-array-join-separator.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/require-number-to-fixed-digits-argument.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/require-number-to-fixed-digits-argument.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/require-post-message-target-origin.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/require-post-message-target-origin.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/switch-case-braces.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/switch-case-braces.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/template-indent.mjs.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `test/template-indent.mjs` 2 | 3 | The actual snapshot is saved in `template-indent.mjs.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## Invalid #1 8 | 1 | expect(foo).toMatchInlineSnapshot(` 9 | 2 | one 10 | 3 | three 11 | 4 | ` 12 | 5 | ) 13 | 14 | > Output 15 | 16 | `␊ 17 | 1 | expect(foo).toMatchInlineSnapshot(\`␊ 18 | 2 | one␊ 19 | 3 | three␊ 20 | 4 | \`␊ 21 | 5 | )␊ 22 | ` 23 | 24 | > Error 1/1 25 | 26 | `␊ 27 | > 1 | expect(foo).toMatchInlineSnapshot(\`␊ 28 | | ^␊ 29 | > 2 | one␊ 30 | | ^^^^^^^␊ 31 | > 3 | three␊ 32 | | ^^^^^^^␊ 33 | > 4 | \`␊ 34 | | ^^^^^^^^ Templates should be properly indented.␊ 35 | 5 | )␊ 36 | ` 37 | -------------------------------------------------------------------------------- /test/snapshots/template-indent.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/template-indent.mjs.snap -------------------------------------------------------------------------------- /test/snapshots/text-encoding-identifier-case.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/snapshots/text-encoding-identifier-case.mjs.snap -------------------------------------------------------------------------------- /test/text-encoding-identifier-case.mjs: -------------------------------------------------------------------------------- 1 | import {getTester} from './utils/test.mjs'; 2 | 3 | const {test} = getTester(import.meta); 4 | 5 | test.snapshot({ 6 | valid: [ 7 | '`UTF-8`', 8 | '"utf8"', 9 | '"utf+8"', 10 | '" utf8 "', 11 | '\'utf8\'', 12 | '"\\u0055tf8"', 13 | 'const ASCII = 1', 14 | 'const UTF8 = 1', 15 | ], 16 | invalid: [ 17 | '"UTF-8"', 18 | '"utf-8"', 19 | '\'utf-8\'', 20 | '"Utf8"', 21 | '"ASCII"', 22 | 'fs.readFile?.(file, "UTF-8")', 23 | 'fs?.readFile(file, "UTF-8")', 24 | 'readFile(file, "UTF-8")', 25 | 'fs.readFile(...file, "UTF-8")', 26 | 'new fs.readFile(file, "UTF-8")', 27 | 'fs.readFile(file, {encoding: "UTF-8"})', 28 | 'fs.readFile("UTF-8")', 29 | 'fs.readFile(file, "UTF-8", () => {})', 30 | 'fs.readFileSync(file, "UTF-8")', 31 | 'fs[readFile](file, "UTF-8")', 32 | 'fs["readFile"](file, "UTF-8")', 33 | 'await fs.readFile(file, "UTF-8",)', 34 | 'fs.promises.readFile(file, "UTF-8",)', 35 | 'whatever.readFile(file, "UTF-8",)', 36 | ], 37 | }); 38 | 39 | // JSX 40 | test.snapshot({ 41 | testerOptions: { 42 | parserOptions: { 43 | ecmaFeatures: { 44 | jsx: true, 45 | }, 46 | }, 47 | }, 48 | valid: [ 49 | '', 50 | '', 51 | ], 52 | invalid: [ 53 | '', 54 | '', 55 | '', 56 | '', 57 | ], 58 | }); 59 | -------------------------------------------------------------------------------- /test/unit/get-documentation-url.mjs: -------------------------------------------------------------------------------- 1 | import {createRequire} from 'node:module'; 2 | import url from 'node:url'; 3 | import test from 'ava'; 4 | import getDocumentationUrl from '../../rules/utils/get-documentation-url.js'; 5 | 6 | const require = createRequire(import.meta.url); 7 | const packageJson = require('../../package.json'); 8 | 9 | const filename = url.fileURLToPath(import.meta.url).replace(/\.mjs$/, '.js'); 10 | 11 | test('returns the URL of the a named rule\'s documentation', t => { 12 | const url = `https://github.com/sindresorhus/eslint-plugin-unicorn/blob/v${packageJson.version}/docs/rules/foo.md`; 13 | t.is(getDocumentationUrl('foo.js'), url); 14 | }); 15 | 16 | test('determines the rule name from the file', t => { 17 | const url = `https://github.com/sindresorhus/eslint-plugin-unicorn/blob/v${packageJson.version}/docs/rules/get-documentation-url.md`; 18 | t.is(getDocumentationUrl(filename), url); 19 | }); 20 | -------------------------------------------------------------------------------- /test/unit/snapshots/assert-token.mjs.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `test/unit/assert-token.mjs` 2 | 3 | The actual snapshot is saved in `assert-token.mjs.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## Error message 8 | 9 | > Snapshot 1 10 | 11 | Error { 12 | message: `Expected token '{"value":"expectedValue"}' or '{"type":"expectedType"}', got '{"value":"b","type":"a"}'.␊ 13 | Please open an issue at https://github.com/sindresorhus/eslint-plugin-unicorn/issues/new?title=%60test-rule%60%3A%20Unexpected%20token%20'%7B%22value%22%3A%22b%22%2C%22type%22%3A%22a%22%7D'.`, 14 | } 15 | -------------------------------------------------------------------------------- /test/unit/snapshots/assert-token.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlancer-lucas/eslint-plugin-unicorn/da8e2a79554469945dd50349611771548eeff453/test/unit/snapshots/assert-token.mjs.snap -------------------------------------------------------------------------------- /test/utils/default-options.mjs: -------------------------------------------------------------------------------- 1 | import eslintPluginUnicorn from '../../index.js'; 2 | 3 | const {env, parserOptions} = eslintPluginUnicorn.configs.recommended; 4 | 5 | const defaultOptions = { 6 | env: { 7 | node: true, 8 | browser: true, 9 | ...env, 10 | }, 11 | parserOptions, 12 | }; 13 | 14 | export default defaultOptions; 15 | -------------------------------------------------------------------------------- /test/utils/not-dom-node-types.mjs: -------------------------------------------------------------------------------- 1 | const notDomNodeTypes = [ 2 | // ArrayExpression 3 | '[]', 4 | '[element]', 5 | '[...elements]', 6 | // ArrowFunctionExpression 7 | '() => {}', 8 | // ClassExpression 9 | 'class Node {}', 10 | // FunctionExpression 11 | 'function() {}', 12 | // Literal 13 | '0', 14 | '1', 15 | '0.1', 16 | '""', 17 | '"string"', 18 | '/regex/', 19 | 'null', 20 | '0n', 21 | '1n', 22 | 'true', 23 | 'false', 24 | // ObjectExpression 25 | '{}', 26 | // TemplateLiteral 27 | '`templateLiteral`', 28 | // Undefined 29 | 'undefined', 30 | ]; 31 | 32 | export default notDomNodeTypes; 33 | -------------------------------------------------------------------------------- /test/utils/not-function-types.mjs: -------------------------------------------------------------------------------- 1 | const notFunctionTypes = [ 2 | // ArrayExpression 3 | '[]', 4 | '[element]', 5 | '[...elements]', 6 | // BinaryExpression 7 | '1 + fn', 8 | '"length" in fn', 9 | 'fn instanceof Function', 10 | // ClassExpression 11 | 'class ClassCantUseAsFunction {}', 12 | // Literal 13 | '0', 14 | '1', 15 | '0.1', 16 | '""', 17 | '"string"', 18 | '/regex/', 19 | 'null', 20 | '0n', 21 | '1n', 22 | 'true', 23 | 'false', 24 | // ObjectExpression 25 | '{}', 26 | // TemplateLiteral 27 | '`templateLiteral`', 28 | // Undefined 29 | 'undefined', 30 | // UnaryExpression 31 | '- fn', 32 | '+ fn', 33 | '~ fn', 34 | 'typeof fn', 35 | 'void fn', 36 | 'delete foo.fn', 37 | // UpdateExpression 38 | '++ fn', 39 | '-- fn', 40 | 41 | // Following are not safe 42 | 'a = fn', // Could be a function 43 | // 'await fn', // This requires async function to test, ignore for now 44 | 'fn()', // Could be a factory returns a function 45 | 'fn1 || fn2', // Could be a function 46 | 'new ClassReturnsFunction()', // `class` constructor could return a function 47 | 'new Function()', // `function` 48 | 'fn``', // Same as `CallExpression` 49 | 'this', // Could be a function 50 | ]; 51 | 52 | export default notFunctionTypes; 53 | --------------------------------------------------------------------------------