@ls-stack/fast-lint 0.4.0 → 0.6.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 CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  Type-aware lint rules running natively on [tsrs](https://github.com/maschwenk/tsrs), the Rust port of the TypeScript 7
4
4
  checker, through [tsrslint](https://github.com/maschwenk/tsrslint)'s rule framework. No ESLint, no Node type checker:
5
- only these rules run, against one shared program per tsconfig. Type-aware rules live here, not in
6
- [`@ls-stack/extended-lint`](../extended-lint).
5
+ only these rules run, against one shared program per tsconfig. It replaces the `@ls-stack/extended-lint` ESLint
6
+ plugin, whose rules were all moved here or removed.
7
7
 
8
8
  ## Rules
9
9
 
@@ -47,6 +47,11 @@ only these rules run, against one shared program per tsconfig. Type-aware rules
47
47
  | `sort-react-components-and-styles` | moved from `@ls-stack/extended-lint` (see below) |
48
48
  | `use-types-directly-above-usage` | moved from `@ls-stack/extended-lint` (see below) |
49
49
  | `react-compiler-extra` | moved from `@ls-stack/extended-lint` (see below) |
50
+ | `no-restricted-comments` | moved from `@ls-stack/extended-lint` (see below) |
51
+ | `require-usage-explanation` | moved from `@ls-stack/extended-lint` (see below) |
52
+ | `use-top-level-regex` | moved from `@ls-stack/extended-lint` (see below) |
53
+ | `no-unused-obj-props` | moved from `@ls-stack/extended-lint`, stricter (see below) |
54
+ | `require-type-guard-explanation` | new, a stricter alternative to `no-type-guards` (see below) |
50
55
 
51
56
  Options are the same as the former ESLint rules' (see `lib/index.d.ts`). Rule names may carry a plugin prefix, so
52
57
  existing `@ls-stack/strict-casting` configs and disable comments keep working.
@@ -697,6 +702,155 @@ type Options = [
697
702
  ]
698
703
  ```
699
704
 
705
+ ### `no-restricted-comments`
706
+
707
+ Disallows comments matching configured patterns:
708
+
709
+ - each pattern matches the comment text (without `//` or `/* */`) by `regex` or, without one, by `includes`
710
+ - a comment is reported once, for the first pattern it matches, with the pattern's `message` or
711
+ `Comment matches restricted pattern '<pattern>'`
712
+ - the comment's removal is offered as a suggestion, or applied as an autofix with `autoFix: true`
713
+
714
+ ```ts
715
+ type Options = [
716
+ {
717
+ patterns: {
718
+ /** Rust `regex` syntax (no lookarounds or backreferences) */
719
+ regex?: string
720
+ includes?: string
721
+ /** Replaces the default message */
722
+ message?: string
723
+ /** Remove the comment as an autofix instead of a suggestion (default: false) */
724
+ autoFix?: boolean
725
+ }[]
726
+ },
727
+ ]
728
+ ```
729
+
730
+ ### `require-usage-explanation`
731
+
732
+ Requires an explanatory comment with a given prefix above matched code:
733
+
734
+ - calls of a function (`fn`, matching `name()` and `obj.name()`) or nodes matching an ESQuery `selector`
735
+ - the comment can be right before the matched node or before any of its ancestors up to the statement holding it
736
+ (e.g. above the `const` or `return` statement, or inside a ternary branch, array or argument list)
737
+ - the comment must start with `commentPrefix` and have some text after it (`// WORKAROUND:` alone doesn't count)
738
+
739
+ ```ts
740
+ type Options = [
741
+ {
742
+ matches: {
743
+ /** Name of the function whose calls need an explanation */
744
+ fn?: string
745
+ /** ESQuery selector of the nodes that need an explanation */
746
+ selector?: string
747
+ /** Prefix the explanation comment must start with, e.g. `WORKAROUND:` */
748
+ commentPrefix: string
749
+ /** Text appended to the error message */
750
+ message?: string
751
+ }[]
752
+ },
753
+ ]
754
+ ```
755
+
756
+ ### `use-top-level-regex`
757
+
758
+ Requires regex literals to be declared at the top level, so functions don't create a new `RegExp` object on each call.
759
+
760
+ - regex literals inside functions, arrow functions and methods (including their default parameters) are reported
761
+ - regexes with the stateful `g` or `y` flags are allowed, since hoisting them would share `lastIndex` between calls
762
+ - regexes passed directly to `new RegExp(...)` are allowed
763
+
764
+ ```ts
765
+ const WORD = /\w+/ // ok
766
+
767
+ function isWord(input: string) {
768
+ return /\w+/.test(input) // Error: move it to the top level
769
+ }
770
+ ```
771
+
772
+ No options.
773
+
774
+ ### `no-unused-obj-props`
775
+
776
+ Reports object literal properties that are never read:
777
+
778
+ - properties of a non-exported `const`/`let` initialized with an object literal (`as const` included) that no
779
+ `obj.key`, `obj['key']`, literal-typed `obj[key]`, destructuring or JSX `<obj.Key />` reads
780
+ (`unusedObjectProperty`); a property that is only assigned (`obj.key = value`) is reported too
781
+ - properties of the objects a non-exported local function returns that none of its callers read, when every call
782
+ initializes a variable (`const { a } = useThing()` or `const thing = useThing()` followed by property reads)
783
+ (`unusedReturnObjectProperty`)
784
+
785
+ The object is skipped whenever it escapes: spread, passed or returned, aliased, enumerated (`in`, `Object.keys`,
786
+ `for...in`), typed with `typeof`, exported, reassigned, read with a non-literal key, or when one of its methods,
787
+ accessors or function values uses `this`. Accessors, spreads, `__proto__` and dynamic computed keys are never
788
+ reported. Typed declarations, `satisfies` and other assertions are skipped.
789
+
790
+ ```ts
791
+ const config = { retries: 3, timeout: 100 }
792
+ fetchWithRetries(config.retries) // Error: "timeout" is not being used
793
+
794
+ function useForm() {
795
+ return { values, errors, reset } // Error: "reset" is not used by any caller
796
+ }
797
+ const { values, errors } = useForm()
798
+ ```
799
+
800
+ No options.
801
+
802
+ ### `require-type-guard-explanation`
803
+
804
+ A stricter alternative to `no-type-guards`: TypeScript doesn't check type guards, so each one must explain why it is
805
+ safe and why TS inference or an alternative can't be used.
806
+
807
+ - every function with a body whose return type is `x is T`, `this is T` or `asserts x is T` (declarations, function
808
+ expressions, arrows, object and class methods, inline callbacks like `.filter((x): x is T => ...)`) needs a
809
+ comment starting with `commentPrefix` and at least `minLength` characters of explanation (`missingExplanation`)
810
+ - the comment goes right before the function or before the statement, `export`, class member or object property
811
+ holding it; consecutive `//` lines form one block, and block/JSDoc comments count too
812
+ - an explicit predicate TypeScript (5.5+) would infer identically is reported even with a comment, with a suggestion
813
+ to remove the annotation (`inferablePredicate`); only null/undefined and `typeof` checks it is sure about are
814
+ reported
815
+ - body-less signatures (overloads, `declare function`, interface members, function types) and `asserts x` are ignored
816
+ - `alternativeMsgs` appends a project-specific hint to the message of guards passed directly to `.find()` or
817
+ `.filter()`, e.g. pointing to a `filterAndMap` helper
818
+
819
+ ```ts
820
+ // Error: missingExplanation
821
+ function isNode(value: unknown): value is Node {
822
+ return isObject(value) && 'kind' in value
823
+ }
824
+
825
+ // ok
826
+ // type-guard-safety: values come from our serializer, which always sets `kind`; a zod schema would parse every
827
+ // render
828
+ function isNode(value: unknown): value is Node {
829
+ return isObject(value) && 'kind' in value
830
+ }
831
+
832
+ // Error: inferablePredicate, TS infers `value is null | undefined`
833
+ function isNullish(value: unknown): value is null | undefined {
834
+ return value === null || value === undefined
835
+ }
836
+ ```
837
+
838
+ ```ts
839
+ type Options = [
840
+ {
841
+ /** Prefix the explanation comment must start with (default: `type-guard-safety:`) */
842
+ commentPrefix?: string
843
+ /** Minimum characters of explanation after the prefix (default: 20) */
844
+ minLength?: number
845
+ /** Extra hints appended to the message of guards passed directly to `.find()`/`.filter()` */
846
+ alternativeMsgs?: {
847
+ inArrayFind?: string
848
+ inArrayFilter?: string
849
+ }
850
+ },
851
+ ]
852
+ ```
853
+
700
854
  ## Usage
701
855
 
702
856
  ```sh
Binary file
Binary file
Binary file
Binary file
Binary file
package/lib/index.d.ts CHANGED
@@ -208,6 +208,33 @@ export type ReactCompilerExtraOptions = {
208
208
  disallowManualMemoization?: boolean
209
209
  }
210
210
 
211
+ export type NoRestrictedCommentsOptions = {
212
+ patterns: {
213
+ regex?: string
214
+ includes?: string
215
+ message?: string
216
+ autoFix?: boolean
217
+ }[]
218
+ }
219
+
220
+ export type RequireTypeGuardExplanationOptions = {
221
+ commentPrefix?: string
222
+ minLength?: number
223
+ alternativeMsgs?: {
224
+ inArrayFind?: string
225
+ inArrayFilter?: string
226
+ }
227
+ }
228
+
229
+ export type RequireUsageExplanationOptions = {
230
+ matches: {
231
+ fn?: string
232
+ selector?: string
233
+ commentPrefix: string
234
+ message?: string
235
+ }[]
236
+ }
237
+
211
238
  type RuleOptions = {
212
239
  'improved-no-unnecessary-condition': ImprovedNoUnnecessaryConditionOptions
213
240
  'strict-casting': StrictCastingOptions
@@ -248,6 +275,11 @@ type RuleOptions = {
248
275
  'sort-react-components-and-styles': SortReactComponentsAndStylesOptions
249
276
  'use-types-directly-above-usage': UseTypesDirectlyAboveUsageOptions
250
277
  'react-compiler-extra': ReactCompilerExtraOptions
278
+ 'no-restricted-comments': NoRestrictedCommentsOptions
279
+ 'require-usage-explanation': RequireUsageExplanationOptions
280
+ 'use-top-level-regex': Record<string, never>
281
+ 'no-unused-obj-props': Record<string, never>
282
+ 'require-type-guard-explanation': RequireTypeGuardExplanationOptions
251
283
  }
252
284
 
253
285
  export type RulesConfig = {
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@ls-stack/fast-lint",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "license": "MIT",
5
- "description": "Type-aware lint rules of @ls-stack/extended-lint running natively on tsrs, the Rust port of the TypeScript checker",
5
+ "description": "Fast type-aware lint rules for TypeScript and React, running natively on tsrs, the Rust port of the TypeScript checker",
6
6
  "type": "module",
7
7
  "bin": {
8
8
  "fast-lint": "./bin/fast-lint.mjs"