Skip to content

Commit 10d645e

Browse files
waynzhFloEdelmann
andauthored
feat(component-name-in-template-casing): global option support regex (#2928)
Co-authored-by: Flo Edelmann <[email protected]>
1 parent a5127b0 commit 10d645e

File tree

4 files changed

+113
-9
lines changed

4 files changed

+113
-9
lines changed

.changeset/rude-heads-serve.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'eslint-plugin-vue': minor
3+
---
4+
5+
Changed `vue/component-name-in-template-casing` `globals` option to support regex patterns

docs/rules/component-name-in-template-casing.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ This rule aims to warn the tag names other than the configured casing in Vue.js
3434
- `registeredComponentsOnly` ... If `true`, only registered components (in PascalCase) are checked. If `false`, check all.
3535
default `true`
3636
- `ignores` (`string[]`) ... The element names to ignore. Sets the element name to allow. For example, custom elements or Vue components with special name. You can set the regexp by writing it like `"/^name/"`.
37-
- `globals` (`string[]`) ... Globally registered component names to check. For example, `RouterView` and `RouterLink` are globally registered by `vue-router` and can't be detected as registered in a SFC file.
37+
- `globals` (`string[]`) ... Globally registered component names to check. For example, `RouterView` and `RouterLink` are globally registered by `vue-router` and can't be detected as registered in a SFC file. You can set the regexp by writing it like `"/^c-/"` to match component names with patterns.
3838

3939
### `"PascalCase", { registeredComponentsOnly: true }` (default)
4040

@@ -143,17 +143,23 @@ export default {
143143

144144
</eslint-code-block>
145145

146-
### `"PascalCase", { globals: ["RouterView"] }`
146+
### `"PascalCase", { globals: ["RouterView", "/^c-/"] }`
147147

148-
<eslint-code-block fix :rules="{'vue/component-name-in-template-casing': ['error', 'PascalCase', {globals: ['RouterView']}]}">
148+
<eslint-code-block fix :rules="{'vue/component-name-in-template-casing': ['error', 'PascalCase', {globals: ['RouterView', '/^c-/']}]}">
149149

150150
```vue
151151
<template>
152152
<!-- ✓ GOOD -->
153153
<RouterView></RouterView>
154+
<CButton />
155+
<CCard />
156+
<CInput />
154157
155158
<!-- ✗ BAD -->
156159
<router-view></router-view>
160+
<c-button />
161+
<c-card />
162+
<c-input />
157163
</template>
158164
```
159165

lib/rules/component-name-in-template-casing.js

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
const utils = require('../utils')
88
const casing = require('../utils/casing')
9-
const { toRegExpGroupMatcher } = require('../utils/regexp')
9+
const { toRegExpGroupMatcher, isRegExp } = require('../utils/regexp')
1010

1111
const allowedCaseOptions = ['PascalCase', 'kebab-case']
1212
const defaultCase = 'PascalCase'
@@ -82,16 +82,26 @@ module.exports = {
8282
? caseOption
8383
: defaultCase
8484
const isIgnored = toRegExpGroupMatcher(options.ignores)
85-
/** @type {string[]} */
86-
const globals = (options.globals || []).map(casing.pascalCase)
85+
86+
const globalStrings = []
87+
const globalPatterns = []
88+
for (const global of options.globals || []) {
89+
if (isRegExp(global)) {
90+
globalPatterns.push(global)
91+
} else {
92+
globalStrings.push(global)
93+
}
94+
}
95+
96+
const isGlobalPattern = toRegExpGroupMatcher(globalPatterns)
8797
const registeredComponentsOnly = options.registeredComponentsOnly !== false
8898
const sourceCode = context.getSourceCode()
8999
const tokens =
90100
sourceCode.parserServices.getTemplateBodyTokenStore &&
91101
sourceCode.parserServices.getTemplateBodyTokenStore()
92102

93103
/** @type { Set<string> } */
94-
const registeredComponents = new Set(globals)
104+
const registeredComponents = new Set(globalStrings.map(casing.pascalCase))
95105

96106
if (utils.isScriptSetup(context)) {
97107
// For <script setup>
@@ -137,8 +147,10 @@ module.exports = {
137147
return true
138148
}
139149

140-
// We only verify the registered components.
141-
return registeredComponents.has(casing.pascalCase(node.rawName))
150+
return (
151+
registeredComponents.has(casing.pascalCase(node.rawName)) ||
152+
isGlobalPattern(node.rawName)
153+
)
142154
}
143155

144156
let hasInvalidEOF = false

tests/lib/rules/component-name-in-template-casing.js

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,21 @@ tester.run('component-name-in-template-casing', rule, {
202202
options: ['kebab-case', { globals: ['RouterView', 'router-link'] }]
203203
},
204204

205+
// globals with regex patterns
206+
{
207+
code: `
208+
<template>
209+
<div>
210+
<c-button />
211+
<c-card />
212+
<c-input />
213+
<other-component />
214+
</div>
215+
</template>
216+
`,
217+
options: ['kebab-case', { globals: ['/^c-/', 'other-component'] }]
218+
},
219+
205220
// type-only imports
206221
...(semver.gte(
207222
require('@typescript-eslint/parser/package.json').version,
@@ -1223,6 +1238,72 @@ tester.run('component-name-in-template-casing', rule, {
12231238
}
12241239
]
12251240
},
1241+
{
1242+
code: `
1243+
<template>
1244+
<c-button />
1245+
<c-card />
1246+
<CInput />
1247+
</template>
1248+
`,
1249+
output: `
1250+
<template>
1251+
<CButton />
1252+
<CCard />
1253+
<CInput />
1254+
</template>
1255+
`,
1256+
options: ['PascalCase', { globals: ['/^c-/'] }],
1257+
errors: [
1258+
{
1259+
message: 'Component name "c-button" is not PascalCase.',
1260+
line: 3,
1261+
column: 11,
1262+
endLine: 3,
1263+
endColumn: 20
1264+
},
1265+
{
1266+
message: 'Component name "c-card" is not PascalCase.',
1267+
line: 4,
1268+
column: 11,
1269+
endLine: 4,
1270+
endColumn: 18
1271+
}
1272+
]
1273+
},
1274+
{
1275+
code: `
1276+
<template>
1277+
<CButton />
1278+
<CCard />
1279+
<c-input />
1280+
</template>
1281+
`,
1282+
output: `
1283+
<template>
1284+
<c-button />
1285+
<c-card />
1286+
<c-input />
1287+
</template>
1288+
`,
1289+
options: ['kebab-case', { globals: ['/^C[A-Z]/', '/^c-/'] }],
1290+
errors: [
1291+
{
1292+
message: 'Component name "CButton" is not kebab-case.',
1293+
line: 3,
1294+
column: 11,
1295+
endLine: 3,
1296+
endColumn: 19
1297+
},
1298+
{
1299+
message: 'Component name "CCard" is not kebab-case.',
1300+
line: 4,
1301+
column: 11,
1302+
endLine: 4,
1303+
endColumn: 17
1304+
}
1305+
]
1306+
},
12261307
// type-only imports
12271308
...(semver.gte(
12281309
require('@typescript-eslint/parser/package.json').version,

0 commit comments

Comments
 (0)