@neralabs/rerail-pack-typescript 0.1.3 → 0.1.4

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 CHANGED
@@ -1,10 +1,10 @@
1
1
  # Rerail rule pack: TypeScript
2
2
 
3
- 21 code rules for TypeScript, ported from typescript-eslint 8.70.1 (on ESLint 10.11.0), each with its default
3
+ 24 code rules for TypeScript, ported from typescript-eslint 8.70.1 (on ESLint 10.11.0), each with its default
4
4
  options. 11 start off (`"on": false`): no approved typescript-eslint configuration turns them on, so they are
5
5
  experimental. Each is scored against the tool's own findings on the tool's test fixtures and on a pinned corpus
6
- (vscode), through the engine's production workspace path, with every disagreement declared in `divergences.json`
7
- (none today). ESLint's core rules are in the [JavaScript pack](../javascript/README.md).
6
+ (vscode), through the engine's production workspace path, with every disagreement declared in `divergences.json`:
7
+ 2 today, both `rerail-unknown`, each an upstream fixture the grammar reads with a syntax error. ESLint's core rules are in the [JavaScript pack](../javascript/README.md).
8
8
 
9
9
  Every typescript-eslint rule runs on `.ts`, `.tsx`, `.mts` and `.cts` files only, and the vscode corpus is scored on
10
10
  its TypeScript files only (`exclude` in `conformance.json`). On JavaScript a port steers CommonJS files away from
