diff --git a/integration_test/__snapshots__/autoprefixer.spec.ts.snap b/integration_test/__snapshots__/autoprefixer.spec.ts.snap index c24d4fdd..c30e2524 100644 --- a/integration_test/__snapshots__/autoprefixer.spec.ts.snap +++ b/integration_test/__snapshots__/autoprefixer.spec.ts.snap @@ -9,10 +9,8 @@ exports[`autoprefixer > autoprefixer 1`] = ` }, "env": { "builtin": true, - "commonjs": true, "es2024": true, "node": true, - "shared-node-browser": true, }, "globals": { "document": "readonly", @@ -299,10 +297,8 @@ exports[`autoprefixer --js-plugins > autoprefixer--js-plugins 1`] = ` }, "env": { "builtin": true, - "commonjs": true, "es2024": true, "node": true, - "shared-node-browser": true, }, "globals": { "document": "readonly", @@ -733,10 +729,8 @@ exports[`autoprefixer --type-aware > autoprefixer--type-aware 1`] = ` }, "env": { "builtin": true, - "commonjs": true, "es2024": true, "node": true, - "shared-node-browser": true, }, "globals": { "document": "readonly", @@ -1024,10 +1018,8 @@ exports[`autoprefixer merge > autoprefixer--merge 1`] = ` }, "env": { "builtin": true, - "commonjs": true, "es2024": true, "node": true, - "shared-node-browser": true, }, "globals": { "document": "readonly", diff --git a/integration_test/__snapshots__/duplicate-overrides.spec.ts.snap b/integration_test/__snapshots__/duplicate-overrides.spec.ts.snap new file mode 100644 index 00000000..fe439025 --- /dev/null +++ b/integration_test/__snapshots__/duplicate-overrides.spec.ts.snap @@ -0,0 +1,153 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`duplicate-overrides > duplicate-overrides 1`] = ` +{ + "config": { + "$schema": "./node_modules/oxlint/configuration_schema.json", + "categories": { + "correctness": "off", + }, + "env": { + "builtin": true, + }, + "overrides": [ + { + "files": [ + "**/*.js", + "**/*.cjs", + "**/*.mjs", + ], + "rules": { + "no-unused-vars": "error", + }, + }, + { + "files": [ + "**/*.ts", + "**/*.mts", + "**/*.cts", + ], + "rules": { + "arrow-body-style": "error", + }, + }, + ], + "plugins": [], + }, + "warnings": [], +} +`; + +exports[`duplicate-overrides --js-plugins > duplicate-overrides--js-plugins 1`] = ` +{ + "config": { + "$schema": "./node_modules/oxlint/configuration_schema.json", + "categories": { + "correctness": "off", + }, + "env": { + "builtin": true, + }, + "overrides": [ + { + "files": [ + "**/*.js", + "**/*.cjs", + "**/*.mjs", + ], + "rules": { + "no-unused-vars": "error", + }, + }, + { + "files": [ + "**/*.ts", + "**/*.mts", + "**/*.cts", + ], + "rules": { + "arrow-body-style": "error", + }, + }, + ], + "plugins": [], + }, + "warnings": [], +} +`; + +exports[`duplicate-overrides --type-aware > duplicate-overrides--type-aware 1`] = ` +{ + "config": { + "$schema": "./node_modules/oxlint/configuration_schema.json", + "categories": { + "correctness": "off", + }, + "env": { + "builtin": true, + }, + "overrides": [ + { + "files": [ + "**/*.js", + "**/*.cjs", + "**/*.mjs", + ], + "rules": { + "no-unused-vars": "error", + }, + }, + { + "files": [ + "**/*.ts", + "**/*.mts", + "**/*.cts", + ], + "rules": { + "arrow-body-style": "error", + }, + }, + ], + "plugins": [], + }, + "warnings": [], +} +`; + +exports[`duplicate-overrides merge > duplicate-overrides--merge 1`] = ` +{ + "config": { + "$schema": "./node_modules/oxlint/configuration_schema.json", + "categories": { + "correctness": "error", + "perf": "error", + }, + "env": { + "builtin": true, + }, + "overrides": [ + { + "files": [ + "**/*.js", + "**/*.cjs", + "**/*.mjs", + ], + "rules": { + "no-unused-vars": "error", + }, + }, + { + "files": [ + "**/*.ts", + "**/*.mts", + "**/*.cts", + ], + "rules": { + "arrow-body-style": "error", + }, + }, + ], + }, + "warnings": [], +} +`; diff --git a/integration_test/__snapshots__/eslint-globals.spec.ts.snap b/integration_test/__snapshots__/eslint-globals.spec.ts.snap index adc22106..86c97759 100644 --- a/integration_test/__snapshots__/eslint-globals.spec.ts.snap +++ b/integration_test/__snapshots__/eslint-globals.spec.ts.snap @@ -14,10 +14,8 @@ exports[`eslint-globals > eslint-globals 1`] = ` { "env": { "browser": true, - "commonjs": true, "es2026": true, "node": true, - "shared-node-browser": true, }, "files": [ "**/*.js", @@ -28,6 +26,19 @@ exports[`eslint-globals > eslint-globals 1`] = ` "no-unused-vars": "error", }, }, + { + "env": { + "browser": true, + "serviceworker": true, + "worker": true, + }, + "files": [ + "**/*.ts", + ], + "rules": { + "arrow-body-style": "error", + }, + }, ], "plugins": [], }, @@ -49,10 +60,8 @@ exports[`eslint-globals --js-plugins > eslint-globals--js-plugins 1`] = ` { "env": { "browser": true, - "commonjs": true, "es2026": true, "node": true, - "shared-node-browser": true, }, "files": [ "**/*.js", @@ -63,6 +72,19 @@ exports[`eslint-globals --js-plugins > eslint-globals--js-plugins 1`] = ` "no-unused-vars": "error", }, }, + { + "env": { + "browser": true, + "serviceworker": true, + "worker": true, + }, + "files": [ + "**/*.ts", + ], + "rules": { + "arrow-body-style": "error", + }, + }, ], "plugins": [], }, @@ -84,10 +106,8 @@ exports[`eslint-globals --type-aware > eslint-globals--type-aware 1`] = ` { "env": { "browser": true, - "commonjs": true, "es2026": true, "node": true, - "shared-node-browser": true, }, "files": [ "**/*.js", @@ -98,6 +118,19 @@ exports[`eslint-globals --type-aware > eslint-globals--type-aware 1`] = ` "no-unused-vars": "error", }, }, + { + "env": { + "browser": true, + "serviceworker": true, + "worker": true, + }, + "files": [ + "**/*.ts", + ], + "rules": { + "arrow-body-style": "error", + }, + }, ], "plugins": [], }, @@ -120,10 +153,8 @@ exports[`eslint-globals merge > eslint-globals--merge 1`] = ` { "env": { "browser": true, - "commonjs": true, "es2026": true, "node": true, - "shared-node-browser": true, }, "files": [ "**/*.js", @@ -134,6 +165,19 @@ exports[`eslint-globals merge > eslint-globals--merge 1`] = ` "no-unused-vars": "error", }, }, + { + "env": { + "browser": true, + "serviceworker": true, + "worker": true, + }, + "files": [ + "**/*.ts", + ], + "rules": { + "arrow-body-style": "error", + }, + }, ], }, "warnings": [], diff --git a/integration_test/__snapshots__/many-extends.spec.ts.snap b/integration_test/__snapshots__/many-extends.spec.ts.snap new file mode 100644 index 00000000..e969d8d3 --- /dev/null +++ b/integration_test/__snapshots__/many-extends.spec.ts.snap @@ -0,0 +1,1294 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`many-extends.spec.ts > many-extends.spec.ts 1`] = ` +{ + "config": { + "$schema": "./node_modules/oxlint/configuration_schema.json", + "categories": { + "correctness": "off", + }, + "env": { + "builtin": true, + "es2018": true, + }, + "overrides": [ + { + "env": { + "node": true, + }, + "files": [ + "foo.js", + ], + "rules": { + "import/no-commonjs": "off", + }, + }, + { + "files": [ + "**/*.ts", + "**/*.tsx", + ], + "rules": { + "@typescript-eslint/adjacent-overload-signatures": "error", + "@typescript-eslint/array-type": "error", + "@typescript-eslint/ban-ts-comment": [ + "error", + { + "minimumDescriptionLength": 10, + }, + ], + "@typescript-eslint/ban-tslint-comment": "error", + "@typescript-eslint/consistent-generic-constructors": "error", + "@typescript-eslint/consistent-indexed-object-style": "error", + "@typescript-eslint/consistent-type-definitions": "error", + "@typescript-eslint/no-confusing-non-null-assertion": "error", + "@typescript-eslint/no-duplicate-enum-values": "error", + "@typescript-eslint/no-dynamic-delete": "error", + "@typescript-eslint/no-empty-object-type": "error", + "@typescript-eslint/no-explicit-any": "error", + "@typescript-eslint/no-extra-non-null-assertion": "error", + "@typescript-eslint/no-extraneous-class": "error", + "@typescript-eslint/no-inferrable-types": "error", + "@typescript-eslint/no-misused-new": "error", + "@typescript-eslint/no-namespace": "error", + "@typescript-eslint/no-non-null-asserted-nullish-coalescing": "error", + "@typescript-eslint/no-non-null-asserted-optional-chain": "error", + "@typescript-eslint/no-non-null-assertion": "error", + "@typescript-eslint/no-require-imports": "error", + "@typescript-eslint/no-this-alias": "error", + "@typescript-eslint/no-unnecessary-type-constraint": "error", + "@typescript-eslint/no-unsafe-declaration-merging": "error", + "@typescript-eslint/no-unsafe-function-type": "error", + "@typescript-eslint/no-wrapper-object-types": "error", + "@typescript-eslint/prefer-as-const": "error", + "@typescript-eslint/prefer-for-of": "error", + "@typescript-eslint/prefer-function-type": "error", + "@typescript-eslint/prefer-literal-enum-member": "error", + "@typescript-eslint/prefer-namespace-keyword": "error", + "@typescript-eslint/triple-slash-reference": "error", + "import/consistent-type-specifier-style": [ + "error", + "prefer-top-level", + ], + "import/no-default-export": "warn", + "jsdoc/check-tag-names": [ + "warn", + { + "typed": true, + }, + ], + "jsdoc/require-param": "off", + "jsdoc/require-param-type": "off", + "jsdoc/require-property-type": "off", + "jsdoc/require-returns": "off", + "jsdoc/require-returns-type": "off", + "no-array-constructor": "error", + "no-class-assign": "off", + "no-const-assign": "off", + "no-dupe-class-members": "off", + "no-dupe-keys": "off", + "no-empty-function": "error", + "no-func-assign": "off", + "no-import-assign": "off", + "no-new-native-nonconstructor": "off", + "no-obj-calls": "off", + "no-redeclare": "off", + "no-setter-return": "off", + "no-this-before-super": "off", + "no-throw-literal": "off", + "no-unsafe-negation": "off", + "no-unused-expressions": "error", + "no-unused-vars": "error", + "no-useless-constructor": "error", + "no-var": "error", + "no-with": "off", + "prefer-promise-reject-errors": "off", + "prefer-rest-params": "error", + "prefer-spread": "error", + "require-await": "off", + }, + }, + { + "files": [ + "**/*.ts", + "**/*.tsx", + ], + "plugins": [ + "typescript", + ], + }, + ], + "plugins": [ + "import", + "jsdoc", + "react", + ], + "rules": { + "for-direction": "error", + "import/default": "error", + "import/namespace": "error", + "import/no-duplicates": "warn", + "import/no-named-as-default": "warn", + "import/no-named-as-default-member": "warn", + "jsdoc/check-access": "warn", + "jsdoc/check-property-names": "warn", + "jsdoc/check-tag-names": "warn", + "jsdoc/empty-tags": "warn", + "jsdoc/implements-on-classes": "warn", + "jsdoc/no-defaults": "warn", + "jsdoc/require-param": "warn", + "jsdoc/require-param-description": "warn", + "jsdoc/require-param-name": "warn", + "jsdoc/require-param-type": "warn", + "jsdoc/require-property": "warn", + "jsdoc/require-property-description": "warn", + "jsdoc/require-property-name": "warn", + "jsdoc/require-property-type": "warn", + "jsdoc/require-returns": "warn", + "jsdoc/require-returns-description": "warn", + "jsdoc/require-returns-type": "warn", + "jsdoc/require-yields": "warn", + "no-async-promise-executor": "error", + "no-case-declarations": "error", + "no-class-assign": "error", + "no-compare-neg-zero": "error", + "no-cond-assign": "error", + "no-const-assign": "error", + "no-constant-binary-expression": "error", + "no-constant-condition": "error", + "no-control-regex": "error", + "no-debugger": "error", + "no-delete-var": "error", + "no-dupe-class-members": "error", + "no-dupe-else-if": "error", + "no-dupe-keys": "error", + "no-duplicate-case": "error", + "no-empty": "error", + "no-empty-character-class": "error", + "no-empty-pattern": "error", + "no-empty-static-block": "error", + "no-ex-assign": "error", + "no-extra-boolean-cast": "error", + "no-fallthrough": "error", + "no-func-assign": "error", + "no-global-assign": "error", + "no-import-assign": "error", + "no-invalid-regexp": "error", + "no-irregular-whitespace": "error", + "no-loss-of-precision": "error", + "no-new-native-nonconstructor": "error", + "no-nonoctal-decimal-escape": "error", + "no-obj-calls": "error", + "no-prototype-builtins": "error", + "no-redeclare": "error", + "no-regex-spaces": "error", + "no-self-assign": "error", + "no-setter-return": "error", + "no-shadow-restricted-names": "error", + "no-sparse-arrays": "error", + "no-this-before-super": "error", + "no-unexpected-multiline": "error", + "no-unsafe-finally": "error", + "no-unsafe-negation": "error", + "no-unsafe-optional-chaining": "error", + "no-unused-labels": "error", + "no-unused-private-class-members": "error", + "no-useless-backreference": "error", + "no-useless-catch": "error", + "no-useless-escape": "error", + "no-with": "error", + "react-hooks/exhaustive-deps": "warn", + "react-hooks/rules-of-hooks": "error", + "react/button-has-type": "error", + "react/jsx-key": "error", + "react/jsx-no-comment-textnodes": "error", + "react/jsx-no-duplicate-props": "error", + "react/jsx-no-target-blank": "error", + "react/jsx-no-undef": "error", + "react/no-children-prop": "error", + "react/no-danger-with-children": "error", + "react/no-direct-mutation-state": "error", + "react/no-find-dom-node": "error", + "react/no-is-mounted": "error", + "react/no-render-return-value": "error", + "react/no-string-refs": "error", + "react/no-unescaped-entities": "error", + "react/no-unknown-property": "error", + "require-yield": "error", + "use-isnan": "error", + "valid-typeof": "error", + }, + }, + "warnings": [ + "unsupported rule, but in development: constructor-super", + "unsupported rule, but in development: getter-return", + "unsupported rule: no-dupe-args", + "unsupported rule, but in development: no-misleading-character-class", + "unsupported rule: no-octal", + "unsupported rule, but in development: no-undef", + "unsupported rule, but in development: no-unreachable", + "unsupported rule: import/no-unresolved", + "unsupported rule, but in development: import/named", + "unsupported rule, but in development: import/export", + "unsupported rule: jsdoc/check-alignment", + "unsupported rule: jsdoc/check-param-names", + "unsupported rule: jsdoc/check-types", + "unsupported rule: jsdoc/check-values", + "unsupported rule: jsdoc/escape-inline-tags", + "unsupported rule: jsdoc/multiline-blocks", + "unsupported rule: jsdoc/no-multi-asterisks", + "unsupported rule: jsdoc/no-undefined-types", + "unsupported rule: jsdoc/reject-any-type", + "unsupported rule: jsdoc/reject-function-type", + "unsupported rule: jsdoc/require-jsdoc", + "unsupported rule: jsdoc/require-next-type", + "unsupported rule: jsdoc/require-returns-check", + "unsupported rule: jsdoc/require-throws-type", + "unsupported rule: jsdoc/require-yields-check", + "unsupported rule: jsdoc/require-yields-type", + "unsupported rule: jsdoc/tag-lines", + "unsupported rule: jsdoc/ts-no-empty-object-type", + "unsupported rule: jsdoc/valid-types", + "unsupported rule: react/display-name", + "unsupported rule: react/jsx-uses-react", + "unsupported rule: react/jsx-uses-vars", + "unsupported rule: react/no-deprecated", + "unsupported rule: react/prop-types", + "unsupported rule, but in development: react/require-render-return", + "unsupported rule: react-hooks/static-components", + "unsupported rule: react-hooks/use-memo", + "unsupported rule: react-hooks/component-hook-factories", + "unsupported rule: react-hooks/preserve-manual-memoization", + "unsupported rule: react-hooks/incompatible-library", + "unsupported rule: react-hooks/immutability", + "unsupported rule: react-hooks/globals", + "unsupported rule: react-hooks/refs", + "unsupported rule: react-hooks/set-state-in-effect", + "unsupported rule: react-hooks/error-boundaries", + "unsupported rule: react-hooks/purity", + "unsupported rule: react-hooks/set-state-in-render", + "unsupported rule: react-hooks/unsupported-syntax", + "unsupported rule: react-hooks/config", + "unsupported rule: react-hooks/gating", + "unsupported rule: prefer-const", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/await-thenable", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-array-delete", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-base-to-string", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-confusing-void-expression", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-deprecated", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-duplicate-type-constituents", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-floating-promises", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-for-in-array", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-implied-eval", + "unsupported rule: @typescript-eslint/no-invalid-void-type", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-meaningless-void-operator", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-misused-promises", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-misused-spread", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-mixed-enums", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-redundant-type-constituents", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unnecessary-boolean-literal-compare", + "unsupported rule: @typescript-eslint/no-unnecessary-condition", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unnecessary-template-expression", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unnecessary-type-arguments", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unnecessary-type-assertion", + "unsupported rule: @typescript-eslint/no-unnecessary-type-conversion", + "unsupported rule: @typescript-eslint/no-unnecessary-type-parameters", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unsafe-argument", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unsafe-assignment", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unsafe-call", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unsafe-enum-comparison", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unsafe-member-access", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unsafe-return", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unsafe-unary-minus", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/only-throw-error", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/prefer-promise-reject-errors", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/prefer-reduce-type-parameter", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/prefer-return-this-type", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/related-getter-setter-pairs", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/require-await", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/restrict-plus-operands", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/restrict-template-expressions", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/return-await", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/unbound-method", + "unsupported rule: @typescript-eslint/unified-signatures", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/use-unknown-in-catch-callback-variable", + "unsupported rule: @typescript-eslint/class-literal-property-style", + "unsupported rule: @typescript-eslint/consistent-type-assertions", + "unsupported rule: @typescript-eslint/dot-notation", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/non-nullable-type-assertion-style", + "unsupported rule: @typescript-eslint/prefer-find", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/prefer-includes", + "unsupported rule: @typescript-eslint/prefer-nullish-coalescing", + "unsupported rule: @typescript-eslint/prefer-optional-chain", + "unsupported rule: @typescript-eslint/prefer-regexp-exec", + "unsupported rule: @typescript-eslint/prefer-string-starts-ends-with", + "unsupported rule: jsdoc/no-types", + "unsupported rule: react/prefer-stateless-function", + "unsupported rule: react/function-component-definition", + ], +} +`; + +exports[`many-extends.spec.ts --js-plugins > many-extends.spec.ts--js-plugins 1`] = ` +{ + "config": { + "$schema": "./node_modules/oxlint/configuration_schema.json", + "categories": { + "correctness": "off", + }, + "env": { + "builtin": true, + "es2018": true, + }, + "overrides": [ + { + "env": { + "node": true, + }, + "files": [ + "foo.js", + ], + "rules": { + "import/no-commonjs": "off", + }, + }, + { + "files": [ + "**/*.ts", + "**/*.tsx", + ], + "rules": { + "@typescript-eslint/adjacent-overload-signatures": "error", + "@typescript-eslint/array-type": "error", + "@typescript-eslint/ban-ts-comment": [ + "error", + { + "minimumDescriptionLength": 10, + }, + ], + "@typescript-eslint/ban-tslint-comment": "error", + "@typescript-eslint/consistent-generic-constructors": "error", + "@typescript-eslint/consistent-indexed-object-style": "error", + "@typescript-eslint/consistent-type-definitions": "error", + "@typescript-eslint/no-confusing-non-null-assertion": "error", + "@typescript-eslint/no-duplicate-enum-values": "error", + "@typescript-eslint/no-dynamic-delete": "error", + "@typescript-eslint/no-empty-object-type": "error", + "@typescript-eslint/no-explicit-any": "error", + "@typescript-eslint/no-extra-non-null-assertion": "error", + "@typescript-eslint/no-extraneous-class": "error", + "@typescript-eslint/no-inferrable-types": "error", + "@typescript-eslint/no-misused-new": "error", + "@typescript-eslint/no-namespace": "error", + "@typescript-eslint/no-non-null-asserted-nullish-coalescing": "error", + "@typescript-eslint/no-non-null-asserted-optional-chain": "error", + "@typescript-eslint/no-non-null-assertion": "error", + "@typescript-eslint/no-require-imports": "error", + "@typescript-eslint/no-this-alias": "error", + "@typescript-eslint/no-unnecessary-type-constraint": "error", + "@typescript-eslint/no-unsafe-declaration-merging": "error", + "@typescript-eslint/no-unsafe-function-type": "error", + "@typescript-eslint/no-wrapper-object-types": "error", + "@typescript-eslint/prefer-as-const": "error", + "@typescript-eslint/prefer-for-of": "error", + "@typescript-eslint/prefer-function-type": "error", + "@typescript-eslint/prefer-literal-enum-member": "error", + "@typescript-eslint/prefer-namespace-keyword": "error", + "@typescript-eslint/triple-slash-reference": "error", + "import/consistent-type-specifier-style": [ + "error", + "prefer-top-level", + ], + "import/no-default-export": "warn", + "jsdoc/check-tag-names": [ + "warn", + { + "typed": true, + }, + ], + "jsdoc/require-param": "off", + "jsdoc/require-param-type": "off", + "jsdoc/require-property-type": "off", + "jsdoc/require-returns": "off", + "jsdoc/require-returns-type": "off", + "no-array-constructor": "error", + "no-class-assign": "off", + "no-const-assign": "off", + "no-dupe-class-members": "off", + "no-dupe-keys": "off", + "no-empty-function": "error", + "no-func-assign": "off", + "no-import-assign": "off", + "no-new-native-nonconstructor": "off", + "no-obj-calls": "off", + "no-redeclare": "off", + "no-setter-return": "off", + "no-this-before-super": "off", + "no-throw-literal": "off", + "no-unsafe-negation": "off", + "no-unused-expressions": "error", + "no-unused-vars": "error", + "no-useless-constructor": "error", + "no-var": "error", + "no-with": "off", + "prefer-promise-reject-errors": "off", + "prefer-rest-params": "error", + "prefer-spread": "error", + "require-await": "off", + }, + }, + { + "files": [ + "**/*.ts", + "**/*.tsx", + ], + "plugins": [ + "typescript", + ], + }, + ], + "plugins": [ + "import", + "jsdoc", + "react", + ], + "rules": { + "for-direction": "error", + "import/default": "error", + "import/namespace": "error", + "import/no-duplicates": "warn", + "import/no-named-as-default": "warn", + "import/no-named-as-default-member": "warn", + "jsdoc/check-access": "warn", + "jsdoc/check-property-names": "warn", + "jsdoc/check-tag-names": "warn", + "jsdoc/empty-tags": "warn", + "jsdoc/implements-on-classes": "warn", + "jsdoc/no-defaults": "warn", + "jsdoc/require-param": "warn", + "jsdoc/require-param-description": "warn", + "jsdoc/require-param-name": "warn", + "jsdoc/require-param-type": "warn", + "jsdoc/require-property": "warn", + "jsdoc/require-property-description": "warn", + "jsdoc/require-property-name": "warn", + "jsdoc/require-property-type": "warn", + "jsdoc/require-returns": "warn", + "jsdoc/require-returns-description": "warn", + "jsdoc/require-returns-type": "warn", + "jsdoc/require-yields": "warn", + "no-async-promise-executor": "error", + "no-case-declarations": "error", + "no-class-assign": "error", + "no-compare-neg-zero": "error", + "no-cond-assign": "error", + "no-const-assign": "error", + "no-constant-binary-expression": "error", + "no-constant-condition": "error", + "no-control-regex": "error", + "no-debugger": "error", + "no-delete-var": "error", + "no-dupe-class-members": "error", + "no-dupe-else-if": "error", + "no-dupe-keys": "error", + "no-duplicate-case": "error", + "no-empty": "error", + "no-empty-character-class": "error", + "no-empty-pattern": "error", + "no-empty-static-block": "error", + "no-ex-assign": "error", + "no-extra-boolean-cast": "error", + "no-fallthrough": "error", + "no-func-assign": "error", + "no-global-assign": "error", + "no-import-assign": "error", + "no-invalid-regexp": "error", + "no-irregular-whitespace": "error", + "no-loss-of-precision": "error", + "no-new-native-nonconstructor": "error", + "no-nonoctal-decimal-escape": "error", + "no-obj-calls": "error", + "no-prototype-builtins": "error", + "no-redeclare": "error", + "no-regex-spaces": "error", + "no-self-assign": "error", + "no-setter-return": "error", + "no-shadow-restricted-names": "error", + "no-sparse-arrays": "error", + "no-this-before-super": "error", + "no-unexpected-multiline": "error", + "no-unsafe-finally": "error", + "no-unsafe-negation": "error", + "no-unsafe-optional-chaining": "error", + "no-unused-labels": "error", + "no-unused-private-class-members": "error", + "no-useless-backreference": "error", + "no-useless-catch": "error", + "no-useless-escape": "error", + "no-with": "error", + "react-hooks/exhaustive-deps": "warn", + "react-hooks/rules-of-hooks": "error", + "react/button-has-type": "error", + "react/jsx-key": "error", + "react/jsx-no-comment-textnodes": "error", + "react/jsx-no-duplicate-props": "error", + "react/jsx-no-target-blank": "error", + "react/jsx-no-undef": "error", + "react/no-children-prop": "error", + "react/no-danger-with-children": "error", + "react/no-direct-mutation-state": "error", + "react/no-find-dom-node": "error", + "react/no-is-mounted": "error", + "react/no-render-return-value": "error", + "react/no-string-refs": "error", + "react/no-unescaped-entities": "error", + "react/no-unknown-property": "error", + "require-yield": "error", + "use-isnan": "error", + "valid-typeof": "error", + }, + }, + "warnings": [ + "unsupported rule, but in development: constructor-super", + "unsupported rule, but in development: getter-return", + "unsupported rule: no-dupe-args", + "unsupported rule, but in development: no-misleading-character-class", + "unsupported rule: no-octal", + "unsupported rule, but in development: no-undef", + "unsupported rule, but in development: no-unreachable", + "unsupported rule: import/no-unresolved", + "unsupported rule, but in development: import/named", + "unsupported rule, but in development: import/export", + "unsupported rule: jsdoc/check-alignment", + "unsupported rule: jsdoc/check-param-names", + "unsupported rule: jsdoc/check-types", + "unsupported rule: jsdoc/check-values", + "unsupported rule: jsdoc/escape-inline-tags", + "unsupported rule: jsdoc/multiline-blocks", + "unsupported rule: jsdoc/no-multi-asterisks", + "unsupported rule: jsdoc/no-undefined-types", + "unsupported rule: jsdoc/reject-any-type", + "unsupported rule: jsdoc/reject-function-type", + "unsupported rule: jsdoc/require-jsdoc", + "unsupported rule: jsdoc/require-next-type", + "unsupported rule: jsdoc/require-returns-check", + "unsupported rule: jsdoc/require-throws-type", + "unsupported rule: jsdoc/require-yields-check", + "unsupported rule: jsdoc/require-yields-type", + "unsupported rule: jsdoc/tag-lines", + "unsupported rule: jsdoc/ts-no-empty-object-type", + "unsupported rule: jsdoc/valid-types", + "unsupported rule: react/display-name", + "unsupported rule: react/jsx-uses-react", + "unsupported rule: react/jsx-uses-vars", + "unsupported rule: react/no-deprecated", + "unsupported rule: react/prop-types", + "unsupported rule, but in development: react/require-render-return", + "unsupported rule: react-hooks/static-components", + "unsupported rule: react-hooks/use-memo", + "unsupported rule: react-hooks/component-hook-factories", + "unsupported rule: react-hooks/preserve-manual-memoization", + "unsupported rule: react-hooks/incompatible-library", + "unsupported rule: react-hooks/immutability", + "unsupported rule: react-hooks/globals", + "unsupported rule: react-hooks/refs", + "unsupported rule: react-hooks/set-state-in-effect", + "unsupported rule: react-hooks/error-boundaries", + "unsupported rule: react-hooks/purity", + "unsupported rule: react-hooks/set-state-in-render", + "unsupported rule: react-hooks/unsupported-syntax", + "unsupported rule: react-hooks/config", + "unsupported rule: react-hooks/gating", + "unsupported rule: prefer-const", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/await-thenable", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-array-delete", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-base-to-string", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-confusing-void-expression", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-deprecated", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-duplicate-type-constituents", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-floating-promises", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-for-in-array", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-implied-eval", + "unsupported rule: @typescript-eslint/no-invalid-void-type", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-meaningless-void-operator", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-misused-promises", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-misused-spread", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-mixed-enums", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-redundant-type-constituents", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unnecessary-boolean-literal-compare", + "unsupported rule: @typescript-eslint/no-unnecessary-condition", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unnecessary-template-expression", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unnecessary-type-arguments", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unnecessary-type-assertion", + "unsupported rule: @typescript-eslint/no-unnecessary-type-conversion", + "unsupported rule: @typescript-eslint/no-unnecessary-type-parameters", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unsafe-argument", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unsafe-assignment", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unsafe-call", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unsafe-enum-comparison", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unsafe-member-access", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unsafe-return", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unsafe-unary-minus", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/only-throw-error", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/prefer-promise-reject-errors", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/prefer-reduce-type-parameter", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/prefer-return-this-type", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/related-getter-setter-pairs", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/require-await", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/restrict-plus-operands", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/restrict-template-expressions", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/return-await", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/unbound-method", + "unsupported rule: @typescript-eslint/unified-signatures", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/use-unknown-in-catch-callback-variable", + "unsupported rule: @typescript-eslint/class-literal-property-style", + "unsupported rule: @typescript-eslint/consistent-type-assertions", + "unsupported rule: @typescript-eslint/dot-notation", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/non-nullable-type-assertion-style", + "unsupported rule: @typescript-eslint/prefer-find", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/prefer-includes", + "unsupported rule: @typescript-eslint/prefer-nullish-coalescing", + "unsupported rule: @typescript-eslint/prefer-optional-chain", + "unsupported rule: @typescript-eslint/prefer-regexp-exec", + "unsupported rule: @typescript-eslint/prefer-string-starts-ends-with", + "unsupported rule: jsdoc/no-types", + "unsupported rule: react/prefer-stateless-function", + "unsupported rule: react/function-component-definition", + ], +} +`; + +exports[`many-extends.spec.ts --type-aware > many-extends.spec.ts--type-aware 1`] = ` +{ + "config": { + "$schema": "./node_modules/oxlint/configuration_schema.json", + "categories": { + "correctness": "off", + }, + "env": { + "builtin": true, + "es2018": true, + }, + "overrides": [ + { + "env": { + "node": true, + }, + "files": [ + "foo.js", + ], + "rules": { + "import/no-commonjs": "off", + }, + }, + { + "files": [ + "**/*.ts", + "**/*.tsx", + ], + "rules": { + "@typescript-eslint/adjacent-overload-signatures": "error", + "@typescript-eslint/array-type": "error", + "@typescript-eslint/await-thenable": "error", + "@typescript-eslint/ban-ts-comment": [ + "error", + { + "minimumDescriptionLength": 10, + }, + ], + "@typescript-eslint/ban-tslint-comment": "error", + "@typescript-eslint/consistent-generic-constructors": "error", + "@typescript-eslint/consistent-indexed-object-style": "error", + "@typescript-eslint/consistent-type-definitions": "error", + "@typescript-eslint/no-array-delete": "error", + "@typescript-eslint/no-base-to-string": "error", + "@typescript-eslint/no-confusing-non-null-assertion": "error", + "@typescript-eslint/no-confusing-void-expression": "error", + "@typescript-eslint/no-deprecated": "error", + "@typescript-eslint/no-duplicate-enum-values": "error", + "@typescript-eslint/no-duplicate-type-constituents": "error", + "@typescript-eslint/no-dynamic-delete": "error", + "@typescript-eslint/no-empty-object-type": "error", + "@typescript-eslint/no-explicit-any": "error", + "@typescript-eslint/no-extra-non-null-assertion": "error", + "@typescript-eslint/no-extraneous-class": "error", + "@typescript-eslint/no-floating-promises": "error", + "@typescript-eslint/no-for-in-array": "error", + "@typescript-eslint/no-implied-eval": "error", + "@typescript-eslint/no-inferrable-types": "error", + "@typescript-eslint/no-meaningless-void-operator": "error", + "@typescript-eslint/no-misused-new": "error", + "@typescript-eslint/no-misused-promises": "error", + "@typescript-eslint/no-misused-spread": "error", + "@typescript-eslint/no-mixed-enums": "error", + "@typescript-eslint/no-namespace": "error", + "@typescript-eslint/no-non-null-asserted-nullish-coalescing": "error", + "@typescript-eslint/no-non-null-asserted-optional-chain": "error", + "@typescript-eslint/no-non-null-assertion": "error", + "@typescript-eslint/no-redundant-type-constituents": "error", + "@typescript-eslint/no-require-imports": "error", + "@typescript-eslint/no-this-alias": "error", + "@typescript-eslint/no-unnecessary-boolean-literal-compare": "error", + "@typescript-eslint/no-unnecessary-template-expression": "error", + "@typescript-eslint/no-unnecessary-type-arguments": "error", + "@typescript-eslint/no-unnecessary-type-assertion": "error", + "@typescript-eslint/no-unnecessary-type-constraint": "error", + "@typescript-eslint/no-unsafe-argument": "error", + "@typescript-eslint/no-unsafe-assignment": "error", + "@typescript-eslint/no-unsafe-call": "error", + "@typescript-eslint/no-unsafe-declaration-merging": "error", + "@typescript-eslint/no-unsafe-enum-comparison": "error", + "@typescript-eslint/no-unsafe-function-type": "error", + "@typescript-eslint/no-unsafe-member-access": "error", + "@typescript-eslint/no-unsafe-return": "error", + "@typescript-eslint/no-unsafe-unary-minus": "error", + "@typescript-eslint/no-wrapper-object-types": "error", + "@typescript-eslint/non-nullable-type-assertion-style": "error", + "@typescript-eslint/only-throw-error": "error", + "@typescript-eslint/prefer-as-const": "error", + "@typescript-eslint/prefer-for-of": "error", + "@typescript-eslint/prefer-function-type": "error", + "@typescript-eslint/prefer-includes": "error", + "@typescript-eslint/prefer-literal-enum-member": "error", + "@typescript-eslint/prefer-namespace-keyword": "error", + "@typescript-eslint/prefer-promise-reject-errors": "error", + "@typescript-eslint/prefer-reduce-type-parameter": "error", + "@typescript-eslint/prefer-return-this-type": "error", + "@typescript-eslint/related-getter-setter-pairs": "error", + "@typescript-eslint/require-await": "error", + "@typescript-eslint/restrict-plus-operands": [ + "error", + { + "allowAny": false, + "allowBoolean": false, + "allowNullish": false, + "allowNumberAndString": false, + "allowRegExp": false, + }, + ], + "@typescript-eslint/restrict-template-expressions": [ + "error", + { + "allowAny": false, + "allowBoolean": false, + "allowNever": false, + "allowNullish": false, + "allowNumber": false, + "allowRegExp": false, + }, + ], + "@typescript-eslint/return-await": [ + "error", + "error-handling-correctness-only", + ], + "@typescript-eslint/triple-slash-reference": "error", + "@typescript-eslint/unbound-method": "error", + "@typescript-eslint/use-unknown-in-catch-callback-variable": "error", + "import/consistent-type-specifier-style": [ + "error", + "prefer-top-level", + ], + "import/no-default-export": "warn", + "jsdoc/check-tag-names": [ + "warn", + { + "typed": true, + }, + ], + "jsdoc/require-param": "off", + "jsdoc/require-param-type": "off", + "jsdoc/require-property-type": "off", + "jsdoc/require-returns": "off", + "jsdoc/require-returns-type": "off", + "no-array-constructor": "error", + "no-class-assign": "off", + "no-const-assign": "off", + "no-dupe-class-members": "off", + "no-dupe-keys": "off", + "no-empty-function": "error", + "no-func-assign": "off", + "no-import-assign": "off", + "no-new-native-nonconstructor": "off", + "no-obj-calls": "off", + "no-redeclare": "off", + "no-setter-return": "off", + "no-this-before-super": "off", + "no-throw-literal": "off", + "no-unsafe-negation": "off", + "no-unused-expressions": "error", + "no-unused-vars": "error", + "no-useless-constructor": "error", + "no-var": "error", + "no-with": "off", + "prefer-promise-reject-errors": "off", + "prefer-rest-params": "error", + "prefer-spread": "error", + "require-await": "off", + }, + }, + { + "files": [ + "**/*.ts", + "**/*.tsx", + ], + "plugins": [ + "typescript", + ], + }, + ], + "plugins": [ + "import", + "jsdoc", + "react", + ], + "rules": { + "for-direction": "error", + "import/default": "error", + "import/namespace": "error", + "import/no-duplicates": "warn", + "import/no-named-as-default": "warn", + "import/no-named-as-default-member": "warn", + "jsdoc/check-access": "warn", + "jsdoc/check-property-names": "warn", + "jsdoc/check-tag-names": "warn", + "jsdoc/empty-tags": "warn", + "jsdoc/implements-on-classes": "warn", + "jsdoc/no-defaults": "warn", + "jsdoc/require-param": "warn", + "jsdoc/require-param-description": "warn", + "jsdoc/require-param-name": "warn", + "jsdoc/require-param-type": "warn", + "jsdoc/require-property": "warn", + "jsdoc/require-property-description": "warn", + "jsdoc/require-property-name": "warn", + "jsdoc/require-property-type": "warn", + "jsdoc/require-returns": "warn", + "jsdoc/require-returns-description": "warn", + "jsdoc/require-returns-type": "warn", + "jsdoc/require-yields": "warn", + "no-async-promise-executor": "error", + "no-case-declarations": "error", + "no-class-assign": "error", + "no-compare-neg-zero": "error", + "no-cond-assign": "error", + "no-const-assign": "error", + "no-constant-binary-expression": "error", + "no-constant-condition": "error", + "no-control-regex": "error", + "no-debugger": "error", + "no-delete-var": "error", + "no-dupe-class-members": "error", + "no-dupe-else-if": "error", + "no-dupe-keys": "error", + "no-duplicate-case": "error", + "no-empty": "error", + "no-empty-character-class": "error", + "no-empty-pattern": "error", + "no-empty-static-block": "error", + "no-ex-assign": "error", + "no-extra-boolean-cast": "error", + "no-fallthrough": "error", + "no-func-assign": "error", + "no-global-assign": "error", + "no-import-assign": "error", + "no-invalid-regexp": "error", + "no-irregular-whitespace": "error", + "no-loss-of-precision": "error", + "no-new-native-nonconstructor": "error", + "no-nonoctal-decimal-escape": "error", + "no-obj-calls": "error", + "no-prototype-builtins": "error", + "no-redeclare": "error", + "no-regex-spaces": "error", + "no-self-assign": "error", + "no-setter-return": "error", + "no-shadow-restricted-names": "error", + "no-sparse-arrays": "error", + "no-this-before-super": "error", + "no-unexpected-multiline": "error", + "no-unsafe-finally": "error", + "no-unsafe-negation": "error", + "no-unsafe-optional-chaining": "error", + "no-unused-labels": "error", + "no-unused-private-class-members": "error", + "no-useless-backreference": "error", + "no-useless-catch": "error", + "no-useless-escape": "error", + "no-with": "error", + "react-hooks/exhaustive-deps": "warn", + "react-hooks/rules-of-hooks": "error", + "react/button-has-type": "error", + "react/jsx-key": "error", + "react/jsx-no-comment-textnodes": "error", + "react/jsx-no-duplicate-props": "error", + "react/jsx-no-target-blank": "error", + "react/jsx-no-undef": "error", + "react/no-children-prop": "error", + "react/no-danger-with-children": "error", + "react/no-direct-mutation-state": "error", + "react/no-find-dom-node": "error", + "react/no-is-mounted": "error", + "react/no-render-return-value": "error", + "react/no-string-refs": "error", + "react/no-unescaped-entities": "error", + "react/no-unknown-property": "error", + "require-yield": "error", + "use-isnan": "error", + "valid-typeof": "error", + }, + }, + "warnings": [ + "unsupported rule, but in development: constructor-super", + "unsupported rule, but in development: getter-return", + "unsupported rule: no-dupe-args", + "unsupported rule, but in development: no-misleading-character-class", + "unsupported rule: no-octal", + "unsupported rule, but in development: no-undef", + "unsupported rule, but in development: no-unreachable", + "unsupported rule: import/no-unresolved", + "unsupported rule, but in development: import/named", + "unsupported rule, but in development: import/export", + "unsupported rule: jsdoc/check-alignment", + "unsupported rule: jsdoc/check-param-names", + "unsupported rule: jsdoc/check-types", + "unsupported rule: jsdoc/check-values", + "unsupported rule: jsdoc/escape-inline-tags", + "unsupported rule: jsdoc/multiline-blocks", + "unsupported rule: jsdoc/no-multi-asterisks", + "unsupported rule: jsdoc/no-undefined-types", + "unsupported rule: jsdoc/reject-any-type", + "unsupported rule: jsdoc/reject-function-type", + "unsupported rule: jsdoc/require-jsdoc", + "unsupported rule: jsdoc/require-next-type", + "unsupported rule: jsdoc/require-returns-check", + "unsupported rule: jsdoc/require-throws-type", + "unsupported rule: jsdoc/require-yields-check", + "unsupported rule: jsdoc/require-yields-type", + "unsupported rule: jsdoc/tag-lines", + "unsupported rule: jsdoc/ts-no-empty-object-type", + "unsupported rule: jsdoc/valid-types", + "unsupported rule: react/display-name", + "unsupported rule: react/jsx-uses-react", + "unsupported rule: react/jsx-uses-vars", + "unsupported rule: react/no-deprecated", + "unsupported rule: react/prop-types", + "unsupported rule, but in development: react/require-render-return", + "unsupported rule: react-hooks/static-components", + "unsupported rule: react-hooks/use-memo", + "unsupported rule: react-hooks/component-hook-factories", + "unsupported rule: react-hooks/preserve-manual-memoization", + "unsupported rule: react-hooks/incompatible-library", + "unsupported rule: react-hooks/immutability", + "unsupported rule: react-hooks/globals", + "unsupported rule: react-hooks/refs", + "unsupported rule: react-hooks/set-state-in-effect", + "unsupported rule: react-hooks/error-boundaries", + "unsupported rule: react-hooks/purity", + "unsupported rule: react-hooks/set-state-in-render", + "unsupported rule: react-hooks/unsupported-syntax", + "unsupported rule: react-hooks/config", + "unsupported rule: react-hooks/gating", + "unsupported rule: prefer-const", + "unsupported rule: @typescript-eslint/no-invalid-void-type", + "unsupported rule: @typescript-eslint/no-unnecessary-condition", + "unsupported rule: @typescript-eslint/no-unnecessary-type-conversion", + "unsupported rule: @typescript-eslint/no-unnecessary-type-parameters", + "unsupported rule: @typescript-eslint/unified-signatures", + "unsupported rule: @typescript-eslint/class-literal-property-style", + "unsupported rule: @typescript-eslint/consistent-type-assertions", + "unsupported rule: @typescript-eslint/dot-notation", + "unsupported rule: @typescript-eslint/prefer-find", + "unsupported rule: @typescript-eslint/prefer-nullish-coalescing", + "unsupported rule: @typescript-eslint/prefer-optional-chain", + "unsupported rule: @typescript-eslint/prefer-regexp-exec", + "unsupported rule: @typescript-eslint/prefer-string-starts-ends-with", + "unsupported rule: jsdoc/no-types", + "unsupported rule: react/prefer-stateless-function", + "unsupported rule: react/function-component-definition", + ], +} +`; + +exports[`many-extends.spec.ts merge > many-extends.spec.ts--merge 1`] = ` +{ + "config": { + "$schema": "./node_modules/oxlint/configuration_schema.json", + "categories": { + "correctness": "error", + "perf": "error", + }, + "env": { + "builtin": true, + "es2018": true, + }, + "overrides": [ + { + "env": { + "node": true, + }, + "files": [ + "foo.js", + ], + "rules": { + "import/no-commonjs": "off", + }, + }, + { + "files": [ + "**/*.ts", + "**/*.tsx", + ], + "rules": { + "@typescript-eslint/adjacent-overload-signatures": "error", + "@typescript-eslint/array-type": "error", + "@typescript-eslint/ban-ts-comment": [ + "error", + { + "minimumDescriptionLength": 10, + }, + ], + "@typescript-eslint/ban-tslint-comment": "error", + "@typescript-eslint/consistent-generic-constructors": "error", + "@typescript-eslint/consistent-indexed-object-style": "error", + "@typescript-eslint/consistent-type-definitions": "error", + "@typescript-eslint/no-confusing-non-null-assertion": "error", + "@typescript-eslint/no-duplicate-enum-values": "error", + "@typescript-eslint/no-dynamic-delete": "error", + "@typescript-eslint/no-empty-object-type": "error", + "@typescript-eslint/no-explicit-any": "error", + "@typescript-eslint/no-extra-non-null-assertion": "error", + "@typescript-eslint/no-extraneous-class": "error", + "@typescript-eslint/no-inferrable-types": "error", + "@typescript-eslint/no-misused-new": "error", + "@typescript-eslint/no-namespace": "error", + "@typescript-eslint/no-non-null-asserted-nullish-coalescing": "error", + "@typescript-eslint/no-non-null-asserted-optional-chain": "error", + "@typescript-eslint/no-non-null-assertion": "error", + "@typescript-eslint/no-require-imports": "error", + "@typescript-eslint/no-this-alias": "error", + "@typescript-eslint/no-unnecessary-type-constraint": "error", + "@typescript-eslint/no-unsafe-declaration-merging": "error", + "@typescript-eslint/no-unsafe-function-type": "error", + "@typescript-eslint/no-wrapper-object-types": "error", + "@typescript-eslint/prefer-as-const": "error", + "@typescript-eslint/prefer-for-of": "error", + "@typescript-eslint/prefer-function-type": "error", + "@typescript-eslint/prefer-literal-enum-member": "error", + "@typescript-eslint/prefer-namespace-keyword": "error", + "@typescript-eslint/triple-slash-reference": "error", + "import/consistent-type-specifier-style": [ + "error", + "prefer-top-level", + ], + "import/no-default-export": "warn", + "jsdoc/check-tag-names": [ + "warn", + { + "typed": true, + }, + ], + "jsdoc/require-param": "off", + "jsdoc/require-param-type": "off", + "jsdoc/require-property-type": "off", + "jsdoc/require-returns": "off", + "jsdoc/require-returns-type": "off", + "no-array-constructor": "error", + "no-class-assign": "off", + "no-const-assign": "off", + "no-dupe-class-members": "off", + "no-dupe-keys": "off", + "no-empty-function": "error", + "no-func-assign": "off", + "no-import-assign": "off", + "no-new-native-nonconstructor": "off", + "no-obj-calls": "off", + "no-redeclare": "off", + "no-setter-return": "off", + "no-this-before-super": "off", + "no-throw-literal": "off", + "no-unsafe-negation": "off", + "no-unused-expressions": "error", + "no-unused-vars": "error", + "no-useless-constructor": "error", + "no-var": "error", + "no-with": "off", + "prefer-promise-reject-errors": "off", + "prefer-rest-params": "error", + "prefer-spread": "error", + "react/jsx-key": "error", + "react/jsx-no-duplicate-props": "error", + "react/jsx-no-undef": "error", + "react/no-children-prop": "error", + "react/no-danger-with-children": "error", + "react/no-direct-mutation-state": "error", + "react/no-find-dom-node": "error", + "react/no-is-mounted": "error", + "react/no-render-return-value": "error", + "react/no-string-refs": "error", + "react/react-in-jsx-scope": "off", + "require-await": "off", + }, + }, + { + "files": [ + "**/*.ts", + "**/*.tsx", + ], + "plugins": [ + "typescript", + ], + }, + ], + "plugins": [ + "import", + "jsdoc", + "react", + ], + "rules": { + "import/no-duplicates": "warn", + "import/no-named-as-default": "warn", + "import/no-named-as-default-member": "warn", + "jsdoc/check-access": "warn", + "jsdoc/check-property-names": "warn", + "jsdoc/check-tag-names": "warn", + "jsdoc/empty-tags": "warn", + "jsdoc/implements-on-classes": "warn", + "jsdoc/no-defaults": "warn", + "jsdoc/require-param": "warn", + "jsdoc/require-param-description": "warn", + "jsdoc/require-param-name": "warn", + "jsdoc/require-param-type": "warn", + "jsdoc/require-property": "warn", + "jsdoc/require-property-description": "warn", + "jsdoc/require-property-name": "warn", + "jsdoc/require-property-type": "warn", + "jsdoc/require-returns": "warn", + "jsdoc/require-returns-description": "warn", + "jsdoc/require-returns-type": "warn", + "jsdoc/require-yields": "warn", + "no-case-declarations": "error", + "no-empty": "error", + "no-fallthrough": "error", + "no-prototype-builtins": "error", + "no-redeclare": "error", + "no-regex-spaces": "error", + "no-unexpected-multiline": "error", + "react-hooks/exhaustive-deps": "warn", + "react-hooks/rules-of-hooks": "error", + "react/button-has-type": "error", + "react/jsx-no-comment-textnodes": "error", + "react/jsx-no-target-blank": "error", + "react/no-unescaped-entities": "error", + "react/no-unknown-property": "error", + "react/react-in-jsx-scope": "error", + }, + }, + "warnings": [ + "unsupported rule, but in development: constructor-super", + "unsupported rule, but in development: getter-return", + "unsupported rule: no-dupe-args", + "unsupported rule, but in development: no-misleading-character-class", + "unsupported rule: no-octal", + "unsupported rule, but in development: no-undef", + "unsupported rule, but in development: no-unreachable", + "unsupported rule: import/no-unresolved", + "unsupported rule, but in development: import/named", + "unsupported rule, but in development: import/export", + "unsupported rule: jsdoc/check-alignment", + "unsupported rule: jsdoc/check-param-names", + "unsupported rule: jsdoc/check-types", + "unsupported rule: jsdoc/check-values", + "unsupported rule: jsdoc/escape-inline-tags", + "unsupported rule: jsdoc/multiline-blocks", + "unsupported rule: jsdoc/no-multi-asterisks", + "unsupported rule: jsdoc/no-undefined-types", + "unsupported rule: jsdoc/reject-any-type", + "unsupported rule: jsdoc/reject-function-type", + "unsupported rule: jsdoc/require-jsdoc", + "unsupported rule: jsdoc/require-next-type", + "unsupported rule: jsdoc/require-returns-check", + "unsupported rule: jsdoc/require-throws-type", + "unsupported rule: jsdoc/require-yields-check", + "unsupported rule: jsdoc/require-yields-type", + "unsupported rule: jsdoc/tag-lines", + "unsupported rule: jsdoc/ts-no-empty-object-type", + "unsupported rule: jsdoc/valid-types", + "unsupported rule: react/display-name", + "unsupported rule: react/jsx-uses-react", + "unsupported rule: react/jsx-uses-vars", + "unsupported rule: react/no-deprecated", + "unsupported rule: react/prop-types", + "unsupported rule, but in development: react/require-render-return", + "unsupported rule: react-hooks/static-components", + "unsupported rule: react-hooks/use-memo", + "unsupported rule: react-hooks/component-hook-factories", + "unsupported rule: react-hooks/preserve-manual-memoization", + "unsupported rule: react-hooks/incompatible-library", + "unsupported rule: react-hooks/immutability", + "unsupported rule: react-hooks/globals", + "unsupported rule: react-hooks/refs", + "unsupported rule: react-hooks/set-state-in-effect", + "unsupported rule: react-hooks/error-boundaries", + "unsupported rule: react-hooks/purity", + "unsupported rule: react-hooks/set-state-in-render", + "unsupported rule: react-hooks/unsupported-syntax", + "unsupported rule: react-hooks/config", + "unsupported rule: react-hooks/gating", + "unsupported rule: prefer-const", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/await-thenable", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-array-delete", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-base-to-string", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-confusing-void-expression", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-deprecated", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-duplicate-type-constituents", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-floating-promises", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-for-in-array", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-implied-eval", + "unsupported rule: @typescript-eslint/no-invalid-void-type", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-meaningless-void-operator", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-misused-promises", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-misused-spread", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-mixed-enums", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-redundant-type-constituents", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unnecessary-boolean-literal-compare", + "unsupported rule: @typescript-eslint/no-unnecessary-condition", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unnecessary-template-expression", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unnecessary-type-arguments", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unnecessary-type-assertion", + "unsupported rule: @typescript-eslint/no-unnecessary-type-conversion", + "unsupported rule: @typescript-eslint/no-unnecessary-type-parameters", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unsafe-argument", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unsafe-assignment", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unsafe-call", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unsafe-enum-comparison", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unsafe-member-access", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unsafe-return", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/no-unsafe-unary-minus", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/only-throw-error", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/prefer-promise-reject-errors", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/prefer-reduce-type-parameter", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/prefer-return-this-type", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/related-getter-setter-pairs", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/require-await", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/restrict-plus-operands", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/restrict-template-expressions", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/return-await", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/unbound-method", + "unsupported rule: @typescript-eslint/unified-signatures", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/use-unknown-in-catch-callback-variable", + "unsupported rule: @typescript-eslint/class-literal-property-style", + "unsupported rule: @typescript-eslint/consistent-type-assertions", + "unsupported rule: @typescript-eslint/dot-notation", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/non-nullable-type-assertion-style", + "unsupported rule: @typescript-eslint/prefer-find", + "type-aware rule detected, but \`--type-aware\` is not enabled: @typescript-eslint/prefer-includes", + "unsupported rule: @typescript-eslint/prefer-nullish-coalescing", + "unsupported rule: @typescript-eslint/prefer-optional-chain", + "unsupported rule: @typescript-eslint/prefer-regexp-exec", + "unsupported rule: @typescript-eslint/prefer-string-starts-ends-with", + "unsupported rule: jsdoc/no-types", + "unsupported rule: react/prefer-stateless-function", + "unsupported rule: react/function-component-definition", + ], +} +`; diff --git a/integration_test/__snapshots__/next-eslint-config-project.spec.ts.snap b/integration_test/__snapshots__/next-eslint-config-project.spec.ts.snap index 46ea0c76..2f90b7a9 100644 --- a/integration_test/__snapshots__/next-eslint-config-project.spec.ts.snap +++ b/integration_test/__snapshots__/next-eslint-config-project.spec.ts.snap @@ -20,9 +20,7 @@ exports[`next-eslint-config-project > next-eslint-config-project 1`] = ` { "env": { "browser": true, - "commonjs": true, "node": true, - "shared-node-browser": true, }, "files": [ "**/*.{js,jsx,mjs,ts,tsx,mts,cts}", @@ -141,9 +139,7 @@ exports[`next-eslint-config-project --js-plugins > next-eslint-config-project--j { "env": { "browser": true, - "commonjs": true, "node": true, - "shared-node-browser": true, }, "files": [ "**/*.{js,jsx,mjs,ts,tsx,mts,cts}", @@ -262,9 +258,7 @@ exports[`next-eslint-config-project --type-aware > next-eslint-config-project--t { "env": { "browser": true, - "commonjs": true, "node": true, - "shared-node-browser": true, }, "files": [ "**/*.{js,jsx,mjs,ts,tsx,mts,cts}", @@ -384,9 +378,7 @@ exports[`next-eslint-config-project merge > next-eslint-config-project--merge 1` { "env": { "browser": true, - "commonjs": true, "node": true, - "shared-node-browser": true, }, "files": [ "**/*.{js,jsx,mjs,ts,tsx,mts,cts}", diff --git a/integration_test/__snapshots__/nuxt-auth.spec.ts.snap b/integration_test/__snapshots__/nuxt-auth.spec.ts.snap index 99030c6f..821f654e 100644 --- a/integration_test/__snapshots__/nuxt-auth.spec.ts.snap +++ b/integration_test/__snapshots__/nuxt-auth.spec.ts.snap @@ -10,10 +10,8 @@ exports[`nuxt-auth > nuxt-auth 1`] = ` "env": { "browser": true, "builtin": true, - "commonjs": true, "es2026": true, "node": true, - "shared-node-browser": true, }, "globals": { "computed": "readonly", @@ -879,10 +877,8 @@ exports[`nuxt-auth --js-plugins > nuxt-auth--js-plugins 1`] = ` "env": { "browser": true, "builtin": true, - "commonjs": true, "es2026": true, "node": true, - "shared-node-browser": true, }, "globals": { "computed": "readonly", @@ -2151,10 +2147,8 @@ exports[`nuxt-auth --type-aware > nuxt-auth--type-aware 1`] = ` "env": { "browser": true, "builtin": true, - "commonjs": true, "es2026": true, "node": true, - "shared-node-browser": true, }, "globals": { "computed": "readonly", @@ -3021,10 +3015,8 @@ exports[`nuxt-auth merge > nuxt-auth--merge 1`] = ` "env": { "browser": true, "builtin": true, - "commonjs": true, "es2026": true, "node": true, - "shared-node-browser": true, }, "globals": { "computed": "readonly", diff --git a/integration_test/__snapshots__/pupeeteer.spec.ts.snap b/integration_test/__snapshots__/pupeeteer.spec.ts.snap index 94650845..3d2ceaaf 100644 --- a/integration_test/__snapshots__/pupeeteer.spec.ts.snap +++ b/integration_test/__snapshots__/pupeeteer.spec.ts.snap @@ -9,10 +9,8 @@ exports[`puppeteer > puppeteer 1`] = ` }, "env": { "builtin": true, - "commonjs": true, "es2026": true, "node": true, - "shared-node-browser": true, }, "ignorePatterns": [ "**/node_modules", @@ -136,22 +134,6 @@ exports[`puppeteer > puppeteer 1`] = ` "typescript", ], }, - { - "files": [ - "**/*.ts", - ], - "plugins": [ - "typescript", - ], - }, - { - "files": [ - "**/*.ts", - ], - "plugins": [ - "typescript", - ], - }, { "files": [ "packages/puppeteer-core/src/**/*.ts", @@ -299,10 +281,8 @@ exports[`puppeteer --js-plugins > puppeteer--js-plugins 1`] = ` }, "env": { "builtin": true, - "commonjs": true, "es2026": true, "node": true, - "shared-node-browser": true, }, "ignorePatterns": [ "**/node_modules", @@ -433,14 +413,6 @@ exports[`puppeteer --js-plugins > puppeteer--js-plugins 1`] = ` "typescript", ], }, - { - "files": [ - "**/*.ts", - ], - "plugins": [ - "typescript", - ], - }, { "files": [ "**/*.ts", @@ -612,10 +584,8 @@ exports[`puppeteer --type-aware > puppeteer--type-aware 1`] = ` }, "env": { "builtin": true, - "commonjs": true, "es2026": true, "node": true, - "shared-node-browser": true, }, "ignorePatterns": [ "**/node_modules", @@ -751,22 +721,6 @@ exports[`puppeteer --type-aware > puppeteer--type-aware 1`] = ` "typescript", ], }, - { - "files": [ - "**/*.ts", - ], - "plugins": [ - "typescript", - ], - }, - { - "files": [ - "**/*.ts", - ], - "plugins": [ - "typescript", - ], - }, { "files": [ "packages/puppeteer-core/src/**/*.ts", @@ -924,10 +878,8 @@ exports[`puppeteer merge > puppeteer--merge 1`] = ` }, "env": { "builtin": true, - "commonjs": true, "es2026": true, "node": true, - "shared-node-browser": true, }, "ignorePatterns": [ "**/node_modules", @@ -1051,22 +1003,6 @@ exports[`puppeteer merge > puppeteer--merge 1`] = ` "typescript", ], }, - { - "files": [ - "**/*.ts", - ], - "plugins": [ - "typescript", - ], - }, - { - "files": [ - "**/*.ts", - ], - "plugins": [ - "typescript", - ], - }, { "files": [ "packages/puppeteer-core/src/**/*.ts", diff --git a/integration_test/__snapshots__/typescript.spec.ts.snap b/integration_test/__snapshots__/typescript.spec.ts.snap index bed684bd..e8be60e2 100644 --- a/integration_test/__snapshots__/typescript.spec.ts.snap +++ b/integration_test/__snapshots__/typescript.spec.ts.snap @@ -9,9 +9,7 @@ exports[`typescript > typescript 1`] = ` }, "env": { "builtin": true, - "commonjs": true, "node": true, - "shared-node-browser": true, }, "ignorePatterns": [ "**/node_modules/**", @@ -376,9 +374,7 @@ exports[`typescript --js-plugins > typescript--js-plugins 1`] = ` }, "env": { "builtin": true, - "commonjs": true, "node": true, - "shared-node-browser": true, }, "ignorePatterns": [ "**/node_modules/**", @@ -746,9 +742,7 @@ exports[`typescript --type-aware > typescript--type-aware 1`] = ` }, "env": { "builtin": true, - "commonjs": true, "node": true, - "shared-node-browser": true, }, "ignorePatterns": [ "**/node_modules/**", @@ -1115,9 +1109,7 @@ exports[`typescript merge > typescript--merge 1`] = ` }, "env": { "builtin": true, - "commonjs": true, "node": true, - "shared-node-browser": true, }, "ignorePatterns": [ "**/node_modules/**", diff --git a/integration_test/duplicate-overrides.spec.ts b/integration_test/duplicate-overrides.spec.ts new file mode 100644 index 00000000..f96c486e --- /dev/null +++ b/integration_test/duplicate-overrides.spec.ts @@ -0,0 +1,4 @@ +import duplicate_overrides_test from './projects/duplicate-overrides-eslint.config.js'; +import { testProject } from './utils.js'; + +testProject('duplicate-overrides', duplicate_overrides_test); diff --git a/integration_test/many-extends.spec.ts b/integration_test/many-extends.spec.ts new file mode 100644 index 00000000..7a59ac98 --- /dev/null +++ b/integration_test/many-extends.spec.ts @@ -0,0 +1,5 @@ +// @ts-expect-error +import many_extends_test from './projects/many-extends.config.mjs'; +import { testProject } from './utils.js'; + +testProject('many-extends.spec.ts', many_extends_test); diff --git a/integration_test/projects/duplicate-overrides-eslint.config.ts b/integration_test/projects/duplicate-overrides-eslint.config.ts new file mode 100644 index 00000000..83e08d9a --- /dev/null +++ b/integration_test/projects/duplicate-overrides-eslint.config.ts @@ -0,0 +1,26 @@ +import { defineConfig } from 'eslint/config'; + +export default defineConfig([ + { + files: ['**/*.js', '**/*.cjs', '**/*.mjs'], + rules: { + 'no-unused-vars': 'error', + }, + }, + // These intentionally have identical rulesets to test merging when converting, this can happen in some complex + // configs, but it's easier to understand with an arbitrary example like this. + // As long as the plugins and rules and globals are all the same, these can + // be safely merged by merging the file globs. + { + files: ['**/*.ts'], + rules: { + 'arrow-body-style': 'error', + }, + }, + { + files: ['**/*.mts', '**/*.cts'], + rules: { + 'arrow-body-style': 'error', + }, + }, +]); diff --git a/integration_test/projects/eslint-globals.config.ts b/integration_test/projects/eslint-globals.config.ts index 03ccd297..6bee87c6 100644 --- a/integration_test/projects/eslint-globals.config.ts +++ b/integration_test/projects/eslint-globals.config.ts @@ -20,4 +20,19 @@ export default defineConfig([ 'no-unused-vars': 'error', }, }, + { + files: ['**/*.ts'], + languageOptions: { + globals: { + ...globals.browser, + ...globals.worker, + ...Object.fromEntries( + Object.entries(globals.serviceworker).slice(0, -5) + ), + }, + }, + rules: { + 'arrow-body-style': 'error', + }, + }, ]); diff --git a/integration_test/projects/many-extends.config.mjs b/integration_test/projects/many-extends.config.mjs new file mode 100644 index 00000000..c72511f6 --- /dev/null +++ b/integration_test/projects/many-extends.config.mjs @@ -0,0 +1,89 @@ +// @ts-check + +import js from '@eslint/js'; +import importPlugin from 'eslint-plugin-import'; +import jsdoc from 'eslint-plugin-jsdoc'; +import react from 'eslint-plugin-react'; +import reactHooks from 'eslint-plugin-react-hooks'; +import globals from 'globals'; +import tseslint from 'typescript-eslint'; + +/** @type {import('typescript-eslint').ConfigArray} */ +export const baseConfig = [ + js.configs.recommended, + importPlugin.flatConfigs.recommended, + jsdoc.configs['flat/recommended'], + { + linterOptions: { + reportUnusedDisableDirectives: 'error', + reportUnusedInlineConfigs: 'error', + }, + rules: { + 'no-unused-vars': 'off', + }, + }, +]; + +export default tseslint.config([ + baseConfig, + react.configs.flat.recommended, + react.configs.flat['jsx-runtime'], + reactHooks.configs.flat.recommended, + importPlugin.flatConfigs.react, + { + rules: { + 'react/button-has-type': 'error', + }, + }, + { + files: ['foo.js'], + + languageOptions: { + globals: { + ...globals.commonjs, + ...globals.node, + }, + }, + + rules: { + 'import/no-commonjs': 'off', + }, + }, + { + files: ['**/*.ts', '**/*.tsx'], + + extends: [ + tseslint.configs.strictTypeChecked, + tseslint.configs.stylisticTypeChecked, + react.configs.flat.recommended, + react.configs.flat['jsx-runtime'], + reactHooks.configs.flat.recommended, + importPlugin.flatConfigs.react, + importPlugin.flatConfigs.typescript, + jsdoc.configs['flat/recommended-typescript'], + ], + + languageOptions: { + parserOptions: { + projectService: true, + }, + }, + + rules: { + 'import/consistent-type-specifier-style': ['error', 'prefer-top-level'], + 'import/no-default-export': 'warn', + + 'jsdoc/require-jsdoc': 'off', + 'jsdoc/require-param': 'off', + 'jsdoc/require-returns': 'off', + + 'react/prefer-stateless-function': 'warn', + 'react/function-component-definition': [ + 'error', + { + namedComponents: 'arrow-function', + }, + ], + }, + }, +]); diff --git a/integration_test/projects/typescript.eslint.config.mjs b/integration_test/projects/typescript.eslint.config.mjs index 83c68cd5..0b2bab1e 100644 --- a/integration_test/projects/typescript.eslint.config.mjs +++ b/integration_test/projects/typescript.eslint.config.mjs @@ -9,8 +9,6 @@ import url from 'url'; const __filename = url.fileURLToPath(new URL(import.meta.url)); const __dirname = path.dirname(__filename); -// const rulesDir = path.join(__dirname, 'scripts', 'eslint', 'rules'); -// const ruleFiles = fs.readdirSync(rulesDir).filter((p) => p.endsWith(ext)); const ruleFiles = []; export default tseslint.config( @@ -145,7 +143,6 @@ export default tseslint.config( leadingUnderscore: 'allow', filter: { regex: '^[A-Za-z]+_[A-Za-z]+$', match: false }, }, - // eslint-disable-next-line no-restricted-syntax { selector: 'property', format: null }, ], diff --git a/package.json b/package.json index 46da885c..9b8f5f3b 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "lint-staged": "^16.1.2", "next": "^16.0.0", "oxlint": "^1.29.0", - "oxlint-tsgolint": "^0.5.1", + "oxlint-tsgolint": "^0.8.0", "prettier": "^3.6.1", "typescript": "^5.8.3", "typescript-eslint": "^8.35.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ac6558fb..9fb56d8b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -119,10 +119,10 @@ importers: version: 16.0.3(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) oxlint: specifier: ^1.29.0 - version: 1.29.0(oxlint-tsgolint@0.5.1) + version: 1.29.0(oxlint-tsgolint@0.8.0) oxlint-tsgolint: - specifier: ^0.5.1 - version: 0.5.1 + specifier: ^0.8.0 + version: 0.8.0 prettier: specifier: ^3.6.1 version: 3.6.2 @@ -981,33 +981,33 @@ packages: '@oxc-project/types@0.97.0': resolution: {integrity: sha512-lxmZK4xFrdvU0yZiDwgVQTCvh2gHWBJCBk5ALsrtsBWhs0uDIi+FTOnXRQeQfs304imdvTdaakT/lqwQ8hkOXQ==} - '@oxlint-tsgolint/darwin-arm64@0.5.1': - resolution: {integrity: sha512-UufHg95w3mto7D78QJkrBfxJlTBSQYj/6nAgfK8L/hBXMEt79VfEF2SyjByiDd98/QWqMygEt5b9rnfFtwLgRQ==} + '@oxlint-tsgolint/darwin-arm64@0.8.0': + resolution: {integrity: sha512-k9b+gy8F2X2uDvjn2WwlHFlI0rfMY7h2B+G/fq7xLcLF6rktR2vvNiJrIkGZ+bRIbQIi3kBSU1/w2Q/Uei/mGA==} cpu: [arm64] os: [darwin] - '@oxlint-tsgolint/darwin-x64@0.5.1': - resolution: {integrity: sha512-BlDTfMxx9SV7rDsW5on74zG0pHs5iomeynfjdigqRb7BULpoJRXKW3vgRvnkOgNE5qqV2KLduAuC0gBFj+hJRw==} + '@oxlint-tsgolint/darwin-x64@0.8.0': + resolution: {integrity: sha512-KPAHaKWIb9zXGwjK/9LWmW46PczearH0q6e/ZY7NFz0GBIgO00tJ/ufPp7UXQy67TfkuWSq7kg11ciiNrLMONA==} cpu: [x64] os: [darwin] - '@oxlint-tsgolint/linux-arm64@0.5.1': - resolution: {integrity: sha512-O5R+HVBS2jCrO99Tv/SeUn5ExlYMhH10X2gzLq7W2F9Jl6PuhZvAKr+pSOLkbJArgLpn20glM6Q2E9lT3ckBQQ==} + '@oxlint-tsgolint/linux-arm64@0.8.0': + resolution: {integrity: sha512-my+nslIogIlOxYOHwkFstnpaSb4JVMx3/8lnXmYKyH8GGl/zizD2M3Szbr3OGtZUPfUhJiG5R6hYjVAiHoEf8w==} cpu: [arm64] os: [linux] - '@oxlint-tsgolint/linux-x64@0.5.1': - resolution: {integrity: sha512-TuzApYs7FFawPlQYePYmtNdf518EsuEUzbOTDb7OUYrepNbBXAgsilyiUoyRCd/NLcThh5JYXCW72yjTEmnBWw==} + '@oxlint-tsgolint/linux-x64@0.8.0': + resolution: {integrity: sha512-3lnwHrivrqgeaY3NO4bxMpFoLQmwDxASTKkvNwob5trRmowhPfPOXqIns2KcdWADHIXBZLJWVA9RMGuix/gybQ==} cpu: [x64] os: [linux] - '@oxlint-tsgolint/win32-arm64@0.5.1': - resolution: {integrity: sha512-MxPYy8PCjobXuw6Z6yh+oHIYjlUIipZHvWU+6FX6vbwZn89iBLkJcw2Umzte/ceYZ7rPYNKbjCdD+ZY084US9g==} + '@oxlint-tsgolint/win32-arm64@0.8.0': + resolution: {integrity: sha512-tnEu/DcU17OtO1jExuo0VqMJ0tb2kr6I/IbdlC+ZbvitpavSPX/B1aEpmoJHtj8EQr6jykWD3LcGmuj+KRWgww==} cpu: [arm64] os: [win32] - '@oxlint-tsgolint/win32-x64@0.5.1': - resolution: {integrity: sha512-gGZafigorGdySViM5M1KIJOgsaYP9U6Fu2AoGosJiOwfPBBCJNo9KLrb2XyUJD08CPxK4wHdb0oBeOLm4IXgkQ==} + '@oxlint-tsgolint/win32-x64@0.8.0': + resolution: {integrity: sha512-Cj1N5s24Ikm0lAtMBwBYeElZyrfaB69QN6Gr7SzBgpSMwwYXm8e1nDH1XR/CQgraBJAwGS9hSeyuF13B9MO6+Q==} cpu: [x64] os: [win32] @@ -3071,8 +3071,8 @@ packages: resolution: {integrity: sha512-gxUfidyxJY97BJ+JEN/PxiIxIU1Y1FAPyMTncgNymgd/Cb+TYprsXZqjVnVCmTUlIBoA1XVjbfP0+Iz+uAt7Ow==} engines: {node: ^20.19.0 || >=22.12.0} - oxlint-tsgolint@0.5.1: - resolution: {integrity: sha512-C1MgHZRjs1uWJzRybwxlqHm2qvv8KrX0sEfZIaIWphJ9FJG5aUqEkGGa2Oug48hUgNbFgNB+KUpO7d8+7raAZQ==} + oxlint-tsgolint@0.8.0: + resolution: {integrity: sha512-uKMeYxrKOyFUOYGSZ7tf7DZ/+RT/BEiuIT9saQHJ3bTPPSutoaxZfHPEyl09IZrGq5PVVMHupKVuzwTiz2KsgA==} hasBin: true oxlint@1.29.0: @@ -4510,22 +4510,22 @@ snapshots: '@oxc-project/types@0.97.0': {} - '@oxlint-tsgolint/darwin-arm64@0.5.1': + '@oxlint-tsgolint/darwin-arm64@0.8.0': optional: true - '@oxlint-tsgolint/darwin-x64@0.5.1': + '@oxlint-tsgolint/darwin-x64@0.8.0': optional: true - '@oxlint-tsgolint/linux-arm64@0.5.1': + '@oxlint-tsgolint/linux-arm64@0.8.0': optional: true - '@oxlint-tsgolint/linux-x64@0.5.1': + '@oxlint-tsgolint/linux-x64@0.8.0': optional: true - '@oxlint-tsgolint/win32-arm64@0.5.1': + '@oxlint-tsgolint/win32-arm64@0.8.0': optional: true - '@oxlint-tsgolint/win32-x64@0.5.1': + '@oxlint-tsgolint/win32-x64@0.8.0': optional: true '@oxlint/darwin-arm64@1.29.0': @@ -6999,16 +6999,16 @@ snapshots: '@oxc-parser/binding-win32-arm64-msvc': 0.97.0 '@oxc-parser/binding-win32-x64-msvc': 0.97.0 - oxlint-tsgolint@0.5.1: + oxlint-tsgolint@0.8.0: optionalDependencies: - '@oxlint-tsgolint/darwin-arm64': 0.5.1 - '@oxlint-tsgolint/darwin-x64': 0.5.1 - '@oxlint-tsgolint/linux-arm64': 0.5.1 - '@oxlint-tsgolint/linux-x64': 0.5.1 - '@oxlint-tsgolint/win32-arm64': 0.5.1 - '@oxlint-tsgolint/win32-x64': 0.5.1 - - oxlint@1.29.0(oxlint-tsgolint@0.5.1): + '@oxlint-tsgolint/darwin-arm64': 0.8.0 + '@oxlint-tsgolint/darwin-x64': 0.8.0 + '@oxlint-tsgolint/linux-arm64': 0.8.0 + '@oxlint-tsgolint/linux-x64': 0.8.0 + '@oxlint-tsgolint/win32-arm64': 0.8.0 + '@oxlint-tsgolint/win32-x64': 0.8.0 + + oxlint@1.29.0(oxlint-tsgolint@0.8.0): optionalDependencies: '@oxlint/darwin-arm64': 1.29.0 '@oxlint/darwin-x64': 1.29.0 @@ -7018,7 +7018,7 @@ snapshots: '@oxlint/linux-x64-musl': 1.29.0 '@oxlint/win32-arm64': 1.29.0 '@oxlint/win32-x64': 1.29.0 - oxlint-tsgolint: 0.5.1 + oxlint-tsgolint: 0.8.0 p-limit@3.1.0: dependencies: diff --git a/src/cleanup.test.ts b/src/cleanup.test.ts new file mode 100644 index 00000000..f3b7f2e7 --- /dev/null +++ b/src/cleanup.test.ts @@ -0,0 +1,214 @@ +import { describe, it, expect } from 'vitest'; +import { cleanUpOxlintConfig } from './cleanup.js'; +import type { OxlintConfig } from './types.js'; + +describe('cleanUpOxlintConfig', () => { + describe('overrides cleanup', () => { + it('should remove empty overrides array', () => { + const config: OxlintConfig = { + overrides: [], + }; + cleanUpOxlintConfig(config); + expect(config.overrides).toBeUndefined(); + }); + + it('should remove overrides with only one key (files)', () => { + const config: OxlintConfig = { + overrides: [{ files: ['*.ts'] }], + }; + cleanUpOxlintConfig(config); + expect(config.overrides).toBeUndefined(); + }); + + it('should keep overrides with files and rules', () => { + const config: OxlintConfig = { + overrides: [ + { + files: ['*.ts'], + rules: { 'no-console': 'error' }, + }, + ], + }; + cleanUpOxlintConfig(config); + expect(config.overrides).toHaveLength(1); + }); + + it('should merge consecutive identical overrides', () => { + const config: OxlintConfig = { + overrides: [ + { + files: ['*.ts', '*.tsx'], + plugins: ['typescript'], + }, + { + files: ['*.ts', '*.tsx'], + plugins: ['typescript'], + }, + ], + }; + cleanUpOxlintConfig(config); + expect(config.overrides).toHaveLength(1); + }); + + it('should not merge non-consecutive identical overrides', () => { + const config: OxlintConfig = { + overrides: [ + { + files: ['*.ts'], + rules: { 'no-console': 'error' }, + }, + { + files: ['*.js'], + rules: { 'no-var': 'error' }, + }, + { + files: ['*.ts'], + rules: { 'no-console': 'error' }, + }, + ], + }; + cleanUpOxlintConfig(config); + expect(config.overrides).toHaveLength(3); + }); + }); + + describe('env cleanup', () => { + it('should remove unsupported es3, es5, es2015 env keys', () => { + const config: OxlintConfig = { + env: { + es3: true, + es5: true, + es2015: true, + es2020: true, + }, + }; + cleanUpOxlintConfig(config); + expect(config.env?.es3).toBeUndefined(); + expect(config.env?.es5).toBeUndefined(); + expect(config.env?.es2015).toBeUndefined(); + expect(config.env?.es2020).toBe(true); + }); + + it('should remove older ES versions when newer one is present', () => { + const config: OxlintConfig = { + env: { + es2020: true, + es2021: true, + es2022: true, + }, + }; + cleanUpOxlintConfig(config); + expect(config.env?.es2020).toBeUndefined(); + expect(config.env?.es2021).toBeUndefined(); + expect(config.env?.es2022).toBe(true); + }); + }); + + describe('globals cleanup', () => { + it('should remove empty globals object', () => { + const config: OxlintConfig = { + globals: {}, + }; + cleanUpOxlintConfig(config); + expect(config.globals).toBeUndefined(); + }); + + it('should keep non-empty globals object', () => { + const config: OxlintConfig = { + globals: { + myGlobal: 'readonly', + }, + }; + cleanUpOxlintConfig(config); + expect(config.globals).toBeDefined(); + expect(config.globals?.myGlobal).toBe('readonly'); + }); + }); + + describe('duplicate overrides with differing files cleanup', () => { + it('should merge consecutive overrides with differing files but identical other settings', () => { + const config: OxlintConfig = { + overrides: [ + { + files: ['*.ts'], + plugins: ['typescript'], + }, + { + files: ['*.tsx'], + plugins: ['typescript'], + }, + ], + }; + cleanUpOxlintConfig(config); + expect(config.overrides).toHaveLength(1); + expect(config.overrides?.[0].files).toEqual(['*.ts', '*.tsx']); + }); + + it('should merge consecutive overrides with differing files but identical other settings, including jsPlugins and categories', () => { + const config: OxlintConfig = { + overrides: [ + { + files: ['*.ts'], + plugins: ['typescript'], + jsPlugins: ['foobar'], + categories: { correctness: 'warn' }, + }, + { + files: ['*.tsx'], + plugins: ['typescript'], + jsPlugins: ['foobar'], + categories: { correctness: 'warn' }, + }, + ], + }; + cleanUpOxlintConfig(config); + expect(config.overrides).toHaveLength(1); + expect(config.overrides?.[0].files).toEqual(['*.ts', '*.tsx']); + expect(config.overrides?.[0].jsPlugins).toEqual(['foobar']); + expect(config.overrides?.[0].categories).toEqual({ correctness: 'warn' }); + }); + + it('should not merge consecutive overrides with differing non-file settings like env', () => { + const config: OxlintConfig = { + overrides: [ + { + files: ['*.ts'], + env: { browser: true }, + rules: { 'no-console': 'error' }, + }, + { + files: ['*.tsx'], + env: { node: true }, + rules: { 'no-console': 'error' }, + }, + ], + }; + cleanUpOxlintConfig(config); + expect(config.overrides).toHaveLength(2); + }); + + // We don't currently handle merging for this, as it's not possible to easily + // determine if the override in the middle is changing the ultimate behavior of the + // configured lint rules. + it('should not merge non-consecutive overrides with differing files', () => { + const config: OxlintConfig = { + overrides: [ + { + files: ['*.ts'], + plugins: ['typescript'], + }, + { + files: ['*.js'], + plugins: ['javascript'], + }, + { + files: ['*.tsx'], + plugins: ['typescript'], + }, + ], + }; + cleanUpOxlintConfig(config); + expect(config.overrides).toHaveLength(3); + }); + }); +}); diff --git a/src/cleanup.ts b/src/cleanup.ts index e3333489..5d686122 100644 --- a/src/cleanup.ts +++ b/src/cleanup.ts @@ -1,4 +1,5 @@ import { + cleanUpSupersetEnvs, cleanUpUselessOverridesEnv, ES_VERSIONS, removeGlobalsWithAreCoveredByEnv, @@ -67,6 +68,7 @@ const cleanUpUselessOverridesEntries = (config: OxlintConfig): void => { cleanUpUselessOverridesRules(config); cleanUpUselessOverridesPlugins(config); cleanUpUselessOverridesEnv(config); + cleanUpSupersetEnvs(config); if (config.overrides === undefined) { return; @@ -83,6 +85,10 @@ const cleanUpUselessOverridesEntries = (config: OxlintConfig): void => { (overrides) => Object.keys(overrides).length > 0 ); + // Merge consecutive identical overrides to avoid redundancy + mergeConsecutiveIdenticalOverrides(config); + mergeConsecutiveOverridesWithDifferingFiles(config); + if (config.overrides.length === 0) { delete config.overrides; } @@ -126,3 +132,150 @@ export const cleanUpOxlintConfig = (config: OxlintConfigOrOverride): void => { cleanUpDisabledRootRules(config); } }; + +/** + * Merges consecutive identical overrides in the config's overrides array + * Merges only if the overrides are directly next to each other + * (otherwise they could be overriden in between one another). + * + * Example: + * + * ```json + * overrides: [ + * { + * "files": [ + * "*.ts", + * "*.tsx", + * ], + * "plugins": [ + * "typescript", + * ], + * }, + * { + * "files": [ + * "*.ts", + * "*.tsx", + * ], + * "plugins": [ + * "typescript", + * ], + * }, + * ] + * ``` + */ +function mergeConsecutiveIdenticalOverrides(config: OxlintConfig) { + if (config.overrides === undefined) { + return; + } + if (config.overrides.length <= 1) { + return; + } + + const mergedOverrides: OxlintConfigOverride[] = []; + let i = 0; + + while (i < config.overrides.length) { + const current = config.overrides[i]; + + // Check if the next override is identical to the current one + if ( + i + 1 < config.overrides.length && + isEqualDeep(current, config.overrides[i + 1]) + ) { + // Skip duplicates - just add the first one + mergedOverrides.push(current); + // Skip all consecutive duplicates + while ( + i + 1 < config.overrides.length && + isEqualDeep(current, config.overrides[i + 1]) + ) { + i++; + } + } else { + mergedOverrides.push(current); + } + + i++; + } + + config.overrides = mergedOverrides; +} + +/** + * Merge consecutive overrides that have differing files but everything else is identical. + * + * ```json + * "overrides": [ + * { + * "files": [ + * "*.ts", + * ], + * "rules": { + * "arrow-body-style": "error", + * }, + * }, + * { + * "files": [ + * "*.mts", + * "*.cts", + * ], + * "rules": { + * "arrow-body-style": "error", + * }, + * }, + * ], + * ``` + */ +function mergeConsecutiveOverridesWithDifferingFiles(config: OxlintConfig) { + if (config.overrides === undefined) { + return; + } + if (config.overrides.length <= 1) { + return; + } + + const mergedOverrides: OxlintConfigOverride[] = []; + let i = 0; + + while (i < config.overrides.length) { + const current = config.overrides[i]; + const currentFiles = current.files; + const { files: _, ...currentWithoutFiles } = current; + + // Look ahead to find consecutive overrides with same properties (except files) + let j = i + 1; + const filesToMerge: string[] = [...currentFiles]; + + while (j < config.overrides.length) { + const next = config.overrides[j]; + const { files: __, ...nextWithoutFiles } = next; + + // Check if everything except files is identical + if (isEqualDeep(currentWithoutFiles, nextWithoutFiles)) { + // Merge the files + filesToMerge.push(...next.files); + j++; + } else { + break; + } + } + + // Create the merged override + if (j > i + 1) { + // We found overrides to merge + // Deduplicate the files array + const uniqueFiles = [...new Set(filesToMerge)]; + mergedOverrides.push({ + ...current, + files: uniqueFiles, + }); + i = j; + } else { + // No merge, keep as is + mergedOverrides.push(current); + i++; + } + } + + config.overrides = mergedOverrides; +} diff --git a/src/env_globals.spec.ts b/src/env_globals.spec.ts index 1d33a9ce..1cfc6966 100644 --- a/src/env_globals.spec.ts +++ b/src/env_globals.spec.ts @@ -1,5 +1,6 @@ import { describe, expect, test } from 'vitest'; import { + cleanUpSupersetEnvs, cleanUpUselessOverridesEnv, detectEnvironmentByGlobals, removeGlobalsWithAreCoveredByEnv, @@ -28,13 +29,13 @@ describe('detectEnvironmentByGlobals', () => { expect(config.env).toBeUndefined(); }); - test('detect browser env with >97% match (missing a few keys)', () => { + test('detect browser env with >=97% match (missing a few keys)', () => { // Create a copy of browser globals and remove a few keys to simulate version differences const browserGlobals: Record = { ...globals.browser, }; const totalKeys = Object.keys(browserGlobals).length; - const keysToRemove = Math.floor(totalKeys * 0.03); // Remove 3% of keys + const keysToRemove = Math.floor(totalKeys * 0.02); // Remove 2% of keys let removed = 0; for (const key in browserGlobals) { @@ -50,6 +51,8 @@ describe('detectEnvironmentByGlobals', () => { detectEnvironmentByGlobals(config); expect(config.env?.browser).toBe(true); + // ensure that browser is the only env detected + expect(Object.keys(config.env || {}).length).toBe(1); }); test('does not detect env when match is <97%', () => { @@ -155,3 +158,271 @@ describe('transformEnvAndGlobals', () => { }); }); }); + +describe('cleanUpSupersetEnvs', () => { + test('removes shared-node-browser when node is present', () => { + const config: OxlintConfig = { + env: { + 'shared-node-browser': true, + node: true, + }, + }; + + cleanUpSupersetEnvs(config); + + expect(config).toStrictEqual({ + env: { + node: true, + }, + }); + }); + + test('does not removes shared-node-browser when node has a different value', () => { + const config: OxlintConfig = { + env: { + 'shared-node-browser': true, + node: false, + }, + }; + + cleanUpSupersetEnvs(config); + + expect(config).toStrictEqual({ + env: { + 'shared-node-browser': true, + node: false, + }, + }); + }); + + test('removes subset env from override when superset is in same override', () => { + const config: OxlintConfig = { + env: { + es2024: true, + }, + overrides: [ + { + files: ['*.test.js'], + env: { + 'shared-node-browser': true, + node: true, + }, + }, + ], + }; + + cleanUpSupersetEnvs(config); + + expect(config).toStrictEqual({ + env: { + es2024: true, + }, + overrides: [ + { + files: ['*.test.js'], + env: { + node: true, + }, + }, + ], + }); + }); + + test('removes subset env from override when superset is in main config', () => { + const config: OxlintConfig = { + env: { + node: true, + }, + overrides: [ + { + files: ['*.test.js'], + env: { + 'shared-node-browser': true, + }, + }, + ], + }; + + cleanUpSupersetEnvs(config); + + expect(config).toStrictEqual({ + env: { + node: true, + }, + overrides: [ + { + files: ['*.test.js'], + }, + ], + }); + }); + + test('keeps subset env in override when it differs from superset in main', () => { + const config: OxlintConfig = { + env: { + node: false, + }, + overrides: [ + { + files: ['*.test.js'], + env: { + 'shared-node-browser': true, + }, + }, + ], + }; + + cleanUpSupersetEnvs(config); + + expect(config).toStrictEqual({ + env: { + node: false, + }, + overrides: [ + { + files: ['*.test.js'], + env: { + 'shared-node-browser': true, + }, + }, + ], + }); + }); + + test('keeps subset env in override when superset is also in override with different value', () => { + const config: OxlintConfig = { + env: { + es2024: true, + }, + overrides: [ + { + files: ['*.test.js'], + env: { + 'shared-node-browser': true, + node: false, + }, + }, + ], + }; + + cleanUpSupersetEnvs(config); + + expect(config).toStrictEqual({ + env: { + es2024: true, + }, + overrides: [ + { + files: ['*.test.js'], + env: { + 'shared-node-browser': true, + node: false, + }, + }, + ], + }); + }); + + test('handles multiple overrides independently', () => { + const config: OxlintConfig = { + env: { + node: true, + }, + overrides: [ + { + files: ['*.test.js'], + env: { + 'shared-node-browser': true, + }, + }, + { + files: ['*.spec.js'], + env: { + commonjs: true, + node: true, + }, + }, + ], + }; + + cleanUpSupersetEnvs(config); + + expect(config).toStrictEqual({ + env: { + node: true, + }, + overrides: [ + { + files: ['*.test.js'], + }, + { + files: ['*.spec.js'], + env: { + node: true, + }, + }, + ], + }); + }); + + test('handles browser superset env in overrides', () => { + const config: OxlintConfig = { + env: { + browser: true, + }, + overrides: [ + { + files: ['*.worker.js'], + env: { + 'shared-node-browser': true, + }, + }, + ], + }; + + cleanUpSupersetEnvs(config); + + expect(config).toStrictEqual({ + env: { + browser: true, + }, + overrides: [ + { + files: ['*.worker.js'], + }, + ], + }); + }); + + test('does not remove anything when no superset envs are present', () => { + const config: OxlintConfig = { + env: { + es2024: true, + }, + overrides: [ + { + files: ['*.test.js'], + env: { + 'shared-node-browser': true, + }, + }, + ], + }; + + cleanUpSupersetEnvs(config); + + expect(config).toStrictEqual({ + env: { + es2024: true, + }, + overrides: [ + { + files: ['*.test.js'], + env: { + 'shared-node-browser': true, + }, + }, + ], + }); + }); +}); diff --git a/src/env_globals.ts b/src/env_globals.ts index 9688e6c7..42801fbf 100644 --- a/src/env_globals.ts +++ b/src/env_globals.ts @@ -7,7 +7,7 @@ export const ES_VERSIONS = [ 6, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025, 2026, ]; -// +// const OTHER_SUPPORTED_ENVS = [ 'browser', 'node', @@ -17,6 +17,7 @@ const OTHER_SUPPORTED_ENVS = [ 'amd', 'applescript', + 'astro', 'atomtest', 'commonjs', 'embertest', @@ -32,9 +33,11 @@ const OTHER_SUPPORTED_ENVS = [ 'prototypejs', 'phantomjs', 'shelljs', + 'svelte', 'webextensions', 'qunit', 'vitest', + 'vue', ]; // these parsers are supported by oxlint and should not be reported @@ -91,6 +94,9 @@ export const transformBoolGlobalToString = (config: OxlintConfigOrOverride) => { } }; +// Environments we want to apply a threshold match for, because they're quite large. +const THRESHOLD_ENVS = ['browser', 'node', 'serviceworker', 'worker']; + export const detectEnvironmentByGlobals = (config: OxlintConfigOrOverride) => { if (config.globals === undefined) { return; @@ -119,15 +125,18 @@ export const detectEnvironmentByGlobals = (config: OxlintConfigOrOverride) => { normalizeGlobValue(config.globals[entry]) === entries[entry] ); - // For browser and node, we allow a match if >97% of keys match + // For especially large globals, we allow a match if >=97% of keys match. // This lets us handle version differences in globals package where // there's a difference of just a few extra/removed keys. // Do not do any other envs, otherwise things like es2024 and es2026 // would match each other. - const useThreshold = env === 'browser' || env === 'node'; + const useThreshold = THRESHOLD_ENVS.includes(env); + + const withinThreshold = + useThreshold && matches.length / search.length >= 0.97; if ( - (useThreshold && matches.length / search.length >= 0.97) || + withinThreshold || (!useThreshold && matches.length === search.length) ) { if (config.env === undefined) { @@ -233,3 +242,82 @@ export const cleanUpUselessOverridesEnv = (config: OxlintConfig): void => { } } }; + +// These are envs where the key includes all of the globals from the values. +// So for example, for shared-node-browser, if the user has either `node` or `browser` already in their `env`, we can remove `shared-node-browser`. +const SUPERSET_ENVS: Record = { + node: ['nodeBuiltin', 'shared-node-browser', 'commonjs'], + browser: ['shared-node-browser'], +}; + +/** + * Cleans up superset environments in the config and its overrides. + * If a superset environment is present, its subset environments are removed, e.g. all globals from `shared-node-browser` are also in `browser` and `node`. + * + * This also applies for overrides, where if a superset env is defined in the override or main config, + * the subset envs can be removed from the override if the override has the same value as the superset. + */ +export const cleanUpSupersetEnvs = (config: OxlintConfig): void => { + // Clean up main config env + if (config.env !== undefined) { + // If we have a superset env, remove its subsets + for (const [supersetEnv, subsetEnvs] of Object.entries(SUPERSET_ENVS)) { + if (!(supersetEnv in config.env)) { + continue; + } + + for (const subsetEnv of subsetEnvs) { + if (config.env[subsetEnv] === config.env[supersetEnv]) { + delete config.env[subsetEnv]; + } + } + } + } + + // Clean up overrides + if (config.overrides !== undefined) { + for (const override of config.overrides) { + if (override.env === undefined) { + continue; + } + + for (const [supersetEnv, subsetEnvs] of Object.entries(SUPERSET_ENVS)) { + // Check if the superset env is in the override + const supersetInOverride = supersetEnv in override.env; + const supersetInMain = + config.env !== undefined && supersetEnv in config.env; + + for (const subsetEnv of subsetEnvs) { + if (!(subsetEnv in override.env)) { + continue; + } + + // Case 1: Both superset and subset are in the override with the same value + // We can safely remove the subset + if ( + supersetInOverride && + override.env[subsetEnv] === override.env[supersetEnv] + ) { + delete override.env[subsetEnv]; + continue; + } + + // Case 2: Superset is in main config, subset is in override + // If they have the same value, the subset is redundant + if ( + supersetInMain && + !supersetInOverride && + config.env![supersetEnv] === override.env[subsetEnv] + ) { + delete override.env[subsetEnv]; + } + } + } + + // Clean up empty env object + if (Object.keys(override.env).length === 0) { + delete override.env; + } + } + } +}; diff --git a/src/walker/comments/index.ts b/src/walker/comments/index.ts index 1ec2aed5..bf0926c5 100644 --- a/src/walker/comments/index.ts +++ b/src/walker/comments/index.ts @@ -9,7 +9,7 @@ export default function replaceComments( const originalComment = comment; comment = comment.trim(); // trim the end too, so we can check for standalone "eslint" comments - // eslint-disable or eslint-enable + // "eslint-disable" or "eslint-enable" if (comment.startsWith('eslint-')) { return replaceRuleDirectiveComment(originalComment, type, options); } else if (type === 'Block') {