@@ -46,7 +46,7 @@ module.exports = {
46
46
fixable : null ,
47
47
schema : [ ] ,
48
48
messages : {
49
- forbidden : 'The `expose` after `await` expression are forbidden .'
49
+ forbidden : '`{{name}}` is forbidden after an `await` expression.'
50
50
}
51
51
} ,
52
52
/** @param {RuleContext } context */
@@ -55,147 +55,192 @@ module.exports = {
55
55
* @typedef {object } SetupScopeData
56
56
* @property {boolean } afterAwait
57
57
* @property {[number,number] } range
58
- * @property {Set< Identifier> } exposeReferenceIds
59
- * @property {Set< Identifier> } contextReferenceIds
58
+ * @property {(node: Identifier, callNode: CallExpression) => boolean } isExposeReferenceId
59
+ * @property {(node: Identifier) => boolean } isContextReferenceId
60
60
*/
61
61
/**
62
62
* @typedef {object } ScopeStack
63
63
* @property {ScopeStack | null } upper
64
- * @property {FunctionDeclaration | FunctionExpression | ArrowFunctionExpression } scopeNode
64
+ * @property {FunctionDeclaration | FunctionExpression | ArrowFunctionExpression | Program } scopeNode
65
65
*/
66
- /** @type {Map<FunctionDeclaration | FunctionExpression | ArrowFunctionExpression, SetupScopeData> } */
66
+ /** @type {Map<FunctionDeclaration | FunctionExpression | ArrowFunctionExpression | Program , SetupScopeData> } */
67
67
const setupScopes = new Map ( )
68
68
69
69
/** @type {ScopeStack | null } */
70
70
let scopeStack = null
71
71
72
- return utils . defineVueVisitor ( context , {
73
- onSetupFunctionEnter ( node ) {
74
- const contextParam = node . params [ 1 ]
75
- if ( ! contextParam ) {
76
- // no arguments
77
- return
78
- }
79
- if ( contextParam . type === 'RestElement' ) {
80
- // cannot check
81
- return
82
- }
83
- if ( contextParam . type === 'ArrayPattern' ) {
84
- // cannot check
85
- return
72
+ return utils . compositingVisitors (
73
+ {
74
+ /**
75
+ * @param {Program } node
76
+ */
77
+ Program ( node ) {
78
+ scopeStack = {
79
+ upper : scopeStack ,
80
+ scopeNode : node
81
+ }
86
82
}
87
- /** @type {Set<Identifier> } */
88
- const contextReferenceIds = new Set ( )
89
- /** @type {Set<Identifier> } */
90
- const exposeReferenceIds = new Set ( )
91
- if ( contextParam . type === 'ObjectPattern' ) {
92
- const exposeProperty = utils . findAssignmentProperty (
93
- contextParam ,
94
- 'expose'
95
- )
96
- if ( ! exposeProperty ) {
97
- return
83
+ } ,
84
+ {
85
+ /**
86
+ * @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression } node
87
+ */
88
+ ':function' ( node ) {
89
+ scopeStack = {
90
+ upper : scopeStack ,
91
+ scopeNode : node
98
92
}
99
- const exposeParam = exposeProperty . value
100
- // `setup(props, {emit})`
101
- const variable =
102
- exposeParam . type === 'Identifier'
103
- ? findVariable ( context . getScope ( ) , exposeParam )
104
- : null
105
- if ( ! variable ) {
93
+ } ,
94
+ ':function:exit' ( ) {
95
+ scopeStack = scopeStack && scopeStack . upper
96
+ } ,
97
+ /** @param { AwaitExpression } node */
98
+ AwaitExpression ( node ) {
99
+ if ( ! scopeStack ) {
106
100
return
107
101
}
108
- for ( const reference of variable . references ) {
109
- if ( ! reference . isRead ( ) ) {
110
- continue
111
- }
112
- exposeReferenceIds . add ( reference . identifier )
102
+ const setupScope = setupScopes . get ( scopeStack . scopeNode )
103
+ if ( ! setupScope || ! utils . inRange ( setupScope . range , node ) ) {
104
+ return
113
105
}
114
- } else if ( contextParam . type === 'Identifier' ) {
115
- // `setup(props, context)`
116
- const variable = findVariable ( context . getScope ( ) , contextParam )
117
- if ( ! variable ) {
106
+ setupScope . afterAwait = true
107
+ } ,
108
+ /** @param {CallExpression } node */
109
+ CallExpression ( node ) {
110
+ if ( ! scopeStack ) {
118
111
return
119
112
}
120
- for ( const reference of variable . references ) {
121
- if ( ! reference . isRead ( ) ) {
122
- continue
123
- }
124
- contextReferenceIds . add ( reference . identifier )
113
+ const setupScope = setupScopes . get ( scopeStack . scopeNode )
114
+ if (
115
+ ! setupScope ||
116
+ ! setupScope . afterAwait ||
117
+ ! utils . inRange ( setupScope . range , node )
118
+ ) {
119
+ return
125
120
}
126
- }
127
- setupScopes . set ( node , {
128
- afterAwait : false ,
129
- range : node . range ,
130
- exposeReferenceIds,
131
- contextReferenceIds
132
- } )
133
- } ,
134
- /**
135
- * @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression } node
136
- */
137
- ':function' ( node ) {
138
- scopeStack = {
139
- upper : scopeStack ,
140
- scopeNode : node
141
- }
142
- } ,
143
- ':function:exit' ( ) {
144
- scopeStack = scopeStack && scopeStack . upper
145
- } ,
146
- /** @param {AwaitExpression } node */
147
- AwaitExpression ( node ) {
148
- if ( ! scopeStack ) {
149
- return
150
- }
151
- const setupScope = setupScopes . get ( scopeStack . scopeNode )
152
- if ( ! setupScope || ! utils . inRange ( setupScope . range , node ) ) {
153
- return
154
- }
155
- setupScope . afterAwait = true
156
- } ,
157
- /** @param {CallExpression } node */
158
- CallExpression ( node ) {
159
- if ( ! scopeStack ) {
160
- return
161
- }
162
- const setupScope = setupScopes . get ( scopeStack . scopeNode )
163
- if (
164
- ! setupScope ||
165
- ! setupScope . afterAwait ||
166
- ! utils . inRange ( setupScope . range , node )
167
- ) {
168
- return
169
- }
170
- const { contextReferenceIds, exposeReferenceIds } = setupScope
171
- if (
172
- node . callee . type === 'Identifier' &&
173
- exposeReferenceIds . has ( node . callee )
174
- ) {
175
- // setup(props,{expose}) {expose()}
176
- context . report ( {
177
- node,
178
- messageId : 'forbidden'
179
- } )
180
- } else {
181
- const expose = getCalleeMemberNode ( node )
121
+ const { isContextReferenceId, isExposeReferenceId } = setupScope
182
122
if (
183
- expose &&
184
- expose . name === 'expose' &&
185
- expose . member . object . type === 'Identifier' &&
186
- contextReferenceIds . has ( expose . member . object )
123
+ node . callee . type === 'Identifier' &&
124
+ isExposeReferenceId ( node . callee , node )
187
125
) {
188
- // setup(props,context ) {context.emit ()}
126
+ // setup(props,{expose} ) {expose ()}
189
127
context . report ( {
190
128
node,
191
- messageId : 'forbidden'
129
+ messageId : 'forbidden' ,
130
+ data : {
131
+ name : node . callee . name
132
+ }
192
133
} )
134
+ } else {
135
+ const expose = getCalleeMemberNode ( node )
136
+ if (
137
+ expose &&
138
+ expose . name === 'expose' &&
139
+ expose . member . object . type === 'Identifier' &&
140
+ isContextReferenceId ( expose . member . object )
141
+ ) {
142
+ // setup(props,context) {context.emit()}
143
+ context . report ( {
144
+ node,
145
+ messageId : 'forbidden' ,
146
+ data : {
147
+ name : expose . name
148
+ }
149
+ } )
150
+ }
193
151
}
194
152
}
195
153
} ,
196
- onSetupFunctionExit ( node ) {
197
- setupScopes . delete ( node )
198
- }
199
- } )
154
+ ( ( ) => {
155
+ const scriptSetup = utils . getScriptSetupElement ( context )
156
+ if ( ! scriptSetup ) {
157
+ return { }
158
+ }
159
+ return {
160
+ /**
161
+ * @param {Program } node
162
+ */
163
+ Program ( node ) {
164
+ context
165
+ . getScope ( )
166
+ . references . find ( ( ref ) => ref . identifier . name === 'defineExpose' )
167
+ setupScopes . set ( node , {
168
+ afterAwait : false ,
169
+ range : scriptSetup . range ,
170
+ isExposeReferenceId : ( _id , callNode ) =>
171
+ callNode . parent . type === 'ExpressionStatement' &&
172
+ callNode . parent . parent === node ,
173
+ isContextReferenceId : ( ) => false
174
+ } )
175
+ }
176
+ }
177
+ } ) ( ) ,
178
+ utils . defineVueVisitor ( context , {
179
+ onSetupFunctionEnter ( node ) {
180
+ const contextParam = node . params [ 1 ]
181
+ if ( ! contextParam ) {
182
+ // no arguments
183
+ return
184
+ }
185
+ if ( contextParam . type === 'RestElement' ) {
186
+ // cannot check
187
+ return
188
+ }
189
+ if ( contextParam . type === 'ArrayPattern' ) {
190
+ // cannot check
191
+ return
192
+ }
193
+ /** @type {Set<Identifier> } */
194
+ const contextReferenceIds = new Set ( )
195
+ /** @type {Set<Identifier> } */
196
+ const exposeReferenceIds = new Set ( )
197
+ if ( contextParam . type === 'ObjectPattern' ) {
198
+ const exposeProperty = utils . findAssignmentProperty (
199
+ contextParam ,
200
+ 'expose'
201
+ )
202
+ if ( ! exposeProperty ) {
203
+ return
204
+ }
205
+ const exposeParam = exposeProperty . value
206
+ // `setup(props, {emit})`
207
+ const variable =
208
+ exposeParam . type === 'Identifier'
209
+ ? findVariable ( context . getScope ( ) , exposeParam )
210
+ : null
211
+ if ( ! variable ) {
212
+ return
213
+ }
214
+ for ( const reference of variable . references ) {
215
+ if ( ! reference . isRead ( ) ) {
216
+ continue
217
+ }
218
+ exposeReferenceIds . add ( reference . identifier )
219
+ }
220
+ } else if ( contextParam . type === 'Identifier' ) {
221
+ // `setup(props, context)`
222
+ const variable = findVariable ( context . getScope ( ) , contextParam )
223
+ if ( ! variable ) {
224
+ return
225
+ }
226
+ for ( const reference of variable . references ) {
227
+ if ( ! reference . isRead ( ) ) {
228
+ continue
229
+ }
230
+ contextReferenceIds . add ( reference . identifier )
231
+ }
232
+ }
233
+ setupScopes . set ( node , {
234
+ afterAwait : false ,
235
+ range : node . range ,
236
+ isExposeReferenceId : ( id ) => exposeReferenceIds . has ( id ) ,
237
+ isContextReferenceId : ( id ) => contextReferenceIds . has ( id )
238
+ } )
239
+ } ,
240
+ onSetupFunctionExit ( node ) {
241
+ setupScopes . delete ( node )
242
+ }
243
+ } )
244
+ )
200
245
}
201
246
}
0 commit comments