@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.
- package/README.md +20 -16
- package/bin/commands/check.sh +387 -146
- package/bin/find-tsc.sh +30 -0
- package/bin/typescript.sh +79 -1
- package/lib/check-architecture.js +89 -0
- package/lib/check-baseline.js +144 -0
- package/lib/check-docs.js +4 -3
- package/lib/check-drift.js +209 -0
- package/lib/check-gitignore.js +4 -4
- package/lib/check-markdown.js +279 -0
- package/lib/check-names.js +125 -0
- package/lib/check-publish.js +150 -0
- package/lib/check-secrets.js +115 -0
- package/lib/check-suppressions.js +355 -0
- package/lib/doctor.js +185 -0
- package/lib/entry-points.js +91 -0
- package/lib/merge-knip-config.js +57 -25
- package/lib/tracked-files.js +165 -0
- package/lib/unsafe-fixers.js +25 -0
- package/lib/workspace-members.js +5 -6
- package/package.json +36 -13
- package/presets/oxfmt/index.js +49 -5
- package/presets/oxlint/profiles/astro.js +10 -0
- package/presets/oxlint/profiles/bun.js +7 -0
- package/presets/oxlint/profiles/expo.js +7 -0
- package/presets/oxlint/profiles/library.js +16 -0
- package/presets/oxlint/profiles/next.js +7 -0
- package/presets/oxlint/profiles/node.js +7 -0
- package/presets/oxlint/profiles/react.js +7 -0
- package/presets/prettier/astro.json +6 -0
- package/presets/tsconfig/astro.json +25 -0
- package/presets/tsconfig/expo.json +16 -6
- package/presets/tsconfig/library.json +17 -0
- package/presets/tsconfig/next.json +12 -2
- package/presets/tsconfig/node.json +18 -4
- package/presets/tsconfig/react.json +33 -0
- package/presets/tsdown/build.d.ts +13 -0
- package/presets/tsdown/bundle.d.ts +13 -0
- package/presets/tsdown/bundle.js +10 -1
- package/rules/README.md +23 -0
- package/rules/_contract.js +207 -0
- package/rules/_contract.test.ts +81 -0
- package/rules/a11y.js +51 -0
- package/rules/architecture/hexagonal.js +56 -0
- package/rules/architecture/layers.js +75 -0
- package/rules/astro.js +56 -0
- package/rules/bundler.js +19 -0
- package/rules/catalog.js +166 -0
- package/rules/catalog.test.ts +98 -0
- package/rules/compile.js +125 -0
- package/rules/core/eslint.js +234 -0
- package/rules/core/import.js +117 -0
- package/rules/core/jsdoc.js +52 -0
- package/rules/core/node.js +36 -0
- package/rules/core/oxc.js +54 -0
- package/rules/core/promise.js +39 -0
- package/rules/core/typescript.js +223 -0
- package/rules/core/unicorn.js +210 -0
- package/rules/next.js +53 -0
- package/rules/profiles.js +95 -0
- package/rules/react-native.js +48 -0
- package/rules/react.js +155 -0
- package/rules/sorted.js +41 -0
- package/rules/vitest.js +178 -0
- package/src/docs.d.ts +4 -4
- package/src/docs.js +75 -57
- package/src/docs.test.ts +43 -31
- package/src/index.d.ts +14 -9
- package/src/index.js +17 -8
- package/src/oxfmt.d.ts +15 -2
- package/src/oxfmt.test.ts +10 -0
- package/src/oxlint.d.ts +59 -10
- package/src/oxlint.js +36 -50
- package/src/oxlint.test.ts +82 -28
- package/presets/oxlint/architectures/hexagonal-rules.js +0 -39
- package/presets/oxlint/architectures/hexagonal.js +0 -13
- package/presets/oxlint/base.js +0 -145
- package/presets/oxlint/expo.js +0 -36
- package/presets/oxlint/next.js +0 -43
- package/presets/oxlint/node.js +0 -14
- package/presets/oxlint/plugins/codestyle.js +0 -231
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { allOn, fragment, off, on, scoped, typeAware, unsafeFix } from '../_contract.js';
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* The `typescript` plugin, all 108 non-nursery rules decided by name. Fifty-one
|
|
5
|
+
* of them need type information: they run because every profile ships
|
|
6
|
+
* `options.typeAware`, which `oxlint-tsgolint` answers (it embeds its own
|
|
7
|
+
* TypeScript, so a consumer is not forced onto TypeScript 7 to get them).
|
|
8
|
+
*/
|
|
9
|
+
/*
|
|
10
|
+
* A JavaScript file has no parameter types. Every value flowing through one is
|
|
11
|
+
* `any` by construction, so the unsafe-any family reports the LANGUAGE, not a
|
|
12
|
+
* defect — measured on this package's own tree, 530 reports of which not one
|
|
13
|
+
* named a bug. The family stays armed everywhere TypeScript types the code.
|
|
14
|
+
*/
|
|
15
|
+
const UNTYPED_BY_CONSTRUCTION = {
|
|
16
|
+
by: 'TypeScript — a .js file has no parameter types, so every value in it is `any` and the rule reports the language, not a defect',
|
|
17
|
+
kind: 'covered',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const UNTYPED_IN_JAVASCRIPT = [
|
|
21
|
+
'no-unsafe-argument',
|
|
22
|
+
'no-unsafe-assignment',
|
|
23
|
+
'no-unsafe-call',
|
|
24
|
+
'no-unsafe-enum-comparison',
|
|
25
|
+
'no-unsafe-member-access',
|
|
26
|
+
'no-unsafe-return',
|
|
27
|
+
'no-unsafe-type-assertion',
|
|
28
|
+
'no-unsafe-unary-minus',
|
|
29
|
+
'strict-boolean-expressions',
|
|
30
|
+
].map((rule) => [`typescript/${rule}`, off(UNTYPED_BY_CONSTRUCTION)]);
|
|
31
|
+
|
|
32
|
+
export default fragment({
|
|
33
|
+
id: 'core/typescript',
|
|
34
|
+
plugins: ['typescript'],
|
|
35
|
+
overrides: [
|
|
36
|
+
scoped({
|
|
37
|
+
files: ['**/*.js', '**/*.cjs', '**/*.mjs'],
|
|
38
|
+
rules: Object.fromEntries(UNTYPED_IN_JAVASCRIPT),
|
|
39
|
+
}),
|
|
40
|
+
],
|
|
41
|
+
rules: {
|
|
42
|
+
...allOn(
|
|
43
|
+
[
|
|
44
|
+
'adjacent-overload-signatures',
|
|
45
|
+
'ban-tslint-comment',
|
|
46
|
+
'class-literal-property-style',
|
|
47
|
+
'consistent-generic-constructors',
|
|
48
|
+
'consistent-indexed-object-style',
|
|
49
|
+
'consistent-type-assertions',
|
|
50
|
+
'no-confusing-non-null-assertion',
|
|
51
|
+
'no-duplicate-enum-values',
|
|
52
|
+
'no-dynamic-delete',
|
|
53
|
+
'no-empty-object-type',
|
|
54
|
+
'no-explicit-any',
|
|
55
|
+
'no-extra-non-null-assertion',
|
|
56
|
+
'no-extraneous-class',
|
|
57
|
+
'no-inferrable-types',
|
|
58
|
+
'no-invalid-void-type',
|
|
59
|
+
'no-misused-new',
|
|
60
|
+
'no-namespace',
|
|
61
|
+
'no-non-null-asserted-nullish-coalescing',
|
|
62
|
+
'no-non-null-asserted-optional-chain',
|
|
63
|
+
'no-non-null-assertion',
|
|
64
|
+
'no-require-imports',
|
|
65
|
+
'no-restricted-types',
|
|
66
|
+
'no-this-alias',
|
|
67
|
+
'no-unnecessary-parameter-property-assignment',
|
|
68
|
+
'no-unnecessary-type-constraint',
|
|
69
|
+
'no-unsafe-declaration-merging',
|
|
70
|
+
'no-unsafe-function-type',
|
|
71
|
+
'no-useless-empty-export',
|
|
72
|
+
'no-wrapper-object-types',
|
|
73
|
+
'parameter-properties',
|
|
74
|
+
'prefer-as-const',
|
|
75
|
+
'prefer-enum-initializers',
|
|
76
|
+
'prefer-for-of',
|
|
77
|
+
'prefer-function-type',
|
|
78
|
+
'prefer-literal-enum-member',
|
|
79
|
+
'prefer-namespace-keyword',
|
|
80
|
+
'prefer-ts-expect-error',
|
|
81
|
+
'triple-slash-reference',
|
|
82
|
+
'unified-signatures',
|
|
83
|
+
].map((rule) => `typescript/${rule}`),
|
|
84
|
+
),
|
|
85
|
+
|
|
86
|
+
// -- On, and type-aware ----------------------------------------------
|
|
87
|
+
...Object.fromEntries(
|
|
88
|
+
[
|
|
89
|
+
'await-thenable',
|
|
90
|
+
'consistent-return',
|
|
91
|
+
'consistent-type-exports',
|
|
92
|
+
'dot-notation',
|
|
93
|
+
'no-array-delete',
|
|
94
|
+
'no-base-to-string',
|
|
95
|
+
'no-deprecated',
|
|
96
|
+
'no-duplicate-type-constituents',
|
|
97
|
+
'no-floating-promises',
|
|
98
|
+
'no-for-in-array',
|
|
99
|
+
'no-implied-eval',
|
|
100
|
+
'no-meaningless-void-operator',
|
|
101
|
+
'no-misused-promises',
|
|
102
|
+
'no-misused-spread',
|
|
103
|
+
'no-mixed-enums',
|
|
104
|
+
'no-redundant-type-constituents',
|
|
105
|
+
'no-unnecessary-boolean-literal-compare',
|
|
106
|
+
'no-unnecessary-qualifier',
|
|
107
|
+
'no-unnecessary-template-expression',
|
|
108
|
+
'no-unnecessary-type-arguments',
|
|
109
|
+
'no-unnecessary-type-conversion',
|
|
110
|
+
'no-unnecessary-type-parameters',
|
|
111
|
+
'no-unsafe-argument',
|
|
112
|
+
'no-unsafe-assignment',
|
|
113
|
+
'no-unsafe-call',
|
|
114
|
+
'no-unsafe-enum-comparison',
|
|
115
|
+
'no-unsafe-member-access',
|
|
116
|
+
'no-unsafe-return',
|
|
117
|
+
'no-unsafe-type-assertion',
|
|
118
|
+
'no-unsafe-unary-minus',
|
|
119
|
+
'no-useless-default-assignment',
|
|
120
|
+
'only-throw-error',
|
|
121
|
+
'prefer-find',
|
|
122
|
+
'prefer-includes',
|
|
123
|
+
'prefer-promise-reject-errors',
|
|
124
|
+
'prefer-readonly',
|
|
125
|
+
'prefer-reduce-type-parameter',
|
|
126
|
+
'prefer-regexp-exec',
|
|
127
|
+
'prefer-return-this-type',
|
|
128
|
+
'prefer-string-starts-ends-with',
|
|
129
|
+
'promise-function-async',
|
|
130
|
+
'related-getter-setter-pairs',
|
|
131
|
+
'require-array-sort-compare',
|
|
132
|
+
'require-await',
|
|
133
|
+
'restrict-plus-operands',
|
|
134
|
+
'restrict-template-expressions',
|
|
135
|
+
'strict-boolean-expressions',
|
|
136
|
+
'strict-void-return',
|
|
137
|
+
'switch-exhaustiveness-check',
|
|
138
|
+
'unbound-method',
|
|
139
|
+
'use-unknown-in-catch-callback-variable',
|
|
140
|
+
].map((rule) => [`typescript/${rule}`, typeAware()]),
|
|
141
|
+
),
|
|
142
|
+
|
|
143
|
+
// -- On, at the strictest value the option carries ---------------------
|
|
144
|
+
'typescript/array-type': on([{ default: 'array' }]),
|
|
145
|
+
'typescript/ban-ts-comment': on([
|
|
146
|
+
{
|
|
147
|
+
'ts-check': false,
|
|
148
|
+
'ts-expect-error': 'allow-with-description',
|
|
149
|
+
'ts-ignore': true,
|
|
150
|
+
'ts-nocheck': true,
|
|
151
|
+
},
|
|
152
|
+
]),
|
|
153
|
+
/* Three fixers that change meaning, so `fix` never applies them and
|
|
154
|
+
* `check` still reports them: one rewrites a `declare module`
|
|
155
|
+
* augmentation into an alias that no longer merges, one wraps a
|
|
156
|
+
* returned promise so nothing awaits it, one drops a cast a widened
|
|
157
|
+
* platform type needs. */
|
|
158
|
+
'typescript/consistent-type-definitions': unsafeFix(
|
|
159
|
+
on(['type']),
|
|
160
|
+
'rewrites a `declare module` augmentation into an alias, which no longer merges',
|
|
161
|
+
),
|
|
162
|
+
'typescript/no-confusing-void-expression': unsafeFix(
|
|
163
|
+
typeAware(),
|
|
164
|
+
'wraps a returned promise, after which nothing awaits it',
|
|
165
|
+
),
|
|
166
|
+
'typescript/no-unnecessary-type-assertion': unsafeFix(
|
|
167
|
+
typeAware(),
|
|
168
|
+
'drops a cast a widened platform type needs',
|
|
169
|
+
),
|
|
170
|
+
/* `separate-type-imports`, for `import/consistent-type-specifier-style`'s
|
|
171
|
+
* reason: an inline type specifier survives `verbatimModuleSyntax` as an
|
|
172
|
+
* empty runtime import. */
|
|
173
|
+
'typescript/consistent-type-imports': on([
|
|
174
|
+
{
|
|
175
|
+
disallowTypeAnnotations: true,
|
|
176
|
+
fixStyle: 'separate-type-imports',
|
|
177
|
+
prefer: 'type-imports',
|
|
178
|
+
},
|
|
179
|
+
]),
|
|
180
|
+
/* The guard for the emit above: an all-inline type import is the shape
|
|
181
|
+
* `verbatimModuleSyntax` turns into a side effect, and this names it. */
|
|
182
|
+
'typescript/no-import-type-side-effects': on(),
|
|
183
|
+
'typescript/explicit-member-accessibility': on([{ accessibility: 'no-public' }]),
|
|
184
|
+
/* Booleans excepted: `false ?? x` is `false` and `false || x` is `x`,
|
|
185
|
+
* so on a boolean the two operators mean different things and the
|
|
186
|
+
* rewrite the rule asks for is not the same expression. */
|
|
187
|
+
'typescript/prefer-nullish-coalescing': typeAware([
|
|
188
|
+
{ ignorePrimitives: { boolean: true } },
|
|
189
|
+
]),
|
|
190
|
+
'typescript/method-signature-style': on(['property']),
|
|
191
|
+
'typescript/return-await': typeAware(['always']),
|
|
192
|
+
|
|
193
|
+
// -- Off, each with its one reason -------------------------------------
|
|
194
|
+
'typescript/ban-types': off({
|
|
195
|
+
by: 'typescript/no-empty-object-type, no-unsafe-function-type, no-wrapper-object-types — its three successors',
|
|
196
|
+
kind: 'covered',
|
|
197
|
+
}),
|
|
198
|
+
'typescript/explicit-function-return-type': off({
|
|
199
|
+
by: 'presets/tsdown/bundle.js — isolatedDeclarations requires the annotation exactly where it is load-bearing, on a published export',
|
|
200
|
+
kind: 'covered',
|
|
201
|
+
}),
|
|
202
|
+
'typescript/explicit-module-boundary-types': off({
|
|
203
|
+
by: 'presets/tsdown/bundle.js — isolatedDeclarations requires the annotation exactly where it is load-bearing, on a published export',
|
|
204
|
+
kind: 'covered',
|
|
205
|
+
}),
|
|
206
|
+
'typescript/no-empty-interface': off({
|
|
207
|
+
by: 'typescript/no-empty-object-type — its upstream successor',
|
|
208
|
+
kind: 'covered',
|
|
209
|
+
}),
|
|
210
|
+
'typescript/non-nullable-type-assertion-style': off({
|
|
211
|
+
by: 'typescript/no-non-null-assertion — its fix is the `!` assertion that rule forbids, so no edit closes both',
|
|
212
|
+
kind: 'exclusive',
|
|
213
|
+
}),
|
|
214
|
+
'typescript/no-var-requires': off({
|
|
215
|
+
by: 'typescript/no-require-imports — its upstream successor',
|
|
216
|
+
kind: 'covered',
|
|
217
|
+
}),
|
|
218
|
+
'typescript/prefer-readonly-parameter-types': off({
|
|
219
|
+
by: 'no-param-reassign, typescript/prefer-readonly — the defect is mutation, and both name it',
|
|
220
|
+
kind: 'covered',
|
|
221
|
+
}),
|
|
222
|
+
},
|
|
223
|
+
});
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { allOn, fragment, off, on, unsafeFix } from '../_contract.js';
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* The `unicorn` plugin, all 137 non-nursery rules decided by name. Two of the
|
|
5
|
+
* offs are the formatter's: unicorn's fixer and oxfmt disagree about the same
|
|
6
|
+
* bytes, and the fixpoint suite is what proves it (specs/cli/preset).
|
|
7
|
+
*/
|
|
8
|
+
export default fragment({
|
|
9
|
+
id: 'core/unicorn',
|
|
10
|
+
plugins: ['unicorn'],
|
|
11
|
+
rules: {
|
|
12
|
+
...allOn(
|
|
13
|
+
[
|
|
14
|
+
'catch-error-name',
|
|
15
|
+
'consistent-assert',
|
|
16
|
+
'consistent-date-clone',
|
|
17
|
+
'consistent-empty-array-spread',
|
|
18
|
+
'consistent-existence-index-check',
|
|
19
|
+
'consistent-function-scoping',
|
|
20
|
+
'consistent-template-literal-escape',
|
|
21
|
+
'custom-error-definition',
|
|
22
|
+
'empty-brace-spaces',
|
|
23
|
+
'error-message',
|
|
24
|
+
'escape-case',
|
|
25
|
+
'explicit-timer-delay',
|
|
26
|
+
'max-nested-calls',
|
|
27
|
+
'new-for-builtins',
|
|
28
|
+
'no-abusive-eslint-disable',
|
|
29
|
+
'no-accessor-recursion',
|
|
30
|
+
'no-anonymous-default-export',
|
|
31
|
+
'no-array-fill-with-reference-type',
|
|
32
|
+
'no-array-for-each',
|
|
33
|
+
'no-array-method-this-argument',
|
|
34
|
+
'no-array-reduce',
|
|
35
|
+
'no-array-reverse',
|
|
36
|
+
'no-array-sort',
|
|
37
|
+
'no-await-expression-member',
|
|
38
|
+
'no-await-in-promise-methods',
|
|
39
|
+
'no-confusing-array-with',
|
|
40
|
+
'no-console-spaces',
|
|
41
|
+
'no-document-cookie',
|
|
42
|
+
'no-empty-file',
|
|
43
|
+
'no-hex-escape',
|
|
44
|
+
'no-immediate-mutation',
|
|
45
|
+
'no-instanceof-builtins',
|
|
46
|
+
'no-invalid-fetch-options',
|
|
47
|
+
'no-invalid-remove-event-listener',
|
|
48
|
+
'no-length-as-slice-end',
|
|
49
|
+
'no-lonely-if',
|
|
50
|
+
'no-magic-array-flat-depth',
|
|
51
|
+
'no-negated-condition',
|
|
52
|
+
'no-negation-in-equality-check',
|
|
53
|
+
'no-new-array',
|
|
54
|
+
'no-new-buffer',
|
|
55
|
+
'no-object-as-default-parameter',
|
|
56
|
+
'no-process-exit',
|
|
57
|
+
'no-single-promise-in-promise-methods',
|
|
58
|
+
'no-static-only-class',
|
|
59
|
+
'no-thenable',
|
|
60
|
+
'no-this-assignment',
|
|
61
|
+
'no-typeof-undefined',
|
|
62
|
+
'no-unnecessary-array-flat-depth',
|
|
63
|
+
'no-unnecessary-array-splice-count',
|
|
64
|
+
'no-unnecessary-await',
|
|
65
|
+
'no-unnecessary-slice-end',
|
|
66
|
+
'no-unreadable-array-destructuring',
|
|
67
|
+
'no-unreadable-iife',
|
|
68
|
+
'no-useless-collection-argument',
|
|
69
|
+
'no-useless-error-capture-stack-trace',
|
|
70
|
+
'no-useless-fallback-in-spread',
|
|
71
|
+
'no-useless-length-check',
|
|
72
|
+
'no-useless-promise-resolve-reject',
|
|
73
|
+
'no-useless-spread',
|
|
74
|
+
'no-useless-switch-case',
|
|
75
|
+
'no-zero-fractions',
|
|
76
|
+
'numeric-separators-style',
|
|
77
|
+
'prefer-add-event-listener',
|
|
78
|
+
'prefer-array-find',
|
|
79
|
+
'prefer-array-flat',
|
|
80
|
+
'prefer-array-flat-map',
|
|
81
|
+
'prefer-array-index-of',
|
|
82
|
+
'prefer-array-some',
|
|
83
|
+
'prefer-at',
|
|
84
|
+
'prefer-bigint-literals',
|
|
85
|
+
'prefer-blob-reading-methods',
|
|
86
|
+
'prefer-class-fields',
|
|
87
|
+
'prefer-classlist-toggle',
|
|
88
|
+
'prefer-code-point',
|
|
89
|
+
'prefer-date-now',
|
|
90
|
+
'prefer-default-parameters',
|
|
91
|
+
'prefer-dom-node-append',
|
|
92
|
+
'prefer-dom-node-dataset',
|
|
93
|
+
'prefer-dom-node-remove',
|
|
94
|
+
'prefer-dom-node-text-content',
|
|
95
|
+
'prefer-event-target',
|
|
96
|
+
'prefer-export-from',
|
|
97
|
+
'prefer-global-this',
|
|
98
|
+
'prefer-keyboard-event-key',
|
|
99
|
+
'prefer-logical-operator-over-ternary',
|
|
100
|
+
'prefer-math-min-max',
|
|
101
|
+
'prefer-math-trunc',
|
|
102
|
+
'prefer-modern-dom-apis',
|
|
103
|
+
'prefer-modern-math-apis',
|
|
104
|
+
'prefer-module',
|
|
105
|
+
'prefer-native-coercion-functions',
|
|
106
|
+
'prefer-negative-index',
|
|
107
|
+
'prefer-node-protocol',
|
|
108
|
+
'prefer-number-properties',
|
|
109
|
+
'prefer-object-from-entries',
|
|
110
|
+
'prefer-optional-catch-binding',
|
|
111
|
+
'prefer-prototype-methods',
|
|
112
|
+
'prefer-query-selector',
|
|
113
|
+
'prefer-reflect-apply',
|
|
114
|
+
'prefer-regexp-test',
|
|
115
|
+
'prefer-response-static-json',
|
|
116
|
+
'prefer-set-has',
|
|
117
|
+
'prefer-set-size',
|
|
118
|
+
'prefer-single-call',
|
|
119
|
+
'prefer-spread',
|
|
120
|
+
'prefer-string-raw',
|
|
121
|
+
'prefer-string-replace-all',
|
|
122
|
+
'prefer-string-slice',
|
|
123
|
+
'prefer-string-trim-start-end',
|
|
124
|
+
'prefer-structured-clone',
|
|
125
|
+
'prefer-ternary',
|
|
126
|
+
'prefer-top-level-await',
|
|
127
|
+
'prefer-type-error',
|
|
128
|
+
'relative-url-style',
|
|
129
|
+
'require-array-join-separator',
|
|
130
|
+
'require-module-attributes',
|
|
131
|
+
'require-module-specifiers',
|
|
132
|
+
'require-number-to-fixed-digits-argument',
|
|
133
|
+
'require-post-message-target-origin',
|
|
134
|
+
'switch-case-braces',
|
|
135
|
+
'switch-case-break-position',
|
|
136
|
+
'throw-new-error',
|
|
137
|
+
].map((rule) => `unicorn/${rule}`),
|
|
138
|
+
),
|
|
139
|
+
|
|
140
|
+
// -- On, at the strictest value the option carries ---------------------
|
|
141
|
+
'unicorn/filename-case': on([{ case: 'kebabCase' }]),
|
|
142
|
+
/* The estate imports a Node builtin by name — `import { resolve } from
|
|
143
|
+
* 'node:path'` — because that is what the type-aware rules read and what
|
|
144
|
+
* `verbatimModuleSyntax` keeps. The rule's default asks for the default
|
|
145
|
+
* import on exactly these three. */
|
|
146
|
+
'unicorn/import-style': on([
|
|
147
|
+
{
|
|
148
|
+
styles: {
|
|
149
|
+
'node:path': { named: true },
|
|
150
|
+
'node:util': { named: true },
|
|
151
|
+
path: { named: true },
|
|
152
|
+
util: { named: true },
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
]),
|
|
156
|
+
/* No `withDash`: the estate writes `'utf8'`, which is the rule's own
|
|
157
|
+
* default preference, and `'utf-8'` is the same encoding spelled longer. */
|
|
158
|
+
'unicorn/text-encoding-identifier-case': on(),
|
|
159
|
+
|
|
160
|
+
/* Both fixers change meaning, so `fix` never applies them and `check`
|
|
161
|
+
* still reports them: one strips a REQUIRED argument, the other drops
|
|
162
|
+
* the trailing slash `new URL('.', import.meta.url)` carries. */
|
|
163
|
+
'unicorn/no-useless-undefined': unsafeFix(
|
|
164
|
+
on(),
|
|
165
|
+
'strips a REQUIRED argument, `mockReturnValue(undefined)` included (TS2554)',
|
|
166
|
+
),
|
|
167
|
+
'unicorn/prefer-import-meta-properties': unsafeFix(
|
|
168
|
+
on(),
|
|
169
|
+
"rewrites `fileURLToPath(new URL('.', import.meta.url))` into `import.meta.dirname`, which carries no trailing slash",
|
|
170
|
+
),
|
|
171
|
+
|
|
172
|
+
// -- Off, each with its one reason -------------------------------------
|
|
173
|
+
'unicorn/explicit-length-check': off({
|
|
174
|
+
by: 'typescript/strict-boolean-expressions — it already refuses a length used as a condition',
|
|
175
|
+
kind: 'covered',
|
|
176
|
+
}),
|
|
177
|
+
'unicorn/no-array-callback-reference': off({
|
|
178
|
+
by: 'measured on this package — every report was a correct point-free callback',
|
|
179
|
+
kind: 'evidence',
|
|
180
|
+
}),
|
|
181
|
+
'unicorn/no-instanceof-array': off({
|
|
182
|
+
by: 'unicorn/no-instanceof-builtins — its upstream successor',
|
|
183
|
+
kind: 'covered',
|
|
184
|
+
}),
|
|
185
|
+
'unicorn/no-nested-ternary': off({
|
|
186
|
+
by: 'oxfmt — its fixer parenthesises the inner ternary and the formatter strips the parentheses, so every fix round ping-pongs',
|
|
187
|
+
kind: 'formatter',
|
|
188
|
+
}),
|
|
189
|
+
'unicorn/no-null': off({
|
|
190
|
+
by: 'docs/07-lint-presets.md — null is what the platform and JSON APIs return, and a codebase that refuses it grows a translation layer at every boundary',
|
|
191
|
+
kind: 'convention',
|
|
192
|
+
}),
|
|
193
|
+
'unicorn/prefer-number-coercion': off({
|
|
194
|
+
by: 'measured on this package — it rewrites `Number.parseInt("01-architecture.md", 10)`, which is 1, into `Math.trunc(Number(...))`, which is NaN; the rule assumes the string holds nothing but digits',
|
|
195
|
+
kind: 'evidence',
|
|
196
|
+
}),
|
|
197
|
+
'unicorn/number-literal-case': off({
|
|
198
|
+
by: 'oxfmt — oxc#21949: the two disagree on the case of a hex literal and rewrite each other',
|
|
199
|
+
kind: 'formatter',
|
|
200
|
+
}),
|
|
201
|
+
'unicorn/prefer-includes': off({
|
|
202
|
+
by: 'typescript/prefer-includes — the type-aware rule of the same name',
|
|
203
|
+
kind: 'covered',
|
|
204
|
+
}),
|
|
205
|
+
'unicorn/prefer-string-starts-ends-with': off({
|
|
206
|
+
by: 'typescript/prefer-string-starts-ends-with — the type-aware rule of the same name',
|
|
207
|
+
kind: 'covered',
|
|
208
|
+
}),
|
|
209
|
+
},
|
|
210
|
+
});
|
package/rules/next.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { allOn, fragment, off, scoped } from './_contract.js';
|
|
2
|
+
import { EXTENSIONS_NEVER } from './core/import.js';
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* The `nextjs` plugin, all 21 rules on, plus the two decisions Next's own
|
|
6
|
+
* resolution forces: a bundler resolves an import, so a specifier carries no
|
|
7
|
+
* extension, and `next-env.d.ts` is a side-effect import Next writes itself.
|
|
8
|
+
*/
|
|
9
|
+
export default fragment({
|
|
10
|
+
id: 'next',
|
|
11
|
+
plugins: ['nextjs'],
|
|
12
|
+
ignorePatterns: ['.next/**', 'next-env.d.ts', 'assets/**', 'public/**'],
|
|
13
|
+
overrides: [
|
|
14
|
+
scoped({
|
|
15
|
+
files: ['**/next-env.d.ts'],
|
|
16
|
+
rules: {
|
|
17
|
+
'import/no-unassigned-import': off({
|
|
18
|
+
by: 'Next writes this file, and its whole body is a triple-slash reference',
|
|
19
|
+
kind: 'convention',
|
|
20
|
+
}),
|
|
21
|
+
},
|
|
22
|
+
}),
|
|
23
|
+
],
|
|
24
|
+
rules: {
|
|
25
|
+
...allOn(
|
|
26
|
+
[
|
|
27
|
+
'google-font-display',
|
|
28
|
+
'google-font-preconnect',
|
|
29
|
+
'inline-script-id',
|
|
30
|
+
'next-script-for-ga',
|
|
31
|
+
'no-assign-module-variable',
|
|
32
|
+
'no-async-client-component',
|
|
33
|
+
'no-before-interactive-script-outside-document',
|
|
34
|
+
'no-css-tags',
|
|
35
|
+
'no-document-import-in-page',
|
|
36
|
+
'no-duplicate-head',
|
|
37
|
+
'no-head-element',
|
|
38
|
+
'no-head-import-in-document',
|
|
39
|
+
'no-html-link-for-pages',
|
|
40
|
+
'no-img-element',
|
|
41
|
+
'no-page-custom-font',
|
|
42
|
+
'no-script-component-in-head',
|
|
43
|
+
'no-styled-jsx-in-document',
|
|
44
|
+
'no-sync-scripts',
|
|
45
|
+
'no-title-in-document-head',
|
|
46
|
+
'no-typos',
|
|
47
|
+
'no-unwanted-polyfillio',
|
|
48
|
+
].map((rule) => `nextjs/${rule}`),
|
|
49
|
+
),
|
|
50
|
+
|
|
51
|
+
'import/extensions': EXTENSIONS_NEVER,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import a11y from './a11y.js';
|
|
2
|
+
import astro from './astro.js';
|
|
3
|
+
import bundler from './bundler.js';
|
|
4
|
+
import eslint from './core/eslint.js';
|
|
5
|
+
import importPlugin from './core/import.js';
|
|
6
|
+
import jsdoc from './core/jsdoc.js';
|
|
7
|
+
import node from './core/node.js';
|
|
8
|
+
import oxc from './core/oxc.js';
|
|
9
|
+
import promise from './core/promise.js';
|
|
10
|
+
import typescript from './core/typescript.js';
|
|
11
|
+
import unicorn from './core/unicorn.js';
|
|
12
|
+
import next from './next.js';
|
|
13
|
+
import reactNative from './react-native.js';
|
|
14
|
+
import react from './react.js';
|
|
15
|
+
import sorted from './sorted.js';
|
|
16
|
+
import vitest from './vitest.js';
|
|
17
|
+
|
|
18
|
+
/*
|
|
19
|
+
* Which fragments each profile carries. A profile ADDS a framework's plugins
|
|
20
|
+
* and its ignore patterns to the core rulebook; it never subtracts from it, and
|
|
21
|
+
* no profile sets a core rule to `off`
|
|
22
|
+
* ([Lint presets](../docs/07-lint-presets.md)).
|
|
23
|
+
*
|
|
24
|
+
* `presets/oxlint/profiles/<name>.js` is this table compiled; nothing else
|
|
25
|
+
* decides what a profile holds.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/** The rulebook every profile carries, whatever it is written in. */
|
|
29
|
+
export const CORE = Object.freeze([
|
|
30
|
+
eslint,
|
|
31
|
+
typescript,
|
|
32
|
+
unicorn,
|
|
33
|
+
oxc,
|
|
34
|
+
importPlugin,
|
|
35
|
+
promise,
|
|
36
|
+
node,
|
|
37
|
+
jsdoc,
|
|
38
|
+
sorted,
|
|
39
|
+
vitest,
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
/*
|
|
43
|
+
* Trees no profile lints. `.artifacts/` is deliberately absent: it is
|
|
44
|
+
* gitignored and oxlint reads `.gitignore` on its own, so naming it here too
|
|
45
|
+
* would make it unlintable even where a caller asks for it by name.
|
|
46
|
+
*/
|
|
47
|
+
const IGNORED = Object.freeze([
|
|
48
|
+
'dist/**',
|
|
49
|
+
'node_modules/**',
|
|
50
|
+
// What a spec stands on and what it is measured against are @jterrazz/test
|
|
51
|
+
// 14's underscored trees: an input is deliberately broken and a golden is
|
|
52
|
+
// Byte-for-byte, so neither is source and neither is linted.
|
|
53
|
+
'**/_fixtures/**',
|
|
54
|
+
'**/_expected/**',
|
|
55
|
+
]);
|
|
56
|
+
|
|
57
|
+
/** Every profile, by the name a consumer imports. */
|
|
58
|
+
export const PROFILES = Object.freeze({
|
|
59
|
+
astro: {
|
|
60
|
+
env: { astro: true, browser: true, builtin: true, node: true },
|
|
61
|
+
fragments: [...CORE, react, a11y, astro],
|
|
62
|
+
ignorePatterns: [...IGNORED],
|
|
63
|
+
},
|
|
64
|
+
bun: {
|
|
65
|
+
env: { builtin: true, node: true },
|
|
66
|
+
fragments: [...CORE],
|
|
67
|
+
globals: { Bun: 'readonly' },
|
|
68
|
+
ignorePatterns: [...IGNORED],
|
|
69
|
+
},
|
|
70
|
+
expo: {
|
|
71
|
+
env: { browser: true, builtin: true, node: true },
|
|
72
|
+
fragments: [...CORE, react, a11y, reactNative],
|
|
73
|
+
ignorePatterns: [...IGNORED],
|
|
74
|
+
},
|
|
75
|
+
library: {
|
|
76
|
+
env: { builtin: true, node: true },
|
|
77
|
+
fragments: [...CORE],
|
|
78
|
+
ignorePatterns: [...IGNORED, 'docs/reference/**'],
|
|
79
|
+
},
|
|
80
|
+
next: {
|
|
81
|
+
env: { browser: true, builtin: true, node: true },
|
|
82
|
+
fragments: [...CORE, react, a11y, next],
|
|
83
|
+
ignorePatterns: [...IGNORED],
|
|
84
|
+
},
|
|
85
|
+
node: {
|
|
86
|
+
env: { builtin: true, node: true },
|
|
87
|
+
fragments: [...CORE],
|
|
88
|
+
ignorePatterns: [...IGNORED],
|
|
89
|
+
},
|
|
90
|
+
react: {
|
|
91
|
+
env: { browser: true, builtin: true, node: true },
|
|
92
|
+
fragments: [...CORE, react, a11y, bundler],
|
|
93
|
+
ignorePatterns: [...IGNORED],
|
|
94
|
+
},
|
|
95
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { fragment, on } from './_contract.js';
|
|
2
|
+
import { EXTENSIONS_NEVER } from './core/import.js';
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* React Native. oxlint 1.83 ships no `react-native` plugin, so this fragment
|
|
6
|
+
* is not a plugin roster: it is the three decisions the platform forces, plus
|
|
7
|
+
* the globals its runtime defines.
|
|
8
|
+
*
|
|
9
|
+
* Metro resolves an import, so a specifier carries no extension — except an
|
|
10
|
+
* asset, which Metro resolves BY its extension, and `require()` is how an
|
|
11
|
+
* asset is named in a React Native tree.
|
|
12
|
+
*/
|
|
13
|
+
export default fragment({
|
|
14
|
+
id: 'react-native',
|
|
15
|
+
globals: {
|
|
16
|
+
__DEV__: 'readonly',
|
|
17
|
+
ErrorUtils: 'readonly',
|
|
18
|
+
FormData: 'readonly',
|
|
19
|
+
XMLHttpRequest: 'readonly',
|
|
20
|
+
fetch: 'readonly',
|
|
21
|
+
requestAnimationFrame: 'readonly',
|
|
22
|
+
},
|
|
23
|
+
ignorePatterns: ['.expo/**', 'assets/**', 'ios/**', 'android/**'],
|
|
24
|
+
rules: {
|
|
25
|
+
'import/extensions': EXTENSIONS_NEVER,
|
|
26
|
+
'no-restricted-imports': on([
|
|
27
|
+
{
|
|
28
|
+
patterns: [
|
|
29
|
+
{
|
|
30
|
+
group: ['react-dom', 'react-dom/*', 'next', 'next/*'],
|
|
31
|
+
message: 'this is a React Native tree — the DOM and Next are not on it',
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
]),
|
|
36
|
+
'typescript/no-require-imports': on([
|
|
37
|
+
{
|
|
38
|
+
allow: [
|
|
39
|
+
String.raw`\.gif$`,
|
|
40
|
+
String.raw`\.jpeg$`,
|
|
41
|
+
String.raw`\.jpg$`,
|
|
42
|
+
String.raw`\.png$`,
|
|
43
|
+
String.raw`\.webp$`,
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
]),
|
|
47
|
+
},
|
|
48
|
+
});
|