@@ -15,23 +15,6 @@ const RESERVED_NAMES_IN_VUE3 = new Set(
1515 require ( '../utils/vue3-builtin-components' )
1616)
1717
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-
3518// ------------------------------------------------------------------------------
3619// Rule Definition
3720// ------------------------------------------------------------------------------
@@ -44,22 +27,92 @@ module.exports = {
4427 categories : [ 'vue3-essential' , 'essential' ] ,
4528 url : 'https://eslint.vuejs.org/rules/multi-word-component-names.html'
4629 } ,
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+ ] ,
4844 messages : {
4945 unexpected : 'Component name "{{value}}" should always be multi-word.'
5046 }
5147 } ,
5248 /** @param {RuleContext } context */
5349 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+ }
5693
5794 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+ } ) ,
58108 {
59109 /** @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 ( / \. [ ^ / . ] + $ / , '' )
61115 if (
62- ! node . body . length &&
63116 utils . isVueFile ( fileName ) &&
64117 ! isValidComponentName ( componentName )
65118 ) {
@@ -72,44 +125,7 @@ module.exports = {
72125 } )
73126 }
74127 }
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+ }
113129 )
114130 }
115131}
0 commit comments