@cookshack/eslint-config 3.0.0 → 5.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/AGENTS.md +12 -0
- package/dist/formatter.cjs +2 -1
- package/dist/formatter.js +2 -1
- package/dist/index.cjs +344 -199
- package/dist/index.js +344 -199
- package/formatter.js +2 -1
- package/index.js +63 -67
- package/package.json +1 -1
- package/plugins/always-let.js +5 -4
- package/plugins/fn-args-nl.js +47 -0
- package/plugins/fn-decl-block-start.js +1 -2
- package/plugins/indent-struct.js +145 -0
- package/plugins/init-before-use.js +57 -62
- package/plugins/narrowest-scope.js +24 -25
- package/plugins/positive-vibes.js +15 -15
- package/plugins/use-risky-equal.js +7 -5
- package/plugins/var-decl-block-start.js +2 -2
- package/test/rules/fn-args-nl.js +114 -0
- package/test/rules/indent-struct.js +198 -0
- package/test/rules/init-before-use.js +8 -4
- package/test/rules/narrowest-scope.js +10 -5
- package/test/rules/no-shadow.js +42 -0
package/formatter.js
CHANGED
package/index.js
CHANGED
|
@@ -7,6 +7,8 @@ import alwaysLetPlugin from './plugins/always-let.js'
|
|
|
7
7
|
import initBeforeUsePlugin from './plugins/init-before-use.js'
|
|
8
8
|
import varDeclBlockStartPlugin from './plugins/var-decl-block-start.js'
|
|
9
9
|
import fnDeclBlockStartPlugin from './plugins/fn-decl-block-start.js'
|
|
10
|
+
import fnArgsNlPlugin from './plugins/fn-args-nl.js'
|
|
11
|
+
import indentStructPlugin from './plugins/indent-struct.js'
|
|
10
12
|
|
|
11
13
|
export { getPrintBuffer }
|
|
12
14
|
|
|
@@ -18,75 +20,69 @@ plugins = { 'cookshack': { rules: { 'positive-vibes': positiveVibesPlugin,
|
|
|
18
20
|
'always-let': alwaysLetPlugin,
|
|
19
21
|
'init-before-use': initBeforeUsePlugin,
|
|
20
22
|
'var-decl-block-start': varDeclBlockStartPlugin,
|
|
21
|
-
'fn-decl-block-start': fnDeclBlockStartPlugin
|
|
23
|
+
'fn-decl-block-start': fnDeclBlockStartPlugin,
|
|
24
|
+
'fn-args-nl': fnArgsNlPlugin,
|
|
25
|
+
'indent-struct': indentStructPlugin } } }
|
|
22
26
|
|
|
23
|
-
rules = {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
//'newline-before-function-paren': ['error', 'always'],
|
|
79
|
-
}
|
|
27
|
+
rules = { 'array-bracket-newline': [ 'error', 'never' ],
|
|
28
|
+
'array-bracket-spacing': [ 'error', 'always' ],
|
|
29
|
+
'arrow-parens': [ 'error', 'as-needed' ],
|
|
30
|
+
'brace-style': [ 'error', 'stroustrup' ],
|
|
31
|
+
'comma-dangle': 'error',
|
|
32
|
+
'curly': [ 'error', 'multi' ],
|
|
33
|
+
'eol-last': [ 'error', 'always' ],
|
|
34
|
+
'function-paren-newline': [ 'error', 'never' ],
|
|
35
|
+
'indent': [ 'error', 2, { ArrayExpression: 'first',
|
|
36
|
+
CallExpression: { arguments: 'first' },
|
|
37
|
+
//flatTernaryExpressions: true,
|
|
38
|
+
//offsetTernaryExpressions: true,
|
|
39
|
+
// ternary, because overhangs strangely (eg multiline in array def)
|
|
40
|
+
'ignoredNodes': [ 'ConditionalExpression', 'ObjectExpression *' ],
|
|
41
|
+
FunctionDeclaration: { parameters: 'first', body: 1 },
|
|
42
|
+
FunctionExpression: { parameters: 'first', body: 1 },
|
|
43
|
+
ImportDeclaration: 'first',
|
|
44
|
+
offsetTernaryExpressions: true,
|
|
45
|
+
VariableDeclarator: 'first' } ],
|
|
46
|
+
'init-declarations': [ 'error', 'never', { 'ignoreForLoopInit': true } ],
|
|
47
|
+
'keyword-spacing': [ 'error', { before: true, after: true } ],
|
|
48
|
+
'linebreak-style': [ 'error', 'unix' ],
|
|
49
|
+
'padding-line-between-statements': [ 'error',
|
|
50
|
+
{ blankLine: 'always', prev: 'let', next: '*' },
|
|
51
|
+
{ blankLine: 'never', prev: 'let', next: 'let' } ],
|
|
52
|
+
'no-case-declarations': 'error',
|
|
53
|
+
'no-global-assign': 'error',
|
|
54
|
+
'cookshack/positive-vibes': 'error',
|
|
55
|
+
'cookshack/narrowest-scope': 'error',
|
|
56
|
+
'cookshack/use-risky-equal': 'error',
|
|
57
|
+
'cookshack/always-let': 'error',
|
|
58
|
+
// using the implicit inititialization to undefined fits better
|
|
59
|
+
//'cookshack/init-before-use': 'error',
|
|
60
|
+
'cookshack/var-decl-block-start': 'error',
|
|
61
|
+
'cookshack/fn-decl-block-start': 'error',
|
|
62
|
+
'cookshack/fn-args-nl': 'error',
|
|
63
|
+
'cookshack/indent-struct': 'error',
|
|
64
|
+
'no-mixed-operators': 'error',
|
|
65
|
+
'no-multi-spaces': 'error',
|
|
66
|
+
'no-multiple-empty-lines': [ 'error', { max: 1, maxEOF: 0 } ],
|
|
67
|
+
'no-negated-condition': 'error',
|
|
68
|
+
'no-redeclare': 'error',
|
|
69
|
+
'no-sequences': 'error',
|
|
70
|
+
'no-shadow': [ 'error', { builtinGlobals: true } ],
|
|
71
|
+
'no-sparse-arrays': 'error',
|
|
72
|
+
'no-tabs': 'error',
|
|
73
|
+
'no-trailing-spaces': 'error',
|
|
74
|
+
'no-undef': 'error',
|
|
75
|
+
'no-unsafe-negation': 'error',
|
|
76
|
+
'no-unused-vars': 'error',
|
|
77
|
+
'no-var': 'error',
|
|
78
|
+
'object-curly-spacing': [ 'error', 'always' ],
|
|
79
|
+
'object-shorthand': [ 'error', 'always' ],
|
|
80
|
+
quotes: [ 'error', 'single', { avoidEscape: true } ],
|
|
81
|
+
semi: [ 'error', 'never' ] }
|
|
80
82
|
|
|
81
|
-
languageOptions = {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
},
|
|
85
|
-
parserOptions: {
|
|
86
|
-
ecmaVersion: 2025,
|
|
87
|
-
sourceType: 'module'
|
|
88
|
-
}
|
|
89
|
-
}
|
|
83
|
+
languageOptions = { globals: { ...globals.node },
|
|
84
|
+
parserOptions: { ecmaVersion: 2025,
|
|
85
|
+
sourceType: 'module' } }
|
|
90
86
|
|
|
91
87
|
export
|
|
92
88
|
default [ { ignores: [ 'TAGS.mjs' ] },
|
package/package.json
CHANGED
package/plugins/always-let.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
function create
|
|
2
2
|
(context) {
|
|
3
|
-
return { VariableDeclaration
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
return { VariableDeclaration
|
|
4
|
+
(node) {
|
|
5
|
+
if (node.kind == 'const' || node.kind == 'var')
|
|
6
|
+
context.report({ node, messageId: 'useLet' })
|
|
7
|
+
} }
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
export
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
function FnArgsNl
|
|
2
|
+
(node, context) {
|
|
3
|
+
let nameLine, parenLine, nameEnd, i, newlines, parent
|
|
4
|
+
|
|
5
|
+
parent = node.parent
|
|
6
|
+
|
|
7
|
+
if (parent?.type == 'Property' && (parent.method || parent.kind == 'get' || parent.kind == 'set')) {
|
|
8
|
+
nameLine = parent.key.loc.start.line
|
|
9
|
+
nameEnd = parent.key.range[1]
|
|
10
|
+
}
|
|
11
|
+
else if (parent?.type == 'MethodDefinition') {
|
|
12
|
+
nameLine = parent.key.loc.start.line
|
|
13
|
+
nameEnd = parent.key.range[1]
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
nameLine = node.loc.start.line
|
|
17
|
+
nameEnd = node.range[0]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
i = nameEnd
|
|
21
|
+
newlines = 0
|
|
22
|
+
while (i < context.sourceCode.text.length) {
|
|
23
|
+
if (context.sourceCode.text[i] == '(')
|
|
24
|
+
break
|
|
25
|
+
if (context.sourceCode.text[i] == '\n')
|
|
26
|
+
newlines++
|
|
27
|
+
i++
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
parenLine = nameLine + newlines
|
|
31
|
+
|
|
32
|
+
if (parenLine - nameLine == 1)
|
|
33
|
+
return
|
|
34
|
+
context.report({ node, messageId: 'fnArgsNl' })
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function create
|
|
38
|
+
(context) {
|
|
39
|
+
return { FunctionDeclaration: node => FnArgsNl(node, context),
|
|
40
|
+
FunctionExpression: node => FnArgsNl(node, context) }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default { meta: { type: 'suggestion',
|
|
44
|
+
docs: { description: 'Require function args on the line immediately after the function name.' },
|
|
45
|
+
messages: { fnArgsNl: 'Fn args must be on the line immediately after the function name.' },
|
|
46
|
+
schema: [] },
|
|
47
|
+
create }
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
function trace
|
|
2
|
+
(...args) {
|
|
3
|
+
if (0)
|
|
4
|
+
console.log(...args)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function unit
|
|
8
|
+
(context) {
|
|
9
|
+
return context.options[0] ?? 2
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function checkObjectExpressionProperties
|
|
13
|
+
(properties, node, context) {
|
|
14
|
+
let firstProp, sourceCode, lastProp, lastPropEnd, closingBrace, firstPropLine, firstPropCol, afterLastProp, closingLine, lastPropValueEndLine
|
|
15
|
+
|
|
16
|
+
sourceCode = context.sourceCode.text
|
|
17
|
+
firstProp = properties[0]
|
|
18
|
+
lastProp = properties[properties.length - 1]
|
|
19
|
+
firstPropLine = firstProp.loc.start.line
|
|
20
|
+
firstPropCol = firstProp.loc.start.column
|
|
21
|
+
lastPropEnd = lastProp.range[1]
|
|
22
|
+
closingBrace = sourceCode.indexOf('}', lastPropEnd)
|
|
23
|
+
closingLine = sourceCode.slice(0, closingBrace).split('\n').length
|
|
24
|
+
afterLastProp = sourceCode.slice(lastPropEnd, closingBrace)
|
|
25
|
+
lastPropValueEndLine = sourceCode.slice(0, lastPropEnd).split('\n').length
|
|
26
|
+
|
|
27
|
+
trace('ALGORITHM 1: Single-line objects: always valid (no check needed)')
|
|
28
|
+
trace('CHECK POINT 1: is single-line? firstPropLine=%d, braceLine=%d, closingLine=%d', firstPropLine, node.loc.start.line, closingLine)
|
|
29
|
+
if (firstPropLine == node.loc.start.line && closingLine == firstPropLine)
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
trace('ALGORITHM 2: When first property is on a line after {: must use 1 indent unit from the column where { appears')
|
|
33
|
+
trace('CHECK POINT 2: is firstPropLine (%d) != braceLine (%d)?', firstPropLine, node.loc.start.line)
|
|
34
|
+
if (firstPropLine == node.loc.start.line) {
|
|
35
|
+
// first property is on brace line
|
|
36
|
+
}
|
|
37
|
+
else if (firstPropCol == node.loc.start.column + unit(context)) {
|
|
38
|
+
// ok
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
trace('CHECK 2 FAIL')
|
|
42
|
+
context.report({ node: firstProp, messageId: 'indentStruct' })
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
trace('ALGORITHM 3: Multi-line: all properties must align to first propertys column')
|
|
46
|
+
trace('CHECK POINT 3: checking property alignment, firstPropCol=%d', firstPropCol)
|
|
47
|
+
for (let i = 1; i < properties.length; i++) {
|
|
48
|
+
let prop
|
|
49
|
+
|
|
50
|
+
prop = properties[i]
|
|
51
|
+
if (prop.loc.start.column == firstPropCol) {
|
|
52
|
+
// ok
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
trace('CHECK 3 FAIL')
|
|
56
|
+
context.report({ node: prop, messageId: 'indentStruct' })
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
trace('ALGORITHM 4: } on a line following the last prop value must align with {')
|
|
61
|
+
trace('CHECK POINT 4: is closing on a line following last prop value? closingLine=%d, lastPropValueEndLine=%d', closingLine, lastPropValueEndLine)
|
|
62
|
+
if (closingLine > lastPropValueEndLine) {
|
|
63
|
+
let braceCol, closingCol
|
|
64
|
+
|
|
65
|
+
braceCol = node.loc.start.column
|
|
66
|
+
closingCol = node.loc.end.column - 1
|
|
67
|
+
trace('CHECK 4: braceCol=%d, closingCol=%d', braceCol, closingCol)
|
|
68
|
+
if (closingCol == braceCol) {
|
|
69
|
+
// ok
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
trace('CHECK 4 FAIL')
|
|
73
|
+
context.report({ node, messageId: 'indentStruct' })
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
trace('ALGORITHM 5: } on same line as last prop value must have a space before it')
|
|
78
|
+
trace('CHECK POINT 5: is closing on same line as last prop value? closingLine=%d, lastPropValueEndLine=%d', closingLine, lastPropValueEndLine)
|
|
79
|
+
if (closingLine == lastPropValueEndLine) {
|
|
80
|
+
trace('CHECK 5: lastPropEnd=%d, closingBrace=%d, afterLastProp=%j', lastPropEnd, closingBrace, afterLastProp)
|
|
81
|
+
if (afterLastProp == ' ') {
|
|
82
|
+
// ok
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
trace('CHECK 5 FAIL')
|
|
86
|
+
context.report({ node, messageId: 'indentStruct' })
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
trace('ALGORITHM 6: When a param list is on a line after the field name, it must align with the field name')
|
|
91
|
+
trace('CHECK POINT 6: checking method param alignment')
|
|
92
|
+
for (let prop of properties)
|
|
93
|
+
if (prop.method) {
|
|
94
|
+
let keyLine, keyEnd, i, newlines, parenLine
|
|
95
|
+
|
|
96
|
+
keyLine = prop.key.loc.start.line
|
|
97
|
+
keyEnd = prop.key.range[1]
|
|
98
|
+
i = keyEnd
|
|
99
|
+
newlines = 0
|
|
100
|
+
|
|
101
|
+
while (i < sourceCode.length) {
|
|
102
|
+
if (sourceCode[i] == '(')
|
|
103
|
+
break
|
|
104
|
+
if (sourceCode[i] == '\n')
|
|
105
|
+
newlines++
|
|
106
|
+
i++
|
|
107
|
+
}
|
|
108
|
+
parenLine = keyLine + newlines
|
|
109
|
+
if (parenLine > keyLine) {
|
|
110
|
+
let parenCol
|
|
111
|
+
|
|
112
|
+
parenCol = i - sourceCode.lastIndexOf('\n', i)
|
|
113
|
+
if (prop.value.type == 'FunctionExpression' && prop.value.async) {
|
|
114
|
+
// async methods can have whitespace between key and paren
|
|
115
|
+
}
|
|
116
|
+
else if (parenCol - 1 == prop.key.loc.start.column) {
|
|
117
|
+
// ok
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
trace('CHECK 6 FAIL')
|
|
121
|
+
context.report({ node: prop, messageId: 'indentStruct' })
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function checkObjectExpression
|
|
128
|
+
(node, context) {
|
|
129
|
+
let properties
|
|
130
|
+
|
|
131
|
+
properties = node.properties
|
|
132
|
+
if (properties.length)
|
|
133
|
+
checkObjectExpressionProperties(properties, node, context)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function create
|
|
137
|
+
(context) {
|
|
138
|
+
return { ObjectExpression: node => checkObjectExpression(node, context) }
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export default { meta: { type: 'suggestion',
|
|
142
|
+
docs: { description: 'Struct alignment rules.' },
|
|
143
|
+
messages: { indentStruct: 'Indent structure' },
|
|
144
|
+
schema: [] },
|
|
145
|
+
create }
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { buildScopeTree } from './narrowest-scope.js'
|
|
2
2
|
|
|
3
|
-
let ostIdCounter, errorCount,
|
|
3
|
+
let ostIdCounter, errorCount, $lastOst
|
|
4
4
|
|
|
5
5
|
ostIdCounter = 0
|
|
6
6
|
errorCount = 0
|
|
7
|
-
|
|
7
|
+
$lastOst = 0
|
|
8
8
|
|
|
9
9
|
function trace
|
|
10
10
|
(...args) {
|
|
@@ -15,34 +15,34 @@ function trace
|
|
|
15
15
|
export
|
|
16
16
|
function lastOst
|
|
17
17
|
() {
|
|
18
|
-
return
|
|
18
|
+
return $lastOst
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
function createInitBeforeUse
|
|
21
|
+
function createInitBeforeUse
|
|
22
|
+
(context) {
|
|
22
23
|
let scopeManager
|
|
23
24
|
|
|
24
25
|
scopeManager = context.sourceCode.scopeManager
|
|
25
26
|
if (scopeManager)
|
|
26
|
-
return {
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
return { 'Program:exit'
|
|
28
|
+
() {
|
|
29
|
+
let scopeToNode, astToTree, astToOst
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
errorCount = 0
|
|
32
|
+
scopeToNode = new Map
|
|
33
|
+
astToTree = new Map
|
|
34
|
+
astToOst = new Map
|
|
35
|
+
buildScopeTree(scopeManager.scopes[0], '1', scopeToNode, astToTree)
|
|
35
36
|
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
ostIdCounter = 0
|
|
38
|
+
$lastOst = processAst(context.sourceCode.ast, null, astToTree, astToOst, '', new Set())
|
|
38
39
|
|
|
39
|
-
|
|
40
|
+
ostAnnotate($lastOst, astToOst, context)
|
|
40
41
|
|
|
41
|
-
|
|
42
|
+
ostCheck($lastOst, context)
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
44
|
+
trace('ERRORS: ' + errorCount)
|
|
45
|
+
} }
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
function isRegularDeclaration
|
|
@@ -55,7 +55,8 @@ function isRegularDeclaration
|
|
|
55
55
|
return 0
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
function processAst
|
|
58
|
+
function processAst
|
|
59
|
+
(astNode, parentOst, astToTree, astToOst, indent, visited) {
|
|
59
60
|
if (astNode) {
|
|
60
61
|
let treeNode, scopeName, lets, reads, writes, ost, children
|
|
61
62
|
|
|
@@ -96,17 +97,15 @@ function processAst(astNode, parentOst, astToTree, astToOst, indent, visited) {
|
|
|
96
97
|
trace(`${indent} | WRITE ${item.name}:${item.varId}`)
|
|
97
98
|
}
|
|
98
99
|
|
|
99
|
-
ost = {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
fnDefOst: null
|
|
109
|
-
}
|
|
100
|
+
ost = { id: ostIdCounter++,
|
|
101
|
+
astNode,
|
|
102
|
+
treeNode,
|
|
103
|
+
scopeItems: treeNode?.items ?? [],
|
|
104
|
+
lets,
|
|
105
|
+
reads,
|
|
106
|
+
writes,
|
|
107
|
+
children: [],
|
|
108
|
+
fnDefOst: null }
|
|
110
109
|
|
|
111
110
|
astToOst.set(astNode, ost)
|
|
112
111
|
|
|
@@ -192,7 +191,8 @@ function processAst(astNode, parentOst, astToTree, astToOst, indent, visited) {
|
|
|
192
191
|
}
|
|
193
192
|
}
|
|
194
193
|
|
|
195
|
-
function ostAnnotate
|
|
194
|
+
function ostAnnotate
|
|
195
|
+
(ost, astToOst, context) {
|
|
196
196
|
if (ost) {
|
|
197
197
|
for (let letInfo of ost.lets) {
|
|
198
198
|
let writeOst
|
|
@@ -204,11 +204,9 @@ function ostAnnotate(ost, astToOst, context) {
|
|
|
204
204
|
if (letInfo.item.defType == 'ImportBinding')
|
|
205
205
|
continue
|
|
206
206
|
errorCount++
|
|
207
|
-
context.report({
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
data: { name: letInfo.item.name }
|
|
211
|
-
})
|
|
207
|
+
context.report({ node: letInfo.item.identifier,
|
|
208
|
+
messageId: 'mustInit',
|
|
209
|
+
data: { name: letInfo.item.name } })
|
|
212
210
|
}
|
|
213
211
|
|
|
214
212
|
if (ost.astNode.type == 'CallExpression' && ost.astNode.callee?.type == 'Identifier')
|
|
@@ -239,7 +237,8 @@ function ostAnnotate(ost, astToOst, context) {
|
|
|
239
237
|
}
|
|
240
238
|
}
|
|
241
239
|
|
|
242
|
-
function findFirstWrite
|
|
240
|
+
function findFirstWrite
|
|
241
|
+
(ost, letInfo) {
|
|
243
242
|
if (ost.astNode.type == 'FunctionDeclaration' || ost.astNode.type == 'ArrowFunctionExpression' || ost.astNode.type == 'FunctionExpression')
|
|
244
243
|
for (let child of ost.children)
|
|
245
244
|
if (child.astNode.type == 'BlockStatement')
|
|
@@ -247,7 +246,8 @@ function findFirstWrite(ost, letInfo) {
|
|
|
247
246
|
return findFirstWriteInSubtree(ost, letInfo)
|
|
248
247
|
}
|
|
249
248
|
|
|
250
|
-
function findFirstWriteInSubtree
|
|
249
|
+
function findFirstWriteInSubtree
|
|
250
|
+
(ost, letInfo) {
|
|
251
251
|
if (ost) {
|
|
252
252
|
if (ost.astNode.type == 'FunctionDeclaration' || ost.astNode.type == 'ArrowFunctionExpression' || ost.astNode.type == 'FunctionExpression')
|
|
253
253
|
return null
|
|
@@ -272,7 +272,8 @@ function findFirstWriteInSubtree(ost, letInfo) {
|
|
|
272
272
|
return null
|
|
273
273
|
}
|
|
274
274
|
|
|
275
|
-
function ostCheck
|
|
275
|
+
function ostCheck
|
|
276
|
+
(ost, context) {
|
|
276
277
|
if (ost) {
|
|
277
278
|
for (let letInfo of ost.lets)
|
|
278
279
|
if (letInfo.firstWrite)
|
|
@@ -283,7 +284,8 @@ function ostCheck(ost, context) {
|
|
|
283
284
|
}
|
|
284
285
|
}
|
|
285
286
|
|
|
286
|
-
function walk2Start
|
|
287
|
+
function walk2Start
|
|
288
|
+
(node, letInfo, context) {
|
|
287
289
|
if (node.astNode.type == 'FunctionDeclaration')
|
|
288
290
|
for (let child of node.children)
|
|
289
291
|
if (child.astNode.type == 'BlockStatement')
|
|
@@ -291,7 +293,8 @@ function walk2Start(node, letInfo, context) {
|
|
|
291
293
|
return walk2(node, letInfo, context, new Set())
|
|
292
294
|
}
|
|
293
295
|
|
|
294
|
-
function walk2
|
|
296
|
+
function walk2
|
|
297
|
+
(node, letInfo, context, visited) {
|
|
295
298
|
if (node) {
|
|
296
299
|
if (node.astNode.type == 'FunctionDeclaration' || node.astNode.type == 'ArrowFunctionExpression' || node.astNode.type == 'FunctionExpression')
|
|
297
300
|
return false
|
|
@@ -300,11 +303,9 @@ function walk2(node, letInfo, context, visited) {
|
|
|
300
303
|
for (let readInfo of node.reads)
|
|
301
304
|
if (readInfo.item.ref.resolved == letInfo.item.variable) {
|
|
302
305
|
errorCount++
|
|
303
|
-
context.report({
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
data: { name: letInfo.item.name }
|
|
307
|
-
})
|
|
306
|
+
context.report({ node: readInfo.item.ref.identifier,
|
|
307
|
+
messageId: 'initBeforeUse',
|
|
308
|
+
data: { name: letInfo.item.name } })
|
|
308
309
|
}
|
|
309
310
|
return true
|
|
310
311
|
}
|
|
@@ -332,11 +333,9 @@ function walk2(node, letInfo, context, visited) {
|
|
|
332
333
|
for (let readInfo of node.reads)
|
|
333
334
|
if (readInfo.item.ref.resolved == letInfo.item.variable) {
|
|
334
335
|
errorCount++
|
|
335
|
-
context.report({
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
data: { name: letInfo.item.name }
|
|
339
|
-
})
|
|
336
|
+
context.report({ node: readInfo.item.ref.identifier,
|
|
337
|
+
messageId: 'initBeforeUse',
|
|
338
|
+
data: { name: letInfo.item.name } })
|
|
340
339
|
}
|
|
341
340
|
|
|
342
341
|
for (let child of node.children)
|
|
@@ -375,13 +374,9 @@ function ostString
|
|
|
375
374
|
return ''
|
|
376
375
|
}
|
|
377
376
|
|
|
378
|
-
export default {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
schema: []
|
|
385
|
-
},
|
|
386
|
-
create: createInitBeforeUse
|
|
387
|
-
}
|
|
377
|
+
export default { meta: { type: 'problem',
|
|
378
|
+
docs: { description: 'Warn when a variable is used before being explicitly initialized.' },
|
|
379
|
+
messages: { initBeforeUse: "'{{name}}' used before initialization.",
|
|
380
|
+
mustInit: "'{{name}}' must be initialized." },
|
|
381
|
+
schema: [] },
|
|
382
|
+
create: createInitBeforeUse }
|