@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.
package/dist/index.cjs CHANGED
@@ -4,31 +4,906 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var globals = require('globals');
6
6
 
7
+ let varIds, nextVarId, printBuffer;
8
+
9
+ printBuffer = [];
10
+ varIds = new Map();
11
+ nextVarId = 0;
12
+
13
+ function print
14
+ (...args) {
15
+ printBuffer.push(args.join(' '));
16
+ }
17
+
18
+ function trace$1
19
+ (...args) {
20
+ }
21
+
22
+ function getPrintBuffer
23
+ () {
24
+ return printBuffer.join('\n')
25
+ }
26
+
27
+ function clearPrintBuffer
28
+ () {
29
+ printBuffer = [];
30
+ }
31
+
32
+ function getNarrowestScope
33
+ (variable) {
34
+ let common;
35
+
36
+ common = null;
37
+ for (let ref of variable.references) {
38
+ if (variable.defs.some(def => def.name == ref.identifier))
39
+ continue
40
+ if (ref.from)
41
+ if (common)
42
+ common = getCommonAncestor(common, ref.from);
43
+ else
44
+ common = ref.from;
45
+ }
46
+ return common
47
+ }
48
+
49
+ function getCommonAncestor
50
+ (scope1, scope2) {
51
+ let ancestors, s;
52
+
53
+ ancestors = [];
54
+ s = scope1;
55
+ while (s) {
56
+ if (s.type == 'global')
57
+ break
58
+ ancestors.push(s);
59
+ s = s.upper;
60
+ }
61
+ s = scope2;
62
+ while (s) {
63
+ if (s.type == 'global')
64
+ break
65
+ if (ancestors.includes(s))
66
+ return s
67
+ s = s.upper;
68
+ }
69
+ return scope1
70
+ }
71
+
72
+ function getDefinitionScope
73
+ (variable) {
74
+ return variable.scope
75
+ }
76
+
77
+ function isWriteRef
78
+ (ref) {
79
+ let parent;
80
+
81
+ parent = ref.identifier.parent;
82
+ if (parent) {
83
+ if (parent.type == 'AssignmentExpression' && parent.left == ref.identifier)
84
+ return 1
85
+ if (parent.type == 'UpdateExpression')
86
+ return 1
87
+ if (parent.type == 'VariableDeclarator' && parent.id == ref.identifier)
88
+ return 1
89
+ }
90
+ return 0
91
+ }
92
+
93
+ function isReadRef
94
+ (ref) {
95
+ if (isWriteRef(ref))
96
+ return 0
97
+ return 1
98
+ }
99
+
100
+ function getConditionalContext
101
+ (ref) {
102
+ let node, prevNode, scopeBlock;
103
+
104
+ scopeBlock = ref.from.block;
105
+ prevNode = ref.identifier;
106
+ node = ref.identifier.parent;
107
+ while (node) {
108
+ if (node == scopeBlock)
109
+ break
110
+ if (node.type == 'IfStatement')
111
+ if (prevNode == node.test || nodeContains(node.test, prevNode))
112
+ prevNode = node;
113
+ else
114
+ return 'B'
115
+ else if ([ 'WhileStatement', 'DoWhileStatement', 'ForStatement', 'ForInStatement', 'ForOfStatement', 'SwitchStatement' ].includes(node.type))
116
+ if (prevNode == node.test || nodeContains(node.test, prevNode))
117
+ prevNode = node;
118
+ else
119
+ return 'B'
120
+ else
121
+ prevNode = node;
122
+ node = node.parent;
123
+ }
124
+ return ''
125
+ }
126
+
127
+ function nodeContains(node, target) {
128
+ if (node == target)
129
+ return true
130
+ if (node && typeof node == 'object')
131
+ for (let key in node)
132
+ if (nodeHas(node[key], target))
133
+ return true
134
+ return false
135
+ }
136
+
137
+ function nodeHas(value, target) {
138
+ if (value == target)
139
+ return true
140
+ if (Array.isArray(value))
141
+ return value.some(v => nodeContains(v, target))
142
+ return false
143
+ }
144
+
145
+ function isConditionalRef
146
+ (ref, narrowestScope) {
147
+ let node;
148
+
149
+ node = ref.identifier.parent;
150
+
151
+ while (node) {
152
+ if (node == narrowestScope.block)
153
+ break
154
+ if (node.type == 'BlockStatement') {
155
+ let parent;
156
+
157
+ parent = node.parent;
158
+ if (parent?.type == 'IfStatement' && (parent.consequent == node || parent.alternate == node))
159
+ return true
160
+ if ([ 'WhileStatement', 'DoWhileStatement', 'ForStatement', 'ForInStatement', 'ForOfStatement' ].includes(parent?.type) && parent.body == node)
161
+ return true
162
+ }
163
+ node = node.parent;
164
+ }
165
+ return false
166
+ }
167
+
168
+ function markConditionalRefs
169
+ (variable, scopeToNode, narrowestScope) {
170
+ for (let ref of variable.references) {
171
+ let refNode, rItems, item;
172
+
173
+ refNode = scopeToNode.get(ref.from);
174
+ rItems = refNode.items.filter(i => i.ref == ref);
175
+ item = rItems[0];
176
+ if (item && (item.ctx == 'B' || isConditionalRef(ref, narrowestScope)))
177
+ item.isConditional = true;
178
+ }
179
+ }
180
+
181
+ function mayBeReadBeforeAnyWrite
182
+ (variable, scopeToNode, narrowestScope) {
183
+ let refs;
184
+
185
+ refs = [ ...variable.references ];
186
+ refs.sort((a, b) => (a.cookshackNarrowestScopeItem?.pos ?? a.identifier.range[0]) - (b.cookshackNarrowestScopeItem?.pos ?? b.identifier.range[0]));
187
+
188
+ for (let ref of refs) {
189
+ let item;
190
+
191
+ if (isReadRef(ref))
192
+ return 1
193
+
194
+ item = ref.cookshackNarrowestScopeItem;
195
+ if (item.ctx == 'B' || isConditionalRef(ref, narrowestScope))
196
+ continue
197
+ return 0
198
+ }
199
+ }
200
+
201
+ function scopeStart(scope) {
202
+ if (scope.block == null)
203
+ return Infinity
204
+ if (scope.type == 'function' && scope.block.id)
205
+ return scope.block.id.range[1]
206
+ if (scope.type == 'class' && scope.block.id)
207
+ return scope.block.id.range[0]
208
+ return scope.block.range[0]
209
+ }
210
+
211
+ function ensureInVarIds
212
+ (variable) {
213
+ if (varIds.has(variable))
214
+ return
215
+ varIds.set(variable, nextVarId++);
216
+ }
217
+
218
+ function isCompoundAssignmentOp
219
+ (op) {
220
+ if (op == '=')
221
+ return 0
222
+ return 1
223
+ }
224
+
225
+ function buildScopeTree
226
+ (scope, prefix, scopeToNode, astToTree) {
227
+ let node, siblingNum;
228
+
229
+ node = {
230
+ scope,
231
+ prefix,
232
+ items: [],
233
+ children: []
234
+ };
235
+ scopeToNode.set(scope, node);
236
+ if (scope.block && astToTree)
237
+ astToTree.set(scope.block, node);
238
+
239
+ siblingNum = 0;
240
+ for (let child of scope.childScopes) {
241
+ siblingNum++;
242
+ node.children.push(buildScopeTree(child, prefix + '.' + siblingNum, scopeToNode, astToTree));
243
+ }
244
+
245
+ for (let variable of scope.variables) {
246
+ if (variable.defs.length > 0) {
247
+ ensureInVarIds(variable);
248
+ node.items.push({ type: 'LET', name: variable.name, pos: variable.defs[0].name.range[0], defNode: variable.defs[0].node, defType: variable.defs[0].type, identifier: variable.defs[0].name, variable, varId: varIds.get(variable) });
249
+ }
250
+
251
+ for (let ref of variable.references) {
252
+ let targetNode;
253
+
254
+ targetNode = scopeToNode.get(ref.from);
255
+ if (targetNode) {
256
+ let parent, sortPos, ctx, item1, item2;
257
+
258
+ ctx = getConditionalContext(ref);
259
+ parent = ref.identifier.parent;
260
+
261
+ if (isWriteRef(ref))
262
+ if (ref.identifier.parent?.type == 'UpdateExpression') {
263
+ item1 = { ref, type: 'READ', name: ref.identifier.name, ctx, pos: ref.identifier.range[0] };
264
+ item2 = { ref, type: 'WRITE', name: ref.identifier.name, pos: ref.identifier.range[0] };
265
+ }
266
+ else if (ref.identifier.parent?.type == 'AssignmentExpression') {
267
+ sortPos = parent.right.range[1] + 0.4;
268
+ if (ref.identifier.parent.left == ref.identifier && isCompoundAssignmentOp(ref.identifier.parent.operator)) {
269
+ item1 = { ref, type: 'READ', name: ref.identifier.name, ctx, pos: ref.identifier.range[0] };
270
+ item2 = { ref, type: 'WRITE', name: ref.identifier.name, pos: sortPos };
271
+ }
272
+ else
273
+ item1 = { ref, type: 'WRITE', name: ref.identifier.name, ctx, pos: sortPos };
274
+ }
275
+ else if (ref.identifier.parent?.type == 'VariableDeclarator')
276
+ item1 = { ref, type: 'WRITE', name: ref.identifier.name, pos: ref.identifier.range[0] + 0.4 };
277
+ else
278
+ item1 = { ref, type: 'WRITE', name: ref.identifier.name, pos: ref.identifier.range[0] };
279
+ else {
280
+ let declarator;
281
+
282
+ declarator = parent;
283
+ while (declarator)
284
+ if (declarator.type == 'VariableDeclarator')
285
+ break
286
+ else
287
+ declarator = declarator.parent;
288
+ if (declarator?.type == 'VariableDeclarator' && nodeContains(declarator.init, ref.identifier))
289
+ sortPos = declarator.id ? declarator.id.range[0] - 0.4 : ref.identifier.range[0];
290
+ else
291
+ sortPos = ref.identifier.range[0];
292
+ item1 = { ref, type: 'READ', name: ref.identifier.name, ctx, pos: sortPos };
293
+ }
294
+ ensureInVarIds(variable);
295
+ item1.varId = varIds.get(variable);
296
+ targetNode.items.push(item1);
297
+ if (item2) {
298
+ item2.varId = varIds.get(variable);
299
+ targetNode.items.push(item2);
300
+ }
301
+ ref.cookshackNarrowestScopeItem = item2 || item1;
302
+ }
303
+ }
304
+ }
305
+
306
+ node.items.sort((a, b) => a.pos - b.pos);
307
+
308
+ return node
309
+ }
310
+
311
+ function checkScopeNode
312
+ (context, treeNode, reported, scopeToNode) {
313
+ let indent;
314
+
315
+ reported = reported || new Set;
316
+ indent = ' '.repeat(treeNode.prefix.split('.').length - 1);
317
+
318
+ for (let variable of treeNode.scope.variables) {
319
+ let defNode;
320
+
321
+ if (reported.has(variable))
322
+ continue
323
+ if (variable.defs.length == 0)
324
+ continue
325
+ if ([ 'Parameter', 'FunctionName', 'ImportBinding', 'CatchClause', 'ClassName' ].includes(variable.defs[0].type))
326
+ continue
327
+ if (variable.defs[0].node.parent?.parent?.type == 'ExportNamedDeclaration')
328
+ continue
329
+
330
+ defNode = variable.defs[0]?.name;
331
+ if (defNode) {
332
+ let defScope, narrowestScope, defNodePrefix;
333
+
334
+ defScope = getDefinitionScope(variable);
335
+ defNodePrefix = scopeToNode.get(defScope)?.prefix ?? '?';
336
+ trace$1(indent, '1 found decl scope of', variable.name + ':', defNodePrefix + ' ' + defScope.type.toUpperCase());
337
+
338
+ narrowestScope = getNarrowestScope(variable);
339
+ if (narrowestScope) {
340
+ let narrowestPrefix;
341
+
342
+ narrowestPrefix = scopeToNode.get(narrowestScope)?.prefix ?? '?';
343
+ trace$1(indent, '2 found narrowest scope of', variable.name + ':', narrowestPrefix + ' ' + narrowestScope?.type.toUpperCase());
344
+
345
+ markConditionalRefs(variable, scopeToNode, narrowestScope);
346
+
347
+ if (defScope == narrowestScope)
348
+ continue
349
+ trace$1(indent, '3', variable.name, 'could be moved to a narrower scope');
350
+
351
+ if (defScope.type == 'for') {
352
+ trace$1(indent, '4 exception:', variable.name, 'is in a for loop header');
353
+ continue
354
+ }
355
+ if (mayBeReadBeforeAnyWrite(variable, scopeToNode, narrowestScope)) {
356
+ trace$1(indent, '4 exception:', variable.name, 'mayBeReadBeforeAnyWrite');
357
+ continue
358
+ }
359
+
360
+ trace$1(indent, '5', variable.name, 'is too broad');
361
+
362
+ reported.add(variable);
363
+ context.report({
364
+ node: defNode,
365
+ messageId: 'tooBroad',
366
+ data: { name: variable.name }
367
+ });
368
+ }
369
+ }
370
+ }
371
+
372
+ for (let child of treeNode.children)
373
+ checkScopeNode(context, child, reported, scopeToNode);
374
+ }
375
+
376
+ function printTree
377
+ (node, siblingNum) {
378
+ let prefix, all, indent;
379
+
380
+ prefix = siblingNum == 0 ? node.prefix : node.prefix.split('.').slice(0, -1).join('.') + '.' + siblingNum;
381
+ indent = ' '.repeat(prefix.split('.').length - 1);
382
+ {
383
+ let name;
384
+
385
+ name = node.scope.block?.id?.name ?? node.scope.block?.parent?.key?.name;
386
+ print(indent + 'SCOPE ' + prefix + ' ' + node.scope.type.toUpperCase() + ' pos ' + scopeStart(node.scope) + (name ? ' name ' + name : ''));
387
+ }
388
+
389
+ all = [ ...node.items.map(i => ({ pos: i.pos, type: 'item', data: i })),
390
+ ...node.children.map((c, i) => ({ pos: scopeStart(c.scope), type: 'scope', data: c, sibling: i + 1 })) ];
391
+ all.sort((a, b) => a.pos - b.pos);
392
+
393
+ for (let entry of all)
394
+ if (entry.type == 'item')
395
+ print(indent
396
+ + ' ' + entry.data.type.padEnd(5)
397
+ + ' ' + entry.data.name
398
+ + (entry.data.ctx ? ' ' + entry.data.ctx : '').padEnd(3)
399
+ + (entry.data.isConditional ? 'C' : ' ').padEnd(2)
400
+ + 'pos ' + entry.data.pos);
401
+ else
402
+ printTree(entry.data, entry.sibling);
403
+ }
404
+
405
+ function createNarrowestScope
406
+ (context) {
407
+ let scopeManager;
408
+
409
+ clearPrintBuffer();
410
+ scopeManager = context.sourceCode.scopeManager;
411
+ if (scopeManager)
412
+ return {
413
+ 'Program:exit'() {
414
+ let tree, scopeToNode;
415
+
416
+ scopeToNode = new Map;
417
+ nextVarId = 0;
418
+ tree = buildScopeTree(scopeManager.scopes[0], '1', scopeToNode);
419
+ checkScopeNode(context, tree, null, scopeToNode);
420
+ printTree(tree, 0);
421
+ }
422
+ }
423
+ }
424
+
425
+ var narrowestScopePlugin = { meta: { type: 'suggestion',
426
+ docs: { description: 'Enforce variables are declared in their narrowest possible scope.' },
427
+ messages: { tooBroad: 'Variable "{{ name }}" is declared in a broader scope than necessary.' },
428
+ schema: [] },
429
+ create: createNarrowestScope };
430
+
431
+ function createPositiveVibes
432
+ (context) {
433
+ return {
434
+ UnaryExpression(node) {
435
+ if (node.operator == '!')
436
+ context.report({ node,
437
+ messageId: 'positiveVibes' });
438
+ },
439
+ BinaryExpression(node) {
440
+ if (node.operator == '!=')
441
+ context.report({ node,
442
+ messageId: 'equality' });
443
+ else if (node.operator == '!==')
444
+ context.report({ node,
445
+ messageId: 'strictEquality' });
446
+ }
447
+ }
448
+ }
449
+
450
+ var positiveVibesPlugin = { meta: { type: 'problem',
451
+ docs: { description: 'Prefer positive expressions.' },
452
+ messages: { positiveVibes: 'Be positive!',
453
+ equality: 'Use ==.',
454
+ strictEquality: 'Use ===.' },
455
+ schema: [] },
456
+ create: createPositiveVibes };
457
+
458
+ var useRiskyEqualPlugin = { meta: { type: 'problem',
459
+ docs: { description: 'Enforce use of == instead of ===.' },
460
+ messages: { risky: 'Use ==.' },
461
+ schema: [] },
462
+ create(context) {
463
+ return { BinaryExpression(node) {
464
+ if (node.operator == '===')
465
+ context.report({ node, messageId: 'risky' });
466
+ } }
467
+ } };
468
+
469
+ function create$2
470
+ (context) {
471
+ return { VariableDeclaration(node) {
472
+ if (node.kind == 'const' || node.kind == 'var')
473
+ context.report({ node, messageId: 'useLet' });
474
+ } }
475
+ }
476
+
477
+ var alwaysLetPlugin = { meta: { type: 'problem',
478
+ docs: { description: 'Enforce use of let instead of const or var.' },
479
+ messages: { useLet: 'Use let.' },
480
+ schema: [] },
481
+ create: create$2 };
482
+
483
+ let ostIdCounter, ost;
484
+
485
+ ostIdCounter = 0;
486
+ ost = 0;
487
+
488
+ function trace
489
+ (...args) {
490
+ }
491
+
492
+ function createInitBeforeUse(context) {
493
+ let scopeManager;
494
+
495
+ scopeManager = context.sourceCode.scopeManager;
496
+ if (scopeManager)
497
+ return {
498
+ 'Program:exit'() {
499
+ let scopeToNode, astToTree, astToOst;
500
+ scopeToNode = new Map;
501
+ astToTree = new Map;
502
+ astToOst = new Map;
503
+ buildScopeTree(scopeManager.scopes[0], '1', scopeToNode, astToTree);
504
+
505
+ ostIdCounter = 0;
506
+ ost = processAst(context.sourceCode.ast, null, astToTree, astToOst, '', new Set());
507
+
508
+ ostAnnotate(ost, astToOst, context);
509
+
510
+ ostCheck(ost, context);
511
+ }
512
+ }
513
+ }
514
+
515
+ function isRegularDeclaration
516
+ (item) {
517
+ if (item.type == 'LET') {
518
+ if (item.defType == 'FunctionName' || item.defType == 'Parameter')
519
+ return 0
520
+ return 1
521
+ }
522
+ return 0
523
+ }
524
+
525
+ function processAst(astNode, parentOst, astToTree, astToOst, indent, visited) {
526
+ if (astNode) {
527
+ let treeNode, scopeName, lets, reads, writes, ost, children;
528
+
529
+ if (visited.has(astNode))
530
+ return
531
+ visited.add(astNode);
532
+
533
+ treeNode = astToTree.get(astNode) ?? parentOst?.treeNode;
534
+
535
+ scopeName = treeNode?.scope ? `${treeNode.scope.type}` : 'no-scope';
536
+ if (treeNode?.scope?.block?.id?.name)
537
+ scopeName += `(${treeNode.scope.block.id.name})`;
538
+ trace(`${indent}${astNode.type}`);
539
+
540
+ lets = [];
541
+ reads = [];
542
+ writes = [];
543
+
544
+ for (let item of treeNode?.items ?? [])
545
+ if (isRegularDeclaration(item)) {
546
+ let scopeCreator;
547
+
548
+ scopeCreator = treeNode?.scope?.block;
549
+ if (scopeCreator && astNode == scopeCreator) {
550
+ lets.push({ item });
551
+ trace(`${indent} | LET ${item.name}:${item.varId}`);
552
+ }
553
+ }
554
+ else if (item.ref)
555
+ if (astNode == item.ref.identifier)
556
+ if (item.type == 'READ') {
557
+ reads.push({ item });
558
+ trace(`${indent} | READ ${item.name}:${item.varId}`);
559
+ }
560
+ else if (item.type == 'WRITE') {
561
+ writes.push({ item });
562
+ trace(`${indent} | WRITE ${item.name}:${item.varId}`);
563
+ }
564
+
565
+ ost = {
566
+ id: ostIdCounter++,
567
+ astNode,
568
+ treeNode,
569
+ scopeItems: treeNode?.items ?? [],
570
+ lets,
571
+ reads,
572
+ writes,
573
+ children: [],
574
+ fnDefOst: null
575
+ };
576
+
577
+ astToOst.set(astNode, ost);
578
+
579
+ children = [];
580
+
581
+ if (astNode.type == 'ForOfStatement' || astNode.type == 'ForInStatement') {
582
+ if (astNode.right)
583
+ children.push(astNode.right);
584
+ if (astNode.left)
585
+ children.push(astNode.left);
586
+ if (astNode.body)
587
+ children.push(astNode.body);
588
+ }
589
+ else if (astNode.type == 'ForStatement') {
590
+ if (astNode.init)
591
+ children.push(astNode.init);
592
+ if (astNode.test)
593
+ children.push(astNode.test);
594
+ if (astNode.update)
595
+ children.push(astNode.update);
596
+ if (astNode.body)
597
+ children.push(astNode.body);
598
+ }
599
+ else if (astNode.type == 'AssignmentExpression') {
600
+ if (astNode.right)
601
+ children.push(astNode.right);
602
+ if (astNode.left)
603
+ children.push(astNode.left);
604
+ }
605
+ else {
606
+ if (astNode.body)
607
+ if (Array.isArray(astNode.body))
608
+ children.push(...astNode.body);
609
+ else
610
+ children.push(astNode.body);
611
+ if (astNode.consequent)
612
+ children.push(astNode.consequent);
613
+ if (astNode.alternate)
614
+ children.push(astNode.alternate);
615
+ if (astNode.block)
616
+ children.push(astNode.block);
617
+ if (astNode.expression)
618
+ children.push(astNode.expression);
619
+ if (astNode.callee)
620
+ children.push(astNode.callee);
621
+ if (astNode.object)
622
+ children.push(astNode.object);
623
+ if (astNode.property)
624
+ children.push(astNode.property);
625
+ if (astNode.init)
626
+ children.push(astNode.init);
627
+ if (astNode.id)
628
+ children.push(astNode.id);
629
+ if (astNode.declarations)
630
+ children.push(...astNode.declarations);
631
+ if (astNode.test)
632
+ children.push(astNode.test);
633
+ if (astNode.update)
634
+ children.push(astNode.update);
635
+ if (astNode.left)
636
+ children.push(astNode.left);
637
+ if (astNode.right)
638
+ children.push(astNode.right);
639
+ if (astNode.argument)
640
+ children.push(astNode.argument);
641
+ if (astNode.arguments)
642
+ children.push(...astNode.arguments);
643
+ if (astNode.elements)
644
+ children.push(...astNode.elements);
645
+ if (astNode.properties)
646
+ children.push(...astNode.properties);
647
+ }
648
+
649
+ for (let child of children) {
650
+ let childOst;
651
+
652
+ childOst = processAst(child, ost, astToTree, astToOst, indent + ' ', visited);
653
+ if (childOst)
654
+ ost.children.push(childOst);
655
+ }
656
+
657
+ return ost
658
+ }
659
+ }
660
+
661
+ function ostAnnotate(ost, astToOst, context) {
662
+ if (ost) {
663
+ for (let letInfo of ost.lets) {
664
+ let writeOst;
665
+
666
+ writeOst = findFirstWrite(ost, letInfo);
667
+ letInfo.firstWrite = writeOst;
668
+ if (writeOst)
669
+ continue
670
+ if (letInfo.item.defType == 'ImportBinding')
671
+ continue
672
+ context.report({
673
+ node: letInfo.item.identifier,
674
+ messageId: 'mustInit',
675
+ data: { name: letInfo.item.name }
676
+ });
677
+ }
678
+
679
+ if (ost.astNode.type == 'CallExpression' && ost.astNode.callee?.type == 'Identifier')
680
+ for (let child of ost.children)
681
+ if (child.astNode == ost.astNode.callee && child.reads.length > 0) {
682
+ let readRef;
683
+
684
+ readRef = child.reads[0].item.ref;
685
+ if (readRef?.resolved) {
686
+ let variable;
687
+
688
+ variable = readRef.resolved;
689
+ if (variable.defs.length > 0) {
690
+ let fnDefAst;
691
+
692
+ fnDefAst = variable.defs[0].node;
693
+ if (fnDefAst) {
694
+ if (fnDefAst.init?.type == 'ArrowFunctionExpression' || fnDefAst.init?.type == 'FunctionExpression')
695
+ fnDefAst = fnDefAst.init;
696
+ ost.fnDefOst = astToOst.get(fnDefAst);
697
+ }
698
+ }
699
+ }
700
+ }
701
+
702
+ for (let child of ost.children)
703
+ ostAnnotate(child, astToOst, context);
704
+ }
705
+ }
706
+
707
+ function findFirstWrite(ost, letInfo) {
708
+ if (ost.astNode.type == 'FunctionDeclaration' || ost.astNode.type == 'ArrowFunctionExpression' || ost.astNode.type == 'FunctionExpression')
709
+ for (let child of ost.children)
710
+ if (child.astNode.type == 'BlockStatement')
711
+ return findFirstWriteInSubtree(child, letInfo)
712
+ return findFirstWriteInSubtree(ost, letInfo)
713
+ }
714
+
715
+ function findFirstWriteInSubtree(ost, letInfo) {
716
+ if (ost) {
717
+ if (ost.astNode.type == 'FunctionDeclaration' || ost.astNode.type == 'ArrowFunctionExpression' || ost.astNode.type == 'FunctionExpression')
718
+ return null
719
+
720
+ for (let writeInfo of ost.writes) {
721
+ let writeVar;
722
+
723
+ writeVar = writeInfo.item.ref.resolved;
724
+ if (writeVar == letInfo.item.variable)
725
+ return ost
726
+ }
727
+
728
+ for (let child of ost.children) {
729
+ let result;
730
+
731
+ result = findFirstWriteInSubtree(child, letInfo);
732
+ if (result)
733
+ return result
734
+ }
735
+ }
736
+
737
+ return null
738
+ }
739
+
740
+ function ostCheck(ost, context) {
741
+ if (ost) {
742
+ for (let letInfo of ost.lets)
743
+ if (letInfo.firstWrite)
744
+ walk2Start(ost, letInfo, context);
745
+
746
+ for (let child of ost.children)
747
+ ostCheck(child, context);
748
+ }
749
+ }
750
+
751
+ function walk2Start(node, letInfo, context) {
752
+ if (node.astNode.type == 'FunctionDeclaration')
753
+ for (let child of node.children)
754
+ if (child.astNode.type == 'BlockStatement')
755
+ return walk2(child, letInfo, context, new Set())
756
+ return walk2(node, letInfo, context, new Set())
757
+ }
758
+
759
+ function walk2(node, letInfo, context, visited) {
760
+ if (node) {
761
+ if (node.astNode.type == 'FunctionDeclaration' || node.astNode.type == 'ArrowFunctionExpression' || node.astNode.type == 'FunctionExpression')
762
+ return false
763
+
764
+ if (node == letInfo.firstWrite) {
765
+ for (let readInfo of node.reads)
766
+ if (readInfo.item.ref.resolved == letInfo.item.variable) {
767
+ context.report({
768
+ node: readInfo.item.ref.identifier,
769
+ messageId: 'initBeforeUse',
770
+ data: { name: letInfo.item.name }
771
+ });
772
+ }
773
+ return true
774
+ }
775
+
776
+ if (node.astNode.type == 'CallExpression' && node.fnDefOst) {
777
+ let fnType;
778
+
779
+ fnType = node.fnDefOst.astNode.type;
780
+
781
+ if (fnType == 'FunctionDeclaration' || fnType == 'ArrowFunctionExpression' || fnType == 'FunctionExpression') {
782
+ let key;
783
+
784
+ key = `${letInfo.item.name}:${node.fnDefOst.id}`;
785
+ if (visited.has(key)) ;
786
+ else {
787
+ visited.add(key);
788
+ for (let child of node.fnDefOst.children)
789
+ if (child.astNode.type == 'BlockStatement' && walk2(child, letInfo, context, visited))
790
+ return true
791
+ }
792
+ }
793
+ }
794
+
795
+ for (let readInfo of node.reads)
796
+ if (readInfo.item.ref.resolved == letInfo.item.variable) {
797
+ context.report({
798
+ node: readInfo.item.ref.identifier,
799
+ messageId: 'initBeforeUse',
800
+ data: { name: letInfo.item.name }
801
+ });
802
+ }
803
+
804
+ for (let child of node.children)
805
+ if (walk2(child, letInfo, context, visited))
806
+ return true
807
+ }
808
+
809
+ return false
810
+ }
811
+
812
+ var initBeforeUsePlugin = {
813
+ meta: {
814
+ type: 'problem',
815
+ docs: { description: 'Warn when a variable is used before being explicitly initialized.' },
816
+ messages: { initBeforeUse: "'{{name}}' used before initialization.",
817
+ mustInit: "'{{name}}' must be initialized." },
818
+ schema: []
819
+ },
820
+ create: createInitBeforeUse
821
+ };
822
+
823
+ function
824
+ VariableDeclaration
825
+ (context, node) {
826
+ let parent;
827
+
828
+ parent = node.parent;
829
+
830
+ for (parent = node.parent; parent; parent = parent.parent)
831
+ if (parent.type == 'BlockStatement')
832
+ break
833
+
834
+ if (parent) {
835
+ let idx;
836
+
837
+ if (parent.parent?.type == 'CatchClause')
838
+ return
839
+
840
+ idx = parent.body.indexOf(node);
841
+ for (let i = 0; i < idx; i++) {
842
+ if (parent.body[i].type == 'VariableDeclaration')
843
+ continue
844
+ context.report({ node, messageId: 'varDeclBlockStart' });
845
+ return
846
+ }
847
+ }
848
+ }
849
+ function create$1
850
+ (context) {
851
+ return { VariableDeclaration: node => VariableDeclaration(context, node) }
852
+ }
853
+
854
+ var varDeclBlockStartPlugin = { meta: { type: 'suggestion',
855
+ docs: { description: 'Require variable declarations to be at the start of the block.' },
856
+ messages: { varDeclBlockStart: 'VarDecl must be at start of block.' },
857
+ schema: [] },
858
+ create: create$1 };
859
+
860
+ function
861
+ FunctionDeclaration
862
+ (context, node) {
863
+ let parent;
864
+
865
+ for (parent = node.parent; parent; parent = parent.parent)
866
+ if (parent.type == 'BlockStatement')
867
+ break
868
+
869
+ if (parent) {
870
+ let idx;
871
+
872
+ if (parent.parent?.type == 'CatchClause')
873
+ return
874
+
875
+ idx = parent.body.indexOf(node);
876
+ for (let i = 0; i < idx; i++) {
877
+ if (parent.body[i].type == 'VariableDeclaration'
878
+ || parent.body[i].type == 'FunctionDeclaration'
879
+ || parent.body[i].type == 'EmptyStatement')
880
+ continue
881
+ context.report({ node, messageId: 'fnDeclBlockStart' });
882
+ return
883
+ }
884
+ }
885
+ }
886
+
887
+ function create
888
+ (context) {
889
+ return { FunctionDeclaration: node => FunctionDeclaration(context, node) }
890
+ }
891
+
892
+ var fnDeclBlockStartPlugin = { meta: { type: 'suggestion',
893
+ docs: { description: 'Require function declarations to be at the start of the block.' },
894
+ messages: { fnDeclBlockStart: 'FnDecl must be the start the block (after VarDecls).' },
895
+ schema: [] },
896
+ create };
897
+
7
898
  exports.rules = void 0; exports.languageOptions = void 0; exports.plugins = void 0;
