@jterrazz/typescript 9.3.0 → 10.1.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 (81) hide show
  1. package/README.md +20 -16
  2. package/bin/commands/check.sh +387 -146
  3. package/bin/find-tsc.sh +30 -0
  4. package/bin/typescript.sh +79 -1
  5. package/lib/check-architecture.js +89 -0
  6. package/lib/check-baseline.js +144 -0
  7. package/lib/check-docs.js +4 -3
  8. package/lib/check-drift.js +209 -0
  9. package/lib/check-gitignore.js +4 -4
  10. package/lib/check-markdown.js +279 -0
  11. package/lib/check-names.js +125 -0
  12. package/lib/check-publish.js +150 -0
  13. package/lib/check-secrets.js +115 -0
  14. package/lib/check-suppressions.js +355 -0
  15. package/lib/doctor.js +185 -0
  16. package/lib/entry-points.js +91 -0
  17. package/lib/merge-knip-config.js +57 -25
  18. package/lib/tracked-files.js +165 -0
  19. package/lib/unsafe-fixers.js +25 -0
  20. package/lib/workspace-members.js +5 -6
  21. package/package.json +36 -13
  22. package/presets/oxfmt/index.js +49 -5
  23. package/presets/oxlint/profiles/astro.js +10 -0
  24. package/presets/oxlint/profiles/bun.js +7 -0
  25. package/presets/oxlint/profiles/expo.js +7 -0
  26. package/presets/oxlint/profiles/library.js +16 -0
  27. package/presets/oxlint/profiles/next.js +7 -0
  28. package/presets/oxlint/profiles/node.js +7 -0
  29. package/presets/oxlint/profiles/react.js +7 -0
  30. package/presets/prettier/astro.json +6 -0
  31. package/presets/tsconfig/astro.json +25 -0
  32. package/presets/tsconfig/expo.json +16 -6
  33. package/presets/tsconfig/library.json +17 -0
  34. package/presets/tsconfig/next.json +12 -2
  35. package/presets/tsconfig/node.json +18 -4
  36. package/presets/tsconfig/react.json +33 -0
  37. package/presets/tsdown/build.d.ts +13 -0
  38. package/presets/tsdown/bundle.d.ts +13 -0
  39. package/presets/tsdown/bundle.js +10 -1
  40. package/rules/README.md +23 -0
  41. package/rules/_contract.js +207 -0
  42. package/rules/_contract.test.ts +81 -0
  43. package/rules/a11y.js +51 -0
  44. package/rules/architecture/hexagonal.js +56 -0
  45. package/rules/architecture/layers.js +75 -0
  46. package/rules/astro.js +56 -0
  47. package/rules/bundler.js +19 -0
  48. package/rules/catalog.js +166 -0
  49. package/rules/catalog.test.ts +98 -0
  50. package/rules/compile.js +125 -0
  51. package/rules/core/eslint.js +234 -0
  52. package/rules/core/import.js +117 -0
  53. package/rules/core/jsdoc.js +52 -0
  54. package/rules/core/node.js +36 -0
  55. package/rules/core/oxc.js +54 -0
  56. package/rules/core/promise.js +39 -0
  57. package/rules/core/typescript.js +223 -0
  58. package/rules/core/unicorn.js +210 -0
  59. package/rules/next.js +53 -0
  60. package/rules/profiles.js +95 -0
  61. package/rules/react-native.js +48 -0
  62. package/rules/react.js +155 -0
  63. package/rules/sorted.js +41 -0
  64. package/rules/vitest.js +178 -0
  65. package/src/docs.d.ts +4 -4
  66. package/src/docs.js +75 -57
  67. package/src/docs.test.ts +43 -31
  68. package/src/index.d.ts +14 -9
  69. package/src/index.js +17 -8
  70. package/src/oxfmt.d.ts +15 -2
  71. package/src/oxfmt.test.ts +10 -0
  72. package/src/oxlint.d.ts +59 -10
  73. package/src/oxlint.js +36 -50
  74. package/src/oxlint.test.ts +82 -28
  75. package/presets/oxlint/architectures/hexagonal-rules.js +0 -39
  76. package/presets/oxlint/architectures/hexagonal.js +0 -13
  77. package/presets/oxlint/base.js +0 -145
  78. package/presets/oxlint/expo.js +0 -36
  79. package/presets/oxlint/next.js +0 -43
  80. package/presets/oxlint/node.js +0 -14
  81. package/presets/oxlint/plugins/codestyle.js +0 -231
