@@ -15,23 +15,6 @@ const RESERVED_NAMES_IN_VUE3 = new Set(
15
15
require ( '../utils/vue3-builtin-components' )
16
16
)
17
17
18
- // ------------------------------------------------------------------------------
19
- // Helpers
20
- // ------------------------------------------------------------------------------
21
-
22
- /**
23
- * Returns true if the given component name is valid, otherwise false.
24
- * @param {string } name
25
- * */
26
- function isValidComponentName ( name ) {
27
- if ( name . toLowerCase ( ) === 'app' || RESERVED_NAMES_IN_VUE3 . has ( name ) ) {
28
- return true
29
- } else {
30
- const elements = casing . kebabCase ( name ) . split ( '-' )
31
- return elements . length > 1
32
- }
33
- }
34
-
35
18
// ------------------------------------------------------------------------------
36
19
// Rule Definition
37
20
// ------------------------------------------------------------------------------
@@ -44,22 +27,92 @@ module.exports = {
44
27
categories : [ 'vue3-essential' , 'essential' ] ,
45
28
url : 'https://eslint.vuejs.org/rules/multi-word-component-names.html'
46
29
} ,
47
- schema : [ ] ,
30
+ schema : [
31
+ {
32
+ type : 'object' ,
33
+ properties : {
34
+ ignores : {
35
+ type : 'array' ,
36
+ items : { type : 'string' } ,
37
+ uniqueItems : true ,
38
+ additionalItems : false
39
+ }
40
+ } ,
41
+ additionalProperties : false
42
+ }
43
+ ] ,
48
44
messages : {
49
45
unexpected : 'Component name "{{value}}" should always be multi-word.'
50
46
}
51
47
} ,
52
48
/** @param {RuleContext } context */
53
49
create ( context ) {
54
- const fileName = context . getFilename ( )
55
- let componentName = fileName . replace ( / \. [ ^ / . ] + $ / , '' )
50
+ /** @type {Set<string> } */
51
+ const ignores = new Set ( )
52
+ ignores . add ( 'App' )
53
+ ignores . add ( 'app' )
54
+ for ( const ignore of ( context . options [ 0 ] && context . options [ 0 ] . ignores ) ||
55
+ [ ] ) {
56
+ ignores . add ( ignore )
57
+ if ( casing . isPascalCase ( ignore ) ) {
58
+ // PascalCase
59
+ ignores . add ( casing . kebabCase ( ignore ) )
60
+ }
61
+ }
62
+ let hasVue = false
63
+ let hasName = false
64
+
65
+ /**
66
+ * Returns true if the given component name is valid, otherwise false.
67
+ * @param {string } name
68
+ * */
69
+ function isValidComponentName ( name ) {
70
+ if ( ignores . has ( name ) || RESERVED_NAMES_IN_VUE3 . has ( name ) ) {
71
+ return true
72
+ }
73
+ const elements = casing . kebabCase ( name ) . split ( '-' )
74
+ return elements . length > 1
75
+ }
76
+
77
+ /**
78
+ * @param {Expression | SpreadElement } nameNode
79
+ */
80
+ function validateName ( nameNode ) {
81
+ if ( nameNode . type !== 'Literal' ) return
82
+ const componentName = `${ nameNode . value } `
83
+ if ( ! isValidComponentName ( componentName ) ) {
84
+ context . report ( {
85
+ node : nameNode ,
86
+ messageId : 'unexpected' ,
87
+ data : {
88
+ value : componentName
89
+ }
90
+ } )
91
+ }
92
+ }
56
93
57
94
return utils . compositingVisitors (
95
+ utils . executeOnCallVueComponent ( context , ( node ) => {
96
+ hasVue = true
97
+ if ( node . arguments . length !== 2 ) return
98
+ hasName = true
99
+ validateName ( node . arguments [ 0 ] )
100
+ } ) ,
101
+ utils . executeOnVue ( context , ( obj ) => {
102
+ hasVue = true
103
+ const node = utils . findProperty ( obj , 'name' )
104
+ if ( ! node ) return
105
+ hasName = true
106
+ validateName ( node . value )
107
+ } ) ,
58
108
{
59
109
/** @param {Program } node */
60
- Program ( node ) {
110
+ 'Program:exit' ( node ) {
111
+ if ( hasName ) return
112
+ if ( ! hasVue && node . body . length > 0 ) return
113
+ const fileName = context . getFilename ( )
114
+ const componentName = fileName . replace ( / \. [ ^ / . ] + $ / , '' )
61
115
if (
62
- ! node . body . length &&
63
116
utils . isVueFile ( fileName ) &&
64
117
! isValidComponentName ( componentName )
65
118
) {
@@ -72,44 +125,7 @@ module.exports = {
72
125
} )
73
126
}
74
127
}
75
- } ,
76
-
77
- utils . executeOnVue ( context , ( obj ) => {
78
- const node = utils . findProperty ( obj , 'name' )
79
-
80
- /** @type {SourceLocation | null } */
81
- let loc = null
82
-
83
- // Check if the component has a name property.
84
- if ( node ) {
85
- const valueNode = node . value
86
- if ( valueNode . type !== 'Literal' ) return
87
-
88
- componentName = `${ valueNode . value } `
89
- loc = node . loc
90
- } else if (
91
- obj . parent . type === 'CallExpression' &&
92
- obj . parent . arguments . length === 2
93
- ) {
94
- // The component is registered globally with 'Vue.component', where
95
- // the first paremter is the component name.
96
- const argument = obj . parent . arguments [ 0 ]
97
- if ( argument . type !== 'Literal' ) return
98
-
99
- componentName = `${ argument . value } `
100
- loc = argument . loc
101
- }
102
-
103
- if ( ! isValidComponentName ( componentName ) ) {
104
- context . report ( {
105
- messageId : 'unexpected' ,
106
- data : {
107
- value : componentName
108
- } ,
109
- loc : loc || { line : 1 , column : 0 }
110
- } )
111
- }
112
- } )
128
+ }
113
129
)
114
130
}
115
131
}
0 commit comments