Skip to content

Commit dabbc13

Browse files
committed
feat(no-ref-as-operand): adapt to emit
1 parent 827ab4b commit dabbc13

File tree

1 file changed

+270
-80
lines changed

1 file changed

+270
-80
lines changed

lib/rules/no-ref-as-operand.js

+270-80
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55
'use strict'
66

7+
const { findVariable } = require('@eslint-community/eslint-utils')
78
const { extractRefObjectReferences } = require('../utils/ref-object-references')
89
const utils = require('../utils')
910

@@ -24,6 +25,40 @@ function isRefInit(data) {
2425
}
2526
return data.defineChain.includes(/** @type {any} */ (init))
2627
}
28+
29+
/**
30+
* Get the callee member node from the given CallExpression
31+
* @param {CallExpression} node CallExpression
32+
*/
33+
function getNameParamNode(node) {
34+
const nameLiteralNode = node.arguments[0]
35+
if (nameLiteralNode && utils.isStringLiteral(nameLiteralNode)) {
36+
const name = utils.getStringLiteralValue(nameLiteralNode)
37+
if (name != null) {
38+
return { name, loc: nameLiteralNode.loc }
39+
}
40+
}
41+
42+
// cannot check
43+
return null
44+
}
45+
46+
/**
47+
* Get the callee member node from the given CallExpression
48+
* @param {CallExpression} node CallExpression
49+
*/
50+
function getCalleeMemberNode(node) {
51+
const callee = utils.skipChainExpression(node.callee)
52+
53+
if (callee.type === 'MemberExpression') {
54+
const name = utils.getStaticPropertyName(callee)
55+
if (name) {
56+
return { name, member: callee }
57+
}
58+
}
59+
return null
60+
}
61+
2762
module.exports = {
2863
meta: {
2964
type: 'suggestion',
@@ -44,6 +79,7 @@ module.exports = {
4479
create(context) {
4580
/** @type {RefObjectReferences} */
4681
let refReferences
82+
const setupContexts = new Map()
4783

4884
/**
4985
* @param {Identifier} node
@@ -64,90 +100,244 @@ module.exports = {
64100
}
65101
})
66102
}
67-
return {
68-
Program() {
69-
refReferences = extractRefObjectReferences(context)
70-
},
71-
// if (refValue)
72-
/** @param {Identifier} node */
73-
'IfStatement>Identifier'(node) {
74-
reportIfRefWrapped(node)
75-
},
76-
// switch (refValue)
77-
/** @param {Identifier} node */
78-
'SwitchStatement>Identifier'(node) {
79-
reportIfRefWrapped(node)
80-
},
81-
// -refValue, +refValue, !refValue, ~refValue, typeof refValue
82-
/** @param {Identifier} node */
83-
'UnaryExpression>Identifier'(node) {
84-
reportIfRefWrapped(node)
85-
},
86-
// refValue++, refValue--
87-
/** @param {Identifier} node */
88-
'UpdateExpression>Identifier'(node) {
89-
reportIfRefWrapped(node)
90-
},
91-
// refValue+1, refValue-1
92-
/** @param {Identifier} node */
93-
'BinaryExpression>Identifier'(node) {
94-
reportIfRefWrapped(node)
95-
},
96-
// refValue+=1, refValue-=1, foo+=refValue, foo-=refValue
97-
/** @param {Identifier & {parent: AssignmentExpression}} node */
98-
'AssignmentExpression>Identifier'(node) {
99-
if (node.parent.operator === '=' && node.parent.left !== node) {
100-
return
101-
}
102-
reportIfRefWrapped(node)
103-
},
104-
// refValue || other, refValue && other. ignore: other || refValue
105-
/** @param {Identifier & {parent: LogicalExpression}} node */
106-
'LogicalExpression>Identifier'(node) {
107-
if (node.parent.left !== node) {
108-
return
109-
}
110-
// Report only constants.
111-
const data = refReferences.get(node)
112-
if (
113-
!data ||
114-
!data.variableDeclaration ||
115-
data.variableDeclaration.kind !== 'const'
116-
) {
117-
return
118-
}
119-
reportIfRefWrapped(node)
120-
},
121-
// refValue ? x : y
122-
/** @param {Identifier & {parent: ConditionalExpression}} node */
123-
'ConditionalExpression>Identifier'(node) {
124-
if (node.parent.test !== node) {
125-
return
126-
}
127-
reportIfRefWrapped(node)
128-
},
129-
// `${refValue}`
130-
/** @param {Identifier} node */
131-
'TemplateLiteral>Identifier'(node) {
132-
reportIfRefWrapped(node)
133-
},
134-
// refValue.x
135-
/** @param {Identifier & {parent: MemberExpression}} node */
136-
'MemberExpression>Identifier'(node) {
137-
if (node.parent.object !== node) {
103+
104+
const programNode = context.getSourceCode().ast
105+
106+
const callVisitor = {
107+
/**
108+
* @param {CallExpression} node
109+
* @param {import('../utils').VueObjectData} [info]
110+
*/
111+
CallExpression(node, info) {
112+
const nameWithLoc = getNameParamNode(node)
113+
if (!nameWithLoc) {
114+
// cannot check
138115
return
139116
}
140-
const name = utils.getStaticPropertyName(node.parent)
141-
if (
142-
name === 'value' ||
143-
name == null ||
144-
// WritableComputedRef
145-
name === 'effect'
146-
) {
147-
return
117+
118+
// verify setup context
119+
const setupContext = setupContexts.get(info ? info.node : programNode)
120+
if (setupContext) {
121+
const { contextReferenceIds, emitReferenceIds } = setupContext
122+
if (
123+
node.callee.type === 'Identifier' &&
124+
emitReferenceIds.has(node.callee)
125+
) {
126+
// verify setup(props,{emit}) {emit()}
127+
node.arguments
128+
.filter(
129+
(node) =>
130+
node.type === 'Identifier' &&
131+
isRefInit(refReferences.get(node))
132+
)
133+
.forEach((node) => {
134+
reportIfRefWrapped(node)
135+
})
136+
} else {
137+
const emit = getCalleeMemberNode(node)
138+
if (
139+
emit &&
140+
emit.name === 'emit' &&
141+
emit.member.object.type === 'Identifier' &&
142+
contextReferenceIds.has(emit.member.object)
143+
) {
144+
// verify setup(props,context) {context.emit()}
145+
emit.member.parent.arguments
146+
.filter(
147+
(node) =>
148+
node.type === 'Identifier' &&
149+
isRefInit(refReferences.get(node))
150+
)
151+
.forEach((node) => {
152+
reportIfRefWrapped(node)
153+
})
154+
}
155+
}
148156
}
149-
reportIfRefWrapped(node)
150157
}
151158
}
159+
160+
return utils.compositingVisitors(
161+
{
162+
Program() {
163+
refReferences = extractRefObjectReferences(context)
164+
},
165+
// if (refValue)
166+
/** @param {Identifier} node */
167+
'IfStatement>Identifier'(node) {
168+
reportIfRefWrapped(node)
169+
},
170+
// switch (refValue)
171+
/** @param {Identifier} node */
172+
'SwitchStatement>Identifier'(node) {
173+
reportIfRefWrapped(node)
174+
},
175+
// -refValue, +refValue, !refValue, ~refValue, typeof refValue
176+
/** @param {Identifier} node */
177+
'UnaryExpression>Identifier'(node) {
178+
reportIfRefWrapped(node)
179+
},
180+
// refValue++, refValue--
181+
/** @param {Identifier} node */
182+
'UpdateExpression>Identifier'(node) {
183+
reportIfRefWrapped(node)
184+
},
185+
// refValue+1, refValue-1
186+
/** @param {Identifier} node */
187+
'BinaryExpression>Identifier'(node) {
188+
reportIfRefWrapped(node)
189+
},
190+
// refValue+=1, refValue-=1, foo+=refValue, foo-=refValue
191+
/** @param {Identifier & {parent: AssignmentExpression}} node */
192+
'AssignmentExpression>Identifier'(node) {
193+
if (node.parent.operator === '=' && node.parent.left !== node) {
194+
return
195+
}
196+
reportIfRefWrapped(node)
197+
},
198+
// refValue || other, refValue && other. ignore: other || refValue
199+
/** @param {Identifier & {parent: LogicalExpression}} node */
200+
'LogicalExpression>Identifier'(node) {
201+
if (node.parent.left !== node) {
202+
return
203+
}
204+
// Report only constants.
205+
const data = refReferences.get(node)
206+
if (
207+
!data ||
208+
!data.variableDeclaration ||
209+
data.variableDeclaration.kind !== 'const'
210+
) {
211+
return
212+
}
213+
reportIfRefWrapped(node)
214+
},
215+
// refValue ? x : y
216+
/** @param {Identifier & {parent: ConditionalExpression}} node */
217+
'ConditionalExpression>Identifier'(node) {
218+
if (node.parent.test !== node) {
219+
return
220+
}
221+
reportIfRefWrapped(node)
222+
},
223+
// `${refValue}`
224+
/** @param {Identifier} node */
225+
'TemplateLiteral>Identifier'(node) {
226+
reportIfRefWrapped(node)
227+
},
228+
// refValue.x
229+
/** @param {Identifier & {parent: MemberExpression}} node */
230+
'MemberExpression>Identifier'(node) {
231+
if (node.parent.object !== node) {
232+
return
233+
}
234+
const name = utils.getStaticPropertyName(node.parent)
235+
if (
236+
name === 'value' ||
237+
name == null ||
238+
// WritableComputedRef
239+
name === 'effect'
240+
) {
241+
return
242+
}
243+
reportIfRefWrapped(node)
244+
}
245+
},
246+
utils.compositingVisitors(
247+
utils.defineScriptSetupVisitor(context, {
248+
onDefineEmitsEnter(node) {
249+
if (
250+
!node.parent ||
251+
node.parent.type !== 'VariableDeclarator' ||
252+
node.parent.init !== node
253+
) {
254+
return
255+
}
256+
257+
const emitParam = node.parent.id
258+
if (emitParam.type !== 'Identifier') {
259+
return
260+
}
261+
262+
// const emit = defineEmits()
263+
const variable = findVariable(
264+
utils.getScope(context, emitParam),
265+
emitParam
266+
)
267+
if (!variable) {
268+
return
269+
}
270+
const emitReferenceIds = new Set()
271+
for (const reference of variable.references) {
272+
emitReferenceIds.add(reference.identifier)
273+
}
274+
setupContexts.set(programNode, {
275+
contextReferenceIds: new Set(),
276+
emitReferenceIds
277+
})
278+
},
279+
...callVisitor
280+
}),
281+
utils.defineVueVisitor(context, {
282+
onSetupFunctionEnter(node, { node: vueNode }) {
283+
const contextParam = utils.skipDefaultParamValue(node.params[1])
284+
if (!contextParam) {
285+
// no arguments
286+
return
287+
}
288+
if (
289+
contextParam.type === 'RestElement' ||
290+
contextParam.type === 'ArrayPattern'
291+
) {
292+
// cannot check
293+
return
294+
}
295+
const contextReferenceIds = new Set()
296+
const emitReferenceIds = new Set()
297+
if (contextParam.type === 'ObjectPattern') {
298+
const emitProperty = utils.findAssignmentProperty(
299+
contextParam,
300+
'emit'
301+
)
302+
if (!emitProperty || emitProperty.value.type !== 'Identifier') {
303+
return
304+
}
305+
const emitParam = emitProperty.value
306+
// `setup(props, {emit})`
307+
const variable = findVariable(
308+
utils.getScope(context, emitParam),
309+
emitParam
310+
)
311+
if (!variable) {
312+
return
313+
}
314+
for (const reference of variable.references) {
315+
emitReferenceIds.add(reference.identifier)
316+
}
317+
} else {
318+
// `setup(props, context)`
319+
const variable = findVariable(
320+
utils.getScope(context, contextParam),
321+
contextParam
322+
)
323+
if (!variable) {
324+
return
325+
}
326+
for (const reference of variable.references) {
327+
contextReferenceIds.add(reference.identifier)
328+
}
329+
}
330+
setupContexts.set(vueNode, {
331+
contextReferenceIds,
332+
emitReferenceIds
333+
})
334+
},
335+
...callVisitor,
336+
onVueObjectExit(node) {
337+
setupContexts.delete(node)
338+
}
339+
})
340+
)
341+
)
152342
}
153343
}

0 commit comments

Comments
 (0)