@@ -20,31 +20,40 @@ module.exports = {
2020 destructuring :
2121 'Destructuring the `props` will cause the value to lose reactivity.' ,
2222 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.'
2424 }
2525 } ,
2626 /** @param {RuleContext } context */
2727 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> } */
2934 const setupScopePropsReferenceIds = new Map ( )
3035
3136 /**
3237 * @param {ESNode } node
3338 * @param {string } messageId
39+ * @param {string } scopeName
3440 */
35- function report ( node , messageId ) {
41+ function report ( node , messageId , scopeName ) {
3642 context . report ( {
3743 node,
38- messageId
44+ messageId,
45+ data : {
46+ scopeName
47+ }
3948 } )
4049 }
4150
4251 /**
4352 * @param {Pattern } left
4453 * @param {Expression | null } right
45- * @param {Set<Identifier> } propsReferenceIds
54+ * @param {ScopePropsReferences } propsReferences
4655 */
47- function verify ( left , right , propsReferenceIds ) {
56+ function verify ( left , right , propsReferences ) {
4857 if ( ! right ) {
4958 return
5059 }
@@ -62,88 +71,142 @@ module.exports = {
6271 while ( rightId . type === 'MemberExpression' ) {
6372 rightId = utils . skipChainExpression ( rightId . object )
6473 }
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 )
6776 }
6877 }
6978 /**
7079 * @typedef {object } ScopeStack
7180 * @property {ScopeStack | null } upper
72- * @property {FunctionDeclaration | FunctionExpression | ArrowFunctionExpression } functionNode
81+ * @property {FunctionDeclaration | FunctionExpression | ArrowFunctionExpression | Program } scopeNode
7382 */
7483 /**
7584 * @type {ScopeStack | null }
7685 */
7786 let scopeStack = null
7887
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+ }
103110
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
107119 }
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
112137 }
138+ } ,
139+ /**
140+ * @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression | Program } node
141+ */
142+ 'Program, :function:exit' ( node ) {
143+ scopeStack = scopeStack && scopeStack . upper
113144
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 )
127176 }
128- verify ( node . id , node . init , propsReferenceIds )
129177 } ,
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>' )
133202 }
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()' )
139208 }
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+ )
148211 }
149212}
0 commit comments