Skip to content

Commit a744a9d

Browse files
authored
Fixed crash in vue/no-async-in-computed-properties, vue/no-setup-props-destructure and vue/no-watch-after-await (#1227)
1 parent 2e75ba6 commit a744a9d

15 files changed

+179
-82
lines changed

lib/rules/no-async-in-computed-properties.js

+17-5
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,13 @@ module.exports = {
8080
create(context) {
8181
/**
8282
* @typedef {object} ScopeStack
83-
* @property {ScopeStack} upper
83+
* @property {ScopeStack | null} upper
8484
* @property {BlockStatement | Expression} body
8585
*/
8686
/** @type {Map<ObjectExpression, ComponentComputedProperty[]>} */
8787
const computedPropertiesMap = new Map()
88-
/** @type {ScopeStack} */
89-
let scopeStack
88+
/** @type {ScopeStack | null} */
89+
let scopeStack = null
9090

9191
const expressionTypes = {
9292
promise: 'asynchronous action',
@@ -105,11 +105,14 @@ module.exports = {
105105
verify(node, node.body, 'async', computedPropertiesMap.get(vueNode))
106106
}
107107

108-
scopeStack = { upper: scopeStack, body: node.body }
108+
scopeStack = {
109+
upper: scopeStack,
110+
body: node.body
111+
}
109112
}
110113

111114
function onFunctionExit() {
112-
scopeStack = scopeStack.upper
115+
scopeStack = scopeStack && scopeStack.upper
113116
}
114117

115118
/**
@@ -146,6 +149,9 @@ module.exports = {
146149
':function:exit': onFunctionExit,
147150

148151
NewExpression(node, { node: vueNode }) {
152+
if (!scopeStack) {
153+
return
154+
}
149155
if (
150156
node.callee.type === 'Identifier' &&
151157
node.callee.name === 'Promise'
@@ -160,6 +166,9 @@ module.exports = {
160166
},
161167

162168
CallExpression(node, { node: vueNode }) {
169+
if (!scopeStack) {
170+
return
171+
}
163172
if (isPromise(node)) {
164173
verify(
165174
node,
@@ -178,6 +187,9 @@ module.exports = {
178187
},
179188

180189
AwaitExpression(node, { node: vueNode }) {
190+
if (!scopeStack) {
191+
return
192+
}
181193
verify(
182194
node,
183195
scopeStack.body,

lib/rules/no-bare-strings-in-template.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ module.exports = {
160160
/** @param {RuleContext} context */
161161
create(context) {
162162
/**
163-
* @typedef { { upper: ElementStack, name: string, attrs: Set<string> } } ElementStack
163+
* @typedef { { upper: ElementStack | null, name: string, attrs: Set<string> } } ElementStack
164164
*/
165165
const opts = context.options[0] || {}
166166
/** @type {string[]} */
@@ -173,8 +173,8 @@ module.exports = {
173173
'gu'
174174
)
175175

176-
/** @type {ElementStack} */
177-
let elementStack
176+
/** @type {ElementStack | null} */
177+
let elementStack = null
178178
/**
179179
* Gets the bare string from given string
180180
* @param {string} str
@@ -231,11 +231,11 @@ module.exports = {
231231
}
232232
},
233233
'VElement:exit'() {
234-
elementStack = elementStack.upper
234+
elementStack = elementStack && elementStack.upper
235235
},
236236
/** @param {VAttribute|VDirective} node */
237237
VAttribute(node) {
238-
if (!node.value) {
238+
if (!node.value || !elementStack) {
239239
return
240240
}
241241
if (node.directive === false) {

lib/rules/no-lifecycle-after-await.js

+16-11
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,16 @@ module.exports = {
4747
*/
4848
/**
4949
* @typedef {object} ScopeStack
50-
* @property {ScopeStack} upper
50+
* @property {ScopeStack | null} upper
5151
* @property {FunctionDeclaration | FunctionExpression | ArrowFunctionExpression} functionNode
5252
*/
5353
/** @type {Set<ESNode>} */
5454
const lifecycleHookCallNodes = new Set()
5555
/** @type {Map<FunctionDeclaration | FunctionExpression | ArrowFunctionExpression, SetupFunctionData>} */
5656
const setupFunctions = new Map()
5757

58-
/** @type {ScopeStack} */
59-
let scopeStack
58+
/** @type {ScopeStack | null} */
59+
let scopeStack = null
6060

6161
return Object.assign(
6262
{
@@ -81,7 +81,10 @@ module.exports = {
8181
},
8282
utils.defineVueVisitor(context, {
8383
':function'(node) {
84-
scopeStack = { upper: scopeStack, functionNode: node }
84+
scopeStack = {
85+
upper: scopeStack,
86+
functionNode: node
87+
}
8588
},
8689
onSetupFunctionEnter(node) {
8790
setupFunctions.set(node, {
@@ -90,18 +93,20 @@ module.exports = {
9093
})
9194
},
9295
AwaitExpression() {
93-
const setupFunctionData = setupFunctions.get(
94-
scopeStack && scopeStack.functionNode
95-
)
96+
if (!scopeStack) {
97+
return
98+
}
99+
const setupFunctionData = setupFunctions.get(scopeStack.functionNode)
96100
if (!setupFunctionData) {
97101
return
98102
}
99103
setupFunctionData.afterAwait = true
100104
},
101105
CallExpression(node) {
102-
const setupFunctionData = setupFunctions.get(
103-
scopeStack && scopeStack.functionNode
104-
)
106+
if (!scopeStack) {
107+
return
108+
}
109+
const setupFunctionData = setupFunctions.get(scopeStack.functionNode)
105110
if (!setupFunctionData || !setupFunctionData.afterAwait) {
106111
return
107112
}
@@ -118,7 +123,7 @@ module.exports = {
118123
}
119124
},
120125
':function:exit'(node) {
121-
scopeStack = scopeStack.upper
126+
scopeStack = scopeStack && scopeStack.upper
122127

123128
setupFunctions.delete(node)
124129
}

lib/rules/no-setup-props-destructure.js

+14-5
Original file line numberDiff line numberDiff line change
@@ -68,17 +68,20 @@ module.exports = {
6868
}
6969
/**
7070
* @typedef {object} ScopeStack
71-
* @property {ScopeStack} upper
71+
* @property {ScopeStack | null} upper
7272
* @property {FunctionDeclaration | FunctionExpression | ArrowFunctionExpression} functionNode
7373
*/
7474
/**
75-
* @type {ScopeStack}
75+
* @type {ScopeStack | null}
7676
*/
77-
let scopeStack
77+
let scopeStack = null
7878

7979
return utils.defineVueVisitor(context, {
8080
':function'(node) {
81-
scopeStack = { upper: scopeStack, functionNode: node }
81+
scopeStack = {
82+
upper: scopeStack,
83+
functionNode: node
84+
}
8285
},
8386
onSetupFunctionEnter(node) {
8487
const propsParam = utils.unwrapAssignmentPattern(node.params[0])
@@ -113,6 +116,9 @@ module.exports = {
113116
setupScopePropsReferenceIds.set(node, propsReferenceIds)
114117
},
115118
VariableDeclarator(node) {
119+
if (!scopeStack) {
120+
return
121+
}
116122
const propsReferenceIds = setupScopePropsReferenceIds.get(
117123
scopeStack.functionNode
118124
)
@@ -122,6 +128,9 @@ module.exports = {
122128
verify(node.id, node.init, propsReferenceIds)
123129
},
124130
AssignmentExpression(node) {
131+
if (!scopeStack) {
132+
return
133+
}
125134
const propsReferenceIds = setupScopePropsReferenceIds.get(
126135
scopeStack.functionNode
127136
)
@@ -131,7 +140,7 @@ module.exports = {
131140
verify(node.left, node.right, propsReferenceIds)
132141
},
133142
':function:exit'(node) {
134-
scopeStack = scopeStack.upper
143+
scopeStack = scopeStack && scopeStack.upper
135144

136145
setupScopePropsReferenceIds.delete(node)
137146
}

lib/rules/no-side-effects-in-computed-properties.js

+17-4
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,26 @@ module.exports = {
3232
/** @type {Map<ObjectExpression, ComponentComputedProperty[]>} */
3333
const computedPropertiesMap = new Map()
3434

35-
/** @type { { upper: any, body: null | BlockStatement | Expression } } */
36-
let scopeStack = { upper: null, body: null }
35+
/**
36+
* @typedef {object} ScopeStack
37+
* @property {ScopeStack | null} upper
38+
* @property {BlockStatement | Expression | null} body
39+
*/
40+
/**
41+
* @type {ScopeStack | null}
42+
*/
43+
let scopeStack = null
3744

3845
/** @param {FunctionExpression | ArrowFunctionExpression | FunctionDeclaration} node */
3946
function onFunctionEnter(node) {
40-
scopeStack = { upper: scopeStack, body: node.body }
47+
scopeStack = {
48+
upper: scopeStack,
49+
body: node.body
50+
}
4151
}
4252

4353
function onFunctionExit() {
44-
scopeStack = scopeStack.upper
54+
scopeStack = scopeStack && scopeStack.upper
4555
}
4656

4757
return utils.defineVueVisitor(context, {
@@ -59,6 +69,9 @@ module.exports = {
5969
node,
6070
{ node: vueNode }
6171
) {
72+
if (!scopeStack) {
73+
return
74+
}
6275
const targetBody = scopeStack.body
6376
const computedProperty = /** @type {ComponentComputedProperty[]} */ (computedPropertiesMap.get(
6477
vueNode

lib/rules/no-template-shadow.js

+10-10
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,11 @@ module.exports = {
4040

4141
/**
4242
* @typedef {object} ScopeStack
43-
* @property {ScopeStack} parent
43+
* @property {ScopeStack | null} parent
4444
* @property {Identifier[]} nodes
4545
*/
46-
/** @type {ScopeStack} */
47-
let scope
46+
/** @type {ScopeStack | null} */
47+
let scopeStack = null
4848

4949
// ----------------------------------------------------------------------
5050
// Public
@@ -55,18 +55,18 @@ module.exports = {
5555
{
5656
/** @param {VElement} node */
5757
VElement(node) {
58-
scope = {
59-
parent: scope,
60-
nodes: scope
61-
? scope.nodes.slice() // make copy
58+
scopeStack = {
59+
parent: scopeStack,
60+
nodes: scopeStack
61+
? scopeStack.nodes.slice() // make copy
6262
: []
6363
}
6464
if (node.variables) {
6565
for (const variable of node.variables) {
6666
const varNode = variable.id
6767
const name = varNode.name
6868
if (
69-
scope.nodes.some((node) => node.name === name) ||
69+
scopeStack.nodes.some((node) => node.name === name) ||
7070
jsVars.has(name)
7171
) {
7272
context.report({
@@ -79,13 +79,13 @@ module.exports = {
7979
}
8080
})
8181
} else {
82-
scope.nodes.push(varNode)
82+
scopeStack.nodes.push(varNode)
8383
}
8484
}
8585
}
8686
},
8787
'VElement:exit'() {
88-
scope = scope.parent
88+
scopeStack = scopeStack && scopeStack.parent
8989
}
9090
},
9191
utils.executeOnVue(context, (obj) => {

lib/rules/no-watch-after-await.js

+14-5
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,11 @@ module.exports = {
5858

5959
/**
6060
* @typedef {object} ScopeStack
61-
* @property {ScopeStack} upper
61+
* @property {ScopeStack | null} upper
6262
* @property {FunctionExpression | ArrowFunctionExpression | FunctionDeclaration} functionNode
6363
*/
64-
/** @type {ScopeStack} */
65-
let scopeStack
64+
/** @type {ScopeStack | null} */
65+
let scopeStack = null
6666

6767
return Object.assign(
6868
{
@@ -88,7 +88,10 @@ module.exports = {
8888
utils.defineVueVisitor(context, {
8989
/** @param {FunctionExpression | ArrowFunctionExpression | FunctionDeclaration} node */
9090
':function'(node) {
91-
scopeStack = { upper: scopeStack, functionNode: node }
91+
scopeStack = {
92+
upper: scopeStack,
93+
functionNode: node
94+
}
9295
},
9396
onSetupFunctionEnter(node) {
9497
setupFunctions.set(node, {
@@ -97,6 +100,9 @@ module.exports = {
97100
})
98101
},
99102
AwaitExpression() {
103+
if (!scopeStack) {
104+
return
105+
}
100106
const setupFunctionData = setupFunctions.get(scopeStack.functionNode)
101107
if (!setupFunctionData) {
102108
return
@@ -105,6 +111,9 @@ module.exports = {
105111
},
106112
/** @param {CallExpression} node */
107113
CallExpression(node) {
114+
if (!scopeStack) {
115+
return
116+
}
108117
const setupFunctionData = setupFunctions.get(scopeStack.functionNode)
109118
if (!setupFunctionData || !setupFunctionData.afterAwait) {
110119
return
@@ -119,7 +128,7 @@ module.exports = {
119128
},
120129
/** @param {FunctionExpression | ArrowFunctionExpression | FunctionDeclaration} node */
121130
':function:exit'(node) {
122-
scopeStack = scopeStack.upper
131+
scopeStack = scopeStack && scopeStack.upper
123132

124133
setupFunctions.delete(node)
125134
}

lib/rules/require-direct-export.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,14 @@ module.exports = {
3939

4040
/**
4141
* @typedef {object} ScopeStack
42-
* @property {ScopeStack} upper
42+
* @property {ScopeStack | null} upper
4343
* @property {boolean} withinVue3FunctionalBody
4444
*/
4545

4646
/** @type { { body: BlockStatement, hasReturnArgument: boolean } } */
4747
let maybeVue3Functional
48-
/** @type {ScopeStack} */
49-
let scopeStack
48+
/** @type {ScopeStack | null} */
49+
let scopeStack = null
5050

5151
return {
5252
/** @param {Declaration | Expression} node */

0 commit comments

Comments
 (0)