8
899
 
9
- exports.plugins = { 'cookshack': { rules: { 'no-logical-not': { meta: { type: 'problem',
10
- docs: { description: 'Prevent !.' },
11
- messages: { logicalNot: 'Logical not used.',
12
- inequality: 'Inequality operator used.',
13
- strictInequality: 'Strict inequality operator used.' },
14
- schema: [] }, // options
15
- create(context) {
16
- return {
17
- UnaryExpression(node) {
18
- if (node.operator == '!')
19
- context.report({ node,
20
- messageId: 'logicalNot' });
21
- },
22
- BinaryExpression(node) {
23
- if (node.operator == '!=')
24
- context.report({ node,
25
- messageId: 'inequality' });
26
- else if (node.operator == '!==')
27
- context.report({ node,
28
- messageId: 'strictInequality' });
29
- }
30
- }
31
- } } } } };
900
+ exports.plugins = { 'cookshack': { rules: { 'positive-vibes': positiveVibesPlugin,
901
+ 'narrowest-scope': narrowestScopePlugin,
902
+ 'use-risky-equal': useRiskyEqualPlugin,
903
+ 'always-let': alwaysLetPlugin,
904
+ 'init-before-use': initBeforeUsePlugin,
905
+ 'var-decl-block-start': varDeclBlockStartPlugin,
906
+ 'fn-decl-block-start': fnDeclBlockStartPlugin } } };
32
907
 
