4
4
*/
5
5
'use strict'
6
6
7
+ const path = require ( 'path' )
7
8
const utils = require ( '../utils' )
8
9
const casing = require ( '../utils/casing' )
9
10
@@ -17,6 +18,93 @@ function camelize(str) {
17
18
return str . replace ( / - ( \w ) / g, ( _ , c ) => ( c ? c . toUpperCase ( ) : '' ) )
18
19
}
19
20
21
+ class DefinedInSetupComponents {
22
+ constructor ( ) {
23
+ /**
24
+ * Component names
25
+ * @type {Set<string> }
26
+ */
27
+ this . names = new Set ( )
28
+ }
29
+
30
+ /**
31
+ * @param {string[] } names
32
+ */
33
+ addName ( ...names ) {
34
+ for ( const name of names ) {
35
+ this . names . add ( name )
36
+ }
37
+ }
38
+
39
+ /**
40
+ * @see https://github.com/vuejs/core/blob/ae4b0783d78670b6e942ae2a4e3ec6efbbffa158/packages/compiler-core/src/transforms/transformElement.ts#L334
41
+ * @param {string } rawName
42
+ */
43
+ isDefinedComponent ( rawName ) {
44
+ if ( this . names . has ( rawName ) ) {
45
+ return true
46
+ }
47
+ const camelName = camelize ( rawName )
48
+ if ( this . names . has ( camelName ) ) {
49
+ return true
50
+ }
51
+ const pascalName = casing . capitalize ( camelName )
52
+ if ( this . names . has ( pascalName ) ) {
53
+ return true
54
+ }
55
+ // Check namespace
56
+ // https://github.com/vuejs/core/blob/ae4b0783d78670b6e942ae2a4e3ec6efbbffa158/packages/compiler-core/src/transforms/transformElement.ts#L305
57
+ const dotIndex = rawName . indexOf ( '.' )
58
+ if ( dotIndex > 0 && this . isDefinedComponent ( rawName . slice ( 0 , dotIndex ) ) ) {
59
+ return true
60
+ }
61
+ return false
62
+ }
63
+ }
64
+
65
+ class DefinedInOptionComponents {
66
+ constructor ( ) {
67
+ /**
68
+ * Component names
69
+ * @type {Set<string> }
70
+ */
71
+ this . names = new Set ( )
72
+ /**
73
+ * Component names, transformed to kebab-case
74
+ * @type {Set<string> }
75
+ */
76
+ this . kebabCaseNames = new Set ( )
77
+ }
78
+
79
+ /**
80
+ * @param {string[] } names
81
+ */
82
+ addName ( ...names ) {
83
+ for ( const name of names ) {
84
+ this . names . add ( name )
85
+ this . kebabCaseNames . add ( casing . kebabCase ( name ) )
86
+ }
87
+ }
88
+
89
+ /**
90
+ * @param {string } rawName
91
+ */
92
+ isDefinedComponent ( rawName ) {
93
+ if ( this . names . has ( rawName ) ) {
94
+ return true
95
+ }
96
+ const kebabCaseName = casing . kebabCase ( rawName )
97
+ if (
98
+ this . kebabCaseNames . has ( kebabCaseName ) &&
99
+ ! casing . isPascalCase ( rawName )
100
+ ) {
101
+ // Component registered as `foo-bar` cannot be used as `FooBar`
102
+ return true
103
+ }
104
+ return false
105
+ }
106
+ }
107
+
20
108
module . exports = {
21
109
meta : {
22
110
type : 'suggestion' ,
@@ -109,13 +197,15 @@ module.exports = {
109
197
110
198
if ( utils . isScriptSetup ( context ) ) {
111
199
// For <script setup>
200
+ const definedInSetupComponents = new DefinedInSetupComponents ( )
201
+ const definedInOptionComponents = new DefinedInOptionComponents ( )
202
+
112
203
/** @type {Set<string> } */
113
- const scriptVariableNames = new Set ( )
114
204
const scriptTypeOnlyNames = new Set ( )
115
205
const globalScope = context . getSourceCode ( ) . scopeManager . globalScope
116
206
if ( globalScope ) {
117
207
for ( const variable of globalScope . variables ) {
118
- scriptVariableNames . add ( variable . name )
208
+ definedInSetupComponents . addName ( variable . name )
119
209
}
120
210
const moduleScope = globalScope . childScopes . find (
121
211
( scope ) => scope . type === 'module'
@@ -146,39 +236,37 @@ module.exports = {
146
236
) {
147
237
scriptTypeOnlyNames . add ( variable . name )
148
238
} else {
149
- scriptVariableNames . add ( variable . name )
239
+ definedInSetupComponents . addName ( variable . name )
150
240
}
151
241
}
152
242
}
153
- /**
154
- * @see https://github.com/vuejs/core/blob/ae4b0783d78670b6e942ae2a4e3ec6efbbffa158/packages/compiler-core/src/transforms/transformElement.ts#L334
155
- * @param {string } name
156
- */
157
- const existsSetupReference = ( name ) => {
158
- if ( scriptVariableNames . has ( name ) ) {
159
- return true
160
- }
161
- const camelName = camelize ( name )
162
- if ( scriptVariableNames . has ( camelName ) ) {
163
- return true
164
- }
165
- const pascalName = casing . capitalize ( camelName )
166
- if ( scriptVariableNames . has ( pascalName ) ) {
167
- return true
243
+
244
+ // For circular references
245
+ const fileName = context . getFilename ( )
246
+ const selfComponentName = path . basename ( fileName , path . extname ( fileName ) )
247
+ definedInSetupComponents . addName ( selfComponentName )
248
+ scriptVisitor = utils . defineVueVisitor ( context , {
249
+ onVueObjectEnter ( node , { type } ) {
250
+ if ( type !== 'export' ) return
251
+ const nameProperty = utils . findProperty ( node , 'name' )
252
+
253
+ if ( nameProperty && utils . isStringLiteral ( nameProperty . value ) ) {
254
+ const name = utils . getStringLiteralValue ( nameProperty . value )
255
+ if ( name ) {
256
+ definedInOptionComponents . addName ( name )
257
+ }
258
+ }
168
259
}
169
- return false
170
- }
260
+ } )
261
+
171
262
verifyName = ( rawName , reportNode ) => {
172
263
if ( ! isVerifyTargetComponent ( rawName ) ) {
173
264
return
174
265
}
175
- if ( existsSetupReference ( rawName ) ) {
266
+ if ( definedInSetupComponents . isDefinedComponent ( rawName ) ) {
176
267
return
177
268
}
178
- // Check namespace
179
- // https://github.com/vuejs/core/blob/ae4b0783d78670b6e942ae2a4e3ec6efbbffa158/packages/compiler-core/src/transforms/transformElement.ts#L305
180
- const dotIndex = rawName . indexOf ( '.' )
181
- if ( dotIndex > 0 && existsSetupReference ( rawName . slice ( 0 , dotIndex ) ) ) {
269
+ if ( definedInOptionComponents . isDefinedComponent ( rawName ) ) {
182
270
return
183
271
}
184
272
@@ -192,26 +280,10 @@ module.exports = {
192
280
}
193
281
} else {
194
282
// For Options API
195
-
196
- /**
197
- * All registered components
198
- * @type {string[] }
199
- */
200
- const registeredComponentNames = [ ]
201
- /**
202
- * All registered components, transformed to kebab-case
203
- * @type {string[] }
204
- */
205
- const registeredComponentKebabCaseNames = [ ]
206
-
207
- /**
208
- * All registered components using kebab-case syntax
209
- * @type {string[] }
210
- */
211
- const componentsRegisteredAsKebabCase = [ ]
283
+ const definedInOptionComponents = new DefinedInOptionComponents ( )
212
284
213
285
scriptVisitor = utils . executeOnVue ( context , ( obj ) => {
214
- registeredComponentNames . push (
286
+ definedInOptionComponents . addName (
215
287
...utils . getRegisteredComponents ( obj ) . map ( ( { name } ) => name )
216
288
)
217
289
@@ -220,33 +292,16 @@ module.exports = {
220
292
if ( nameProperty && utils . isStringLiteral ( nameProperty . value ) ) {
221
293
const name = utils . getStringLiteralValue ( nameProperty . value )
222
294
if ( name ) {
223
- registeredComponentNames . push ( name )
295
+ definedInOptionComponents . addName ( name )
224
296
}
225
297
}
226
-
227
- registeredComponentKebabCaseNames . push (
228
- ...registeredComponentNames . map ( ( name ) => casing . kebabCase ( name ) )
229
- )
230
- componentsRegisteredAsKebabCase . push (
231
- ...registeredComponentNames . filter (
232
- ( name ) => name === casing . kebabCase ( name )
233
- )
234
- )
235
298
} )
236
299
237
300
verifyName = ( rawName , reportNode ) => {
238
301
if ( ! isVerifyTargetComponent ( rawName ) ) {
239
302
return
240
303
}
241
- if ( registeredComponentNames . includes ( rawName ) ) {
242
- return
243
- }
244
- const kebabCaseName = casing . kebabCase ( rawName )
245
- if (
246
- registeredComponentKebabCaseNames . includes ( kebabCaseName ) &&
247
- ! casing . isPascalCase ( rawName )
248
- ) {
249
- // Component registered as `foo-bar` cannot be used as `FooBar`
304
+ if ( definedInOptionComponents . isDefinedComponent ( rawName ) ) {
250
305
return
251
306
}
252
307
0 commit comments