@cookshack/eslint-config 2.0.5 → 4.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/formatter.cjs +2 -1
- package/dist/formatter.js +2 -1
- package/dist/index.cjs +784 -156
- package/dist/index.js +784 -156
- package/formatter.js +2 -1
- package/index.js +80 -526
- package/package.json +2 -2
- package/plugins/always-let.js +15 -0
- package/plugins/fn-args-nl.js +47 -0
- package/plugins/fn-decl-block-start.js +37 -0
- package/plugins/indent-struct.js +145 -0
- package/plugins/init-before-use.js +382 -0
- package/plugins/narrowest-scope.js +472 -0
- package/plugins/positive-vibes.js +27 -0
- package/plugins/use-risky-equal.js +13 -0
- package/plugins/var-decl-block-start.js +37 -0
- package/test/rules/always-let.js +46 -0
- package/test/rules/fn-args-nl.js +114 -0
- package/test/rules/fn-decl-block-start.js +48 -0
- package/test/rules/indent-struct.js +198 -0
- package/test/rules/init-before-use.js +196 -0
- package/test/rules/narrowest-scope.js +36 -5
- package/test/rules/use-risky-equal.js +42 -0
- package/test/rules/var-decl-block-start.js +108 -0
package/dist/index.js
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
import globals from 'globals';
|
|
2
2
|
|
|
3
|
-
let
|
|
4
|
-
|
|
5
|
-
let printBuffer;
|
|
3
|
+
let varIds, nextVarId, printBuffer;
|
|
6
4
|
|
|
7
5
|
printBuffer = [];
|
|
6
|
+
varIds = new Map();
|
|
7
|
+
nextVarId = 0;
|
|
8
8
|
|
|
9
9
|
function print
|
|
10
10
|
(...args) {
|
|
11
|
-
//console.log(args.join(' '))
|
|
12
11
|
printBuffer.push(args.join(' '));
|
|
13
12
|
}
|
|
14
13
|
|
|
15
|
-
function trace
|
|
14
|
+
function trace$2
|
|
16
15
|
(...args) {
|
|
17
16
|
}
|
|
18
17
|
|
|
@@ -32,7 +31,7 @@ function getNarrowestScope
|
|
|
32
31
|
|
|
33
32
|
common = null;
|
|
34
33
|
for (let ref of variable.references) {
|
|
35
|
-
if (variable.defs.some(def => def.name
|
|
34
|
+
if (variable.defs.some(def => def.name == ref.identifier))
|
|
36
35
|
continue
|
|
37
36
|
if (ref.from)
|
|
38
37
|
if (common)
|
|
@@ -102,15 +101,15 @@ function getConditionalContext
|
|
|
102
101
|
prevNode = ref.identifier;
|
|
103
102
|
node = ref.identifier.parent;
|
|
104
103
|
while (node) {
|
|
105
|
-
if (node
|
|
104
|
+
if (node == scopeBlock)
|
|
106
105
|
break
|
|
107
|
-
if (node.type
|
|
108
|
-
if (prevNode
|
|
106
|
+
if (node.type == 'IfStatement')
|
|
107
|
+
if (prevNode == node.test || nodeContains(node.test, prevNode))
|
|
109
108
|
prevNode = node;
|
|
110
109
|
else
|
|
111
110
|
return 'B'
|
|
112
111
|
else if ([ 'WhileStatement', 'DoWhileStatement', 'ForStatement', 'ForInStatement', 'ForOfStatement', 'SwitchStatement' ].includes(node.type))
|
|
113
|
-
if (prevNode
|
|
112
|
+
if (prevNode == node.test || nodeContains(node.test, prevNode))
|
|
114
113
|
prevNode = node;
|
|
115
114
|
else
|
|
116
115
|
return 'B'
|
|
@@ -121,18 +120,20 @@ function getConditionalContext
|
|
|
121
120
|
return ''
|
|
122
121
|
}
|
|
123
122
|
|
|
124
|
-
function nodeContains
|
|
125
|
-
|
|
123
|
+
function nodeContains
|
|
124
|
+
(node, target) {
|
|
125
|
+
if (node == target)
|
|
126
126
|
return true
|
|
127
|
-
if (node && typeof node
|
|
127
|
+
if (node && typeof node == 'object')
|
|
128
128
|
for (let key in node)
|
|
129
129
|
if (nodeHas(node[key], target))
|
|
130
130
|
return true
|
|
131
131
|
return false
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
function nodeHas
|
|
135
|
-
|
|
134
|
+
function nodeHas
|
|
135
|
+
(value, target) {
|
|
136
|
+
if (value == target)
|
|
136
137
|
return true
|
|
137
138
|
if (Array.isArray(value))
|
|
138
139
|
return value.some(v => nodeContains(v, target))
|
|
@@ -146,15 +147,15 @@ function isConditionalRef
|
|
|
146
147
|
node = ref.identifier.parent;
|
|
147
148
|
|
|
148
149
|
while (node) {
|
|
149
|
-
if (node
|
|
150
|
+
if (node == narrowestScope.block)
|
|
150
151
|
break
|
|
151
|
-
if (node.type
|
|
152
|
+
if (node.type == 'BlockStatement') {
|
|
152
153
|
let parent;
|
|
153
154
|
|
|
154
155
|
parent = node.parent;
|
|
155
|
-
if (parent?.type
|
|
156
|
+
if (parent?.type == 'IfStatement' && (parent.consequent == node || parent.alternate == node))
|
|
156
157
|
return true
|
|
157
|
-
if ([ 'WhileStatement', 'DoWhileStatement', 'ForStatement', 'ForInStatement', 'ForOfStatement' ].includes(parent?.type) && parent.body
|
|
158
|
+
if ([ 'WhileStatement', 'DoWhileStatement', 'ForStatement', 'ForInStatement', 'ForOfStatement' ].includes(parent?.type) && parent.body == node)
|
|
158
159
|
return true
|
|
159
160
|
}
|
|
160
161
|
node = node.parent;
|
|
@@ -186,19 +187,17 @@ function mayBeReadBeforeAnyWrite
|
|
|
186
187
|
let item;
|
|
187
188
|
|
|
188
189
|
if (isReadRef(ref))
|
|
189
|
-
// a possible read
|
|
190
190
|
return 1
|
|
191
191
|
|
|
192
192
|
item = ref.cookshackNarrowestScopeItem;
|
|
193
193
|
if (item.ctx == 'B' || isConditionalRef(ref, narrowestScope))
|
|
194
|
-
// a conditional write
|
|
195
194
|
continue
|
|
196
|
-
// A guaranteed write before any possible read.
|
|
197
195
|
return 0
|
|
198
196
|
}
|
|
199
197
|
}
|
|
200
198
|
|
|
201
|
-
function scopeStart
|
|
199
|
+
function scopeStart
|
|
200
|
+
(scope) {
|
|
202
201
|
if (scope.block == null)
|
|
203
202
|
return Infinity
|
|
204
203
|
if (scope.type == 'function' && scope.block.id)
|
|
@@ -208,50 +207,73 @@ function scopeStart(scope) {
|
|
|
208
207
|
return scope.block.range[0]
|
|
209
208
|
}
|
|
210
209
|
|
|
211
|
-
function
|
|
210
|
+
function ensureInVarIds
|
|
211
|
+
(variable) {
|
|
212
|
+
if (varIds.has(variable))
|
|
213
|
+
return
|
|
214
|
+
varIds.set(variable, nextVarId++);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function isCompoundAssignmentOp
|
|
218
|
+
(op) {
|
|
219
|
+
if (op == '=')
|
|
220
|
+
return 0
|
|
221
|
+
return 1
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function buildScopeTree
|
|
225
|
+
(scope, prefix, scopeToNode, astToTree) {
|
|
212
226
|
let node, siblingNum;
|
|
213
227
|
|
|
214
|
-
node = {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
};
|
|
228
|
+
node = { scope,
|
|
229
|
+
prefix,
|
|
230
|
+
items: [],
|
|
231
|
+
children: [] };
|
|
232
|
+
|
|
220
233
|
scopeToNode.set(scope, node);
|
|
234
|
+
if (scope.block && astToTree)
|
|
235
|
+
astToTree.set(scope.block, node);
|
|
221
236
|
|
|
222
237
|
siblingNum = 0;
|
|
223
238
|
for (let child of scope.childScopes) {
|
|
224
239
|
siblingNum++;
|
|
225
|
-
node.children.push(buildScopeTree(child, prefix + '.' + siblingNum, scopeToNode));
|
|
240
|
+
node.children.push(buildScopeTree(child, prefix + '.' + siblingNum, scopeToNode, astToTree));
|
|
226
241
|
}
|
|
227
242
|
|
|
228
243
|
for (let variable of scope.variables) {
|
|
229
|
-
if (variable.defs.length > 0)
|
|
230
|
-
|
|
244
|
+
if (variable.defs.length > 0) {
|
|
245
|
+
ensureInVarIds(variable);
|
|
246
|
+
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) });
|
|
247
|
+
}
|
|
231
248
|
|
|
232
249
|
for (let ref of variable.references) {
|
|
233
250
|
let targetNode;
|
|
234
251
|
|
|
235
252
|
targetNode = scopeToNode.get(ref.from);
|
|
236
253
|
if (targetNode) {
|
|
237
|
-
let parent, sortPos, ctx,
|
|
254
|
+
let parent, sortPos, ctx, item1, item2;
|
|
238
255
|
|
|
239
256
|
ctx = getConditionalContext(ref);
|
|
240
257
|
parent = ref.identifier.parent;
|
|
241
258
|
|
|
242
259
|
if (isWriteRef(ref))
|
|
243
260
|
if (ref.identifier.parent?.type == 'UpdateExpression') {
|
|
244
|
-
|
|
245
|
-
|
|
261
|
+
item1 = { ref, type: 'READ', name: ref.identifier.name, ctx, pos: ref.identifier.range[0] };
|
|
262
|
+
item2 = { ref, type: 'WRITE', name: ref.identifier.name, pos: ref.identifier.range[0] };
|
|
246
263
|
}
|
|
247
264
|
else if (ref.identifier.parent?.type == 'AssignmentExpression') {
|
|
248
265
|
sortPos = parent.right.range[1] + 0.4;
|
|
249
|
-
|
|
266
|
+
if (ref.identifier.parent.left == ref.identifier && isCompoundAssignmentOp(ref.identifier.parent.operator)) {
|
|
267
|
+
item1 = { ref, type: 'READ', name: ref.identifier.name, ctx, pos: ref.identifier.range[0] };
|
|
268
|
+
item2 = { ref, type: 'WRITE', name: ref.identifier.name, pos: sortPos };
|
|
269
|
+
}
|
|
270
|
+
else
|
|
271
|
+
item1 = { ref, type: 'WRITE', name: ref.identifier.name, ctx, pos: sortPos };
|
|
250
272
|
}
|
|
251
273
|
else if (ref.identifier.parent?.type == 'VariableDeclarator')
|
|
252
|
-
|
|
274
|
+
item1 = { ref, type: 'WRITE', name: ref.identifier.name, pos: ref.identifier.range[0] + 0.4 };
|
|
253
275
|
else
|
|
254
|
-
|
|
276
|
+
item1 = { ref, type: 'WRITE', name: ref.identifier.name, pos: ref.identifier.range[0] };
|
|
255
277
|
else {
|
|
256
278
|
let declarator;
|
|
257
279
|
|
|
@@ -265,10 +287,16 @@ function buildScopeTree(scope, prefix, scopeToNode) {
|
|
|
265
287
|
sortPos = declarator.id ? declarator.id.range[0] - 0.4 : ref.identifier.range[0];
|
|
266
288
|
else
|
|
267
289
|
sortPos = ref.identifier.range[0];
|
|
268
|
-
|
|
290
|
+
item1 = { ref, type: 'READ', name: ref.identifier.name, ctx, pos: sortPos };
|
|
291
|
+
}
|
|
292
|
+
ensureInVarIds(variable);
|
|
293
|
+
item1.varId = varIds.get(variable);
|
|
294
|
+
targetNode.items.push(item1);
|
|
295
|
+
if (item2) {
|
|
296
|
+
item2.varId = varIds.get(variable);
|
|
297
|
+
targetNode.items.push(item2);
|
|
269
298
|
}
|
|
270
|
-
|
|
271
|
-
ref.cookshackNarrowestScopeItem = item;
|
|
299
|
+
ref.cookshackNarrowestScopeItem = item2 || item1;
|
|
272
300
|
}
|
|
273
301
|
}
|
|
274
302
|
}
|
|
@@ -278,7 +306,8 @@ function buildScopeTree(scope, prefix, scopeToNode) {
|
|
|
278
306
|
return node
|
|
279
307
|
}
|
|
280
308
|
|
|
281
|
-
function checkScopeNode
|
|
309
|
+
function checkScopeNode
|
|
310
|
+
(context, treeNode, reported, scopeToNode) {
|
|
282
311
|
let indent;
|
|
283
312
|
|
|
284
313
|
reported = reported || new Set;
|
|
@@ -289,11 +318,11 @@ function checkScopeNode(context, treeNode, reported, scopeToNode) {
|
|
|
289
318
|
|
|
290
319
|
if (reported.has(variable))
|
|
291
320
|
continue
|
|
292
|
-
if (variable.defs.length
|
|
321
|
+
if (variable.defs.length == 0)
|
|
293
322
|
continue
|
|
294
323
|
if ([ 'Parameter', 'FunctionName', 'ImportBinding', 'CatchClause', 'ClassName' ].includes(variable.defs[0].type))
|
|
295
324
|
continue
|
|
296
|
-
if (variable.defs[0].node.parent?.parent?.type
|
|
325
|
+
if (variable.defs[0].node.parent?.parent?.type == 'ExportNamedDeclaration')
|
|
297
326
|
continue
|
|
298
327
|
|
|
299
328
|
defNode = variable.defs[0]?.name;
|
|
@@ -302,38 +331,36 @@ function checkScopeNode(context, treeNode, reported, scopeToNode) {
|
|
|
302
331
|
|
|
303
332
|
defScope = getDefinitionScope(variable);
|
|
304
333
|
defNodePrefix = scopeToNode.get(defScope)?.prefix ?? '?';
|
|
305
|
-
trace(indent, '1 found decl scope of', variable.name + ':', defNodePrefix + ' ' + defScope.type.toUpperCase());
|
|
334
|
+
trace$2(indent, '1 found decl scope of', variable.name + ':', defNodePrefix + ' ' + defScope.type.toUpperCase());
|
|
306
335
|
|
|
307
336
|
narrowestScope = getNarrowestScope(variable);
|
|
308
337
|
if (narrowestScope) {
|
|
309
338
|
let narrowestPrefix;
|
|
310
339
|
|
|
311
340
|
narrowestPrefix = scopeToNode.get(narrowestScope)?.prefix ?? '?';
|
|
312
|
-
trace(indent, '2 found narrowest scope of', variable.name + ':', narrowestPrefix + ' ' + narrowestScope?.type.toUpperCase());
|
|
341
|
+
trace$2(indent, '2 found narrowest scope of', variable.name + ':', narrowestPrefix + ' ' + narrowestScope?.type.toUpperCase());
|
|
313
342
|
|
|
314
343
|
markConditionalRefs(variable, scopeToNode, narrowestScope);
|
|
315
344
|
|
|
316
345
|
if (defScope == narrowestScope)
|
|
317
346
|
continue
|
|
318
|
-
trace(indent, '3', variable.name, 'could be moved to a narrower scope');
|
|
347
|
+
trace$2(indent, '3', variable.name, 'could be moved to a narrower scope');
|
|
319
348
|
|
|
320
349
|
if (defScope.type == 'for') {
|
|
321
|
-
trace(indent, '4 exception:', variable.name, 'is in a for loop header');
|
|
350
|
+
trace$2(indent, '4 exception:', variable.name, 'is in a for loop header');
|
|
322
351
|
continue
|
|
323
352
|
}
|
|
324
353
|
if (mayBeReadBeforeAnyWrite(variable, scopeToNode, narrowestScope)) {
|
|
325
|
-
trace(indent, '4 exception:', variable.name, 'mayBeReadBeforeAnyWrite');
|
|
354
|
+
trace$2(indent, '4 exception:', variable.name, 'mayBeReadBeforeAnyWrite');
|
|
326
355
|
continue
|
|
327
356
|
}
|
|
328
357
|
|
|
329
|
-
trace(indent, '5', variable.name, 'is too broad');
|
|
358
|
+
trace$2(indent, '5', variable.name, 'is too broad');
|
|
330
359
|
|
|
331
360
|
reported.add(variable);
|
|
332
|
-
context.report({
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
data: { name: variable.name }
|
|
336
|
-
});
|
|
361
|
+
context.report({ node: defNode,
|
|
362
|
+
messageId: 'tooBroad',
|
|
363
|
+
data: { name: variable.name } });
|
|
337
364
|
}
|
|
338
365
|
}
|
|
339
366
|
}
|
|
@@ -342,10 +369,11 @@ function checkScopeNode(context, treeNode, reported, scopeToNode) {
|
|
|
342
369
|
checkScopeNode(context, child, reported, scopeToNode);
|
|
343
370
|
}
|
|
344
371
|
|
|
345
|
-
function printTree
|
|
372
|
+
function printTree
|
|
373
|
+
(node, siblingNum) {
|
|
346
374
|
let prefix, all, indent;
|
|
347
375
|
|
|
348
|
-
prefix = siblingNum
|
|
376
|
+
prefix = siblingNum == 0 ? node.prefix : node.prefix.split('.').slice(0, -1).join('.') + '.' + siblingNum;
|
|
349
377
|
indent = ' '.repeat(prefix.split('.').length - 1);
|
|
350
378
|
{
|
|
351
379
|
let name;
|
|
@@ -359,13 +387,11 @@ function printTree(node, siblingNum) {
|
|
|
359
387
|
all.sort((a, b) => a.pos - b.pos);
|
|
360
388
|
|
|
361
389
|
for (let entry of all)
|
|
362
|
-
if (entry.type
|
|
390
|
+
if (entry.type == 'item')
|
|
363
391
|
print(indent
|
|
364
392
|
+ ' ' + entry.data.type.padEnd(5)
|
|
365
393
|
+ ' ' + entry.data.name
|
|
366
|
-
// B: is the ref conditional within the scope that "owns" the ref (for single statement `if`)
|
|
367
394
|
+ (entry.data.ctx ? ' ' + entry.data.ctx : '').padEnd(3)
|
|
368
|
-
// C: is the ref conditional within the variable's narrowestScope?
|
|
369
395
|
+ (entry.data.isConditional ? 'C' : ' ').padEnd(2)
|
|
370
396
|
+ 'pos ' + entry.data.pos);
|
|
371
397
|
else
|
|
@@ -379,111 +405,713 @@ function createNarrowestScope
|
|
|
379
405
|
clearPrintBuffer();
|
|
380
406
|
scopeManager = context.sourceCode.scopeManager;
|
|
381
407
|
if (scopeManager)
|
|
382
|
-
return {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
408
|
+
return { 'Program:exit'
|
|
409
|
+
() {
|
|
410
|
+
let tree, scopeToNode;
|
|
411
|
+
|
|
412
|
+
scopeToNode = new Map;
|
|
413
|
+
nextVarId = 0;
|
|
414
|
+
tree = buildScopeTree(scopeManager.scopes[0], '1', scopeToNode);
|
|
415
|
+
checkScopeNode(context, tree, null, scopeToNode);
|
|
416
|
+
printTree(tree, 0);
|
|
417
|
+
} }
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
var narrowestScopePlugin = { meta: { type: 'suggestion',
|
|
421
|
+
docs: { description: 'Enforce variables are declared in their narrowest possible scope.' },
|
|
422
|
+
messages: { tooBroad: 'Variable "{{ name }}" is declared in a broader scope than necessary.' },
|
|
423
|
+
schema: [] },
|
|
424
|
+
create: createNarrowestScope };
|
|
425
|
+
|
|
426
|
+
function createPositiveVibes
|
|
427
|
+
(context) {
|
|
428
|
+
return { UnaryExpression
|
|
429
|
+
(node) {
|
|
430
|
+
if (node.operator == '!')
|
|
431
|
+
context.report({ node,
|
|
432
|
+
messageId: 'positiveVibes' });
|
|
433
|
+
},
|
|
434
|
+
BinaryExpression
|
|
435
|
+
(node) {
|
|
436
|
+
if (node.operator == '!=')
|
|
437
|
+
context.report({ node,
|
|
438
|
+
messageId: 'equality' });
|
|
439
|
+
else if (node.operator == '!==')
|
|
440
|
+
context.report({ node,
|
|
441
|
+
messageId: 'strictEquality' });
|
|
442
|
+
} }
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
var positiveVibesPlugin = { meta: { type: 'problem',
|
|
446
|
+
docs: { description: 'Prefer positive expressions.' },
|
|
447
|
+
messages: { positiveVibes: 'Be positive!',
|
|
448
|
+
equality: 'Use ==.',
|
|
449
|
+
strictEquality: 'Use ===.' },
|
|
450
|
+
schema: [] },
|
|
451
|
+
create: createPositiveVibes };
|
|
452
|
+
|
|
453
|
+
var useRiskyEqualPlugin = { meta: { type: 'problem',
|
|
454
|
+
docs: { description: 'Enforce use of == instead of ===.' },
|
|
455
|
+
messages: { risky: 'Use ==.' },
|
|
456
|
+
schema: [] },
|
|
457
|
+
create
|
|
458
|
+
(context) {
|
|
459
|
+
return { BinaryExpression
|
|
460
|
+
(node) {
|
|
461
|
+
if (node.operator == '===')
|
|
462
|
+
context.report({ node, messageId: 'risky' });
|
|
463
|
+
} }
|
|
464
|
+
} };
|
|
465
|
+
|
|
466
|
+
function create$4
|
|
467
|
+
(context) {
|
|
468
|
+
return { VariableDeclaration
|
|
469
|
+
(node) {
|
|
470
|
+
if (node.kind == 'const' || node.kind == 'var')
|
|
471
|
+
context.report({ node, messageId: 'useLet' });
|
|
472
|
+
} }
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
var alwaysLetPlugin = { meta: { type: 'problem',
|
|
476
|
+
docs: { description: 'Enforce use of let instead of const or var.' },
|
|
477
|
+
messages: { useLet: 'Use let.' },
|
|
478
|
+
schema: [] },
|
|
479
|
+
create: create$4 };
|
|
480
|
+
|
|
481
|
+
let ostIdCounter, ost;
|
|
482
|
+
|
|
483
|
+
ostIdCounter = 0;
|
|
484
|
+
ost = 0;
|
|
485
|
+
|
|
486
|
+
function trace$1
|
|
487
|
+
(...args) {
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function createInitBeforeUse
|
|
491
|
+
(context) {
|
|
492
|
+
let scopeManager;
|
|
493
|
+
|
|
494
|
+
scopeManager = context.sourceCode.scopeManager;
|
|
495
|
+
if (scopeManager)
|
|
496
|
+
return { 'Program:exit'
|
|
497
|
+
() {
|
|
498
|
+
let scopeToNode, astToTree, astToOst;
|
|
499
|
+
scopeToNode = new Map;
|
|
500
|
+
astToTree = new Map;
|
|
501
|
+
astToOst = new Map;
|
|
502
|
+
buildScopeTree(scopeManager.scopes[0], '1', scopeToNode, astToTree);
|
|
503
|
+
|
|
504
|
+
ostIdCounter = 0;
|
|
505
|
+
ost = processAst(context.sourceCode.ast, null, astToTree, astToOst, '', new Set());
|
|
506
|
+
|
|
507
|
+
ostAnnotate(ost, astToOst, context);
|
|
508
|
+
|
|
509
|
+
ostCheck(ost, context);
|
|
510
|
+
} }
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function isRegularDeclaration
|
|
514
|
+
(item) {
|
|
515
|
+
if (item.type == 'LET') {
|
|
516
|
+
if (item.defType == 'FunctionName' || item.defType == 'Parameter')
|
|
517
|
+
return 0
|
|
518
|
+
return 1
|
|
519
|
+
}
|
|
520
|
+
return 0
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
function processAst
|
|
524
|
+
(astNode, parentOst, astToTree, astToOst, indent, visited) {
|
|
525
|
+
if (astNode) {
|
|
526
|
+
let treeNode, scopeName, lets, reads, writes, ost, children;
|
|
527
|
+
|
|
528
|
+
if (visited.has(astNode))
|
|
529
|
+
return
|
|
530
|
+
visited.add(astNode);
|
|
531
|
+
|
|
532
|
+
treeNode = astToTree.get(astNode) ?? parentOst?.treeNode;
|
|
533
|
+
|
|
534
|
+
scopeName = treeNode?.scope ? `${treeNode.scope.type}` : 'no-scope';
|
|
535
|
+
if (treeNode?.scope?.block?.id?.name)
|
|
536
|
+
scopeName += `(${treeNode.scope.block.id.name})`;
|
|
537
|
+
trace$1(`${indent}${astNode.type}`);
|
|
538
|
+
|
|
539
|
+
lets = [];
|
|
540
|
+
reads = [];
|
|
541
|
+
writes = [];
|
|
542
|
+
|
|
543
|
+
for (let item of treeNode?.items ?? [])
|
|
544
|
+
if (isRegularDeclaration(item)) {
|
|
545
|
+
let scopeCreator;
|
|
546
|
+
|
|
547
|
+
scopeCreator = treeNode?.scope?.block;
|
|
548
|
+
if (scopeCreator && astNode == scopeCreator) {
|
|
549
|
+
lets.push({ item });
|
|
550
|
+
trace$1(`${indent} | LET ${item.name}:${item.varId}`);
|
|
551
|
+
}
|
|
390
552
|
}
|
|
553
|
+
else if (item.ref)
|
|
554
|
+
if (astNode == item.ref.identifier)
|
|
555
|
+
if (item.type == 'READ') {
|
|
556
|
+
reads.push({ item });
|
|
557
|
+
trace$1(`${indent} | READ ${item.name}:${item.varId}`);
|
|
558
|
+
}
|
|
559
|
+
else if (item.type == 'WRITE') {
|
|
560
|
+
writes.push({ item });
|
|
561
|
+
trace$1(`${indent} | WRITE ${item.name}:${item.varId}`);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
ost = { id: ostIdCounter++,
|
|
565
|
+
astNode,
|
|
566
|
+
treeNode,
|
|
567
|
+
scopeItems: treeNode?.items ?? [],
|
|
568
|
+
lets,
|
|
569
|
+
reads,
|
|
570
|
+
writes,
|
|
571
|
+
children: [],
|
|
572
|
+
fnDefOst: null };
|
|
573
|
+
|
|
574
|
+
astToOst.set(astNode, ost);
|
|
575
|
+
|
|
576
|
+
children = [];
|
|
577
|
+
|
|
578
|
+
if (astNode.type == 'ForOfStatement' || astNode.type == 'ForInStatement') {
|
|
579
|
+
if (astNode.right)
|
|
580
|
+
children.push(astNode.right);
|
|
581
|
+
if (astNode.left)
|
|
582
|
+
children.push(astNode.left);
|
|
583
|
+
if (astNode.body)
|
|
584
|
+
children.push(astNode.body);
|
|
585
|
+
}
|
|
586
|
+
else if (astNode.type == 'ForStatement') {
|
|
587
|
+
if (astNode.init)
|
|
588
|
+
children.push(astNode.init);
|
|
589
|
+
if (astNode.test)
|
|
590
|
+
children.push(astNode.test);
|
|
591
|
+
if (astNode.update)
|
|
592
|
+
children.push(astNode.update);
|
|
593
|
+
if (astNode.body)
|
|
594
|
+
children.push(astNode.body);
|
|
595
|
+
}
|
|
596
|
+
else if (astNode.type == 'AssignmentExpression') {
|
|
597
|
+
if (astNode.right)
|
|
598
|
+
children.push(astNode.right);
|
|
599
|
+
if (astNode.left)
|
|
600
|
+
children.push(astNode.left);
|
|
601
|
+
}
|
|
602
|
+
else {
|
|
603
|
+
if (astNode.body)
|
|
604
|
+
if (Array.isArray(astNode.body))
|
|
605
|
+
children.push(...astNode.body);
|
|
606
|
+
else
|
|
607
|
+
children.push(astNode.body);
|
|
608
|
+
if (astNode.consequent)
|
|
609
|
+
children.push(astNode.consequent);
|
|
610
|
+
if (astNode.alternate)
|
|
611
|
+
children.push(astNode.alternate);
|
|
612
|
+
if (astNode.block)
|
|
613
|
+
children.push(astNode.block);
|
|
614
|
+
if (astNode.expression)
|
|
615
|
+
children.push(astNode.expression);
|
|
616
|
+
if (astNode.callee)
|
|
617
|
+
children.push(astNode.callee);
|
|
618
|
+
if (astNode.object)
|
|
619
|
+
children.push(astNode.object);
|
|
620
|
+
if (astNode.property)
|
|
621
|
+
children.push(astNode.property);
|
|
622
|
+
if (astNode.init)
|
|
623
|
+
children.push(astNode.init);
|
|
624
|
+
if (astNode.id)
|
|
625
|
+
children.push(astNode.id);
|
|
626
|
+
if (astNode.declarations)
|
|
627
|
+
children.push(...astNode.declarations);
|
|
628
|
+
if (astNode.test)
|
|
629
|
+
children.push(astNode.test);
|
|
630
|
+
if (astNode.update)
|
|
631
|
+
children.push(astNode.update);
|
|
632
|
+
if (astNode.left)
|
|
633
|
+
children.push(astNode.left);
|
|
634
|
+
if (astNode.right)
|
|
635
|
+
children.push(astNode.right);
|
|
636
|
+
if (astNode.argument)
|
|
637
|
+
children.push(astNode.argument);
|
|
638
|
+
if (astNode.arguments)
|
|
639
|
+
children.push(...astNode.arguments);
|
|
640
|
+
if (astNode.elements)
|
|
641
|
+
children.push(...astNode.elements);
|
|
642
|
+
if (astNode.properties)
|
|
643
|
+
children.push(...astNode.properties);
|
|
391
644
|
}
|
|
645
|
+
|
|
646
|
+
for (let child of children) {
|
|
647
|
+
let childOst;
|
|
648
|
+
|
|
649
|
+
childOst = processAst(child, ost, astToTree, astToOst, indent + ' ', visited);
|
|
650
|
+
if (childOst)
|
|
651
|
+
ost.children.push(childOst);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
return ost
|
|
655
|
+
}
|
|
392
656
|
}
|
|
393
657
|
|
|
394
|
-
function
|
|
658
|
+
function ostAnnotate
|
|
659
|
+
(ost, astToOst, context) {
|
|
660
|
+
if (ost) {
|
|
661
|
+
for (let letInfo of ost.lets) {
|
|
662
|
+
let writeOst;
|
|
663
|
+
|
|
664
|
+
writeOst = findFirstWrite(ost, letInfo);
|
|
665
|
+
letInfo.firstWrite = writeOst;
|
|
666
|
+
if (writeOst)
|
|
667
|
+
continue
|
|
668
|
+
if (letInfo.item.defType == 'ImportBinding')
|
|
669
|
+
continue
|
|
670
|
+
context.report({ node: letInfo.item.identifier,
|
|
671
|
+
messageId: 'mustInit',
|
|
672
|
+
data: { name: letInfo.item.name } });
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
if (ost.astNode.type == 'CallExpression' && ost.astNode.callee?.type == 'Identifier')
|
|
676
|
+
for (let child of ost.children)
|
|
677
|
+
if (child.astNode == ost.astNode.callee && child.reads.length > 0) {
|
|
678
|
+
let readRef;
|
|
679
|
+
|
|
680
|
+
readRef = child.reads[0].item.ref;
|
|
681
|
+
if (readRef?.resolved) {
|
|
682
|
+
let variable;
|
|
683
|
+
|
|
684
|
+
variable = readRef.resolved;
|
|
685
|
+
if (variable.defs.length > 0) {
|
|
686
|
+
let fnDefAst;
|
|
687
|
+
|
|
688
|
+
fnDefAst = variable.defs[0].node;
|
|
689
|
+
if (fnDefAst) {
|
|
690
|
+
if (fnDefAst.init?.type == 'ArrowFunctionExpression' || fnDefAst.init?.type == 'FunctionExpression')
|
|
691
|
+
fnDefAst = fnDefAst.init;
|
|
692
|
+
ost.fnDefOst = astToOst.get(fnDefAst);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
for (let child of ost.children)
|
|
699
|
+
ostAnnotate(child, astToOst, context);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
function findFirstWrite
|
|
704
|
+
(ost, letInfo) {
|
|
705
|
+
if (ost.astNode.type == 'FunctionDeclaration' || ost.astNode.type == 'ArrowFunctionExpression' || ost.astNode.type == 'FunctionExpression')
|
|
706
|
+
for (let child of ost.children)
|
|
707
|
+
if (child.astNode.type == 'BlockStatement')
|
|
708
|
+
return findFirstWriteInSubtree(child, letInfo)
|
|
709
|
+
return findFirstWriteInSubtree(ost, letInfo)
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
function findFirstWriteInSubtree
|
|
713
|
+
(ost, letInfo) {
|
|
714
|
+
if (ost) {
|
|
715
|
+
if (ost.astNode.type == 'FunctionDeclaration' || ost.astNode.type == 'ArrowFunctionExpression' || ost.astNode.type == 'FunctionExpression')
|
|
716
|
+
return null
|
|
717
|
+
|
|
718
|
+
for (let writeInfo of ost.writes) {
|
|
719
|
+
let writeVar;
|
|
720
|
+
|
|
721
|
+
writeVar = writeInfo.item.ref.resolved;
|
|
722
|
+
if (writeVar == letInfo.item.variable)
|
|
723
|
+
return ost
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
for (let child of ost.children) {
|
|
727
|
+
let result;
|
|
728
|
+
|
|
729
|
+
result = findFirstWriteInSubtree(child, letInfo);
|
|
730
|
+
if (result)
|
|
731
|
+
return result
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
return null
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
function ostCheck
|
|
739
|
+
(ost, context) {
|
|
740
|
+
if (ost) {
|
|
741
|
+
for (let letInfo of ost.lets)
|
|
742
|
+
if (letInfo.firstWrite)
|
|
743
|
+
walk2Start(ost, letInfo, context);
|
|
744
|
+
|
|
745
|
+
for (let child of ost.children)
|
|
746
|
+
ostCheck(child, context);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
function walk2Start
|
|
751
|
+
(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
|
|
760
|
+
(node, letInfo, context, visited) {
|
|
761
|
+
if (node) {
|
|
762
|
+
if (node.astNode.type == 'FunctionDeclaration' || node.astNode.type == 'ArrowFunctionExpression' || node.astNode.type == 'FunctionExpression')
|
|
763
|
+
return false
|
|
764
|
+
|
|
765
|
+
if (node == letInfo.firstWrite) {
|
|
766
|
+
for (let readInfo of node.reads)
|
|
767
|
+
if (readInfo.item.ref.resolved == letInfo.item.variable) {
|
|
768
|
+
context.report({ node: readInfo.item.ref.identifier,
|
|
769
|
+
messageId: 'initBeforeUse',
|
|
770
|
+
data: { name: letInfo.item.name } });
|
|
771
|
+
}
|
|
772
|
+
return true
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
if (node.astNode.type == 'CallExpression' && node.fnDefOst) {
|
|
776
|
+
let fnType;
|
|
777
|
+
|
|
778
|
+
fnType = node.fnDefOst.astNode.type;
|
|
779
|
+
|
|
780
|
+
if (fnType == 'FunctionDeclaration' || fnType == 'ArrowFunctionExpression' || fnType == 'FunctionExpression') {
|
|
781
|
+
let key;
|
|
782
|
+
|
|
783
|
+
key = `${letInfo.item.name}:${node.fnDefOst.id}`;
|
|
784
|
+
if (visited.has(key)) ;
|
|
785
|
+
else {
|
|
786
|
+
visited.add(key);
|
|
787
|
+
for (let child of node.fnDefOst.children)
|
|
788
|
+
if (child.astNode.type == 'BlockStatement' && walk2(child, letInfo, context, visited))
|
|
789
|
+
return true
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
for (let readInfo of node.reads)
|
|
795
|
+
if (readInfo.item.ref.resolved == letInfo.item.variable) {
|
|
796
|
+
context.report({ node: readInfo.item.ref.identifier,
|
|
797
|
+
messageId: 'initBeforeUse',
|
|
798
|
+
data: { name: letInfo.item.name } });
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
for (let child of node.children)
|
|
802
|
+
if (walk2(child, letInfo, context, visited))
|
|
803
|
+
return true
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
return false
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
var initBeforeUsePlugin = { meta: { type: 'problem',
|
|
810
|
+
docs: { description: 'Warn when a variable is used before being explicitly initialized.' },
|
|
811
|
+
messages: { initBeforeUse: "'{{name}}' used before initialization.",
|
|
812
|
+
mustInit: "'{{name}}' must be initialized." },
|
|
813
|
+
schema: [] },
|
|
814
|
+
create: createInitBeforeUse };
|
|
815
|
+
|
|
816
|
+
function VariableDeclaration
|
|
817
|
+
(context, node) {
|
|
818
|
+
let parent;
|
|
819
|
+
|
|
820
|
+
parent = node.parent;
|
|
821
|
+
|
|
822
|
+
for (parent = node.parent; parent; parent = parent.parent)
|
|
823
|
+
if (parent.type == 'BlockStatement')
|
|
824
|
+
break
|
|
825
|
+
|
|
826
|
+
if (parent) {
|
|
827
|
+
let idx;
|
|
828
|
+
|
|
829
|
+
if (parent.parent?.type == 'CatchClause')
|
|
830
|
+
return
|
|
831
|
+
|
|
832
|
+
idx = parent.body.indexOf(node);
|
|
833
|
+
for (let i = 0; i < idx; i++) {
|
|
834
|
+
if (parent.body[i].type == 'VariableDeclaration')
|
|
835
|
+
continue
|
|
836
|
+
context.report({ node, messageId: 'varDeclBlockStart' });
|
|
837
|
+
return
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
function create$3
|
|
395
843
|
(context) {
|
|
396
|
-
return {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
844
|
+
return { VariableDeclaration: node => VariableDeclaration(context, node) }
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
var varDeclBlockStartPlugin = { meta: { type: 'suggestion',
|
|
848
|
+
docs: { description: 'Require variable declarations to be at the start of the block.' },
|
|
849
|
+
messages: { varDeclBlockStart: 'VarDecl must be at start of block.' },
|
|
850
|
+
schema: [] },
|
|
851
|
+
create: create$3 };
|
|
852
|
+
|
|
853
|
+
function FunctionDeclaration
|
|
854
|
+
(context, node) {
|
|
855
|
+
let parent;
|
|
856
|
+
|
|
857
|
+
for (parent = node.parent; parent; parent = parent.parent)
|
|
858
|
+
if (parent.type == 'BlockStatement')
|
|
859
|
+
break
|
|
860
|
+
|
|
861
|
+
if (parent) {
|
|
862
|
+
let idx;
|
|
863
|
+
|
|
864
|
+
if (parent.parent?.type == 'CatchClause')
|
|
865
|
+
return
|
|
866
|
+
|
|
867
|
+
idx = parent.body.indexOf(node);
|
|
868
|
+
for (let i = 0; i < idx; i++) {
|
|
869
|
+
if (parent.body[i].type == 'VariableDeclaration'
|
|
870
|
+
|| parent.body[i].type == 'FunctionDeclaration'
|
|
871
|
+
|| parent.body[i].type == 'EmptyStatement')
|
|
872
|
+
continue
|
|
873
|
+
context.report({ node, messageId: 'fnDeclBlockStart' });
|
|
874
|
+
return
|
|
409
875
|
}
|
|
410
876
|
}
|
|
411
877
|
}
|
|
412
878
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
'
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
879
|
+
function create$2
|
|
880
|
+
(context) {
|
|
881
|
+
return { FunctionDeclaration: node => FunctionDeclaration(context, node) }
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
var fnDeclBlockStartPlugin = { meta: { type: 'suggestion',
|
|
885
|
+
docs: { description: 'Require function declarations to be at the start of the block.' },
|
|
886
|
+
messages: { fnDeclBlockStart: 'FnDecl must be the start the block (after VarDecls).' },
|
|
887
|
+
schema: [] },
|
|
888
|
+
create: create$2 };
|
|
889
|
+
|
|
890
|
+
function FnArgsNl
|
|
891
|
+
(node, context) {
|
|
892
|
+
let nameLine, parenLine, nameEnd, i, newlines, parent;
|
|
893
|
+
|
|
894
|
+
parent = node.parent;
|
|
895
|
+
|
|
896
|
+
if (parent?.type == 'Property' && (parent.method || parent.kind == 'get' || parent.kind == 'set')) {
|
|
897
|
+
nameLine = parent.key.loc.start.line;
|
|
898
|
+
nameEnd = parent.key.range[1];
|
|
899
|
+
}
|
|
900
|
+
else if (parent?.type == 'MethodDefinition') {
|
|
901
|
+
nameLine = parent.key.loc.start.line;
|
|
902
|
+
nameEnd = parent.key.range[1];
|
|
903
|
+
}
|
|
904
|
+
else {
|
|
905
|
+
nameLine = node.loc.start.line;
|
|
906
|
+
nameEnd = node.range[0];
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
i = nameEnd;
|
|
910
|
+
newlines = 0;
|
|
911
|
+
while (i < context.sourceCode.text.length) {
|
|
912
|
+
if (context.sourceCode.text[i] == '(')
|
|
913
|
+
break
|
|
914
|
+
if (context.sourceCode.text[i] == '\n')
|
|
915
|
+
newlines++;
|
|
916
|
+
i++;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
parenLine = nameLine + newlines;
|
|
920
|
+
|
|
921
|
+
if (parenLine - nameLine == 1)
|
|
922
|
+
return
|
|
923
|
+
context.report({ node, messageId: 'fnArgsNl' });
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
function create$1
|
|
927
|
+
(context) {
|
|
928
|
+
return { FunctionDeclaration: node => FnArgsNl(node, context),
|
|
929
|
+
FunctionExpression: node => FnArgsNl(node, context) }
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
var fnArgsNlPlugin = { meta: { type: 'suggestion',
|
|
933
|
+
docs: { description: 'Require function args on the line immediately after the function name.' },
|
|
934
|
+
messages: { fnArgsNl: 'Fn args must be on the line immediately after the function name.' },
|
|
935
|
+
schema: [] },
|
|
936
|
+
create: create$1 };
|
|
937
|
+
|
|
938
|
+
function trace
|
|
939
|
+
(...args) {
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
function unit
|
|
943
|
+
(context) {
|
|
944
|
+
return context.options[0] ?? 2
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
function checkObjectExpressionProperties
|
|
948
|
+
(properties, node, context) {
|
|
949
|
+
let firstProp, sourceCode, lastProp, lastPropEnd, closingBrace, firstPropLine, firstPropCol, afterLastProp, closingLine, lastPropValueEndLine;
|
|
950
|
+
|
|
951
|
+
sourceCode = context.sourceCode.text;
|
|
952
|
+
firstProp = properties[0];
|
|
953
|
+
lastProp = properties[properties.length - 1];
|
|
954
|
+
firstPropLine = firstProp.loc.start.line;
|
|
955
|
+
firstPropCol = firstProp.loc.start.column;
|
|
956
|
+
lastPropEnd = lastProp.range[1];
|
|
957
|
+
closingBrace = sourceCode.indexOf('}', lastPropEnd);
|
|
958
|
+
closingLine = sourceCode.slice(0, closingBrace).split('\n').length;
|
|
959
|
+
afterLastProp = sourceCode.slice(lastPropEnd, closingBrace);
|
|
960
|
+
lastPropValueEndLine = sourceCode.slice(0, lastPropEnd).split('\n').length;
|
|
961
|
+
trace('CHECK POINT 1: is single-line? firstPropLine=%d, braceLine=%d, closingLine=%d', firstPropLine, node.loc.start.line, closingLine);
|
|
962
|
+
if (firstPropLine == node.loc.start.line && closingLine == firstPropLine)
|
|
963
|
+
return
|
|
964
|
+
trace('CHECK POINT 2: is firstPropLine (%d) != braceLine (%d)?', firstPropLine, node.loc.start.line);
|
|
965
|
+
if (firstPropLine == node.loc.start.line) ;
|
|
966
|
+
else if (firstPropCol == node.loc.start.column + unit(context)) ;
|
|
967
|
+
else {
|
|
968
|
+
context.report({ node: firstProp, messageId: 'indentStruct' });
|
|
485
969
|
}
|
|
486
|
-
|
|
970
|
+
for (let i = 1; i < properties.length; i++) {
|
|
971
|
+
let prop;
|
|
972
|
+
|
|
973
|
+
prop = properties[i];
|
|
974
|
+
if (prop.loc.start.column == firstPropCol) ;
|
|
975
|
+
else {
|
|
976
|
+
context.report({ node: prop, messageId: 'indentStruct' });
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
if (closingLine > lastPropValueEndLine) {
|
|
980
|
+
let braceCol, closingCol;
|
|
981
|
+
|
|
982
|
+
braceCol = node.loc.start.column;
|
|
983
|
+
closingCol = node.loc.end.column - 1;
|
|
984
|
+
if (closingCol == braceCol) ;
|
|
985
|
+
else {
|
|
986
|
+
context.report({ node, messageId: 'indentStruct' });
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
if (closingLine == lastPropValueEndLine) {
|
|
990
|
+
if (afterLastProp == ' ') ;
|
|
991
|
+
else {
|
|
992
|
+
context.report({ node, messageId: 'indentStruct' });
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
for (let prop of properties)
|
|
996
|
+
if (prop.method) {
|
|
997
|
+
let keyLine, keyEnd, i, newlines, parenLine;
|
|
998
|
+
|
|
999
|
+
keyLine = prop.key.loc.start.line;
|
|
1000
|
+
keyEnd = prop.key.range[1];
|
|
1001
|
+
i = keyEnd;
|
|
1002
|
+
newlines = 0;
|
|
1003
|
+
|
|
1004
|
+
while (i < sourceCode.length) {
|
|
1005
|
+
if (sourceCode[i] == '(')
|
|
1006
|
+
break
|
|
1007
|
+
if (sourceCode[i] == '\n')
|
|
1008
|
+
newlines++;
|
|
1009
|
+
i++;
|
|
1010
|
+
}
|
|
1011
|
+
parenLine = keyLine + newlines;
|
|
1012
|
+
if (parenLine > keyLine) {
|
|
1013
|
+
let parenCol;
|
|
1014
|
+
|
|
1015
|
+
parenCol = i - sourceCode.lastIndexOf('\n', i);
|
|
1016
|
+
if (prop.value.type == 'FunctionExpression' && prop.value.async) ;
|
|
1017
|
+
else if (parenCol - 1 == prop.key.loc.start.column) ;
|
|
1018
|
+
else {
|
|
1019
|
+
context.report({ node: prop, messageId: 'indentStruct' });
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
function checkObjectExpression
|
|
1026
|
+
(node, context) {
|
|
1027
|
+
let properties;
|
|
1028
|
+
|
|
1029
|
+
properties = node.properties;
|
|
1030
|
+
if (properties.length)
|
|
1031
|
+
checkObjectExpressionProperties(properties, node, context);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
function create
|
|
1035
|
+
(context) {
|
|
1036
|
+
return { ObjectExpression: node => checkObjectExpression(node, context) }
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
var indentStructPlugin = { meta: { type: 'suggestion',
|
|
1040
|
+
docs: { description: 'Struct alignment rules.' },
|
|
1041
|
+
messages: { indentStruct: 'Indent structure' },
|
|
1042
|
+
schema: [] },
|
|
1043
|
+
create };
|
|
1044
|
+
|
|
1045
|
+
let rules, languageOptions, plugins;
|
|
1046
|
+
|
|
1047
|
+
plugins = { 'cookshack': { rules: { 'positive-vibes': positiveVibesPlugin,
|
|
1048
|
+
'narrowest-scope': narrowestScopePlugin,
|
|
1049
|
+
'use-risky-equal': useRiskyEqualPlugin,
|
|
1050
|
+
'always-let': alwaysLetPlugin,
|
|
1051
|
+
'init-before-use': initBeforeUsePlugin,
|
|
1052
|
+
'var-decl-block-start': varDeclBlockStartPlugin,
|
|
1053
|
+
'fn-decl-block-start': fnDeclBlockStartPlugin,
|
|
1054
|
+
'fn-args-nl': fnArgsNlPlugin,
|
|
1055
|
+
'indent-struct': indentStructPlugin } } };
|
|
1056
|
+
|
|
1057
|
+
rules = { 'array-bracket-newline': [ 'error', 'never' ],
|
|
1058
|
+
'array-bracket-spacing': [ 'error', 'always' ],
|
|
1059
|
+
'arrow-parens': [ 'error', 'as-needed' ],
|
|
1060
|
+
'brace-style': [ 'error', 'stroustrup' ],
|
|
1061
|
+
'comma-dangle': 'error',
|
|
1062
|
+
'curly': [ 'error', 'multi' ],
|
|
1063
|
+
'eol-last': [ 'error', 'always' ],
|
|
1064
|
+
'function-paren-newline': [ 'error', 'never' ],
|
|
1065
|
+
'indent': [ 'error', 2, { ArrayExpression: 'first',
|
|
1066
|
+
CallExpression: { arguments: 'first' },
|
|
1067
|
+
//flatTernaryExpressions: true,
|
|
1068
|
+
//offsetTernaryExpressions: true,
|
|
1069
|
+
// ternary, because overhangs strangely (eg multiline in array def)
|
|
1070
|
+
'ignoredNodes': [ 'ConditionalExpression', 'ObjectExpression *' ],
|
|
1071
|
+
FunctionDeclaration: { parameters: 'first', body: 1 },
|
|
1072
|
+
FunctionExpression: { parameters: 'first', body: 1 },
|
|
1073
|
+
ImportDeclaration: 'first',
|
|
1074
|
+
offsetTernaryExpressions: true,
|
|
1075
|
+
VariableDeclarator: 'first' } ],
|
|
1076
|
+
'init-declarations': [ 'error', 'never', { 'ignoreForLoopInit': true } ],
|
|
1077
|
+
'keyword-spacing': [ 'error', { before: true, after: true } ],
|
|
1078
|
+
'linebreak-style': [ 'error', 'unix' ],
|
|
1079
|
+
'padding-line-between-statements': [ 'error',
|
|
1080
|
+
{ blankLine: 'always', prev: 'let', next: '*' },
|
|
1081
|
+
{ blankLine: 'never', prev: 'let', next: 'let' } ],
|
|
1082
|
+
'no-case-declarations': 'error',
|
|
1083
|
+
'no-global-assign': 'error',
|
|
1084
|
+
'cookshack/positive-vibes': 'error',
|
|
1085
|
+
'cookshack/narrowest-scope': 'error',
|
|
1086
|
+
'cookshack/use-risky-equal': 'error',
|
|
1087
|
+
'cookshack/always-let': 'error',
|
|
1088
|
+
// using the implicit inititialization to undefined fits better
|
|
1089
|
+
//'cookshack/init-before-use': 'error',
|
|
1090
|
+
'cookshack/var-decl-block-start': 'error',
|
|
1091
|
+
'cookshack/fn-decl-block-start': 'error',
|
|
1092
|
+
'cookshack/fn-args-nl': 'error',
|
|
1093
|
+
'cookshack/indent-struct': 'error',
|
|
1094
|
+
'no-mixed-operators': 'error',
|
|
1095
|
+
'no-multi-spaces': 'error',
|
|
1096
|
+
'no-multiple-empty-lines': [ 'error', { max: 1, maxEOF: 0 } ],
|
|
1097
|
+
'no-negated-condition': 'error',
|
|
1098
|
+
'no-redeclare': 'error',
|
|
1099
|
+
'no-sequences': 'error',
|
|
1100
|
+
'no-sparse-arrays': 'error',
|
|
1101
|
+
'no-tabs': 'error',
|
|
1102
|
+
'no-trailing-spaces': 'error',
|
|
1103
|
+
'no-undef': 'error',
|
|
1104
|
+
'no-unsafe-negation': 'error',
|
|
1105
|
+
'no-unused-vars': 'error',
|
|
1106
|
+
'no-var': 'error',
|
|
1107
|
+
'object-curly-spacing': [ 'error', 'always' ],
|
|
1108
|
+
'object-shorthand': [ 'error', 'always' ],
|
|
1109
|
+
quotes: [ 'error', 'single', { avoidEscape: true } ],
|
|
1110
|
+
semi: [ 'error', 'never' ] };
|
|
1111
|
+
|
|
1112
|
+
languageOptions = { globals: { ...globals.node },
|
|
1113
|
+
parserOptions: { ecmaVersion: 2025,
|
|
1114
|
+
sourceType: 'module' } };
|
|
487
1115
|
|
|
488
1116
|
var index = [ { ignores: [ 'TAGS.mjs' ] },
|
|
489
1117
|
{ languageOptions,
|