@@ -0,0 +1,234 @@
1
+ import { allOn, fragment, off, on } from '../_contract.js';
2
+
3
+ /*
4
+ * The `eslint` plugin: oxlint's port of the ESLint core rules, all 183
5
+ * non-nursery ones decided by name. The offs below are the whole list, and
6
+ * each carries the one reason that justifies it.
7
+ */
8
+ export default fragment({
9
+ id: 'core/eslint',
10
+ plugins: ['eslint'],
11
+ rules: {
12
+ ...allOn([
13
+ 'accessor-pairs',
14
+ 'array-callback-return',
15
+ 'block-scoped-var',
16
+ 'class-methods-use-this',
17
+ 'constructor-super',
18
+ 'curly',
19
+ 'default-case',
20
+ 'default-case-last',
21
+ 'default-param-last',
22
+ 'eqeqeq',
23
+ 'for-direction',
24
+ 'func-name-matching',
25
+ 'func-names',
26
+ 'getter-return',
27
+ 'grouped-accessor-pairs',
28
+ 'guard-for-in',
29
+ 'id-denylist',
30
+ 'id-match',
31
+ 'logical-assignment-operators',
32
+ 'max-classes-per-file',
33
+ 'max-nested-callbacks',
34
+ 'new-cap',
35
+ 'no-alert',
36
+ 'no-array-constructor',
37
+ 'no-async-promise-executor',
38
+ 'no-await-in-loop',
39
+ 'no-bitwise',
40
+ 'no-caller',
41
+ 'no-case-declarations',
42
+ 'no-class-assign',
43
+ 'no-compare-neg-zero',
44
+ 'no-cond-assign',
45
+ 'no-console',
46
+ 'no-const-assign',
47
+ 'no-constant-binary-expression',
48
+ 'no-constant-condition',
49
+ 'no-constructor-return',
50
+ 'no-control-regex',
51
+ 'no-debugger',
52
+ 'no-delete-var',
53
+ 'no-div-regex',
54
+ 'no-dupe-class-members',
55
+ 'no-dupe-else-if',
56
+ 'no-dupe-keys',
57
+ 'no-duplicate-case',
58
+ 'no-else-return',
59
+ 'no-empty',
60
+ 'no-empty-character-class',
61
+ 'no-empty-function',
62
+ 'no-empty-pattern',
63
+ 'no-empty-static-block',
64
+ 'no-eq-null',
65
+ 'no-eval',
66
+ 'no-ex-assign',
67
+ 'no-extend-native',
68
+ 'no-extra-bind',
69
+ 'no-extra-boolean-cast',
70
+ 'no-extra-label',
71
+ 'no-fallthrough',
72
+ 'no-func-assign',
73
+ 'no-global-assign',
74
+ 'no-implicit-coercion',
75
+ 'no-implicit-globals',
76
+ 'no-import-assign',
77
+ 'no-inline-comments',
78
+ 'no-inner-declarations',
79
+ 'no-invalid-regexp',
80
+ 'no-irregular-whitespace',
81
+ 'no-iterator',
82
+ 'no-label-var',
83
+ 'no-labels',
84
+ 'no-lone-blocks',
85
+ 'no-loop-func',
86
+ 'no-loss-of-precision',
87
+ 'no-misleading-character-class',
88
+ 'no-multi-assign',
89
+ 'no-multi-str',
90
+ 'no-new',
91
+ 'no-new-func',
92
+ 'no-new-native-nonconstructor',
93
+ 'no-new-wrappers',
94
+ 'no-nonoctal-decimal-escape',
95
+ 'no-obj-calls',
96
+ 'no-object-constructor',
97
+ 'no-param-reassign',
98
+ 'no-plusplus',
99
+ 'no-promise-executor-return',
100
+ 'no-proto',
101
+ 'no-prototype-builtins',
102
+ 'no-redeclare',
103
+ 'no-regex-spaces',
104
+ 'no-restricted-globals',
105
+ 'no-restricted-imports',
106
+ 'no-restricted-properties',
107
+ 'no-return-assign',
108
+ 'no-script-url',
109
+ 'no-self-assign',
110
+ 'no-self-compare',
111
+ 'no-sequences',
112
+ 'no-setter-return',
113
+ 'no-shadow',
114
+ 'no-shadow-restricted-names',
115
+ 'no-sparse-arrays',
116
+ 'no-template-curly-in-string',
117
+ 'no-this-before-super',
118
+ 'no-unassigned-vars',
119
+ 'no-underscore-dangle',
120
+ 'no-unexpected-multiline',
121
+ 'no-unneeded-ternary',
122
+ 'no-unreachable',
123
+ 'no-unsafe-finally',
124
+ 'no-unsafe-negation',
125
+ 'no-unsafe-optional-chaining',
126
+ 'no-unused-labels',
127
+ 'no-unused-private-class-members',
128
+ 'no-unused-vars',
129
+ 'no-useless-backreference',
130
+ 'no-useless-call',
131
+ 'no-useless-catch',
132
+ 'no-useless-computed-key',
133
+ 'no-useless-concat',
134
+ 'no-useless-constructor',
135
+ 'no-useless-escape',
136
+ 'no-useless-rename',
137
+ 'no-useless-return',
138
+ 'no-var',
139
+ 'no-warning-comments',
140
+ 'no-with',
141
+ 'object-shorthand',
142
+ 'operator-assignment',
143
+ 'prefer-arrow-callback',
144
+ 'prefer-const',
145
+ 'prefer-exponentiation-operator',
146
+ 'prefer-named-capture-group',
147
+ 'prefer-numeric-literals',
148
+ 'prefer-object-has-own',
149
+ 'prefer-object-spread',
150
+ 'prefer-regex-literals',
151
+ 'prefer-rest-params',
152
+ 'prefer-spread',
153
+ 'prefer-template',
154
+ 'preserve-caught-error',
155
+ 'radix',
156
+ 'require-unicode-regexp',
157
+ 'require-yield',
158
+ 'symbol-description',
159
+ 'unicode-bom',
160
+ 'use-isnan',
161
+ 'valid-typeof',
162
+ 'vars-on-top',
163
+ 'yoda',
164
+ ]),
165
+
166
+ // -- On, at the strictest value the option carries --------------------
167
+ 'arrow-body-style': on(['as-needed']),
168
+ complexity: on([12]),
169
+ 'func-style': on(['declaration', { allowArrowFunctions: true }]),
170
+ 'max-depth': on([4]),
171
+ 'max-params': on([4]),
172
+ 'no-duplicate-imports': on([{ allowSeparateTypeImports: true }]),
173
+ 'no-unmodified-loop-condition': on([{ checkConditionalExpressions: true }]),
174
+ /* A hoisted function declaration has no temporal dead zone, and the
175
+ * estate writes the public function first and its helpers below it.
176
+ * The defect this rule names — reading a binding before it exists — is
177
+ * a `let`, a `const` or a `class`, and all three stay refused. */
178
+ 'no-use-before-define': on([{ classes: true, functions: false, variables: true }]),
179
+ 'no-unused-expressions': on([
180
+ { allowShortCircuit: true, allowTaggedTemplates: true, allowTernary: true },
181
+ ]),
182
+ 'no-void': on([{ allowAsStatement: true }]),
183
+ /* The object form names what it takes; the array form past index 0
184
+ * counts commas, and `const [, , , operating] = SPINE` is not clearer
185
+ * than `SPINE[3]`. */
186
+ 'prefer-destructuring': on([{ array: false, object: true }]),
187
+ /* `never`, not `always`. Both exist and both fix, but the `always` fixer
188
+ * FUSES adjacent declarations, and a comment standing between two of
189
+ * them is pulled inside the chain — which silently relocates the
190
+ * `// Given -` / `// Then -` narration @jterrazz/test requires. `never`
191
+ * splits chains and leaves every comment where it stands. */
192
+ 'one-var': on(['never']),
193
+
194
+ // -- Off, each with its one reason ------------------------------------
195
+ 'capitalized-comments': off({
196
+ by: 'jterrazz-design — 198 of 198 reports were false positives (quality study, 2026-09-15)',
197
+ kind: 'evidence',
198
+ }),
199
+ 'id-length': off({
200
+ by: 'measured on this package — every report was a conventional short binding (k, v, x)',
201
+ kind: 'evidence',
202
+ }),
203
+ 'init-declarations': off({ by: 'no-unassigned-vars, prefer-const', kind: 'covered' }),
204
+ 'max-lines': off({ by: 'complexity (12)', kind: 'covered' }),
205
+ 'max-lines-per-function': off({ by: 'complexity (12)', kind: 'covered' }),
206
+ 'max-statements': off({ by: 'complexity (12)', kind: 'covered' }),
207
+ 'no-continue': off({ by: 'max-depth (4)', kind: 'exclusive' }),
208
+ 'no-implied-eval': off({ by: 'typescript/no-implied-eval', kind: 'covered' }),
209
+ 'no-lonely-if': off({ by: 'unicorn/no-lonely-if', kind: 'covered' }),
210
+ 'no-magic-numbers': off({
211
+ by: 'measured on this package — 125 reports, not one of them a defect',
212
+ kind: 'evidence',
213
+ }),
214
+ 'no-negated-condition': off({ by: 'unicorn/no-negated-condition', kind: 'covered' }),
215
+ 'no-nested-ternary': off({ by: 'unicorn/prefer-ternary', kind: 'exclusive' }),
216
+ 'no-ternary': off({ by: 'unicorn/prefer-ternary', kind: 'exclusive' }),
217
+ 'no-throw-literal': off({ by: 'typescript/only-throw-error', kind: 'covered' }),
218
+ 'no-undefined': off({
219
+ by: 'no-shadow-restricted-names, no-global-assign — `undefined` cannot be rebound, which is the hazard this ES3-era rule was written for',
220
+ kind: 'covered',
221
+ }),
222
+ 'prefer-promise-reject-errors': off({
223
+ by: 'typescript/prefer-promise-reject-errors',
224
+ kind: 'covered',
225
+ }),
226
+ 'require-await': off({ by: 'typescript/require-await', kind: 'covered' }),
227
+ 'sort-imports': off({ by: 'oxfmt sortImports', kind: 'covered' }),
228
+ 'sort-keys': off({
229
+ by: 'docs/07-lint-presets.md — an object orders by meaning, and generic inference is order-sensitive',
230
+ kind: 'convention',
231
+ }),
232
+ 'sort-vars': off({ by: 'one-var (never)', kind: 'covered' }),
233
+ },
234
+ });
@@ -0,0 +1,117 @@
1
+ import { allOn, fragment, off, on } from '../_contract.js';
2
+
3
+ /*
4
+ * The `import` plugin, all 31 non-nursery rules decided by name.
5
+ *
6
+ * `import/extensions` is the one rule two platforms answer differently: Node
7
+ * ESM resolves a specifier literally and needs the `.js`, a bundler resolves it
8
+ * and refuses one. Core states the Node answer; `rules/next.js`,
9
+ * `rules/astro.js`, `rules/react-native.js` and `rules/bundler.js` re-decide it
10
+ * for their platform, at `error` either way — a profile changes the convention,
11
+ * never the level.
12
+ *
13
+ * `import/no-cycle` rides here for +0.05s. Mind what it does NOT see: oxlint's
14
+ * implementation ignores type-only imports, so a value import one way and an
15
+ * `import type` back is invisible to it (oxc#20551).
16
+ */
17
+
18
+ /** Asset specifiers keep their extension on every platform — a bundler resolves them by it. */
19
+ export const ASSETS = {
20
+ avif: 'always',
21
+ css: 'always',
22
+ gif: 'always',
23
+ jpeg: 'always',
24
+ jpg: 'always',
25
+ json: 'always',
26
+ less: 'always',
27
+ png: 'always',
28
+ sass: 'always',
29
+ scss: 'always',
30
+ svg: 'always',
31
+ webp: 'always',
32
+ };
33
+
34
+ export const EXTENSIONS_ALWAYS = on(['always', { ignorePackages: true, ...ASSETS }]);
35
+ export const EXTENSIONS_NEVER = on(['never', ASSETS]);
36
+
37
+ export default fragment({
38
+ id: 'core/import',
39
+ plugins: ['import'],
40
+ rules: {
41
+ ...allOn(
42
+ [
43
+ 'default',
44
+ 'first',
45
+ 'namespace',
46
+ 'newline-after-import',
47
+ 'no-absolute-path',
48
+ 'no-amd',
49
+ 'no-commonjs',
50
+ 'no-cycle',
51
+ 'no-duplicates',
52
+ 'no-dynamic-require',
53
+ 'no-empty-named-blocks',
54
+ 'no-mutable-exports',
55
+ 'no-named-as-default',
56
+ 'no-named-as-default-member',
57
+ 'no-named-default',
58
+ 'no-namespace',
59
+ 'no-self-import',
60
+ 'no-unassigned-import',
61
+ 'no-webpack-loader-syntax',
62
+ ].map((rule) => `import/${rule}`),
63
+ ),
64
+
65
+ // -- On, at the strictest value the option carries ---------------------
66
+ /*
67
+ * Top level, not inline, and `verbatimModuleSyntax` is why: under it
68
+ * `import { type X } from 'leaflet'` is emitted as
69
+ * `import {} from 'leaflet'` — a runtime side-effect import of a module
70
+ * that may only exist in a browser. signews-web served a 500 from it.
71
+ */
72
+ 'import/consistent-type-specifier-style': on(['prefer-top-level']),
73
+ 'import/extensions': EXTENSIONS_ALWAYS,
74
+
75
+ // -- Off, each with its one reason -------------------------------------
76
+ 'import/exports-last': off({
77
+ by: 'docs/07-lint-presets.md — an export sits with the declaration it exports; three repositories had refused this rule locally before the decision moved here',
78
+ kind: 'convention',
79
+ }),
80
+ 'import/group-exports': off({
81
+ by: 'docs/07-lint-presets.md — an export sits with the declaration it exports',
82
+ kind: 'convention',
83
+ }),
84
+ 'import/max-dependencies': off({
85
+ by: 'oxc/no-barrel-file — the defect a dependency count stands in for is the barrel, and that rule names it',
86
+ kind: 'covered',
87
+ }),
88
+ 'import/no-anonymous-default-export': off({
89
+ by: 'unicorn/no-anonymous-default-export',
90
+ kind: 'covered',
91
+ }),
92
+ 'import/no-default-export': off({
93
+ by: 'docs/07-lint-presets.md — every framework config file and every preset of this package is a default export',
94
+ kind: 'convention',
95
+ }),
96
+ 'import/no-named-export': off({
97
+ by: 'docs/07-lint-presets.md — a module exports what it owns by name',
98
+ kind: 'convention',
99
+ }),
100
+ 'import/no-nodejs-modules': off({
101
+ by: 'docs/07-lint-presets.md — the estate ships Node services, and a browser boundary is the profile that states it',
102
+ kind: 'convention',
103
+ }),
104
+ 'import/no-relative-parent-imports': off({
105
+ by: 'docs/07-lint-presets.md — a relative parent import is how a package reaches its own sibling module; layer boundaries are the layer map, not the path shape',
106
+ kind: 'convention',
107
+ }),
108
+ 'import/prefer-default-export': off({
109
+ by: 'docs/07-lint-presets.md — a module exports what it owns by name',
110
+ kind: 'convention',
111
+ }),
112
+ 'import/unambiguous': off({
113
+ by: 'TypeScript — `verbatimModuleSyntax` and a package\'s `"type": "module"` already make every file a module, and what the rule reports beyond that is a file with nothing to export: an entry script, an Astro `is:inline` block',
114
+ kind: 'covered',
115
+ }),
116
+ },
117
+ });
@@ -0,0 +1,52 @@
1
+ import { allOn, fragment, off } from '../_contract.js';
2
+
3
+ /*
4
+ * The `jsdoc` plugin, all 23 rules decided by name. JSDoc is never REQUIRED —
5
+ * TypeScript carries the types and `typescript docs` derives the reference from
6
+ * them — but a block that is written must be well formed.
7
+ */
8
+ const BY_TYPESCRIPT = {
9
+ by: 'TypeScript — the signature carries the type, and `typescript docs` reads it from there',
10
+ kind: 'covered',
11
+ };
12
+
13
+ export default fragment({
14
+ id: 'core/jsdoc',
15
+ plugins: ['jsdoc'],
16
+ rules: {
17
+ ...allOn(
18
+ [
19
+ 'check-access',
20
+ 'check-property-names',
21
+ 'check-tag-names',
22
+ 'empty-tags',
23
+ 'implements-on-classes',
24
+ 'no-blank-blocks',
25
+ 'no-defaults',
26
+ 'require-param-description',
27
+ 'require-param-name',
28
+ 'require-property',
29
+ 'require-property-description',
30
+ 'require-property-name',
31
+ 'require-returns-description',
32
+ 'require-throws-description',
33
+ 'require-yields',
34
+ 'require-yields-description',
35
+ ].map((rule) => `jsdoc/${rule}`),
36
+ ),
37
+
38
+ 'jsdoc/require-param': off({
39
+ by: 'TypeScript — a parameter is documented by its type, and a description is optional prose',
40
+ kind: 'covered',
41
+ }),
42
+ 'jsdoc/require-param-type': off(BY_TYPESCRIPT),
43
+ 'jsdoc/require-property-type': off(BY_TYPESCRIPT),
44
+ 'jsdoc/require-returns': off({
45
+ by: 'TypeScript — a return is documented by its type, and a description is optional prose',
46
+ kind: 'covered',
47
+ }),
48
+ 'jsdoc/require-returns-type': off(BY_TYPESCRIPT),
49
+ 'jsdoc/require-throws-type': off(BY_TYPESCRIPT),
50
+ 'jsdoc/require-yields-type': off(BY_TYPESCRIPT),
51
+ },
52
+ });
@@ -0,0 +1,36 @@
1
+ import { allOn, fragment, off } from '../_contract.js';
2
+
3
+ /*
4
+ * The `node` plugin, all 11 rules decided by name.
5
+ */
6
+ export default fragment({
7
+ id: 'core/node',
8
+ plugins: ['node'],
9
+ rules: {
10
+ ...allOn(
11
+ [
12
+ 'callback-return',
13
+ 'exports-style',
14
+ 'global-require',
15
+ 'handle-callback-err',
16
+ 'no-exports-assign',
17
+ 'no-mixed-requires',
18
+ 'no-new-require',
19
+ 'no-path-concat',
20
+ ].map((rule) => `node/${rule}`),
21
+ ),
22
+
23
+ 'node/no-process-env': off({
24
+ by: 'docs/07-lint-presets.md — the estate reads configuration from the environment at the edge and has no config-module to funnel it through',
25
+ kind: 'convention',
26
+ }),
27
+ 'node/no-sync': off({
28
+ by: 'docs/07-lint-presets.md — a one-shot CLI script has no event loop to protect, and the toolchain gates are exactly that',
29
+ kind: 'convention',
30
+ }),
31
+ 'node/no-top-level-await': off({
32
+ by: 'unicorn/prefer-top-level-await — one rule demands it, the other forbids it',
33
+ kind: 'exclusive',
34
+ }),
35
+ },
36
+ });
@@ -0,0 +1,54 @@
1
+ import { allOn, fragment, off } from '../_contract.js';
2
+
3
+ /*
4
+ * The `oxc` plugin, all 27 rules decided by name. Three of them are
5
+ * downlevel-syntax restrictions — they exist for a target older than the one
6
+ * the estate compiles to — and they are the only three that are off.
7
+ */
8
+ export default fragment({
9
+ id: 'core/oxc',
10
+ plugins: ['oxc'],
11
+ rules: {
12
+ ...allOn(
13
+ [
14
+ 'approx-constant',
15
+ 'bad-array-method-on-arguments',
16
+ 'bad-bitwise-operator',
17
+ 'bad-char-at-comparison',
18
+ 'bad-comparison-sequence',
19
+ 'bad-match-all-arg',
20
+ 'bad-min-max-func',
21
+ 'bad-object-literal-comparison',
22
+ 'bad-replace-all-arg',
23
+ 'branches-sharing-code',
24
+ 'const-comparisons',
25
+ 'double-comparisons',
26
+ 'erasing-op',
27
+ 'misrefactored-assign-op',
28
+ 'missing-throw',
29
+ 'no-accumulating-spread',
30
+ 'no-async-endpoint-handlers',
31
+ 'no-barrel-file',
32
+ 'no-const-enum',
33
+ 'no-map-spread',
34
+ 'no-this-in-exported-function',
35
+ 'number-arg-out-of-range',
36
+ 'only-used-in-recursion',
37
+ 'uninvoked-array-callback',
38
+ ].map((rule) => `oxc/${rule}`),
39
+ ),
40
+
41
+ 'oxc/no-async-await': off({
42
+ by: 'promise/prefer-await-to-then — one rule demands await, the other forbids it',
43
+ kind: 'exclusive',
44
+ }),
45
+ 'oxc/no-optional-chaining': off({
46
+ by: 'docs/07-lint-presets.md — the estate compiles to ESNext on Node 24, where optional chaining is native syntax',
47
+ kind: 'convention',
48
+ }),
49
+ 'oxc/no-rest-spread-properties': off({
50
+ by: 'docs/07-lint-presets.md — the estate compiles to ESNext on Node 24, where object rest and spread are native syntax',
51
+ kind: 'convention',
52
+ }),
53
+ },
54
+ });
@@ -0,0 +1,39 @@
1
+ import { allOn, fragment, off } from '../_contract.js';
2
+
3
+ /*
4
+ * The `promise` plugin, all 15 non-nursery rules decided by name. The estate
5
+ * writes `await`, so the two rules that describe a well-behaved `.then()`
6
+ * chain are the ones that cannot hold beside `prefer-await-to-then`.
7
+ */
8
+ export default fragment({
9
+ id: 'core/promise',
10
+ plugins: ['promise'],
11
+ rules: {
12
+ ...allOn(
13
+ [
14
+ 'avoid-new',
15
+ 'no-callback-in-promise',
16
+ 'no-multiple-resolved',
17
+ 'no-nesting',
18
+ 'no-new-statics',
19
+ 'no-promise-in-callback',
20
+ 'no-return-wrap',
21
+ 'param-names',
22
+ 'prefer-await-to-callbacks',
23
+ 'prefer-await-to-then',
24
+ 'prefer-catch',
25
+ 'spec-only',
26
+ 'valid-params',
27
+ ].map((rule) => `promise/${rule}`),
28
+ ),
29
+
30
+ 'promise/always-return': off({
31
+ by: 'promise/prefer-await-to-then — it describes a .then() chain the estate does not write',
32
+ kind: 'exclusive',
33
+ }),
34
+ 'promise/catch-or-return': off({
35
+ by: 'promise/prefer-await-to-then — it describes a .then() chain the estate does not write',
36
+ kind: 'exclusive',
37
+ }),
38
+ },
39
+ });