@@ -25,12 +25,16 @@ It is checked by `npx vitest run test/engine/catalog/packs.test.ts -t "pack type
25
25
  | `candidates/rules/` | Ports still being evaluated, never shipped: typescript-eslint and @vitest/eslint-plugin 1.6.27 ports that fail a rule gate ([docs/port-readiness.md](../../docs/port-readiness.md)) or rerail's pack contract. Each but `prefer-optional-chain` (it requires type information, which the recordings do not have) and `no-explicit-any` (the fixtures recording does not cover it yet) is listed in `conformance.json`, scored and reported on the fixtures, never gated; the vscode recording covers the shipped rules only. The three `vitest-*.json` documents are generated: edit `scripts/generate/packs/typescript-vitest-policies.mjs`, not the JSON, and run `node scripts/generate/packs/typescript-vitest-policies.mjs` from the repository root |
26
26
  | `licenses/<tool>/` | The tool's licence and `UPSTREAM.md`, listing every copied file |
27
27
 
28
- Re-recording runs offline against the pinned tool versions (the recorder's header lists them):
28
+ Re-recording is a maintainer deriving the pack's test data from its pins and archiving it
29
+ ([CONTRIBUTING.md](../../CONTRIBUTING.md#pack-test-data)); it is never committed. The recipe
30
+ (`scripts/pack-data/recipes/typescript.ts`) runs each tool's recorder against the pinned tool versions, the upstream
31
+ checkouts and the vscode corpus (the recorder's header lists them). The fixtures Rerail wrote (`fixtures/*/rerail-*`)
32
+ are authored: a change to them goes in your pull request with `git add -f`.
29
33
 
30
34
  ```sh
31
- node upstream/generate.mjs --tools <dir> --typescript-eslint <typescript-eslint checkout at v8.70.1> \
32
- --fixtures --corpus vscode=<vscode checkout>
33
- node upstream/eslint-plugin-vitest/generate.mjs --tools <dir> --vitest <eslint-plugin-vitest checkout at v1.6.27>
35
+ npm run fixtures:derive -- --pack typescript # every tool
36
+ npm run fixtures:derive -- --pack typescript --tool eslint-plugin-vitest # one
37
+ npm run fixtures:push -- --pack typescript --tool eslint-plugin-vitest # on your own branch: archive it and rewrite its test-data.lock
34
38
  ```
35
39
 
36
40
  Scoring one rule with every false positive and false negative printed:
@@ -39,6 +43,31 @@ Scoring one rule with every false positive and false negative printed:
39
43
  node --import tsx conformance/languages/cli.ts --pack typescript --rule typescript-eslint-no-namespace --fixtures
40
44
  ```
41
45
 
46
+ ## Not ported: `no-restricted-types`
47
+
48
+ Every typescript-eslint 8.70.1 rule that needs no type information has a port here, shipped or candidate, except
49
+ `no-restricted-types`. It reports only the types a project lists in its `types` option, and that list is empty by
50
+ default. At its default options it reports nothing, so there is nothing to port. A project that wants a type banned
51
+ writes its own code rule.
52
+
53
+ ## `no-magic-numbers` passes the gates and stays a candidate
54
+
55
+ It passes the blocking port readiness gates but `passes-conformance` ([docs/port-readiness.md](../../docs/port-readiness.md)) and agrees with typescript-eslint on every fixture and every vscode file. A shipped rule's vscode
56
+ recording has to cover it, though, and at its default options it reports nearly every number literal: about 187,500
57
+ findings in vscode's `src/`. That would grow `corpus-vscode.expected.json` from 4.5 MB to about 41 MB, so the rule
58
+ stays in `candidates/` until the recording can hold it. It would start off anyway: no approved typescript-eslint
59
+ configuration turns it on.
60
+
61
+ ## `init-declarations` can't match upstream's tests
62
+
63
+ typescript-eslint declares `'always'` as `init-declarations`' default. Its wrapper around ESLint's core rule has no
64
+ `meta.defaultOptions`, so with no options ESLint 10 passes the core rule no mode, and it reports nothing. Every upstream
65
+ test that expects a finding sets the option, so upstream's own tests never notice. `matches-upstream-tests` runs each case at the rule's
66
+ default options, sees no upstream finding at all, and fails the rule whatever its document says; on real code it
67
+ differs from upstream throughout, so `gates.yaml` lists it under `real_code_gaps`. The candidate ports the
68
+ declared default, `'always'`. With that option set, it agrees with typescript-eslint on every upstream test case and on
69
+ the vscode corpus.
70
+
42
71
  ## Rule details
43
72
 
44
73
  What each rule matches beyond its one-line description: exact matching, edge cases, upstream settings and known divergences.
@@ -47,6 +76,10 @@ What each rule matches beyond its one-line description: exact matching, edge cas
47
76
 
48
77
  Reports a function, method, call or construct signature whose name was already declared earlier in the same body when the member just before it is not an overload of the same name. Method names are compared as typescript-eslint's getNameFromMember does: literal and identifier keys by static name, a computed identifier by its name, and other computed keys by their source text.
49
78
 
79
+ ### `typescript-eslint-array-type`
80
+
81
+ Default option `array` (for mutable and readonly arrays alike). Flags a type reference named `Array` or `ReadonlyArray` with exactly one type argument, and `Readonly<T[]>`, which should be written `T[]` or `readonly T[]`. As upstream walks the scope chain from the reference, any declaration of that name, value or type, in a scope on the chain other than the global one leaves the reference alone. The chain is built in the order upstream's scope manager opens scopes: a class member's computed key lies outside the member's own scope, and a scope opened inside a class decorator, a class method's parameter decorator, a switch discriminant, an interface method's computed key or a conditional type's false branch skips the scope that holds that position. No fix is offered.
82
+
50
83
  ### `typescript-eslint-ban-ts-comment`
51
84
 
52
85
  Matches TypeScript's directive regexes on comment text (block comments: last line only); description length counts approximate grapheme clusters. Defaults as upstream: @ts-expect-error allowed with a description of at least 3 characters, @ts-ignore and @ts-nocheck banned, @ts-check allowed.
@@ -57,7 +90,7 @@ Flags comments whose value matches TSLint's enable/disable directive regex; the
57
90
 
58
91
  ### `typescript-eslint-class-literal-property-style`
59
92
 
60
- Default option `fields`. Flags getters (not `override`) whose first statement returns a literal, unless the class body has a setter of the same member name (typescript-eslint's getStaticMemberAccessValue: computed keys folded, constant bindings followed, `#a` same as `a`). A getter whose computed name has no static value is reported; a setter with none never pairs. The message tells the agent to check for subclasses that override the getter as an accessor, which a field would break (TS2611); the rule cannot see other classes, so it reports these getters as upstream does (NER-1785).
93
+ Default option `fields`. Flags getters (not `override`) whose first statement returns a literal, unless the class body has a setter of the same member name (typescript-eslint's getStaticMemberAccessValue: computed keys folded, constant bindings followed, `#a` same as `a`). A getter whose computed name has no static value is reported; a setter with none never pairs. The message tells the agent to check for subclasses that override the getter as an accessor, which a field would break (TS2611); the rule cannot see other classes, so it reports these getters as upstream does.
61
94
 
62
95
  ### `typescript-eslint-class-methods-use-this`
63
96
 
@@ -65,7 +98,15 @@ Flags a non-static, non-constructor class method, or a class field initialized w
65
98
 
66
99
  ### `typescript-eslint-consistent-generic-constructors`
67
100
 
68
- Covers variable declarators, class fields and parameter defaults initialized with `new Name(...)`, default mode `constructor`; typed-array built-ins are excepted. The fix rebuilds the declaration from its parts, so whitespace and comments inside it are not kept, and a declaration with modifiers is not fixed. typescript-eslint skips the constructor-mode report when the `isolatedDeclarations` parser option is set; Rerail has no such fact, so the port reports it and its message says to keep the annotation under isolatedDeclarations (TS9010, NER-1786).
101
+ Covers variable declarators, class fields and parameter defaults initialized with `new Name(...)`, default mode `constructor`; typed-array built-ins are excepted. The fix rebuilds the declaration from its parts, so whitespace and comments inside it are not kept, and a declaration with modifiers is not fixed. typescript-eslint skips the constructor-mode report when the `isolatedDeclarations` parser option is set; Rerail has no such fact, so the port reports it and its message says to keep the annotation under isolatedDeclarations (TS9010).
102
+
103
+ ### `typescript-eslint-consistent-indexed-object-style`
104
+
105
+ Default option `record`. Flags an interface or object type literal whose only member is an index signature, and a mapped type that does not use its key and whose constraint is not a bare `keyof`, unless the type refers back to the type alias or interface that declares it (directly or through other local type declarations). Fixes to a `Record` type where upstream does; no fix when a comment would be lost, for `-readonly`, for an interface with `extends` or under `export default`, when an interface's type parameter holds a comma and the parameters are not already joined by `, ` on one line (upstream re-joins them), or when the key or value type continues on a line indented with spaces or holds a multi-line string (the engine re-indents multi-line text, upstream copies it as is).
106
+
107
+ ### `typescript-eslint-consistent-type-assertions`
108
+
109
+ Default options (assertion style `as`, object and array literal assertions allowed). Flags every angle-bracket type assertion `<T>value`, which should be written `value as T`.
69
110
 
70
111
  ### `typescript-eslint-consistent-type-definitions`
71
112
 
@@ -83,6 +124,14 @@ Allowances with defaults: typed function expressions, higher-order functions and
83
124
 
84
125
  Reports from the first token after a member's decorators.
85
126
 
127
+ ### `typescript-eslint-explicit-module-boundary-types`
128
+
129
+ Flags functions that cross the module boundary without explicit types: functions, classes' public members and object members reached from an export (named, default, `export =`, or a local binding an export names, followed to its declaration and its writes within the reference's own scope), plus functions they return directly, that lack a return type, and their parameters that are untyped or typed `any`. Typed contexts, returned-into-typed functions, `as const` arrow bodies, higher-order functions (unless an export before them reaches them, which ESLint checks before it has seen their returns), constructors and setters are exempt from the return type; reports sit on the function head and on each parameter. Default options; they are declared in the `options` define.
130
+
131
+ ### `typescript-eslint-init-declarations`
132
+
133
+ In `always` mode, the default the rule declares. typescript-eslint 8.70.1 does not hand that default on to ESLint's core rule, so configured without options it reports nothing; this document is the rule as `["error", "always"]` runs it. It reports each variable declared by a plain name without an initial value, at the name. The head of a `for` loop counts as initialized; a declaration that is itself the body of a `for`, `for...in` or `for...of` loop counts as uninitialized even with a value, and is then reported over the whole declarator. Declarations marked `declare`, and everything inside a `declare namespace`, `declare module` or `declare global` block, are skipped. The ancestor walk (`outside-declared-module`) recognizes a declared module by its leading `declare` keyword using positive tests only, so the document is still evaluated in files with syntax errors. As in TypeScript, comments after `declare` are skipped, and a line break after it (bare or inside a block comment) makes `declare` an ordinary name rather than a modifier, so what follows is checked as a plain statement.
134
+
86
135
  ### `typescript-eslint-max-params`
87
136
 
88
137
  Extends ESLint's max-params; default max 3, countThis `except-void`. Counts parameters of functions, methods, arrows, declared functions and function types; the message names the function as getFunctionNameWithKind does.
@@ -115,6 +164,10 @@ String, number and negated number literal keys are allowed.
115
164
 
116
165
  A body with a comment is not empty; constructors with parameter properties are exempt. Default options (allow: []).
117
166
 
167
+ ### `typescript-eslint-no-empty-object-type`
168
+
169
+ Default options (allowInterfaces: never, allowObjectTypes: never, no allowWithName). Reports the name of an interface that declares no members and extends at most one type, and every `{}` type literal with no members that is not an operand of an intersection type. Upstream only suggests replacements, so there is no fix.
170
+
118
171
  ### `typescript-eslint-no-extra-non-null-assertion`
119
172
 
120
173
  Parentheses are transparent, as in ESTree.
@@ -135,6 +188,10 @@ Standalone port (extends ESLint's no-invalid-this). Flags `this` whose nearest n
135
188
 
136
189
  Port of typescript-eslint `no-invalid-void-type`. void is allowed as a return type (and as the annotation of non-identifier bindings, mirroring the rule's grandparent test); in a union whose members are all void, never, or generics with a void argument; in a union inside a function or method implementation that has overload signatures of the same name (an anonymous `export default function` has them when the module has an `export default function` signature; method names compared like getStaticMemberAccessValue, two names without a static value treated as equal); and, with allowInGenericTypeArguments (default true), as a generic type argument outside a call or as a type parameter default.
137
190
 
191
+ ### `typescript-eslint-no-magic-numbers`
192
+
193
+ Default options (no `ignore` values, every `ignore*` switch, `detectObjects` and `enforceConst` off). Every numeric or BigInt literal is reported, a unary `-` or `+` in front counting as part of it, unless its parent node (parentheses and computed-key brackets looked through, as ESTree does) lets it pass: a variable initializer, an object literal or pattern property (key or value), the right side of an assignment to anything but a plain identifier, the second argument of `parseInt` or `Number.parseInt`, and a JSX expression container. Enum member values, numeric literal types, type indexes and readonly class fields are reported too; in an enum member value or a readonly field a leading `+` is left out of the reported span.
194
+
138
195
  ### `typescript-eslint-no-misused-new`
139
196
 
140
197
  Reports a bodiless class method named `new` returning its own class, an interface construct signature returning its own interface, and a `constructor` method signature in an interface or type literal.
@@ -143,6 +200,10 @@ Reports a bodiless class method named `new` returning its own class, an interfac
143
200
 
144
201
  typescript-eslint 8.70.1, default options. allowDefinitionFiles is fixed at its default (true): files matched by isDefinitionFile (`\.d\.(ts|cts|mts|.*\.ts)$`) are not checked. Not flagged: `declare global`, `global {}` inside `declare module 'name'`, and `declare module 'name'`. Declared namespaces are flagged (allowDeclarations default false).
145
202
 
203
+ ### `typescript-eslint-no-non-null-asserted-nullish-coalescing`
204
+
205
+ Reports a non-null assertion that is the left operand of `??`. As upstream, an assertion that belongs to an optional chain (`a?.b!`) is left alone, since ESTree folds it into the chain. A bare asserted name is also left alone when it resolves to a variable of the file, or to a builtin or declared global (ESLint's globals, the esnext lib, `/* global */` comments), that nothing writes before the assertion: no initializer, definite-assignment `!`, parameter default, assignment or update of it occurs earlier in the file. As in ESLint's scope analysis, a plain, compound or update target counts as a write through parentheses and at most one `!`, `as` or `<T>` around the name (never `satisfies`), while a destructuring or for-in/of target counts through any of them.
206
+
146
207
  ### `typescript-eslint-no-non-null-assertion`
147
208
 
148
209
  typescript-eslint 8.70.1, default options. Where tree-sitter misparses a prefix operator applied to an assertion (`!x!` as `(!x)!`), the finding starts at the real operand.
@@ -159,6 +220,18 @@ Flags each definition after the first in the same scope; with builtinGlobals, de
159
220
 
160
221
  `require(...)` is not flagged when a declaration of `require` is in scope. Default options: allow [], allowAsImport false.
161
222
 
223
+ ### `typescript-eslint-no-shadow`
224
+
225
+ Default options. Flags a declaration whose name is already declared in an enclosing scope: the nearest enclosing scope declaring the name is found structurally (eslint-scope's scope boundaries, `var` hoisting to the function), and upstream's default exemptions apply: a type shadowing a value or a value shadowing a type (or a type-only import), parameters of signatures without a body, a later declaration that is not a function or type (hoist: functions-and-types), a function or class expression named after the variable it initializes, a static method's type parameter shadowing its class's, a module augmentation merging a type-only import, and `declare global` blocks.
226
+
227
+ ### `typescript-eslint-no-unnecessary-parameter-property-assignment`
228
+
229
+ Inside a constructor (or an arrow function called directly there), flags `this.x = x`, `this.x ||= x`, `this.x &&= x` or `this.x ??= x` when `x` is a parameter and the constructor declares `x` as a parameter property, so TypeScript already makes that assignment. It is not flagged when, earlier in the constructor and within the same class body, the parameter was written or the member was compound-assigned (`+=` and the like), or when a class field initializer of the same class assigns the member. As upstream decides whether a name is the parameter by the first reference to that name in the enclosing scope, a parameter or write is ignored when that first reference is a type reference, as in `constructor(private config: config.Settings)`.
230
+
231
+ ### `typescript-eslint-no-unsafe-declaration-merging`
232
+
233
+ Reports the name of a class declaration when an interface of the same name is declared in the same scope, and the name of an interface without type parameters (an interface with them is read in a scope of its own) when a class of the same name is declared in the same scope: TypeScript merges the two without checking that the class implements the interface's members. The scope is matched structurally: the nearest program, block (a function, namespace, module, `declare global` or static block body), switch body, `with` statement, or loop whose head declares a let or const binding above the declaration, reached through `export`, `declare` and `case` wrappers and through statement bodies that open no scope (if, else, while, do, labels, and loops whose head declares no let or const binding).
234
+
162
235
  ### `typescript-eslint-no-unsafe-function-type`
163
236
 
164
237
  typescript-eslint 8.70.1, default options. Flags `Function` as a type reference, in class implements, or interface extends, only when the name resolves to no file-local type binding.
@@ -167,10 +240,18 @@ typescript-eslint 8.70.1, default options. Flags `Function` as a type reference,
167
240
 
168
241
  Unwraps parentheses and TS `as` / `<T>` / `!` / instantiation wrappers; allowed: call, new, assignment, update, await, yield, import(), void, delete; directive prologue strings exempt. tree-sitter reads `e as A.B.C` and `e satisfies A.B.C` (type name with two or more dots) as a member access on `e as A.B` / `e satisfies A.B`; such a member access is treated as the `as` (unwrapped) or `satisfies` (never flagged, as upstream).
169
242
 
243
+ ### `typescript-eslint-no-unused-private-class-members`
244
+
245
+ Flags a `#name` member, or a member or constructor parameter property declared `private`, that nothing in its class reads (for a getter, setter or `accessor` field a write counts too), at the last declaration of its key. Keys compare as upstream's do: names, literal values (`0x10` is `16`, escapes decoded) and a template's raw text; a class whose names are all written plainly is checked by text alone, which is faster. As upstream's class scope analysis, which covers the class's decorators and heritage as well as its body, a `#name` is used by any reference that resolves to it, and a `private` member through `this` in the matching static or instance context (functions rebind it, arrow functions do not, a field initializer is the instance), through a variable declared from `this`, through the class name (static), through a parameter typed as the class (`typeof` for static), or by destructuring `this`.
246
+
170
247
  ### `typescript-eslint-no-unused-vars`
