@ls-stack/fast-lint 0.3.0 → 0.5.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.
@@ -683,13 +688,158 @@ Extra checks for code optimized by the React compiler:
683
688
  - React components and hooks should create JSX, call hooks or use the `"use memo"` directive (with a suggestion to
684
689
  add it)
685
690
  - functions calling hooks must be components or hooks, and functions using `"use memo"` must be named like one
686
- - functions starting with the `"use no memo"` directive are skipped
691
+ - with `disallowManualMemoization`, `useMemo` and `useCallback` calls are reported, as the compiler memoizes for you
692
+ - functions starting with the `"use no memo"` directive are skipped (only the nearest enclosing function counts)
687
693
 
688
694
  ```ts
689
695
  type Options = [
690
696
  {
691
697
  /** Only run in files with an `eslint react-compiler/react-compiler: ["error"` comment (default: false) */
692
698
  runOnlyWithEnableCompilerDirective?: boolean
699
+ /** Report `useMemo` and `useCallback` calls (default: false) */
700
+ disallowManualMemoization?: boolean
701
+ },
702
+ ]
703
+ ```
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
+
817
+ ```ts
818
+ // Error: missingExplanation
819
+ function isNode(value: unknown): value is Node {
820
+ return isObject(value) && 'kind' in value
821
+ }
822
+
823
+ // ok
824
+ // type-guard-safety: values come from our serializer, which always sets `kind`; a zod schema would parse every
825
+ // render
826
+ function isNode(value: unknown): value is Node {
827
+ return isObject(value) && 'kind' in value
828
+ }
829
+
830
+ // Error: inferablePredicate, TS infers `value is null | undefined`
831
+ function isNullish(value: unknown): value is null | undefined {
832
+ return value === null || value === undefined
833
+ }
834
+ ```
835
+
836
+ ```ts
837
+ type Options = [
838
+ {
839
+ /** Prefix the explanation comment must start with (default: `type-guard-safety:`) */
840
+ commentPrefix?: string
841
+ /** Minimum characters of explanation after the prefix (default: 20) */
842
+ minLength?: number
693
843
  },
694
844
  ]
695
845
  ```
Binary file
Binary file
Binary file
Binary file
Binary file
package/lib/index.d.ts CHANGED
@@ -205,6 +205,30 @@ export type UseTypesDirectlyAboveUsageOptions = {
205
205
 
206
206
  export type ReactCompilerExtraOptions = {
207
207
  runOnlyWithEnableCompilerDirective?: boolean
208
+ disallowManualMemoization?: boolean
209
+ }
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
+ }
224
+
225
+ export type RequireUsageExplanationOptions = {
226
+ matches: {
227
+ fn?: string
228
+ selector?: string
229
+ commentPrefix: string
230
+ message?: string
231
+ }[]
208
232
  }
209
233
 
210
234
  type RuleOptions = {
@@ -247,6 +271,11 @@ type RuleOptions = {
247
271
  'sort-react-components-and-styles': SortReactComponentsAndStylesOptions
248
272
  'use-types-directly-above-usage': UseTypesDirectlyAboveUsageOptions
249
273
  'react-compiler-extra': ReactCompilerExtraOptions
274
+ 'no-restricted-comments': NoRestrictedCommentsOptions
275
+ 'require-usage-explanation': RequireUsageExplanationOptions
276
+ 'use-top-level-regex': Record<string, never>
277
+ 'no-unused-obj-props': Record<string, never>
278
+ 'require-type-guard-explanation': RequireTypeGuardExplanationOptions
250
279
  }
251
280
 
252
281
  export type RulesConfig = {
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@ls-stack/fast-lint",
3
- "version": "0.3.0",
3
+ "version": "0.5.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"