ultracite 7.8.3 → 7.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +18 -19
  2. package/config/biome/core/biome.jsonc +68 -4
  3. package/config/biome/next/biome.jsonc +3 -1
  4. package/config/biome/react/biome.jsonc +1 -0
  5. package/config/biome/vue/biome.jsonc +22 -2
  6. package/config/eslint/astro/rules/astro.mjs +4 -1
  7. package/config/eslint/core/eslint.config.mjs +9 -0
  8. package/config/eslint/core/rules/eslint-typescript.mjs +0 -2
  9. package/config/eslint/core/rules/eslint.mjs +15 -1
  10. package/config/eslint/core/rules/github.mjs +4 -0
  11. package/config/eslint/core/rules/import.mjs +10 -1
  12. package/config/eslint/core/rules/jsdoc.mjs +31 -0
  13. package/config/eslint/core/rules/n.mjs +5 -0
  14. package/config/eslint/core/rules/promise.mjs +6 -1
  15. package/config/eslint/core/rules/sonarjs.mjs +17 -0
  16. package/config/eslint/core/rules/typescript.mjs +12 -1
  17. package/config/eslint/core/rules/unicorn.mjs +21 -5
  18. package/config/eslint/next/eslint.config.mjs +4 -0
  19. package/config/eslint/next/rules/react-doctor.mjs +27 -0
  20. package/config/eslint/react/eslint.config.mjs +20 -1
  21. package/config/eslint/react/rules/react-doctor.mjs +153 -0
  22. package/config/eslint/react/rules/react.mjs +7 -6
  23. package/config/eslint/svelte/rules/svelte.mjs +9 -2
  24. package/config/eslint/tanstack/eslint.config.mjs +4 -0
  25. package/config/eslint/tanstack/rules/react-doctor.mjs +25 -0
  26. package/config/eslint/vue/eslint.config.mjs +9 -0
  27. package/config/oxfmt/index.mjs +1 -0
  28. package/config/oxlint/core/index.mjs +10 -3
  29. package/config/oxlint/github/index.d.mts +5 -0
  30. package/config/oxlint/github/index.mjs +36 -0
  31. package/config/oxlint/next/index.mjs +53 -1
  32. package/config/oxlint/react/index.mjs +245 -2
  33. package/config/oxlint/sonarjs/index.d.mts +5 -0
  34. package/config/oxlint/sonarjs/index.mjs +207 -0
  35. package/config/oxlint/tanstack/index.mjs +26 -1
  36. package/config/oxlint/vue/index.mjs +3 -0
  37. package/config/shared/ignores.mjs +3 -0
  38. package/dist/index.js +64 -100
  39. package/package.json +24 -20
@@ -10,9 +10,15 @@ const baseRules = Object.fromEntries(
10
10
  availableKeys.map((key) => [`@typescript-eslint/${key}`, "error"])
11
11
  );
12
12
 