171
248
 
172
249
  Reported where ESLint reports (last write from the variable's own function, else its first definition). Usedness, exports, `/* exported */` comments and report anchors come from the engine's per-variable references (`binds`, `resolves.write: "last"`); options filter on them. An unused `/* global */` name with no declaration is reported at the name in the first comment that declares it (M12a eslintDirective parse; first three names of a comment).
173
250
 
251
+ ### `typescript-eslint-no-use-before-define`
252
+
253
+ Default options. Flags a value reference that resolves to a binding of this file whose first declaration comes later in the source, or that sits in the initializer of the very binding it reads (within the same scope). Type positions and `typeof` queries are ignored, as are names read from the scope of a function type or type-member signature and a class named inside its own decorator, while a named export is checked even when it names a type or nothing at all.
254
+
174
255
  ### `typescript-eslint-no-useless-constructor`
175
256
 
176
257
  Exempt: private/protected constructors, `public` constructors in derived classes, parameter properties and decorated parameters. Flags an empty-body constructor in a class without extends, or one whose body is only `super(...)` passing parameters through unchanged (ESLint isPassingThrough for any number of simple parameters: positional name match, spread of a rest parameter, or one spread of `arguments`).
@@ -187,6 +268,10 @@ Default options, `class-property` mode: flags constructor parameter properties w
187
268
 
188
269
  typescript-eslint 8.70.1, default options. Flags a literal type that repeats the raw text of the literal it asserts (`x as 'a'`, `<'a'>x`) or annotates (`let v: 'a' = 'a'`, class fields); assertions are auto-fixed to `const`.
189
270
 
271
+ ### `typescript-eslint-prefer-for-of`
272
+
273
+ Flags a `for` loop that declares one non-const index starting at 0, runs while the index is below an array's `length`, steps it by one, and uses the index in its body only to read elements of that same array.
274
+
190
275
  ### `typescript-eslint-prefer-function-type`
191
276
 
192
277
  Applies to a type literal, or an interface with no supertype other than Function, whose only member is a call or construct signature with a return type; in such an interface the first `this` type (outside nested literals) is reported instead. Fixes cover call signatures without comments; checks are split by fix shape.
@@ -199,6 +284,10 @@ Allowed initializers: a literal, a template literal without substitutions, or a
199
284
 
200
285
  Only `module Name {}` with an identifier name; reports the whole declaration (from `declare` when present) and fixes the keyword.
201
286
 
287
+ ### `typescript-eslint-triple-slash-reference`
288
+
289
+ Default options (lib: always, path: never, types: prefer-import). Looks only at the line comments that come before the file's first token (a shebang may precede them). Every `/// <reference path="..." />` directive there is reported. A `/// <reference types="..." />` directive there is reported once for each import declaration or `import x = require(...)` anywhere in the file whose module string has the same value. A comment counts as leading when the first child of the program that is either a code node or the comment itself turns out to be that comment. Because that test is stated positively, the rule still runs on files with syntax errors. The directive regexes list ECMAScript's whitespace characters (which include U+FEFF but not U+0085) instead of using the engine's Unicode `\s`, so they accept exactly the spacing upstream's JavaScript regex accepts.
290
+
202
291
  ### `typescript-eslint-unified-signatures`
203
292
 
204
293
  Compares each overload with every earlier overload of the same name in the same container, pairing parameters by position for any number of parameters, requiring equal return types and type parameters. Not ported: the outer type-parameter usage test and message line numbers. With three or more overloads of a name, the message names the line of the other overload ESLint picks (M12a `line` transform).
@@ -103,8 +103,8 @@ Paths are relative to the pack directory, `packs/typescript/`.
103
103
  | `upstream/eslint-plugin-vitest/fixtures/no-focused-tests/020.test.ts` | `tests/no-focused-tests.test.ts` | derived |
104
104
 
105
105
  > `recording.json` is a port readiness recording, and no file of this pack, so it is recorded here rather than in the
106
- > table above: the tool-pack's `recording.lock` names it and a GitHub release holds the bytes (docs/port-readiness.md).
107
- > It carries excerpts of this upstream's tests (`tests/*.test.ts`: the `code` of each RuleTester case), redistributed
108
- > under the licence above, together with Rerail's Apache-2.0; the LICENSE beside this file is this upstream's own, at
109
- > the pinned commit.
106
+ > table above: the tool-pack's `recording.lock` names it by its sha256, and the bytes are an object in S3, not a file in
107
+ > git (docs/port-readiness.md). It carries excerpts of this upstream's tests (`tests/*.test.ts`: the `code` of each
108
+ > RuleTester case), redistributed under the licence above, together with Rerail's Apache-2.0; `LICENSE` beside this file
109
+ > is this upstream's own, at the pinned commit.
110
110