33
908
  exports.rules = {
34
909
  'array-bracket-newline': [ 'error', 'never' ],
@@ -59,7 +934,14 @@ exports.rules = {
59
934
  { blankLine: 'never', prev: 'let', next: 'let' } ],
60
935
  'no-case-declarations': 'error',
61
936
  'no-global-assign': 'error',
62
- 'cookshack/no-logical-not': 'error',
937
+ 'cookshack/positive-vibes': 'error',
938
+ 'cookshack/narrowest-scope': 'error',
939
+ 'cookshack/use-risky-equal': 'error',
940
+ 'cookshack/always-let': 'error',
941
+ // using the implicit inititialization to undefined fits better
942
+ //'cookshack/init-before-use': 'error',
943
+ 'cookshack/var-decl-block-start': 'error',
944
+ 'cookshack/fn-decl-block-start': 'error',
63
945
  'no-mixed-operators': 'error',
64
946
  'no-multi-spaces': 'error',
65
947
  'no-multiple-empty-lines': [ 'error', { max: 1, maxEOF: 0 } ],
@@ -97,3 +979,4 @@ var index = [ { ignores: [ 'TAGS.mjs' ] },
97
979
  rules: exports.rules } ];
98
980
 
99
981
  exports.default = index;
982
+ exports.getPrintBuffer = getPrintBuffer;