Skip to content

Commit 689f1b9

Browse files
authored
Fix false negatives for member expression in no-dynamic-keys rule (#150)
1 parent e180387 commit 689f1b9

File tree

2 files changed

+93
-10
lines changed

2 files changed

+93
-10
lines changed

lib/rules/no-dynamic-keys.ts

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,53 @@ import { defineTemplateBodyVisitor } from '../utils/index'
55
import type { RuleContext, RuleListener } from '../types'
66
import type { AST as VAST } from 'vue-eslint-parser'
77

8+
function isStatic(
9+
node:
10+
| VAST.ESLintExpression
11+
| VAST.ESLintSpreadElement
12+
| VAST.VFilterSequenceExpression
13+
| VAST.VForExpression
14+
| VAST.VOnExpression
15+
| VAST.VSlotScopeExpression
16+
): boolean {
17+
if (node.type === 'Literal') {
18+
return true
19+
}
20+
if (node.type === 'TemplateLiteral' && node.expressions.length === 0) {
21+
return true
22+
}
23+
return false
24+
}
25+
26+
function getNodeName(context: RuleContext, node: VAST.Node): string {
27+
if (node.type === 'Identifier') {
28+
return node.name
29+
}
30+
const sourceCode = context.getSourceCode()
31+
if (
32+
sourceCode.ast.range[0] <= node.range[0] &&
33+
node.range[1] <= sourceCode.ast.range[1]
34+
) {
35+
return sourceCode
36+
.getTokens(node)
37+
.map(t => t.value)
38+
.join('')
39+
}
40+
const tokenStore = context.parserServices.getTemplateBodyTokenStore()
41+
return tokenStore
42+
.getTokens(node)
43+
.map(t => t.value)
44+
.join('')
45+
}
46+
847
function checkDirective(context: RuleContext, node: VAST.VDirective) {
948
if (
1049
node.value &&
1150
node.value.type === 'VExpressionContainer' &&
1251
node.value.expression &&
13-
node.value.expression.type === 'Identifier'
52+
!isStatic(node.value.expression)
1453
) {
15-
const name = node.value.expression.name
54+
const name = getNodeName(context, node.value.expression)
1655
context.report({
1756
node,
1857
message: `'${name}' dynamic key is used'`
@@ -21,19 +60,18 @@ function checkDirective(context: RuleContext, node: VAST.VDirective) {
2160
}
2261

2362
function checkComponent(context: RuleContext, node: VAST.VDirectiveKey) {
24-
const parent: VAST.VDirective = node.parent as never // typebug?
2563
if (
2664
node.name.type === 'VIdentifier' &&
2765
node.name.name === 'bind' &&
2866
node.argument &&
2967
node.argument.type === 'VIdentifier' &&
3068
node.argument.name === 'path' &&
31-
parent.value &&
32-
parent.value.type === 'VExpressionContainer' &&
33-
parent.value.expression &&
34-
parent.value.expression.type === 'Identifier'
69+
node.parent.value &&
70+
node.parent.value.type === 'VExpressionContainer' &&
71+
node.parent.value.expression &&
72+
!isStatic(node.parent.value.expression)
3573
) {
36-
const name = parent.value.expression.name
74+
const name = getNodeName(context, node.parent.value.expression)
3775
context.report({
3876
node,
3977
message: `'${name}' dynamic key is used'`
@@ -61,8 +99,8 @@ function checkCallExpression(
6199
}
62100

63101
const [keyNode] = node.arguments
64-
if (keyNode.type === 'Identifier') {
65-
const name = keyNode.name
102+
if (!isStatic(keyNode)) {
103+
const name = getNodeName(context, keyNode)
66104
context.report({
67105
node,
68106
message: `'${name}' dynamic key is used'`

tests/lib/rules/no-dynamic-keys.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,17 @@ tester.run('no-dynamic-keys', rule as never, {
4646
code: `<template>
4747
<p v-t="'hello'"></p>
4848
</template>`
49+
},
50+
{
51+
code: `
52+
<template>
53+
<p>{{ $t(\`foo\`) }}</p>
54+
<i18n :path="\`foo\`"/>
55+
<p v-t="\`foo\`"></p>
56+
</template>
57+
<script>
58+
i18n.t(\`foo\`)
59+
</script>`
4960
}
5061
],
5162

@@ -82,6 +93,40 @@ tester.run('no-dynamic-keys', rule as never, {
8293
<p v-t="missing"></p>
8394
</template>`,
8495
errors: [`'missing' dynamic key is used'`]
96+
},
97+
{
98+
code: `
99+
<template>
100+
<p>{{ $t(foo.dynamic) }}</p>
101+
<i18n :path="foo.dynamic"/>
102+
<p v-t="foo.dynamic"></p>
103+
</template>
104+
<script>
105+
i18n.t(foo.dynamic)
106+
</script>`,
107+
errors: [
108+
"'foo.dynamic' dynamic key is used'",
109+
"'foo.dynamic' dynamic key is used'",
110+
"'foo.dynamic' dynamic key is used'",
111+
"'foo.dynamic' dynamic key is used'"
112+
]
113+
},
114+
{
115+
code: `
116+
<template>
117+
<p>{{ $t(foo()) }}</p>
118+
<i18n :path="foo()"/>
119+
<p v-t="foo()"></p>
120+
</template>
121+
<script>
122+
i18n.t(foo())
123+
</script>`,
124+
errors: [
125+
"'foo()' dynamic key is used'",
126+
"'foo()' dynamic key is used'",
127+
"'foo()' dynamic key is used'",
128+
"'foo()' dynamic key is used'"
129+
]
85130
}
86131
]
87132
})

0 commit comments

Comments
 (0)