@cookshack/eslint-config 1.1.0 → 2.0.5

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,571 @@
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
+ fail(1, 'let x = 1; function foo() { return x }',
476
+ `SCOPE 1 GLOBAL pos 0
477
+ SCOPE 1.1 MODULE pos 0
478
+ LET x pos 4
479
+ WRITE x pos 4.4
480
+ LET foo pos 20
481
+ SCOPE 1.1.1 FUNCTION pos 23 name foo
482
+ READ x pos 35`)
483
+
484
+ fail(1, 'let x; { let y = 1; x = y }',
485
+ `SCOPE 1 GLOBAL pos 0
486
+ SCOPE 1.1 MODULE pos 0
487
+ LET x pos 4
488
+ SCOPE 1.1.1 BLOCK pos 7
489
+ LET y pos 13
490
+ WRITE y pos 13.4
491
+ READ y pos 24
492
+ WRITE x pos 25.4`)
493
+
494
+ fail(2, `
495
+ function f
496
+ (a, b, otherwise) {
497
+ let c1, c2, ok
498
+
499
+ if (a)
500
+ ok = 4
501
+ else
502
+ ok = 1
503
+
504
+ if (b) {
505
+ c1 = 2
506
+ b.forEach(d => {
507
+ c1 += d
508
+ })
509
+ return c1
510
+ }
511
+
512
+ {
513
+ c2 = ok
514
+ c2 += ok * ok
515
+ if (c2 > 22)
516
+ return ok
517
+ }
518
+
519
+ return otherwise
520
+ }
521
+ `,
522
+ `SCOPE 1 GLOBAL pos 1
523
+ SCOPE 1.1 MODULE pos 1
524
+ LET f pos 10
525
+ SCOPE 1.1.1 FUNCTION pos 11 name f
526
+ LET a pos 13
527
+ LET b pos 16
528
+ LET otherwise pos 19
529
+ LET c1 pos 38
530
+ LET c2 pos 42
531
+ LET ok pos 46
532
+ READ a pos 56
533
+ WRITE ok B C pos 69.4
534
+ WRITE ok B C pos 87.4
535
+ READ b pos 95
536
+ SCOPE 1.1.1.1 BLOCK pos 98
537
+ WRITE c1 pos 110.4
538
+ READ b pos 115
539
+ SCOPE 1.1.1.1.1 FUNCTION pos 125
540
+ LET d pos 125
541
+ READ d pos 144
542
+ WRITE c1 pos 145.4
543
+ READ c1 pos 164
544
+ SCOPE 1.1.1.2 BLOCK pos 174
545
+ READ ok pos 185
546
+ WRITE c2 pos 187.4
547
+ READ ok pos 198
548
+ READ ok pos 203
549
+ WRITE c2 pos 205.4
550
+ READ c2 pos 214
551
+ READ ok B C pos 236
552
+ READ otherwise pos 253`)
553
+
554
+ fail(1, 'let a; try { f() } catch (err) { a = err.message; console.log(a) }',
555
+ `SCOPE 1 GLOBAL pos 0
556
+ SCOPE 1.1 MODULE pos 0
557
+ LET a pos 4
558
+ SCOPE 1.1.1 BLOCK pos 11
559
+ SCOPE 1.1.2 CATCH pos 19
560
+ LET err pos 26
561
+ SCOPE 1.1.2.1 BLOCK pos 31
562
+ READ err pos 37
563
+ WRITE a pos 48.4
564
+ READ a pos 62`)
565
+
566
+ globalThis.describe('narrowest-scope', () => {
567
+ for (let tc of validCases)
568
+ globalThis.it(tc.code, () => _pass(tc))
569
+ for (let tc of invalidCases)
570
+ globalThis.it(tc.code, () => _fail(tc))
571
+ })
@@ -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 }))