13
+ // Overrides mirror the oxlint core config (config/oxlint/core/index.mjs),
14
+ // which is the benchmark for rule decisions across linters. Notably,
15
+ // consistent-type-definitions is left at its default ("interface"),
16
+ // matching oxlint and Biome's useConsistentTypeDefinitions.
13
17
  const overrideRules = {
14
- "@typescript-eslint/consistent-type-definitions": ["error", "type"],
15
18
  "@typescript-eslint/explicit-function-return-type": "off",
19
+ "@typescript-eslint/explicit-member-accessibility": "off",
20
+ "@typescript-eslint/explicit-module-boundary-types": "off",
21
+ "@typescript-eslint/init-declarations": "off",
16
22
  "@typescript-eslint/naming-convention": [
17
23
  "error",
18
24
  {
@@ -26,7 +32,12 @@ const overrideRules = {
26
32
  },
27
33
  ],
28
34
  "@typescript-eslint/no-magic-numbers": "off",
35
+ "@typescript-eslint/no-require-imports": "off",
29
36
  "@typescript-eslint/prefer-readonly-parameter-types": "off",
37
+ // Kept on even though oxlint sets typescript/require-await off — oxlint
38
+ // applies the base require-await rule to TS files natively, so this is
39
+ // the behavioral equivalent here.
40
+ "@typescript-eslint/require-await": "error",
30
41
  "@typescript-eslint/return-await": ["error", "always"],
31
42
  };
32
43
 
@@ -10,12 +10,15 @@ const baseRules = Object.fromEntries(
10
10
  availableKeys.map((key) => [`unicorn/${key}`, "error"])
11
11
  );
12
12
 
13
+ // Overrides mirror the oxlint core config (config/oxlint/core/index.mjs),
14
+ // which is the benchmark for rule decisions across linters.
13
15
  const overrideRules = {
14
- "unicorn/no-array-callback-reference": "off",
15
- "unicorn/no-keyword-prefix": "off",
16
- "unicorn/no-null": "off",
17
- "unicorn/prefer-dom-node-dataset": "off",
18
- "unicorn/prevent-abbreviations": [
16
+ // Renamed from unicorn/prefer-dom-node-dataset in v65.
17
+ "unicorn/dom-node-dataset": "off",
18
+ "unicorn/explicit-length-check": "off",
19
+ "unicorn/max-nested-calls": "off",
20
+ // Renamed from unicorn/prevent-abbreviations in v68; same allowList option.
21
+ "unicorn/name-replacements": [
19
22
  "error",
20
23
  {
21
24
  allowList: {
@@ -27,6 +30,19 @@ const overrideRules = {
27
30
  },
28
31
  },
29
32
  ],
33
+ "unicorn/no-array-callback-reference": "off",
34
+ // Strips the leading `*` from JSDoc lines, fighting the conventional
35
+ // documentation-comment style that the formatter preserves.
36
+ "unicorn/no-asterisk-prefix-in-documentation-comments": "off",
37
+ "unicorn/no-keyword-prefix": "off",
38
+ "unicorn/no-null": "off",
39
+ "unicorn/no-process-exit": "off",
40
+ "unicorn/prefer-global-this": "off",
41
+ "unicorn/prefer-string-raw": "off",
42
+ // Enforces Temporal over Date, but Temporal still lacks broad runtime
43
+ // support; unicorn ships it off by default. Premature to enforce.
44
+ "unicorn/prefer-temporal": "off",
45
+ "unicorn/prefer-top-level-await": "off",
30
46
  "unicorn/text-encoding-identifier-case": ["error", { withDash: true }],
31
47
  };
32
48
 
@@ -1,17 +1,21 @@
1
1
  /* eslint-disable n/no-unpublished-import, n/no-extraneous-import, import/no-extraneous-dependencies, id-length */
2
2
 
3
3
  import next from "@next/eslint-plugin-next";
4
+ import reactDoctor from "eslint-plugin-react-doctor";
4
5
 
5
6
  import nextRules from "./rules/next.mjs";
7
+ import reactDoctorRules from "./rules/react-doctor.mjs";
6
8
 
7
9
  const config = [
8
10
  {
9
11
  files: ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"],
10
12
  plugins: {
11
13
  "@next/next": next,
14
+ "react-doctor": reactDoctor,
12
15
  },
13
16
  rules: {
14
17
  ...nextRules,
18
+ ...reactDoctorRules,
15
19
  },
16
20
  },
17
21
  {
@@ -0,0 +1,27 @@
1
+ const config = {
2
+ "react-doctor/nextjs-async-client-component": "error",
3
+ "react-doctor/nextjs-error-boundary-missing-use-client": "error",
4
+ "react-doctor/nextjs-global-error-missing-html-body": "error",
5
+ "react-doctor/nextjs-image-missing-sizes": "error",
6
+ "react-doctor/nextjs-inline-script-missing-id": "error",
7
+ "react-doctor/nextjs-missing-metadata": "error",
8
+ "react-doctor/nextjs-no-a-element": "error",
9
+ "react-doctor/nextjs-no-client-fetch-for-server-data": "error",
10
+ "react-doctor/nextjs-no-client-side-redirect": "error",
11
+ "react-doctor/nextjs-no-css-link": "error",
12
+ "react-doctor/nextjs-no-default-export-in-route-handler": "error",
13
+ "react-doctor/nextjs-no-edge-og-runtime": "error",
14
+ "react-doctor/nextjs-no-font-link": "error",
15
+ "react-doctor/nextjs-no-google-analytics-script": "error",
16
+ "react-doctor/nextjs-no-head-import": "error",
17
+ "react-doctor/nextjs-no-img-element": "error",
18
+ "react-doctor/nextjs-no-native-script": "error",
19
+ "react-doctor/nextjs-no-polyfill-script": "error",
20
+ "react-doctor/nextjs-no-redirect-in-try-catch": "error",
21
+ "react-doctor/nextjs-no-script-in-head": "error",
22
+ "react-doctor/nextjs-no-side-effect-in-get-handler": "error",
23
+ "react-doctor/nextjs-no-use-search-params-without-suspense": "error",
24
+ "react-doctor/nextjs-no-vercel-og-import": "error",
25
+ };
26
+
27
+ export default config;
@@ -1,13 +1,26 @@
1
1
  /* eslint-disable n/no-unpublished-import, n/no-extraneous-import, import/no-extraneous-dependencies, id-length */
2
2
 
3
+ import eslintPrettier from "eslint-config-prettier";
3
4
  import jsxA11y from "eslint-plugin-jsx-a11y";
4
5
  import react from "eslint-plugin-react";
6
+ import reactDoctor from "eslint-plugin-react-doctor";
5
7
  import reactHooks from "eslint-plugin-react-hooks";
6
8
 
7
9
  import jsxA11yRules from "./rules/jsx-a11y.mjs";
10
+ import reactDoctorRules from "./rules/react-doctor.mjs";
8
11
  import reactHooksRules from "./rules/react-hooks.mjs";
9
12
  import reactRules from "./rules/react.mjs";
10
13
 
14
+ // Only the react/ entries — this block merges after the core one, so the
15
+ // all-on react rules would otherwise re-enable the JSX formatting rules
16
+ // that eslint-config-prettier turns off (several crash under ESLint 10).
17
+ // Filtered to react/ so the spread can't clobber core-block decisions.
18
+ const reactPrettierOverrides = Object.fromEntries(
19
+ Object.entries(eslintPrettier.rules).filter(([key]) =>
20
+ key.startsWith("react/")
21
+ )
22
+ );
23
+
11
24
  const config = [
12
25
  {
13
26
  files: ["**/*.jsx", "**/*.tsx"],
@@ -21,16 +34,22 @@ const config = [
21
34
  plugins: {
22
35
  "jsx-a11y": jsxA11y,
23
36
  react,
37
+ "react-doctor": reactDoctor,
24
38
  "react-hooks": reactHooks,
25
39
  },
26
40
  rules: {
27
41
  ...reactRules,
28
42
  ...reactHooksRules,
29
43
  ...jsxA11yRules,
44
+ ...reactDoctorRules,
45
+ ...reactPrettierOverrides,
30
46
  },
31
47
  settings: {
32
48
  react: {
33
- version: "detect",
49
+ // "detect" calls the removed context.getFilename() under ESLint 10
50
+ // and crashes every version-aware rule. The preset targets React
51
+ // 19+ (see the react-doctor rules), so pin instead of detecting.
52
+ version: "19.0.0",
34
53
  },
35
54
  },
36
55
  },
@@ -0,0 +1,153 @@
1
+ const config = {
2
+ "react-doctor/activity-wraps-effect-heavy-subtree": "error",
3
+ "react-doctor/advanced-event-handler-refs": "error",
4
+ "react-doctor/async-await-in-loop": "error",
5
+ "react-doctor/async-defer-await": "error",
6
+ "react-doctor/async-parallel": "error",
7
+ "react-doctor/auth-token-in-web-storage": "error",
8
+ "react-doctor/client-localstorage-no-version": "error",
9
+ "react-doctor/client-passive-event-listeners": "error",
10
+ "react-doctor/dialog-has-accessible-name": "error",
11
+ "react-doctor/effect-needs-cleanup": "error",
12
+ "react-doctor/hooks-no-nan-in-deps": "error",
13
+ "react-doctor/html-no-invalid-paragraph-child": "error",
14
+ "react-doctor/html-no-invalid-table-nesting": "error",
15
+ "react-doctor/html-no-nested-interactive": "error",
16
+ "react-doctor/jotai-derived-atom-returns-fresh-object": "error",
17
+ "react-doctor/jotai-select-atom-in-render-body": "error",
18
+ "react-doctor/jotai-tq-use-raw-query-atom": "error",
19
+ "react-doctor/js-async-reduce-without-awaited-acc": "error",
20
+ "react-doctor/js-batch-dom-css": "error",
21
+ "react-doctor/js-cache-property-access": "error",
22
+ "react-doctor/js-cache-storage": "error",
23
+ "react-doctor/js-combine-iterations": "error",
24
+ "react-doctor/js-early-exit": "error",
25
+ "react-doctor/js-flatmap-filter": "error",
26
+ "react-doctor/js-hoist-intl": "error",
27
+ "react-doctor/js-hoist-regexp": "error",
28
+ "react-doctor/js-index-maps": "error",
29
+ "react-doctor/js-length-check-first": "error",
30
+ "react-doctor/js-min-max-loop": "error",
31
+ "react-doctor/js-set-map-lookups": "error",
32
+ "react-doctor/js-tosorted-immutable": "error",
33
+ "react-doctor/no-adjust-state-on-prop-change": "error",
34
+ "react-doctor/no-array-index-as-key": "error",
35
+ "react-doctor/no-async-effect-callback": "error",
36
+ "react-doctor/no-barrel-import": "error",
37
+ "react-doctor/no-call-component-as-function": "error",
38
+ "react-doctor/no-cascading-set-state": "error",
39
+ "react-doctor/no-chain-state-updates": "error",
40
+ "react-doctor/no-create-context-in-render": "error",
41
+ "react-doctor/no-create-ref-in-function-component": "error",
42
+ "react-doctor/no-create-store-in-render": "error",
43
+ "react-doctor/no-derived-state": "error",
44
+ "react-doctor/no-derived-state-effect": "error",
45
+ "react-doctor/no-derived-useState": "error",
46
+ "react-doctor/no-direct-state-mutation": "error",
47
+ "react-doctor/no-disabled-zoom": "error",
48
+ "react-doctor/no-document-start-view-transition": "error",
49
+ "react-doctor/no-document-write": "error",
50
+ "react-doctor/no-dynamic-import-path": "error",
51
+ "react-doctor/no-effect-chain": "error",
52
+ "react-doctor/no-effect-event-handler": "error",
53
+ "react-doctor/no-effect-event-in-deps": "error",
54
+ "react-doctor/no-effect-with-fresh-deps": "error",
55
+ "react-doctor/no-eval": "error",
56
+ "react-doctor/no-event-handler": "error",
57
+ "react-doctor/no-event-trigger-state": "error",
58
+ "react-doctor/no-fetch-in-effect": "error",
59
+ "react-doctor/no-flush-sync": "error",
60
+ "react-doctor/no-full-lodash-import": "error",
61
+ "react-doctor/no-giant-component": "error",
62
+ "react-doctor/no-global-css-variable-animation": "error",
63
+ "react-doctor/no-gray-on-colored-background": "error",
64
+ "react-doctor/no-img-lazy-with-high-fetchpriority": "error",
65
+ "react-doctor/no-initialize-state": "error",
66
+ "react-doctor/no-inline-bounce-easing": "error",
67
+ "react-doctor/no-inline-exhaustive-style": "error",
68
+ "react-doctor/no-inline-prop-on-memo-component": "error",
69
+ "react-doctor/no-json-parse-stringify-clone": "error",
70
+ "react-doctor/no-jsx-element-type": "error",
71
+ "react-doctor/no-large-animated-blur": "error",
72
+ "react-doctor/no-layout-property-animation": "error",
73
+ "react-doctor/no-layout-transition-inline": "error",
74
+ "react-doctor/no-legacy-class-lifecycles": "error",
75
+ "react-doctor/no-legacy-context-api": "error",
76
+ "react-doctor/no-long-transition-duration": "error",
77
+ "react-doctor/no-many-boolean-props": "error",
78
+ "react-doctor/no-mirror-prop-effect": "error",
79
+ "react-doctor/no-moment": "error",
80
+ "react-doctor/no-mutable-in-deps": "error",
81
+ "react-doctor/no-mutating-reducer-state": "error",
82
+ "react-doctor/no-nested-component-definition": "error",
83
+ "react-doctor/no-outline-none": "error",
84
+ "react-doctor/no-pass-data-to-parent": "error",
85
+ "react-doctor/no-pass-live-state-to-parent": "error",
86
+ "react-doctor/no-permanent-will-change": "error",
87
+ "react-doctor/no-polymorphic-children": "error",
88
+ "react-doctor/no-prevent-default": "error",
89
+ "react-doctor/no-prop-callback-in-effect": "error",
90
+ "react-doctor/no-random-key": "error",
91
+ "react-doctor/no-react-dom-deprecated-apis": "error",
92
+ "react-doctor/no-react19-deprecated-apis": "error",
93
+ "react-doctor/no-render-in-render": "error",
94
+ "react-doctor/no-render-prop-children": "error",
95
+ "react-doctor/no-reset-all-state-on-prop-change": "error",
96
+ "react-doctor/no-scale-from-zero": "error",
97
+ "react-doctor/no-secrets-in-client-code": "error",
98
+ "react-doctor/no-self-updating-effect": "error",
99
+ "react-doctor/no-set-state-in-render": "error",
100
+ "react-doctor/no-string-false-on-boolean-attribute": "error",
101
+ "react-doctor/no-sync-xhr": "error",
102
+ "react-doctor/no-tiny-text": "error",
103
+ "react-doctor/no-transition-all": "error",
104
+ "react-doctor/no-uncontrolled-input": "error",
105
+ "react-doctor/no-undeferred-third-party": "error",
106
+ "react-doctor/no-usememo-simple-expression": "error",
107
+ "react-doctor/only-export-components": "error",
108
+ "react-doctor/prefer-dynamic-import": "error",
109
+ "react-doctor/prefer-explicit-variants": "error",
110
+ "react-doctor/prefer-html-dialog": "error",
111
+ "react-doctor/prefer-module-scope-pure-function": "error",
112
+ "react-doctor/prefer-module-scope-static-value": "error",
113
+ "react-doctor/prefer-stable-empty-fallback": "error",
114
+ "react-doctor/prefer-use-effect-event": "error",
115
+ "react-doctor/prefer-use-sync-external-store": "error",
116
+ "react-doctor/prefer-useReducer": "error",
117
+ "react-doctor/react-compiler-no-manual-memoization": "error",
118
+ "react-doctor/redux-useselector-inline-derivation": "error",
119
+ "react-doctor/redux-useselector-returns-new-collection": "error",
120
+ "react-doctor/rendering-animate-svg-wrapper": "error",
121
+ "react-doctor/rendering-conditional-render": "error",
122
+ "react-doctor/rendering-hoist-jsx": "error",
123
+ "react-doctor/rendering-hydration-mismatch-time": "error",
124
+ "react-doctor/rendering-hydration-no-flicker": "error",
125
+ "react-doctor/rendering-script-defer-async": "error",
126
+ "react-doctor/rendering-svg-precision": "error",
127
+ "react-doctor/rendering-usetransition-loading": "error",
128
+ "react-doctor/rerender-defer-reads-hook": "error",
129
+ "react-doctor/rerender-dependencies": "error",
130
+ "react-doctor/rerender-derived-state-from-hook": "error",
131
+ "react-doctor/rerender-functional-setstate": "error",
132
+ "react-doctor/rerender-lazy-ref-init": "error",
133
+ "react-doctor/rerender-lazy-state-init": "error",
134
+ "react-doctor/rerender-memo-before-early-return": "error",
135
+ "react-doctor/rerender-memo-with-default-value": "error",
136
+ "react-doctor/rerender-state-only-in-handlers": "error",
137
+ "react-doctor/rerender-transitions-scroll": "error",
138
+ "react-doctor/server-after-nonblocking": "error",
139
+ "react-doctor/server-auth-actions": "error",
140
+ "react-doctor/server-cache-with-object-literal": "error",
141
+ "react-doctor/server-dedup-props": "error",
142
+ "react-doctor/server-fetch-without-revalidate": "error",
143
+ "react-doctor/server-hoist-static-io": "error",
144
+ "react-doctor/server-no-mutable-module-state": "error",
145
+ "react-doctor/server-sequential-independent-await": "error",
146
+ "react-doctor/use-lazy-motion": "error",
147
+ "react-doctor/zod-v4-no-deprecated-error-apis": "error",
148
+ "react-doctor/zod-v4-no-deprecated-error-customization": "error",
149
+ "react-doctor/zod-v4-no-deprecated-schema-apis": "error",
150
+ "react-doctor/zod-v4-prefer-top-level-string-formats": "error",
151
+ };
152
+
153
+ export default config;
@@ -12,6 +12,10 @@ const baseRules = Object.fromEntries(
12
12
 
13
13
  const overrideRules = {
14
14
  "react/forbid-component-props": "off",
15
+ // Crashes under ESLint 10 (uses the removed context.getSourceCode());
16
+ // forwardRef is deprecated in React 19 and
17
+ // react-doctor/no-react19-deprecated-apis covers it.
18
+ "react/forward-ref-uses-ref": "off",
15
19
  "react/function-component-definition": [
16
20
  "error",
17
21
  {
@@ -19,12 +23,9 @@ const overrideRules = {
19
23
  },
20
24
  ],
21
25
  "react/jsx-boolean-value": "off",
22
- "react/jsx-filename-extension": [
23
- "error",
24
- {
25
- extensions: [".tsx"],
26
- },
27
- ],
26
+ // Off in the oxlint react config, and its implementation uses the
27
+ // removed context.getFilename() under ESLint 10.
28
+ "react/jsx-filename-extension": "off",
28
29
  "react/jsx-max-depth": "off",
29
30
  "react/jsx-max-props-per-line": "off",
30
31
  "react/jsx-newline": "off",
@@ -10,8 +10,15 @@ const baseRules = Object.fromEntries(
10
10
  availableKeys.map((key) => [`svelte/${key}`, "error"])
11
11
  );
12
12
 
13
- const overrideRules = {};
13
+ // prettier-plugin-svelte owns formatting for .svelte files, so keep the
14
+ // formatting rules that the plugin's own prettier preset disables off.
15
+ const prettierConfig = plugin.configs["flat/prettier"];
16
+ const prettierOverrides = Object.fromEntries(
17
+ (Array.isArray(prettierConfig) ? prettierConfig : [prettierConfig])
18
+ .flatMap((entry) => Object.entries(entry.rules ?? {}))
19
+ .filter(([key]) => key.startsWith("svelte/"))
20
+ );
14
21
 
15
- const config = Object.assign(baseRules, overrideRules);
22
+ const config = Object.assign(baseRules, prettierOverrides);
16
23
 
17
24
  export default config;
@@ -3,8 +3,10 @@
3
3
  import query from "@tanstack/eslint-plugin-query";
4
4
  import router from "@tanstack/eslint-plugin-router";
5
5
  import start from "@tanstack/eslint-plugin-start";
6
+ import reactDoctor from "eslint-plugin-react-doctor";
6
7
 
7
8
  import queryRules from "./rules/query.mjs";
9
+ import reactDoctorRules from "./rules/react-doctor.mjs";
8
10
  import routerRules from "./rules/router.mjs";
9
11
  import startRules from "./rules/start.mjs";
10
12
 
@@ -22,11 +24,13 @@ const config = [
22
24
  "@tanstack/query": query,
23
25
  "@tanstack/router": router,
24
26
  "@tanstack/start": start,
27
+ "react-doctor": reactDoctor,
25
28
  },
26
29
  rules: {
27
30
  ...queryRules,
28
31
  ...routerRules,
29
32
  ...startRules,
33
+ ...reactDoctorRules,
30
34
  },
31
35
  },
32
36
  ];
@@ -0,0 +1,25 @@
1
+ const config = {
2
+ "react-doctor/query-destructure-result": "error",
3
+ "react-doctor/query-mutation-missing-invalidation": "error",
4
+ "react-doctor/query-no-query-in-effect": "error",
5
+ "react-doctor/query-no-rest-destructuring": "error",
6
+ "react-doctor/query-no-usequery-for-mutation": "error",
7
+ "react-doctor/query-no-void-query-fn": "error",
8
+ "react-doctor/query-stable-query-client": "error",
9
+ "react-doctor/tanstack-start-get-mutation": "error",
10
+ "react-doctor/tanstack-start-loader-parallel-fetch": "error",
11
+ "react-doctor/tanstack-start-missing-head-content": "error",
12
+ "react-doctor/tanstack-start-no-anchor-element": "error",
13
+ "react-doctor/tanstack-start-no-direct-fetch-in-loader": "error",
14
+ "react-doctor/tanstack-start-no-dynamic-server-fn-import": "error",
15
+ "react-doctor/tanstack-start-no-navigate-in-render": "error",
16
+ "react-doctor/tanstack-start-no-secrets-in-loader": "error",
17
+ "react-doctor/tanstack-start-no-use-server-in-handler": "error",
18
+ "react-doctor/tanstack-start-no-useeffect-fetch": "error",
19
+ "react-doctor/tanstack-start-redirect-in-try-catch": "error",
20
+ "react-doctor/tanstack-start-route-property-order": "error",
21
+ "react-doctor/tanstack-start-server-fn-method-order": "error",
22
+ "react-doctor/tanstack-start-server-fn-validate-input": "error",
23
+ };
24
+
25
+ export default config;
@@ -1,9 +1,17 @@
1
1
  /* eslint-disable n/no-unpublished-import, n/no-extraneous-import, import/no-extraneous-dependencies, id-length */
2
2
 
3
+ import eslintPrettier from "eslint-config-prettier";
3
4
  import vue from "eslint-plugin-vue";
4
5
 
5
6
  import vueRules from "./rules/vue.mjs";
6
7
 
8
+ // Only the vue/ entries — the all-on vue rules would otherwise re-enable
9
+ // the template formatting rules that eslint-config-prettier turns off.
10
+ // Prettier owns formatting for .vue files.
11
+ const vuePrettierOverrides = Object.fromEntries(
12
+ Object.entries(eslintPrettier.rules).filter(([key]) => key.startsWith("vue/"))
13
+ );
14
+
7
15
  const config = [
8
16
  {
9
17
  files: ["**/*.vue"],
@@ -12,6 +20,7 @@ const config = [
12
20
  },
13
21
  rules: {
14
22
  ...vueRules,
23
+ ...vuePrettierOverrides,
15
24
  },
16
25
  },
17
26
  ];
@@ -10,6 +10,7 @@ export default defineConfig({
10
10
  ignorePatterns,
11
11
  jsxSingleQuote: false,
12
12
  printWidth: 80,
13
+ proseWrap: "never",
13
14
  quoteProps: "as-needed",
14
15
  semi: true,
15
16
  singleQuote: false,
@@ -16,9 +16,7 @@ export default defineConfig({
16
16
  "**/__tests__/**/*.{ts,tsx,js,jsx}",
17
17
  ],
18
18
  rules: {
19
- // Disabled: mock callbacks often need empty functions
20
19
  "no-empty-function": "off",
21
- // Disabled: mock factories use Promise.resolve/reject (conflicts with require-await)
22
20
  "promise/prefer-await-to-then": "off",
23
21
  },
24
22
  },
@@ -58,6 +56,7 @@ export default defineConfig({
58
56
  allowArrowFunctions: true,
59
57
  },
60
58
  ],
59
+ "getter-return": "error",
61
60
  "grouped-accessor-pairs": "error",
62
61
  "guard-for-in": "error",
63
62
  "id-length": "off",
@@ -150,7 +149,6 @@ export default defineConfig({
150
149
  "no-regex-spaces": "error",
151
150
  "no-restricted-globals": "error",
152
151
  "no-restricted-imports": "error",
153
- // Disabled: requires project-specific configuration to be useful
154
152
  "no-restricted-properties": "off",
155
153
  "no-return-assign": "error",
156
154
  "no-script-url": "error",
@@ -171,6 +169,7 @@ export default defineConfig({
171
169
  "no-unexpected-multiline": "error",
172
170
  "no-unmodified-loop-condition": "error",
173
171
  "no-unneeded-ternary": "error",
172
+ "no-unreachable": "error",
174
173
  "no-unsafe-finally": "error",
175
174
  "no-unsafe-negation": "error",
176
175
  "no-unsafe-optional-chaining": "error",
@@ -286,9 +285,11 @@ export default defineConfig({
286
285
  "node/global-require": "error",
287
286
  "node/handle-callback-err": "error",
288
287
  "node/no-exports-assign": "error",
288
+ "node/no-mixed-requires": "error",
289
289
  "node/no-new-require": "error",
290
290
  "node/no-path-concat": "error",
291
291
  "node/no-process-env": "off",
292
+ "node/no-sync": "off",
292
293
 
293
294
  // ── oxc ────────────────────────────────────────────────────────────
294
295
  "oxc/approx-constant": "error",
@@ -299,6 +300,7 @@ export default defineConfig({
299
300
  "oxc/bad-min-max-func": "error",
300
301
  "oxc/bad-object-literal-comparison": "error",
301
302
  "oxc/bad-replace-all-arg": "error",
303
+ "oxc/branches-sharing-code": "error",
302
304
  "oxc/const-comparisons": "error",
303
305
  "oxc/double-comparisons": "error",
304
306
  "oxc/erasing-op": "error",
@@ -459,11 +461,13 @@ export default defineConfig({
459
461
  "unicorn/explicit-length-check": "off",
460
462
  "unicorn/filename-case": "error",
461
463
  "unicorn/import-style": "error",
464
+ "unicorn/max-nested-calls": "off",
462
465
  "unicorn/new-for-builtins": "error",
463
466
  "unicorn/no-abusive-eslint-disable": "error",
464
467
  "unicorn/no-accessor-recursion": "error",
465
468
  "unicorn/no-anonymous-default-export": "error",
466
469
  "unicorn/no-array-callback-reference": "off",
470
+ "unicorn/no-array-fill-with-reference-type": "error",
467
471
  "unicorn/no-array-for-each": "error",
468
472
  "unicorn/no-array-method-this-argument": "error",
469
473
  "unicorn/no-array-reduce": "error",
@@ -533,6 +537,7 @@ export default defineConfig({
533
537
  "unicorn/prefer-dom-node-remove": "error",
534
538
  "unicorn/prefer-dom-node-text-content": "error",
535
539
  "unicorn/prefer-event-target": "error",
540
+ "unicorn/prefer-export-from": "error",
536
541
  "unicorn/prefer-global-this": "off",
537
542
  "unicorn/prefer-import-meta-properties": "error",
538
543
  "unicorn/prefer-includes": "error",
@@ -546,6 +551,7 @@ export default defineConfig({
546
551
  "unicorn/prefer-native-coercion-functions": "error",
547
552
  "unicorn/prefer-negative-index": "error",
548
553
  "unicorn/prefer-node-protocol": "error",
554
+ "unicorn/prefer-number-coercion": "error",
549
555
  "unicorn/prefer-number-properties": "error",
550
556
  "unicorn/prefer-object-from-entries": "error",
551
557
  "unicorn/prefer-optional-catch-binding": "error",
@@ -556,6 +562,7 @@ export default defineConfig({
556
562
  "unicorn/prefer-response-static-json": "error",
557
563
  "unicorn/prefer-set-has": "error",
558
564
  "unicorn/prefer-set-size": "error",
565
+ "unicorn/prefer-single-call": "error",
559
566
  "unicorn/prefer-spread": "error",
560
567
  "unicorn/prefer-string-raw": "off",
561
568
  "unicorn/prefer-string-replace-all": "error",
@@ -0,0 +1,5 @@
1
+ import type { OxlintConfig } from "oxlint";
2
+
3
+ declare const config: OxlintConfig;
4
+
5
+ export default config;
@@ -0,0 +1,36 @@
1
+ import { defineConfig } from "oxlint";
2
+
3
+ // Runs eslint-plugin-github through oxlint's JS plugin support to close
4
+ // the gap with the ESLint preset. Rules that require type information are
5
+ // not supported by the JS plugin bridge and are excluded.
6
+ export default defineConfig({
7
+ jsPlugins: [{ name: "github", specifier: "eslint-plugin-github" }],
8
+ rules: {
9
+ "github/a11y-aria-label-is-well-formatted": "error",
10
+ "github/a11y-no-title-attribute": "error",
11
+ "github/a11y-no-visually-hidden-interactive-element": "error",
12
+ "github/a11y-role-supports-aria-props": "error",
13
+ "github/a11y-svg-has-accessible-name": "error",
14
+ "github/array-foreach": "error",
15
+ "github/async-currenttarget": "error",
16
+ "github/async-preventdefault": "error",
17
+ "github/authenticity-token": "error",
18
+ "github/filenames-match-regex": "error",
19
+ "github/get-attribute": "error",
20
+ "github/js-class-name": "error",
21
+ "github/no-blur": "error",
22
+ "github/no-d-none": "error",
23
+ // Conflicts with unicorn/prefer-dom-node-dataset, which is the benchmark.
24
+ "github/no-dataset": "off",
25
+ "github/no-dynamic-script-tag": "error",
26
+ "github/no-implicit-buggy-globals": "error",
27
+ "github/no-inner-html": "error",
28
+ "github/no-innerText": "error",
29
+ "github/no-then": "error",
30
+ "github/no-useless-passive": "error",
31
+ "github/prefer-observers": "error",
32
+ "github/require-passive-events": "error",
33
+ // Mirrors the ESLint preset.
34
+ "github/unescaped-html-literal": "off",
35
+ },
36
+ });