@@ -20,31 +20,40 @@ module.exports = {
20
20
destructuring :
21
21
'Destructuring the `props` will cause the value to lose reactivity.' ,
22
22
getProperty :
23
- 'Getting a value from the `props` in root scope of `setup() ` will cause the value to lose reactivity.'
23
+ 'Getting a value from the `props` in root scope of `{{scopeName}} ` will cause the value to lose reactivity.'
24
24
}
25
25
} ,
26
26
/** @param {RuleContext } context */
27
27
create ( context ) {
28
- /** @type {Map<FunctionDeclaration | FunctionExpression | ArrowFunctionExpression, Set<Identifier>> } */
28
+ /**
29
+ * @typedef {object } ScopePropsReferences
30
+ * @property {Set<Identifier> } refs
31
+ * @property {string } scopeName
32
+ */
33
+ /** @type {Map<FunctionDeclaration | FunctionExpression | ArrowFunctionExpression | Program, ScopePropsReferences> } */
29
34
const setupScopePropsReferenceIds = new Map ( )
30
35
31
36
/**
32
37
* @param {ESNode } node
33
38
* @param {string } messageId
39
+ * @param {string } scopeName
34
40
*/
35
- function report ( node , messageId ) {
41
+ function report ( node , messageId , scopeName ) {
36
42
context . report ( {
37
43
node,
38
- messageId
44
+ messageId,
45
+ data : {
46
+ scopeName
47
+ }
39
48
} )
40
49
}
41
50
42
51
/**
43
52
* @param {Pattern } left
44
53
* @param {Expression | null } right
45
- * @param {Set<Identifier> } propsReferenceIds
54
+ * @param {ScopePropsReferences } propsReferences
46
55
*/
47
- function verify ( left , right , propsReferenceIds ) {
56
+ function verify ( left , right , propsReferences ) {
48
57
if ( ! right ) {
49
58
return
50
59
}
@@ -62,88 +71,142 @@ module.exports = {
62
71
while ( rightId . type === 'MemberExpression' ) {
63
72
rightId = utils . skipChainExpression ( rightId . object )
64
73
}
65
- if ( rightId . type === 'Identifier' && propsReferenceIds . has ( rightId ) ) {
66
- report ( left , 'getProperty' )
74
+ if ( rightId . type === 'Identifier' && propsReferences . refs . has ( rightId ) ) {
75
+ report ( left , 'getProperty' , propsReferences . scopeName )
67
76
}
68
77
}
69
78
/**
70
79
* @typedef {object } ScopeStack
71
80
* @property {ScopeStack | null } upper
72
- * @property {FunctionDeclaration | FunctionExpression | ArrowFunctionExpression } functionNode
81
+ * @property {FunctionDeclaration | FunctionExpression | ArrowFunctionExpression | Program } scopeNode
73
82
*/
74
83
/**
75
84
* @type {ScopeStack | null }
76
85
*/
77
86
let scopeStack = null
78
87
79
- return utils . defineVueVisitor ( context , {
80
- ':function' ( node ) {
81
- scopeStack = {
82
- upper : scopeStack ,
83
- functionNode : node
84
- }
85
- } ,
86
- onSetupFunctionEnter ( node ) {
87
- const propsParam = utils . skipDefaultParamValue ( node . params [ 0 ] )
88
- if ( ! propsParam ) {
89
- // no arguments
90
- return
91
- }
92
- if ( propsParam . type === 'RestElement' ) {
93
- // cannot check
94
- return
95
- }
96
- if (
97
- propsParam . type === 'ArrayPattern' ||
98
- propsParam . type === 'ObjectPattern'
99
- ) {
100
- report ( propsParam , 'destructuring' )
101
- return
102
- }
88
+ /**
89
+ * @param {Pattern | null } node
90
+ * @param {FunctionDeclaration | FunctionExpression | ArrowFunctionExpression | Program } scopeNode
91
+ * @param {string } scopeName
92
+ */
93
+ function processPattern ( node , scopeNode , scopeName ) {
94
+ if ( ! node ) {
95
+ // no arguments
96
+ return
97
+ }
98
+ if (
99
+ node . type === 'RestElement' ||
100
+ node . type === 'AssignmentPattern' ||
101
+ node . type === 'MemberExpression'
102
+ ) {
103
+ // cannot check
104
+ return
105
+ }
106
+ if ( node . type === 'ArrayPattern' || node . type === 'ObjectPattern' ) {
107
+ report ( node , 'destructuring' , scopeName )
108
+ return
109
+ }
103
110
104
- const variable = findVariable ( context . getScope ( ) , propsParam )
105
- if ( ! variable ) {
106
- return
111
+ const variable = findVariable ( context . getScope ( ) , node )
112
+ if ( ! variable ) {
113
+ return
114
+ }
115
+ const propsReferenceIds = new Set ( )
116
+ for ( const reference of variable . references ) {
117
+ if ( ! reference . isRead ( ) ) {
118
+ continue
107
119
}
108
- const propsReferenceIds = new Set ( )
109
- for ( const reference of variable . references ) {
110
- if ( ! reference . isRead ( ) ) {
111
- continue
120
+
121
+ propsReferenceIds . add ( reference . identifier )
122
+ }
123
+ setupScopePropsReferenceIds . set ( scopeNode , {
124
+ refs : propsReferenceIds ,
125
+ scopeName
126
+ } )
127
+ }
128
+ return utils . compositingVisitors (
129
+ {
130
+ /**
131
+ * @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression | Program } node
132
+ */
133
+ 'Program, :function' ( node ) {
134
+ scopeStack = {
135
+ upper : scopeStack ,
136
+ scopeNode : node
112
137
}
138
+ } ,
139
+ /**
140
+ * @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression | Program } node
141
+ */
142
+ 'Program, :function:exit' ( node ) {
143
+ scopeStack = scopeStack && scopeStack . upper
113
144
114
- propsReferenceIds . add ( reference . identifier )
115
- }
116
- setupScopePropsReferenceIds . set ( node , propsReferenceIds )
117
- } ,
118
- VariableDeclarator ( node ) {
119
- if ( ! scopeStack ) {
120
- return
121
- }
122
- const propsReferenceIds = setupScopePropsReferenceIds . get (
123
- scopeStack . functionNode
124
- )
125
- if ( ! propsReferenceIds ) {
126
- return
145
+ setupScopePropsReferenceIds . delete ( node )
146
+ } ,
147
+ /**
148
+ * @param {VariableDeclarator } node
149
+ */
150
+ VariableDeclarator ( node ) {
151
+ if ( ! scopeStack ) {
152
+ return
153
+ }
154
+ const propsReferenceIds = setupScopePropsReferenceIds . get (
155
+ scopeStack . scopeNode
156
+ )
157
+ if ( ! propsReferenceIds ) {
158
+ return
159
+ }
160
+ verify ( node . id , node . init , propsReferenceIds )
161
+ } ,
162
+ /**
163
+ * @param {AssignmentExpression } node
164
+ */
165
+ AssignmentExpression ( node ) {
166
+ if ( ! scopeStack ) {
167
+ return
168
+ }
169
+ const propsReferenceIds = setupScopePropsReferenceIds . get (
170
+ scopeStack . scopeNode
171
+ )
172
+ if ( ! propsReferenceIds ) {
173
+ return
174
+ }
175
+ verify ( node . left , node . right , propsReferenceIds )
127
176
}
128
- verify ( node . id , node . init , propsReferenceIds )
129
177
} ,
130
- AssignmentExpression ( node ) {
131
- if ( ! scopeStack ) {
132
- return
178
+ utils . defineScriptSetupVisitor ( context , {
179
+ onDefinePropsEnter ( node ) {
180
+ let target = node
181
+ if (
182
+ target . parent &&
183
+ target . parent . type === 'CallExpression' &&
184
+ target . parent . arguments [ 0 ] === target &&
185
+ target . parent . callee . type === 'Identifier' &&
186
+ target . parent . callee . name === 'withDefaults'
187
+ ) {
188
+ target = target . parent
189
+ }
190
+ if ( ! target . parent ) {
191
+ return
192
+ }
193
+
194
+ /** @type {Pattern|null } */
195
+ let id = null
196
+ if ( target . parent . type === 'VariableDeclarator' ) {
197
+ id = target . parent . init === target ? target . parent . id : null
198
+ } else if ( target . parent . type === 'AssignmentExpression' ) {
199
+ id = target . parent . right === target ? target . parent . left : null
200
+ }
201
+ processPattern ( id , context . getSourceCode ( ) . ast , '<script setup>' )
133
202
}
134
- const propsReferenceIds = setupScopePropsReferenceIds . get (
135
- scopeStack . functionNode
136
- )
137
- if ( ! propsReferenceIds ) {
138
- return
203
+ } ) ,
204
+ utils . defineVueVisitor ( context , {
205
+ onSetupFunctionEnter ( node ) {
206
+ const propsParam = utils . skipDefaultParamValue ( node . params [ 0 ] )
207
+ processPattern ( propsParam , node , 'setup()' )
139
208
}
140
- verify ( node . left , node . right , propsReferenceIds )
141
- } ,
142
- ':function:exit' ( node ) {
143
- scopeStack = scopeStack && scopeStack . upper
144
-
145
- setupScopePropsReferenceIds . delete ( node )
146
- }
147
- } )
209
+ } )
210
+ )
148
211
}
149
212
}
0 commit comments