vouchington-tooling 0.14.0 → 0.15.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 +3 -0
- package/ast-grep/rules/backend-no-test-file-imports.yml +1 -1
- package/ast-grep/rules/no-dynamic-import-in-vitest-config.yml +1 -1
- package/ast-grep/rules/no-dynamic-server-components.yml +41 -5
- package/ast-grep/rules/no-enable-indexscan-off.yml +17 -4
- package/ast-grep/rules/no-exports-in-tests-folder.yml +27 -4
- package/ast-grep/rules/no-object-assign-new-error.yml +1 -1
- package/ast-grep/rules/no-runner-temp-nullish.yml +1 -1
- package/ast-grep/rules/no-self-comparison-assertion.yml +21 -1
- package/ast-grep/rules/no-sql-offset.yml +1 -1
- package/ast-grep/rules/no-sync-throw-assert-on-async-enqueue.yml +1 -1
- package/ast-grep/rules/no-tautological-literal-assertion.yml +15 -1
- package/ast-grep/rules/no-test-max-lines-disable.yml +26 -4
- package/ast-grep/rules/no-three-sequential-awaits.yml +220 -12
- package/ast-grep/rules/nullable-options.yml +1 -1
- package/ast-grep/rules/pagination-no-exact-limit-has-next.yml +1 -1
- package/ast-grep/rules/playwright-no-bare-meta-press.yml +1 -1
- package/ast-grep/rules/storybook-no-webpack-final.yml +1 -1
- package/ast-grep/sgconfig.yml +1 -2
- package/package.json +1 -1
- package/ast-grep/rules/no-dynamic-server-components-tsx.yml +0 -71
- package/ast-grep/rules/no-enable-indexscan-off-tsx.yml +0 -33
- package/ast-grep/rules/no-exports-in-tests-folder-tsx.yml +0 -42
- package/ast-grep/rules/no-self-comparison-assertion-tsx.yml +0 -43
- package/ast-grep/rules/no-tautological-literal-assertion-tsx.yml +0 -31
- package/ast-grep/rules/no-test-max-lines-disable-tsx.yml +0 -41
- package/ast-grep/rules/no-three-sequential-awaits-tsx.yml +0 -466
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
id: no-three-sequential-awaits
|
|
2
|
-
language:
|
|
2
|
+
language: Tsx
|
|
3
3
|
severity: error
|
|
4
4
|
message: 'Avoid 3 sequential await statements; abstract dependent work or parallelize independent work with Promise.all().'
|
|
5
5
|
files:
|
|
6
6
|
- '**/*.ts'
|
|
7
7
|
- '**/*.mts'
|
|
8
8
|
- '**/*.cts'
|
|
9
|
+
- '**/*.tsx'
|
|
9
10
|
ignores:
|
|
10
11
|
- '**/*.test.ts'
|
|
11
12
|
- '**/*.test.mts'
|
|
@@ -18,19 +19,23 @@ ignores:
|
|
|
18
19
|
- '**/*.mock.test.cts'
|
|
19
20
|
- '**/*.stories.ts'
|
|
20
21
|
- '**/*.stories.mts'
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
22
|
+
- backend/test-helpers/**
|
|
23
|
+
- test-helpers/**
|
|
24
|
+
- test-infra/**
|
|
25
|
+
- web/test-helpers/**
|
|
25
26
|
- '**/test-helpers/**'
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
27
|
+
- playwright/**
|
|
28
|
+
- playwright/setup/**
|
|
29
|
+
- integration-tests/**
|
|
30
|
+
- seed/**
|
|
30
31
|
- '**/seeds/**'
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
32
|
+
- backend/scripts/seeds/**
|
|
33
|
+
- backend/scripts/seed/**
|
|
34
|
+
- backend/scripts/explain-analyze/**
|
|
35
|
+
- '**/*.test.tsx'
|
|
36
|
+
- '**/*.spec.tsx'
|
|
37
|
+
- '**/*.mock.test.tsx'
|
|
38
|
+
- '**/*.stories.tsx'
|
|
34
39
|
rule:
|
|
35
40
|
all:
|
|
36
41
|
- not:
|
|
@@ -45,6 +50,10 @@ rule:
|
|
|
45
50
|
- pattern:
|
|
46
51
|
context: await $P
|
|
47
52
|
selector: expression_statement
|
|
53
|
+
- not:
|
|
54
|
+
pattern: await params
|
|
55
|
+
- not:
|
|
56
|
+
pattern: await searchParams
|
|
48
57
|
- not:
|
|
49
58
|
has:
|
|
50
59
|
pattern: Promise.all($$)
|
|
@@ -53,18 +62,30 @@ rule:
|
|
|
53
62
|
pattern: Promise.allSettled($$)
|
|
54
63
|
- all:
|
|
55
64
|
- pattern: const $W = await $P
|
|
65
|
+
- not:
|
|
66
|
+
pattern: const $W = await params
|
|
67
|
+
- not:
|
|
68
|
+
pattern: const $W = await searchParams
|
|
56
69
|
- not:
|
|
57
70
|
pattern: const $W = await Promise.all($$$)
|
|
58
71
|
- not:
|
|
59
72
|
pattern: const $W = await Promise.allSettled($$$)
|
|
60
73
|
- all:
|
|
61
74
|
- pattern: let $W = await $P
|
|
75
|
+
- not:
|
|
76
|
+
pattern: let $W = await params
|
|
77
|
+
- not:
|
|
78
|
+
pattern: let $W = await searchParams
|
|
62
79
|
- not:
|
|
63
80
|
pattern: let $W = await Promise.all($$$)
|
|
64
81
|
- not:
|
|
65
82
|
pattern: let $W = await Promise.allSettled($$$)
|
|
66
83
|
- all:
|
|
67
84
|
- pattern: var $W = await $P
|
|
85
|
+
- not:
|
|
86
|
+
pattern: var $W = await params
|
|
87
|
+
- not:
|
|
88
|
+
pattern: var $W = await searchParams
|
|
68
89
|
- not:
|
|
69
90
|
pattern: var $W = await Promise.all($$$)
|
|
70
91
|
- not:
|
|
@@ -73,6 +94,10 @@ rule:
|
|
|
73
94
|
- pattern:
|
|
74
95
|
context: $W = await $P
|
|
75
96
|
selector: expression_statement
|
|
97
|
+
- not:
|
|
98
|
+
pattern: $W = await params
|
|
99
|
+
- not:
|
|
100
|
+
pattern: $W = await searchParams
|
|
76
101
|
- not:
|
|
77
102
|
pattern: $W = await Promise.all($$$)
|
|
78
103
|
- not:
|
|
@@ -86,6 +111,10 @@ rule:
|
|
|
86
111
|
- pattern:
|
|
87
112
|
context: await $A
|
|
88
113
|
selector: expression_statement
|
|
114
|
+
- not:
|
|
115
|
+
pattern: await params
|
|
116
|
+
- not:
|
|
117
|
+
pattern: await searchParams
|
|
89
118
|
- not:
|
|
90
119
|
has:
|
|
91
120
|
pattern: Promise.all($$)
|
|
@@ -94,18 +123,30 @@ rule:
|
|
|
94
123
|
pattern: Promise.allSettled($$)
|
|
95
124
|
- all:
|
|
96
125
|
- pattern: const $X = await $A
|
|
126
|
+
- not:
|
|
127
|
+
pattern: const $X = await params
|
|
128
|
+
- not:
|
|
129
|
+
pattern: const $X = await searchParams
|
|
97
130
|
- not:
|
|
98
131
|
pattern: const $X = await Promise.all($$$)
|
|
99
132
|
- not:
|
|
100
133
|
pattern: const $X = await Promise.allSettled($$$)
|
|
101
134
|
- all:
|
|
102
135
|
- pattern: let $X = await $A
|
|
136
|
+
- not:
|
|
137
|
+
pattern: let $X = await params
|
|
138
|
+
- not:
|
|
139
|
+
pattern: let $X = await searchParams
|
|
103
140
|
- not:
|
|
104
141
|
pattern: let $X = await Promise.all($$$)
|
|
105
142
|
- not:
|
|
106
143
|
pattern: let $X = await Promise.allSettled($$$)
|
|
107
144
|
- all:
|
|
108
145
|
- pattern: var $X = await $A
|
|
146
|
+
- not:
|
|
147
|
+
pattern: var $X = await params
|
|
148
|
+
- not:
|
|
149
|
+
pattern: var $X = await searchParams
|
|
109
150
|
- not:
|
|
110
151
|
pattern: var $X = await Promise.all($$$)
|
|
111
152
|
- not:
|
|
@@ -114,6 +155,10 @@ rule:
|
|
|
114
155
|
- pattern:
|
|
115
156
|
context: $X = await $A
|
|
116
157
|
selector: expression_statement
|
|
158
|
+
- not:
|
|
159
|
+
pattern: $X = await params
|
|
160
|
+
- not:
|
|
161
|
+
pattern: $X = await searchParams
|
|
117
162
|
- not:
|
|
118
163
|
pattern: $X = await Promise.all($$$)
|
|
119
164
|
- not:
|
|
@@ -126,6 +171,10 @@ rule:
|
|
|
126
171
|
- pattern:
|
|
127
172
|
context: await $B
|
|
128
173
|
selector: expression_statement
|
|
174
|
+
- not:
|
|
175
|
+
pattern: await params
|
|
176
|
+
- not:
|
|
177
|
+
pattern: await searchParams
|
|
129
178
|
- not:
|
|
130
179
|
has:
|
|
131
180
|
pattern: Promise.all($$)
|
|
@@ -134,18 +183,30 @@ rule:
|
|
|
134
183
|
pattern: Promise.allSettled($$)
|
|
135
184
|
- all:
|
|
136
185
|
- pattern: const $Y = await $B
|
|
186
|
+
- not:
|
|
187
|
+
pattern: const $Y = await params
|
|
188
|
+
- not:
|
|
189
|
+
pattern: const $Y = await searchParams
|
|
137
190
|
- not:
|
|
138
191
|
pattern: const $Y = await Promise.all($$$)
|
|
139
192
|
- not:
|
|
140
193
|
pattern: const $Y = await Promise.allSettled($$$)
|
|
141
194
|
- all:
|
|
142
195
|
- pattern: let $Y = await $B
|
|
196
|
+
- not:
|
|
197
|
+
pattern: let $Y = await params
|
|
198
|
+
- not:
|
|
199
|
+
pattern: let $Y = await searchParams
|
|
143
200
|
- not:
|
|
144
201
|
pattern: let $Y = await Promise.all($$$)
|
|
145
202
|
- not:
|
|
146
203
|
pattern: let $Y = await Promise.allSettled($$$)
|
|
147
204
|
- all:
|
|
148
205
|
- pattern: var $Y = await $B
|
|
206
|
+
- not:
|
|
207
|
+
pattern: var $Y = await params
|
|
208
|
+
- not:
|
|
209
|
+
pattern: var $Y = await searchParams
|
|
149
210
|
- not:
|
|
150
211
|
pattern: var $Y = await Promise.all($$$)
|
|
151
212
|
- not:
|
|
@@ -154,6 +215,10 @@ rule:
|
|
|
154
215
|
- pattern:
|
|
155
216
|
context: $Y = await $B
|
|
156
217
|
selector: expression_statement
|
|
218
|
+
- not:
|
|
219
|
+
pattern: $Y = await params
|
|
220
|
+
- not:
|
|
221
|
+
pattern: $Y = await searchParams
|
|
157
222
|
- not:
|
|
158
223
|
pattern: $Y = await Promise.all($$$)
|
|
159
224
|
- not:
|
|
@@ -173,6 +238,10 @@ rule:
|
|
|
173
238
|
- pattern:
|
|
174
239
|
context: await $C
|
|
175
240
|
selector: expression_statement
|
|
241
|
+
- not:
|
|
242
|
+
pattern: await params
|
|
243
|
+
- not:
|
|
244
|
+
pattern: await searchParams
|
|
176
245
|
- not:
|
|
177
246
|
has:
|
|
178
247
|
pattern: Promise.all($$)
|
|
@@ -181,18 +250,30 @@ rule:
|
|
|
181
250
|
pattern: Promise.allSettled($$)
|
|
182
251
|
- all:
|
|
183
252
|
- pattern: const $Z = await $C
|
|
253
|
+
- not:
|
|
254
|
+
pattern: const $Z = await params
|
|
255
|
+
- not:
|
|
256
|
+
pattern: const $Z = await searchParams
|
|
184
257
|
- not:
|
|
185
258
|
pattern: const $Z = await Promise.all($$$)
|
|
186
259
|
- not:
|
|
187
260
|
pattern: const $Z = await Promise.allSettled($$$)
|
|
188
261
|
- all:
|
|
189
262
|
- pattern: let $Z = await $C
|
|
263
|
+
- not:
|
|
264
|
+
pattern: let $Z = await params
|
|
265
|
+
- not:
|
|
266
|
+
pattern: let $Z = await searchParams
|
|
190
267
|
- not:
|
|
191
268
|
pattern: let $Z = await Promise.all($$$)
|
|
192
269
|
- not:
|
|
193
270
|
pattern: let $Z = await Promise.allSettled($$$)
|
|
194
271
|
- all:
|
|
195
272
|
- pattern: var $Z = await $C
|
|
273
|
+
- not:
|
|
274
|
+
pattern: var $Z = await params
|
|
275
|
+
- not:
|
|
276
|
+
pattern: var $Z = await searchParams
|
|
196
277
|
- not:
|
|
197
278
|
pattern: var $Z = await Promise.all($$$)
|
|
198
279
|
- not:
|
|
@@ -201,6 +282,10 @@ rule:
|
|
|
201
282
|
- pattern:
|
|
202
283
|
context: $Z = await $C
|
|
203
284
|
selector: expression_statement
|
|
285
|
+
- not:
|
|
286
|
+
pattern: $Z = await params
|
|
287
|
+
- not:
|
|
288
|
+
pattern: $Z = await searchParams
|
|
204
289
|
- not:
|
|
205
290
|
pattern: $Z = await Promise.all($$$)
|
|
206
291
|
- not:
|
|
@@ -409,3 +494,126 @@ examples:
|
|
|
409
494
|
}
|
|
410
495
|
isValid: false
|
|
411
496
|
file: 'backend/services/example.mts'
|
|
497
|
+
- code: |
|
|
498
|
+
async function run() {
|
|
499
|
+
await one()
|
|
500
|
+
await two()
|
|
501
|
+
await three()
|
|
502
|
+
}
|
|
503
|
+
isValid: false
|
|
504
|
+
file: web/app/example.tsx
|
|
505
|
+
- code: |
|
|
506
|
+
async function run() {
|
|
507
|
+
const { id } = await params
|
|
508
|
+
const topic = await getTopic(id)
|
|
509
|
+
const aliases = await getAliases(id)
|
|
510
|
+
return <div>{topic.name}{aliases.length}</div>
|
|
511
|
+
}
|
|
512
|
+
isValid: true
|
|
513
|
+
file: web/app/example.tsx
|
|
514
|
+
- code: |
|
|
515
|
+
async function run() {
|
|
516
|
+
await Promise.all([one(), two(), three()])
|
|
517
|
+
return <div />
|
|
518
|
+
}
|
|
519
|
+
isValid: true
|
|
520
|
+
file: web/app/example.tsx
|
|
521
|
+
- code: |
|
|
522
|
+
async function run() {
|
|
523
|
+
await using transaction = await beginTransaction()
|
|
524
|
+
await applyChange(transaction)
|
|
525
|
+
await transaction.commit()
|
|
526
|
+
return <div />
|
|
527
|
+
}
|
|
528
|
+
isValid: true
|
|
529
|
+
file: web/app/example.tsx
|
|
530
|
+
- code: |
|
|
531
|
+
async function run() {
|
|
532
|
+
await using transaction = await beginBoundedTransaction({ timeoutMs: 1000 })
|
|
533
|
+
await applyFirstChange(transaction)
|
|
534
|
+
await applySecondChange(transaction)
|
|
535
|
+
await transaction.commit()
|
|
536
|
+
return <div />
|
|
537
|
+
}
|
|
538
|
+
isValid: true
|
|
539
|
+
file: web/app/example.tsx
|
|
540
|
+
- code: |
|
|
541
|
+
async function run() {
|
|
542
|
+
await using transaction = await beginTransaction()
|
|
543
|
+
await applyFirstChange(transaction)
|
|
544
|
+
await applySecondChange(transaction)
|
|
545
|
+
await applyThirdChange(transaction)
|
|
546
|
+
await transaction.commit()
|
|
547
|
+
return <div />
|
|
548
|
+
}
|
|
549
|
+
isValid: false
|
|
550
|
+
file: web/app/example.tsx
|
|
551
|
+
- code: |
|
|
552
|
+
async function run() {
|
|
553
|
+
await unrelatedFirst()
|
|
554
|
+
await unrelatedSecond()
|
|
555
|
+
await unrelatedThird()
|
|
556
|
+
await using transaction = await beginTransaction()
|
|
557
|
+
await applyChange(transaction)
|
|
558
|
+
await transaction.commit()
|
|
559
|
+
return <div />
|
|
560
|
+
}
|
|
561
|
+
isValid: false
|
|
562
|
+
file: web/app/example.tsx
|
|
563
|
+
- code: |
|
|
564
|
+
async function run() {
|
|
565
|
+
await using transaction = await beginTransaction()
|
|
566
|
+
await applyFirstChange(transaction)
|
|
567
|
+
await applySecondChange(transaction)
|
|
568
|
+
await other.commit()
|
|
569
|
+
return <div />
|
|
570
|
+
}
|
|
571
|
+
isValid: false
|
|
572
|
+
file: web/app/example.tsx
|
|
573
|
+
- code: |
|
|
574
|
+
async function run() {
|
|
575
|
+
await using transaction = await beginTransaction()
|
|
576
|
+
await applyChange(transaction)
|
|
577
|
+
await transaction.commit()
|
|
578
|
+
await afterFirst()
|
|
579
|
+
await afterSecond()
|
|
580
|
+
await afterThird()
|
|
581
|
+
return <div />
|
|
582
|
+
}
|
|
583
|
+
isValid: false
|
|
584
|
+
file: web/app/example.tsx
|
|
585
|
+
- code: |
|
|
586
|
+
async function run() {
|
|
587
|
+
await using outer = await acquire()
|
|
588
|
+
{
|
|
589
|
+
await using inner = await acquire()
|
|
590
|
+
await innerFirst(inner)
|
|
591
|
+
await innerSecond(inner)
|
|
592
|
+
await innerThird(inner)
|
|
593
|
+
await inner.commit()
|
|
594
|
+
}
|
|
595
|
+
await outer.commit()
|
|
596
|
+
return <div />
|
|
597
|
+
}
|
|
598
|
+
isValid: false
|
|
599
|
+
file: web/app/example.tsx
|
|
600
|
+
- code: |
|
|
601
|
+
async function run() {
|
|
602
|
+
await using transaction = await beginTransaction()
|
|
603
|
+
await applyFirstChange(transaction)
|
|
604
|
+
await applySecondChange(transaction)
|
|
605
|
+
await transaction.rollback()
|
|
606
|
+
return <div />
|
|
607
|
+
}
|
|
608
|
+
isValid: false
|
|
609
|
+
file: web/app/example.tsx
|
|
610
|
+
- code: |
|
|
611
|
+
async function run() {
|
|
612
|
+
await using resource = await acquire()
|
|
613
|
+
await useFirst(resource)
|
|
614
|
+
await useSecond(resource)
|
|
615
|
+
await resource.dispose()
|
|
616
|
+
return <div />
|
|
617
|
+
}
|
|
618
|
+
isValid: false
|
|
619
|
+
file: web/app/example.tsx
|
package/ast-grep/sgconfig.yml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vouchington-tooling",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "Vouchington CLI and extractable tooling libraries.",
|
|
5
5
|
"homepage": "https://github.com/vouchington/vouchington-tooling/tree/main/packages/vouchington-tooling#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
# NOTE: This rule flags calls to `dynamic` or `nextDynamic` (next/dynamic) that use a dynamic import.
|
|
2
|
-
# It cannot verify the import source — it matches by function name.
|
|
3
|
-
# If you have other functions named `dynamic` or `nextDynamic`, add a suppression comment.
|
|
4
|
-
id: no-dynamic-server-components-tsx
|
|
5
|
-
language: Tsx
|
|
6
|
-
severity: error
|
|
7
|
-
message: "dynamic()/nextDynamic() may wrap a server component — ensure the target has 'use client'"
|
|
8
|
-
# NOTE: ast-grep maps `.tsx` to language `Tsx`, not `TypeScript`. This rule
|
|
9
|
-
# only scans `.tsx` files; see no-dynamic-server-components.yml for the
|
|
10
|
-
# companion rule that covers `.ts` files.
|
|
11
|
-
files:
|
|
12
|
-
- 'web/**/*.tsx'
|
|
13
|
-
ignores:
|
|
14
|
-
- '**/*.test.tsx'
|
|
15
|
-
- '**/*.spec.tsx'
|
|
16
|
-
rule:
|
|
17
|
-
any:
|
|
18
|
-
- pattern: '$FN(() => import($PATH))'
|
|
19
|
-
- pattern: '$FN(() => { return import($PATH); })'
|
|
20
|
-
- pattern: '$FN(() => import($PATH).then($$$))'
|
|
21
|
-
- pattern: '$FN(() => import($PATH), $OPTS)'
|
|
22
|
-
- pattern: '$FN(() => { return import($PATH); }, $OPTS)'
|
|
23
|
-
- pattern: '$FN(() => import($PATH).then($$$), $OPTS)'
|
|
24
|
-
constraints:
|
|
25
|
-
FN:
|
|
26
|
-
regex: '^(dynamic|nextDynamic)$'
|
|
27
|
-
PATH:
|
|
28
|
-
regex: "^['\"`](\\.\\./|\\./|@/)"
|
|
29
|
-
# NOTE: `$OPTS` is only bound by the two-argument pattern arms above. This
|
|
30
|
-
# `not` constraint filters out `{ ssr: false }` only for those arms; the
|
|
31
|
-
# single-argument dynamic import arms still match and remain violations.
|
|
32
|
-
OPTS:
|
|
33
|
-
not:
|
|
34
|
-
regex: '(?:\bssr\b|[''"]ssr[''"])\s*:\s*false\b'
|
|
35
|
-
examples:
|
|
36
|
-
- code: "const Comp = dynamic(() => import('./my-component'))"
|
|
37
|
-
isValid: false
|
|
38
|
-
file: 'web/app/page.tsx'
|
|
39
|
-
- code: 'const Comp = dynamic(() => import(`./my-component`))'
|
|
40
|
-
isValid: false
|
|
41
|
-
file: 'web/app/page.tsx'
|
|
42
|
-
- code: "const Comp = dynamic(() => import('../components/comp'))"
|
|
43
|
-
isValid: false
|
|
44
|
-
file: 'web/app/page.tsx'
|
|
45
|
-
- code: "const Comp = dynamic(() => import('./add-tag-form').then(m => ({ default: m.AddTagForm })))"
|
|
46
|
-
isValid: false
|
|
47
|
-
file: 'web/app/page.tsx'
|
|
48
|
-
- code: "const Comp = nextDynamic(() => import('./my-component'))"
|
|
49
|
-
isValid: false
|
|
50
|
-
file: 'web/app/page.tsx'
|
|
51
|
-
- code: "const Comp = dynamic(() => import('./c'), { loading: () => null })"
|
|
52
|
-
isValid: false
|
|
53
|
-
file: 'web/app/page.tsx'
|
|
54
|
-
- code: "const Comp = dynamic(() => import('./c'), { not_ssr: false })"
|
|
55
|
-
isValid: false
|
|
56
|
-
file: 'web/app/page.tsx'
|
|
57
|
-
- code: "const Comp = dynamic(() => import('./c'), { ssr: falsey })"
|
|
58
|
-
isValid: false
|
|
59
|
-
file: 'web/app/page.tsx'
|
|
60
|
-
- code: "const Comp = dynamic(() => import('./c'), { ssr: false })"
|
|
61
|
-
isValid: true
|
|
62
|
-
file: 'web/app/page.tsx'
|
|
63
|
-
- code: 'const Comp = dynamic(() => import("./c"), { "ssr": false })'
|
|
64
|
-
isValid: true
|
|
65
|
-
file: 'web/app/page.tsx'
|
|
66
|
-
- code: "const Comp = dynamic(() => import('./c'), { 'ssr': false })"
|
|
67
|
-
isValid: true
|
|
68
|
-
file: 'web/app/page.tsx'
|
|
69
|
-
- code: "const Comp = lazy(() => import('./my-component'))"
|
|
70
|
-
isValid: true
|
|
71
|
-
file: 'web/app/page.tsx'
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# NOTE: ast-grep maps `.tsx` to language `Tsx`, not `TypeScript`. This rule
|
|
2
|
-
# only scans `.tsx` files; see no-enable-indexscan-off.yml for the companion
|
|
3
|
-
# rule that covers `.ts/.mts/.cts` files.
|
|
4
|
-
id: no-enable-indexscan-off-tsx
|
|
5
|
-
language: Tsx
|
|
6
|
-
severity: error
|
|
7
|
-
message: >-
|
|
8
|
-
Do not use `enable_indexscan = off` — it disables index scans repo-wide within the transaction
|
|
9
|
-
(not just for HNSW vector search), forcing a full sequential scan. For HNSW overfiltering on a
|
|
10
|
-
small hard-capped result window, use `applyFilteredVectorScan()` from `@modules/search-utils`
|
|
11
|
-
(pgvector `hnsw.iterative_scan`) instead.
|
|
12
|
-
rule:
|
|
13
|
-
any:
|
|
14
|
-
- kind: string
|
|
15
|
-
- kind: template_string
|
|
16
|
-
regex: '(?i)enable_indexscan[\s\S]{0,12}(=|to)[\s\S]{0,12}off'
|
|
17
|
-
examples:
|
|
18
|
-
- code: |-
|
|
19
|
-
import sql from 'sql-template-strings'
|
|
20
|
-
export const query = sql`SET LOCAL enable_indexscan = off`
|
|
21
|
-
isValid: false
|
|
22
|
-
file: 'web/lib/__tests__/example.tsx'
|
|
23
|
-
- code: |-
|
|
24
|
-
import sql from 'sql-template-strings'
|
|
25
|
-
export const query = sql`SET LOCAL enable_indexscan TO off`
|
|
26
|
-
isValid: false
|
|
27
|
-
file: 'web/lib/__tests__/example.tsx'
|
|
28
|
-
- code: |-
|
|
29
|
-
export function Example() {
|
|
30
|
-
return <div>hnsw.iterative_scan = strict_order</div>
|
|
31
|
-
}
|
|
32
|
-
isValid: true
|
|
33
|
-
file: 'web/components/example.tsx'
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
# NOTE: `__tests__` directories must contain tests only. Shared helpers, registries,
|
|
2
|
-
# fixtures, and other imported support modules belong in a nearby `test-helpers/`
|
|
3
|
-
# directory so test roots stay easy to scan.
|
|
4
|
-
# NOTE: ast-grep maps `.tsx` to language `Tsx`, not `TypeScript`. This rule
|
|
5
|
-
# only scans `.tsx` files; see no-exports-in-tests-folder.yml for the companion
|
|
6
|
-
# rule that covers `.ts/.mts/.cts` files.
|
|
7
|
-
id: no-exports-in-tests-folder-tsx
|
|
8
|
-
language: Tsx
|
|
9
|
-
severity: error
|
|
10
|
-
message: 'Do not export from files under __tests__; move shared test support to a test-helpers directory.'
|
|
11
|
-
files:
|
|
12
|
-
- '**/__tests__/**/*.tsx'
|
|
13
|
-
rule:
|
|
14
|
-
all:
|
|
15
|
-
- kind: export_statement
|
|
16
|
-
- not:
|
|
17
|
-
pattern: export {}
|
|
18
|
-
examples:
|
|
19
|
-
- code: |
|
|
20
|
-
export function renderThing() {
|
|
21
|
-
return <div />
|
|
22
|
-
}
|
|
23
|
-
isValid: false
|
|
24
|
-
file: 'web/components/example/__tests__/comment-tree-test-helpers.tsx'
|
|
25
|
-
- code: 'export type RenderThing = () => JSX.Element'
|
|
26
|
-
isValid: false
|
|
27
|
-
file: 'web/components/__tests__/render-helpers.tsx'
|
|
28
|
-
- code: |
|
|
29
|
-
it('renders shell exports', () => {
|
|
30
|
-
expect(`export NEXT_PORT=3901`).toContain('export')
|
|
31
|
-
})
|
|
32
|
-
isValid: true
|
|
33
|
-
file: 'web/components/example/__tests__/comment-tree.test.tsx'
|
|
34
|
-
- code: |
|
|
35
|
-
function renderThing() {
|
|
36
|
-
return <div />
|
|
37
|
-
}
|
|
38
|
-
isValid: true
|
|
39
|
-
file: 'web/components/example/__tests__/comment-tree.test.tsx'
|
|
40
|
-
- code: 'export {}'
|
|
41
|
-
isValid: true
|
|
42
|
-
file: 'web/components/example/__tests__/global-jsx.test.tsx'
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
id: no-self-comparison-assertion-tsx
|
|
2
|
-
language: Tsx
|
|
3
|
-
severity: error
|
|
4
|
-
message: 'This compares an expression against itself (e.g. expect(result.id).toBe(result.id)). ast-grep metavariables enforce identical text, so both sides read the same value the same way; nearly every real occurrence of this shape is a copy-paste bug where the second operand should have been an independently derived expected value. Compare against that instead, or a fixture-provided value the code under test did not produce. (Two syntactic shapes are exempted because they cannot be tautological by construction: an expression containing a call anywhere in it, e.g. expect(f(a)).toBe(f(a)) or expect((await load()).id).toBe((await load()).id), is a fresh invocation on each side and legitimately tests that the call is deterministic/pure; and a postfix/prefix increment or decrement, e.g. expect(index++).toBe(index++), advances state between the two reads. A getter or other stateful property access is NOT exempted -- ast-grep cannot see whether a given `.foo` is a plain field or a getter with side effects, and every real violation this rule has caught in this repo is a plain field, so this stays flagged by design; if you are deliberately asserting that such an accessor is stable across two reads, add an `ast-grep-ignore: no-self-comparison-assertion-tsx` comment with that rationale instead of weakening this rule.)'
|
|
5
|
-
files:
|
|
6
|
-
- '**/*.test.tsx'
|
|
7
|
-
- '**/*.spec.tsx'
|
|
8
|
-
rule:
|
|
9
|
-
pattern: 'expect($X).toBe($X)'
|
|
10
|
-
constraints:
|
|
11
|
-
X:
|
|
12
|
-
not:
|
|
13
|
-
any:
|
|
14
|
-
- kind: 'true'
|
|
15
|
-
- kind: 'false'
|
|
16
|
-
- kind: 'number'
|
|
17
|
-
- kind: 'string'
|
|
18
|
-
- kind: 'null'
|
|
19
|
-
- kind: 'undefined'
|
|
20
|
-
- kind: 'call_expression'
|
|
21
|
-
- kind: 'update_expression'
|
|
22
|
-
- has:
|
|
23
|
-
kind: 'call_expression'
|
|
24
|
-
stopBy: end
|
|
25
|
-
examples:
|
|
26
|
-
- code: 'expect(item.id).toBe(item.id)'
|
|
27
|
-
isValid: false
|
|
28
|
-
file: 'web/components/example/__tests__/add-to-list-menu-item.mock.test.tsx'
|
|
29
|
-
- code: 'expect(item.id).toBe(expectedId)'
|
|
30
|
-
isValid: true
|
|
31
|
-
file: 'web/components/example/__tests__/add-to-list-menu-item.mock.test.tsx'
|
|
32
|
-
- code: 'expect(formatItemLabel(item)).toBe(formatItemLabel(item))'
|
|
33
|
-
isValid: true
|
|
34
|
-
file: 'web/components/example/__tests__/add-to-list-menu-item.mock.test.tsx'
|
|
35
|
-
- code: 'expect((await loadItem()).id).toBe((await loadItem()).id)'
|
|
36
|
-
isValid: true
|
|
37
|
-
file: 'web/components/example/__tests__/add-to-list-menu-item.mock.test.tsx'
|
|
38
|
-
- code: 'expect(item.id).toBe(item.id)'
|
|
39
|
-
isValid: true
|
|
40
|
-
file: 'web/components/example/add-to-list-menu-item.tsx'
|
|
41
|
-
- code: 'expect(index++).toBe(index++)'
|
|
42
|
-
isValid: true
|
|
43
|
-
file: 'web/components/example/__tests__/add-to-list-menu-item.mock.test.tsx'
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
id: no-tautological-literal-assertion-tsx
|
|
2
|
-
language: Tsx
|
|
3
|
-
severity: error
|
|
4
|
-
message: 'This asserts a literal against an identical literal (e.g. expect(true).toBe(true)), which can never fail and provides zero real coverage of the code under test. Assert on the actual value the code under test produced instead -- for example expect(await run()).not.toBeNull(), or a specific value or shape derived from calling the function being tested.'
|
|
5
|
-
files:
|
|
6
|
-
- '**/*.test.tsx'
|
|
7
|
-
- '**/*.spec.tsx'
|
|
8
|
-
rule:
|
|
9
|
-
pattern: 'expect($LIT).toBe($LIT)'
|
|
10
|
-
constraints:
|
|
11
|
-
LIT:
|
|
12
|
-
any:
|
|
13
|
-
- kind: 'true'
|
|
14
|
-
- kind: 'false'
|
|
15
|
-
- kind: 'number'
|
|
16
|
-
- kind: 'string'
|
|
17
|
-
- kind: 'null'
|
|
18
|
-
- kind: 'undefined'
|
|
19
|
-
examples:
|
|
20
|
-
- code: 'expect(true).toBe(true)'
|
|
21
|
-
isValid: false
|
|
22
|
-
file: 'web/components/example/__tests__/add-to-list-menu-item.mock.test.tsx'
|
|
23
|
-
- code: "expect('placeholder').toBe('placeholder')"
|
|
24
|
-
isValid: false
|
|
25
|
-
file: 'web/components/example/__tests__/source-list-item.interactions.mock.test.tsx'
|
|
26
|
-
- code: 'expect(screen.getByRole("button")).toBeInTheDocument()'
|
|
27
|
-
isValid: true
|
|
28
|
-
file: 'web/components/example/__tests__/add-to-list-menu-item.mock.test.tsx'
|
|
29
|
-
- code: 'expect(true).toBe(true)'
|
|
30
|
-
isValid: true
|
|
31
|
-
file: 'web/components/example/add-to-list-menu-item.tsx'
|