@cookshack/eslint-config 1.2.0 → 3.0.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.
@@ -0,0 +1,597 @@
1
+ import { Linter } from 'eslint'
2
+ import { plugins, getPrintBuffer } from '../../index.js'
3
+ import { diffLines } from 'diff'
4
+
5
+ let linter, validCases, invalidCases, config
6
+
7
+ linter = new Linter()
8
+ validCases = []
9
+ invalidCases = []
10
+
11
+ config = [ { languageOptions: { ecmaVersion: 2025,
12
+ sourceType: 'module' },
13
+ plugins,
14
+ rules: { 'cookshack/narrowest-scope': 'error' } } ]
15
+
16
+ function pass(code, expected) {
17
+ validCases.push({ code, expected })
18
+ }
19
+
20
+ function patch(expected, output) {
21
+ return diffLines(expected.trim(), output.trim()).flatMap(p => p.value.split('\n').filter(l => l).map(l => ({ ...p, line: l })))
22
+ .map(p => (p.removed ? '- ' : p.added ? '+ ' : ' ') + p.line).join('\n')
23
+ }
24
+
25
+ function _pass(tc) {
26
+ let messages, output
27
+
28
+ messages = linter.verify(tc.code, config)
29
+ output = getPrintBuffer()
30
+ if (messages.length > 0)
31
+ throw new Error('unexpected errors: ' + JSON.stringify(messages) + '\noutput:\n' + output)
32
+ if (tc.expected?.trim() == output.trim())
33
+ return
34
+ throw new Error('output mismatch:\n' + patch(tc.expected, output))
35
+ }
36
+
37
+ function _fail(tc) {
38
+ let messages, output
39
+
40
+ messages = linter.verify(tc.code, config)
41
+ output = getPrintBuffer()
42
+ if (messages.length == tc.errors.length) {
43
+ if (tc.expected?.trim() == output.trim())
44
+ return
45
+ throw new Error('output mismatch:\n' + patch(tc.expected, output))
46
+ }
47
+ throw new Error('expected ' + tc.errors.length + ' errors, got ' + messages.length + '\n' + JSON.stringify(messages, null, 2))
48
+ }
49
+
50
+ function fail(count, code, expected) {
51
+ let errors
52
+
53
+ errors = []
54
+ while (count > 0) {
55
+ errors.push({ messageId: 'tooBroad' })
56
+ count--
57
+ }
58
+ invalidCases.push({ code, errors, expected })
59
+ }
60
+
61
+ pass('if (g) { let x = 0; x++; console.log(x); }',
62
+ `SCOPE 1 GLOBAL pos 0
63
+ SCOPE 1.1 MODULE pos 0
64
+ SCOPE 1.1.1 BLOCK pos 7
65
+ LET x pos 13
66
+ WRITE x pos 13.4
67
+ READ x pos 20
68
+ WRITE x pos 20
69
+ READ x pos 37`)
70
+
71
+ pass('let x = 0; x++; console.log(x);',
72
+ `SCOPE 1 GLOBAL pos 0
73
+ SCOPE 1.1 MODULE pos 0
74
+ LET x pos 4
75
+ WRITE x pos 4.4
76
+ READ x pos 11
77
+ WRITE x pos 11
78
+ READ x pos 28`)
79
+
80
+ pass('{ let x = 0; if (x) console.log(1); }',
81
+ `SCOPE 1 GLOBAL pos 0
82
+ SCOPE 1.1 MODULE pos 0
83
+ SCOPE 1.1.1 BLOCK pos 0
84
+ LET x pos 6
85
+ WRITE x pos 6.4
86
+ READ x pos 17`)
87
+
88
+ pass('let x = 0; x++; let y = x; console.log(y);',
89
+ `SCOPE 1 GLOBAL pos 0
90
+ SCOPE 1.1 MODULE pos 0
91
+ LET x pos 4
92
+ WRITE x pos 4.4
93
+ READ x pos 11
94
+ WRITE x pos 11
95
+ READ x pos 19.6
96
+ LET y pos 20
97
+ WRITE y pos 20.4
98
+ READ y pos 39`)
99
+
100
+ pass('let x = 0; x++; let y = x + 1; console.log(y);',
101
+ `SCOPE 1 GLOBAL pos 0
102
+ SCOPE 1.1 MODULE pos 0
103
+ LET x pos 4
104
+ WRITE x pos 4.4
105
+ READ x pos 11
106
+ WRITE x pos 11
107
+ READ x pos 19.6
108
+ LET y pos 20
109
+ WRITE y pos 20.4
110
+ READ y pos 43`)
111
+
112
+ pass('function foo() { let x; x = 1; return x }',
113
+ `SCOPE 1 GLOBAL pos 0
114
+ SCOPE 1.1 MODULE pos 0
115
+ LET foo pos 9
116
+ SCOPE 1.1.1 FUNCTION pos 12 name foo
117
+ LET x pos 21
118
+ WRITE x pos 29.4
119
+ READ x pos 38`)
120
+
121
+ pass('for (let i = 0; i < 10; i++) { console.log(i) }',
122
+ `SCOPE 1 GLOBAL pos 0
123
+ SCOPE 1.1 MODULE pos 0
124
+ SCOPE 1.1.1 FOR pos 0
125
+ LET i pos 9
126
+ WRITE i pos 9.4
127
+ READ i pos 16
128
+ READ i pos 24
129
+ WRITE i pos 24
130
+ SCOPE 1.1.1.1 BLOCK pos 29
131
+ READ i C pos 43`)
132
+
133
+ pass('function outer() { let x; function inner() { x = 1 } return x }',
134
+ `SCOPE 1 GLOBAL pos 0
135
+ SCOPE 1.1 MODULE pos 0
136
+ LET outer pos 9
137
+ SCOPE 1.1.1 FUNCTION pos 14 name outer
138
+ LET x pos 23
139
+ LET inner pos 35
140
+ SCOPE 1.1.1.1 FUNCTION pos 40 name inner
141
+ WRITE x pos 50.4
142
+ READ x pos 60`)
143
+
144
+ pass('let x; function foo() { x = 1 } function bar() { return x }',
145
+ `SCOPE 1 GLOBAL pos 0
146
+ SCOPE 1.1 MODULE pos 0
147
+ LET x pos 4
148
+ LET foo pos 16
149
+ SCOPE 1.1.1 FUNCTION pos 19 name foo
150
+ WRITE x pos 29.4
151
+ LET bar pos 41
152
+ SCOPE 1.1.2 FUNCTION pos 44 name bar
153
+ READ x pos 56`)
154
+
155
+ pass(`
156
+ function f(s1, s2, otherwise) {
157
+ let a, s
158
+
159
+ a = []
160
+ s = s1
161
+ while (s) {
162
+ if (s.done)
163
+ break
164
+ a.push(s)
165
+ s = s.next
166
+ }
167
+ s = s2
168
+ while (s) {
169
+ if (s.done)
170
+ break
171
+ if (a.includes(s))
172
+ return s
173
+ s = s.next
174
+ }
175
+ return otherwise
176
+ }
177
+ `,
178
+ `SCOPE 1 GLOBAL pos 1
179
+ SCOPE 1.1 MODULE pos 1
180
+ LET f pos 10
181
+ SCOPE 1.1.1 FUNCTION pos 11 name f
182
+ LET s1 pos 12
183
+ LET s2 pos 16
184
+ LET otherwise pos 20
185
+ LET a pos 39
186
+ LET s pos 42
187
+ WRITE a pos 53.4
188
+ READ s1 pos 60
189
+ WRITE s pos 62.4
190
+ READ s pos 72
191
+ SCOPE 1.1.1.1 BLOCK pos 75
192
+ READ s C pos 85
193
+ READ a C pos 109
194
+ READ s C pos 116
195
+ READ s C pos 127
196
+ WRITE s C pos 133.4
197
+ READ s2 pos 144
198
+ WRITE s pos 146.4
199
+ READ s pos 156
200
+ SCOPE 1.1.1.2 BLOCK pos 159
201
+ READ s C pos 169
202
+ READ a C pos 197
203
+ READ s C pos 208
204
+ READ s B C pos 225
205
+ READ s C pos 235
206
+ WRITE s C pos 241.4
207
+ READ otherwise pos 255`)
208
+
209
+ pass('import { a } from \'a.js\'; { a.f() }',
210
+ `SCOPE 1 GLOBAL pos 0
211
+ SCOPE 1.1 MODULE pos 0
212
+ LET a pos 9
213
+ SCOPE 1.1.1 BLOCK pos 26
214
+ READ a pos 28`)
215
+
216
+ pass('function foo() { return 1 } function bar() { return foo() }',
217
+ `SCOPE 1 GLOBAL pos 0
218
+ SCOPE 1.1 MODULE pos 0
219
+ LET foo pos 9
220
+ SCOPE 1.1.1 FUNCTION pos 12 name foo
221
+ LET bar pos 37
222
+ SCOPE 1.1.2 FUNCTION pos 40 name bar
223
+ READ foo pos 52`)
224
+
225
+ pass(`
226
+ let tout
227
+
228
+ function update
229
+ (view) {
230
+ if (tout)
231
+ clearTimeout(tout)
232
+ tout = setTimeout(() => console.log('hi'), 10000)
233
+ }
234
+ `,
235
+ `SCOPE 1 GLOBAL pos 1
236
+ SCOPE 1.1 MODULE pos 1
237
+ LET tout pos 5
238
+ LET update pos 20
239
+ SCOPE 1.1.1 FUNCTION pos 26 name update
240
+ LET view pos 28
241
+ READ tout pos 42
242
+ READ tout B C pos 65
243
+ SCOPE 1.1.1.1 FUNCTION pos 91
244
+ WRITE tout pos 122.4`)
245
+
246
+ pass(`
247
+ function init
248
+ () {
249
+ let out2
250
+
251
+ function update
252
+ (view) {
253
+ if (out2)
254
+ clearTimeout(out2)
255
+ out2 = setTimeout(() => console.log('hi'), 10000)
256
+ }
257
+ }
258
+ `,
259
+ `SCOPE 1 GLOBAL pos 1
260
+ SCOPE 1.1 MODULE pos 1
261
+ LET init pos 10
262
+ SCOPE 1.1.1 FUNCTION pos 14 name init
263
+ LET out2 pos 26
264
+ LET update pos 43
265
+ SCOPE 1.1.1.1 FUNCTION pos 49 name update
266
+ LET view pos 53
267
+ READ out2 pos 69
268
+ READ out2 B C pos 94
269
+ SCOPE 1.1.1.1.1 FUNCTION pos 122
270
+ WRITE out2 pos 153.4`)
271
+
272
+ pass(`
273
+ function init
274
+ () {
275
+ let stopTimeout
276
+
277
+ function f() {
278
+ if (stopTimeout) {
279
+ clearTimeout(stopTimeout)
280
+ stopTimeout = 0
281
+ }
282
+ else {
283
+ console.log('Again to stop')
284
+ stopTimeout = setTimeout(() => {
285
+ stopTimeout = 0
286
+ console.log('stop timed out')
287
+ }, 5000)
288
+ }
289
+ }
290
+
291
+ return f
292
+ }
293
+ `,
294
+ `SCOPE 1 GLOBAL pos 1
295
+ SCOPE 1.1 MODULE pos 1
296
+ LET init pos 10
297
+ SCOPE 1.1.1 FUNCTION pos 14 name init
298
+ LET stopTimeout pos 26
299
+ LET f pos 50
300
+ SCOPE 1.1.1.1 FUNCTION pos 51 name f
301
+ READ stopTimeout pos 64
302
+ SCOPE 1.1.1.1.1 BLOCK pos 77
303
+ READ stopTimeout C pos 98
304
+ WRITE stopTimeout C pos 132.4
305
+ SCOPE 1.1.1.1.2 BLOCK pos 148
306
+ SCOPE 1.1.1.1.2.1 FUNCTION pos 216
307
+ WRITE stopTimeout C pos 247.4
308
+ WRITE stopTimeout C pos 300.4
309
+ READ f pos 321`)
310
+
311
+ pass('try { f() } catch (err) { console.log(err.message) }',
312
+ `SCOPE 1 GLOBAL pos 0
313
+ SCOPE 1.1 MODULE pos 0
314
+ SCOPE 1.1.1 BLOCK pos 4
315
+ SCOPE 1.1.2 CATCH pos 12
316
+ LET err pos 19
317
+ SCOPE 1.1.2.1 BLOCK pos 24
318
+ READ err pos 38`)
319
+
320
+ pass(`
321
+ class A extends B {
322
+ constructor
323
+ (name) {
324
+ super()
325
+ this.name = name
326
+ }
327
+
328
+ update
329
+ (state) {
330
+ if (state.docChanged)
331
+ console.log('yes')
332
+ }
333
+ }
334
+
335
+ g = f({ a() { return new A('eg') } })
336
+ `,
337
+ `SCOPE 1 GLOBAL pos 3
338
+ SCOPE 1.1 MODULE pos 3
339
+ LET A pos 9
340
+ SCOPE 1.1.1 CLASS pos 9 name A
341
+ LET A pos 9
342
+ SCOPE 1.1.1.1 FUNCTION pos 43 name constructor
343
+ LET name pos 44
344
+ READ name pos 84
345
+ SCOPE 1.1.1.2 FUNCTION pos 111 name update
346
+ LET state pos 112
347
+ READ state pos 131
348
+ SCOPE 1.1.2 FUNCTION pos 198 name a
349
+ READ A pos 214`)
350
+
351
+ pass(`
352
+ let clen
353
+
354
+ function parse
355
+ () {
356
+ if (maybe())
357
+ clen = get()
358
+
359
+ if (check(clen)) {
360
+ run()
361
+ clen = 0
362
+ }
363
+ }
364
+ `,
365
+ `SCOPE 1 GLOBAL pos 1
366
+ SCOPE 1.1 MODULE pos 1
367
+ LET clen pos 5
368
+ LET parse pos 20
369
+ SCOPE 1.1.1 FUNCTION pos 25 name parse
370
+ WRITE clen B C pos 62.4
371
+ READ clen pos 76
372
+ SCOPE 1.1.1.1 BLOCK pos 83
373
+ WRITE clen C pos 107.4`)
374
+
375
+ pass(`
376
+ function make
377
+ () {
378
+ let clen
379
+
380
+ function parse
381
+ () {
382
+ while (1) {
383
+ if (maybe())
384
+ clen = get()
385
+
386
+ if (check(clen)) {
387
+ run()
388
+ clen = undefined
389
+ }
390
+ else
391
+ break
392
+ }
393
+ }
394
+
395
+ return 0
396
+ }
397
+ `,
398
+ `SCOPE 1 GLOBAL pos 1
399
+ SCOPE 1.1 MODULE pos 1
400
+ LET make pos 10
401
+ SCOPE 1.1.1 FUNCTION pos 14 name make
402
+ LET clen pos 26
403
+ LET parse pos 43
404
+ SCOPE 1.1.1.1 FUNCTION pos 48 name parse
405
+ SCOPE 1.1.1.1.1 BLOCK pos 70
406
+ WRITE clen B C pos 111.4
407
+ READ clen pos 129
408
+ SCOPE 1.1.1.1.1.1 BLOCK pos 136
409
+ READ undefined pos 167
410
+ WRITE clen C pos 176.4`)
411
+
412
+ pass(`
413
+ function initMouse
414
+ () {
415
+ let hover
416
+
417
+ function updateHover
418
+ () {
419
+ if (maybe()) {
420
+ hover = 1
421
+ }
422
+ else if (hover) {
423
+ hover = 0
424
+ }
425
+ }
426
+ }
427
+ `,
428
+ `SCOPE 1 GLOBAL pos 1
429
+ SCOPE 1.1 MODULE pos 1
430
+ LET initMouse pos 10
431
+ SCOPE 1.1.1 FUNCTION pos 19 name initMouse
432
+ LET hover pos 31
433
+ LET updateHover pos 49
434
+ SCOPE 1.1.1.1 FUNCTION pos 60 name updateHover
435
+ SCOPE 1.1.1.1.1 BLOCK pos 85
436
+ WRITE hover C pos 102.4
437
+ READ hover B C pos 122
438
+ SCOPE 1.1.1.1.2 BLOCK pos 129
439
+ WRITE hover C pos 146.4`)
440
+
441
+ pass(`
442
+ export let wexts
443
+
444
+ export
445
+ function init
446
+ () {
447
+ wexts = Mk.array
448
+ }
449
+ `,
450
+ `SCOPE 1 GLOBAL pos 1
451
+ SCOPE 1.1 MODULE pos 1
452
+ LET wexts pos 12
453
+ LET init pos 35
454
+ SCOPE 1.1.1 FUNCTION pos 39 name init
455
+ WRITE wexts pos 63.4`)
456
+
457
+ pass(`
458
+ let classCache
459
+
460
+ export
461
+ function add
462
+ () {
463
+ classCache = classCache || new Map()
464
+ }
465
+ `,
466
+ `SCOPE 1 GLOBAL pos 1
467
+ SCOPE 1.1 MODULE pos 1
468
+ LET classCache pos 5
469
+ LET add pos 33
470
+ SCOPE 1.1.1 FUNCTION pos 36 name add
471
+ READ classCache pos 57
472
+ READ Map pos 75
473
+ WRITE classCache pos 80.4`)
474
+
475
+ pass(`function a1
476
+ () {
477
+ let p
478
+
479
+ function a2
480
+ () {
481
+ return p[p.length - 1]
482
+ }
483
+
484
+ p = []
485
+ return a2(0)
486
+ }`,
487
+ `SCOPE 1 GLOBAL pos 0
488
+ SCOPE 1.1 MODULE pos 0
489
+ LET a1 pos 9
490
+ SCOPE 1.1.1 FUNCTION pos 11 name a1
491
+ LET p pos 23
492
+ LET a2 pos 37
493
+ SCOPE 1.1.1.1 FUNCTION pos 39 name a2
494
+ READ p pos 58
495
+ READ p pos 60
496
+ WRITE p pos 87.4
497
+ READ a2 pos 97`)
498
+
499
+ fail(1, 'let x = 1; function foo() { return x }',
500
+ `SCOPE 1 GLOBAL pos 0
501
+ SCOPE 1.1 MODULE pos 0
502
+ LET x pos 4
503
+ WRITE x pos 4.4
504
+ LET foo pos 20
505
+ SCOPE 1.1.1 FUNCTION pos 23 name foo
506
+ READ x pos 35`)
507
+
508
+ fail(1, 'let x; { let y = 1; x = y }',
509
+ `SCOPE 1 GLOBAL pos 0
510
+ SCOPE 1.1 MODULE pos 0
511
+ LET x pos 4
512
+ SCOPE 1.1.1 BLOCK pos 7
513
+ LET y pos 13
514
+ WRITE y pos 13.4
515
+ READ y pos 24
516
+ WRITE x pos 25.4`)
517
+
518
+ fail(2, `
519
+ function f
520
+ (a, b, otherwise) {
521
+ let c1, c2, ok
522
+
523
+ if (a)
524
+ ok = 4
525
+ else
526
+ ok = 1
527
+
528
+ if (b) {
529
+ c1 = 2
530
+ b.forEach(d => {
531
+ c1 += d
532
+ })
533
+ return c1
534
+ }
535
+
536
+ {
537
+ c2 = ok
538
+ c2 += ok * ok
539
+ if (c2 > 22)
540
+ return ok
541
+ }
542
+
543
+ return otherwise
544
+ }
545
+ `,
546
+ `SCOPE 1 GLOBAL pos 1
547
+ SCOPE 1.1 MODULE pos 1
548
+ LET f pos 10
549
+ SCOPE 1.1.1 FUNCTION pos 11 name f
550
+ LET a pos 13
551
+ LET b pos 16
552
+ LET otherwise pos 19
553
+ LET c1 pos 38
554
+ LET c2 pos 42
555
+ LET ok pos 46
556
+ READ a pos 56
557
+ WRITE ok B C pos 69.4
558
+ WRITE ok B C pos 87.4
559
+ READ b pos 95
560
+ SCOPE 1.1.1.1 BLOCK pos 98
561
+ WRITE c1 pos 110.4
562
+ READ b pos 115
563
+ SCOPE 1.1.1.1.1 FUNCTION pos 125
564
+ LET d pos 125
565
+ READ c1 pos 138
566
+ READ d pos 144
567
+ WRITE c1 pos 145.4
568
+ READ c1 pos 164
569
+ SCOPE 1.1.1.2 BLOCK pos 174
570
+ READ ok pos 185
571
+ WRITE c2 pos 187.4
572
+ READ c2 pos 192
573
+ READ ok pos 198
574
+ READ ok pos 203
575
+ WRITE c2 pos 205.4
576
+ READ c2 pos 214
577
+ READ ok B C pos 236
578
+ READ otherwise pos 253`)
579
+
580
+ fail(1, 'let a; try { f() } catch (err) { a = err.message; console.log(a) }',
581
+ `SCOPE 1 GLOBAL pos 0
582
+ SCOPE 1.1 MODULE pos 0
583
+ LET a pos 4
584
+ SCOPE 1.1.1 BLOCK pos 11
585
+ SCOPE 1.1.2 CATCH pos 19
586
+ LET err pos 26
587
+ SCOPE 1.1.2.1 BLOCK pos 31
588
+ READ err pos 37
589
+ WRITE a pos 48.4
590
+ READ a pos 62`)
591
+
592
+ globalThis.describe('narrowest-scope', () => {
593
+ for (let tc of validCases)
594
+ globalThis.it(tc.code, () => _pass(tc))
595
+ for (let tc of invalidCases)
596
+ globalThis.it(tc.code, () => _fail(tc))
597
+ })
@@ -0,0 +1,34 @@
1
+ import { RuleTester } from 'eslint'
2
+ import { plugins } from '../../index.js'
3
+
4
+ let ruleTester, validCases, invalidCases
5
+
6
+ ruleTester = new RuleTester()
7
+ validCases = []
8
+ invalidCases = []
9
+
10
+ function pass
11
+ (code) {
12
+ validCases.push({ code })
13
+ }
14
+
15
+ function fail
16
+ (messageId, code) {
17
+ invalidCases.push({ code, errors: [ { messageId } ] })
18
+ }
19
+
20
+ pass('if (x) { }')
21
+
22
+ fail('positiveVibes', 'if (!x) { }')
23
+
24
+ fail('equality', 'if (x != y) { }')
25
+
26
+ fail('strictEquality', 'if (x !== y) { }')
27
+
28
+ fail('equality', 'function f(x, y) { return x != y }')
29
+
30
+ globalThis.describe('positive-vibes',
31
+ () => ruleTester.run('positive-vibes',
32
+ plugins.cookshack.rules['positive-vibes'],
33
+ { valid: validCases,
34
+ invalid: invalidCases }))
@@ -0,0 +1,42 @@
1
+ import { RuleTester } from 'eslint'
2
+ import { plugins } from '../../index.js'
3
+
4
+ let ruleTester, validCases, invalidCases
5
+
6
+ ruleTester = new RuleTester()
7
+ validCases = []
8
+ invalidCases = []
9
+
10
+ function pass
11
+ (code) {
12
+ validCases.push({ code })
13
+ }
14
+
15
+ function fail
16
+ (count, code) {
17
+ let errors
18
+
19
+ errors = []
20
+ while (count > 0) {
21
+ errors.push({ messageId: 'risky' })
22
+ count--
23
+ }
24
+
25
+ invalidCases.push({ code, errors })
26
+ }
27
+
28
+ pass('if (x == y) { }')
29
+
30
+ pass('if (x != y) { }')
31
+
32
+ pass('if (x !== y) { }')
33
+
34
+ fail(1, 'if (x === y) { }')
35
+
36
+ fail(2, 'if (x === y || a === y || a > 4) { }')
37
+
38
+ globalThis.describe('use-risky-equal',
39
+ () => ruleTester.run('use-risky-equal',
40
+ plugins.cookshack.rules['use-risky-equal'],
41
+ { valid: validCases,
42
+ invalid: invalidCases }))