44 */
55'use strict'
66
7+ const { findVariable } = require ( '@eslint-community/eslint-utils' )
78const { extractRefObjectReferences } = require ( '../utils/ref-object-references' )
89const 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+
2762module . exports = {
2863 meta : {
2964 type : 'suggestion' ,
@@ -44,6 +79,22 @@ module.exports = {
4479 create ( context ) {
4580 /** @type {RefObjectReferences } */
4681 let refReferences
82+ const setupContexts = new Map ( )
83+
84+ /**
85+ * Collect identifier id
86+ * @param {Identifier } node
87+ * @param {Set<Identifier> } referenceIds
88+ */
89+ function collectReferenceIds ( node , referenceIds ) {
90+ const variable = findVariable ( utils . getScope ( context , node ) , node )
91+ if ( ! variable ) {
92+ return
93+ }
94+ for ( const reference of variable . references ) {
95+ referenceIds . add ( reference . identifier )
96+ }
97+ }
4798
4899 /**
49100 * @param {Identifier } node
@@ -64,90 +115,213 @@ module.exports = {
64115 }
65116 } )
66117 }
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 ) {
118+
119+ /**
120+ * @param {CallExpression } node
121+ */
122+ function reportWrappedIdentifiers ( node ) {
123+ const nodes = node . arguments . filter ( ( node ) => node . type === 'Identifier' )
124+ for ( const node of nodes ) {
94125 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 ) {
126+ }
127+ }
128+
129+ const programNode = context . getSourceCode ( ) . ast
130+
131+ const callVisitor = {
132+ /**
133+ * @param {CallExpression } node
134+ * @param {import('../utils').VueObjectData } [info]
135+ */
136+ CallExpression ( node , info ) {
137+ const nameWithLoc = getNameParamNode ( node )
138+ if ( ! nameWithLoc ) {
139+ // cannot check
100140 return
101141 }
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 ) {
142+
143+ // verify setup context
144+ const setupContext = setupContexts . get ( info ? info . node : programNode )
145+ if ( ! setupContext ) {
108146 return
109147 }
110- // Report only constants.
111- const data = refReferences . get ( node )
148+
149+ const { contextReferenceIds , emitReferenceIds } = setupContext
112150 if (
113- ! data ||
114- ! data . variableDeclaration ||
115- data . variableDeclaration . kind !== 'const'
151+ node . callee . type === 'Identifier' &&
152+ emitReferenceIds . has ( node . callee )
116153 ) {
117- return
154+ // verify setup(props,{emit}) {emit()}
155+ reportWrappedIdentifiers ( node )
156+ } else {
157+ const emit = getCalleeMemberNode ( node )
158+ if (
159+ emit &&
160+ emit . name === 'emit' &&
161+ emit . member . object . type === 'Identifier' &&
162+ contextReferenceIds . has ( emit . member . object )
163+ ) {
164+ // verify setup(props,context) {context.emit()}
165+ reportWrappedIdentifiers ( node )
166+ }
118167 }
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
168+ }
169+ }
170+
171+ return utils . compositingVisitors (
172+ {
173+ Program ( ) {
174+ refReferences = extractRefObjectReferences ( context )
175+ } ,
176+ // if (refValue)
177+ /** @param {Identifier } node */
178+ 'IfStatement>Identifier' ( node ) {
179+ reportIfRefWrapped ( node )
180+ } ,
181+ // switch (refValue)
182+ /** @param {Identifier } node */
183+ 'SwitchStatement>Identifier' ( node ) {
184+ reportIfRefWrapped ( node )
185+ } ,
186+ // -refValue, +refValue, !refValue, ~refValue, typeof refValue
187+ /** @param {Identifier } node */
188+ 'UnaryExpression>Identifier' ( node ) {
189+ reportIfRefWrapped ( node )
190+ } ,
191+ // refValue++, refValue--
192+ /** @param {Identifier } node */
193+ 'UpdateExpression>Identifier' ( node ) {
194+ reportIfRefWrapped ( node )
195+ } ,
196+ // refValue+1, refValue-1
197+ /** @param {Identifier } node */
198+ 'BinaryExpression>Identifier' ( node ) {
199+ reportIfRefWrapped ( node )
200+ } ,
201+ // refValue+=1, refValue-=1, foo+=refValue, foo-=refValue
202+ /** @param {Identifier & {parent: AssignmentExpression} } node */
203+ 'AssignmentExpression>Identifier' ( node ) {
204+ if ( node . parent . operator === '=' && node . parent . left !== node ) {
205+ return
206+ }
207+ reportIfRefWrapped ( node )
208+ } ,
209+ // refValue || other, refValue && other. ignore: other || refValue
210+ /** @param {Identifier & {parent: LogicalExpression} } node */
211+ 'LogicalExpression>Identifier' ( node ) {
212+ if ( node . parent . left !== node ) {
213+ return
214+ }
215+ // Report only constants.
216+ const data = refReferences . get ( node )
217+ if (
218+ ! data ||
219+ ! data . variableDeclaration ||
220+ data . variableDeclaration . kind !== 'const'
221+ ) {
222+ return
223+ }
224+ reportIfRefWrapped ( node )
225+ } ,
226+ // refValue ? x : y
227+ /** @param {Identifier & {parent: ConditionalExpression} } node */
228+ 'ConditionalExpression>Identifier' ( node ) {
229+ if ( node . parent . test !== node ) {
230+ return
231+ }
232+ reportIfRefWrapped ( node )
233+ } ,
234+ // `${refValue}`
235+ /** @param {Identifier } node */
236+ 'TemplateLiteral>Identifier' ( node ) {
237+ reportIfRefWrapped ( node )
238+ } ,
239+ // refValue.x
240+ /** @param {Identifier & {parent: MemberExpression} } node */
241+ 'MemberExpression>Identifier' ( node ) {
242+ if ( node . parent . object !== node ) {
243+ return
244+ }
245+ const name = utils . getStaticPropertyName ( node . parent )
246+ if (
247+ name === 'value' ||
248+ name == null ||
249+ // WritableComputedRef
250+ name === 'effect'
251+ ) {
252+ return
253+ }
254+ reportIfRefWrapped ( node )
126255 }
127- reportIfRefWrapped ( node )
128256 } ,
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 ) {
138- return
139- }
140- const name = utils . getStaticPropertyName ( node . parent )
141- if (
142- name === 'value' ||
143- name == null ||
144- // WritableComputedRef
145- name === 'effect'
146- ) {
147- return
257+ utils . defineScriptSetupVisitor ( context , {
258+ onDefineEmitsEnter ( node ) {
259+ if (
260+ ! node . parent ||
261+ node . parent . type !== 'VariableDeclarator' ||
262+ node . parent . init !== node
263+ ) {
264+ return
265+ }
266+
267+ const emitParam = node . parent . id
268+ if ( emitParam . type !== 'Identifier' ) {
269+ return
270+ }
271+
272+ // const emit = defineEmits()
273+ const emitReferenceIds = new Set ( )
274+ collectReferenceIds ( emitParam , emitReferenceIds )
275+
276+ setupContexts . set ( programNode , {
277+ contextReferenceIds : new Set ( ) ,
278+ emitReferenceIds
279+ } )
280+ } ,
281+ ...callVisitor
282+ } ) ,
283+ utils . defineVueVisitor ( context , {
284+ onSetupFunctionEnter ( node , { node : vueNode } ) {
285+ const contextParam = utils . skipDefaultParamValue ( node . params [ 1 ] )
286+ if ( ! contextParam ) {
287+ // no arguments
288+ return
289+ }
290+ if (
291+ contextParam . type === 'RestElement' ||
292+ contextParam . type === 'ArrayPattern'
293+ ) {
294+ // cannot check
295+ return
296+ }
297+
298+ const contextReferenceIds = new Set ( )
299+ const emitReferenceIds = new Set ( )
300+ if ( contextParam . type === 'ObjectPattern' ) {
301+ const emitProperty = utils . findAssignmentProperty (
302+ contextParam ,
303+ 'emit'
304+ )
305+ if ( ! emitProperty || emitProperty . value . type !== 'Identifier' ) {
306+ return
307+ }
308+
309+ // `setup(props, {emit})`
310+ collectReferenceIds ( emitProperty . value , emitReferenceIds )
311+ } else {
312+ // `setup(props, context)`
313+ collectReferenceIds ( contextParam , contextReferenceIds )
314+ }
315+ setupContexts . set ( vueNode , {
316+ contextReferenceIds,
317+ emitReferenceIds
318+ } )
319+ } ,
320+ ...callVisitor ,
321+ onVueObjectExit ( node ) {
322+ setupContexts . delete ( node )
148323 }
149- reportIfRefWrapped ( node )
150- }
151- }
324+ } )
325+ )
152326 }
153327}
0 commit comments