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