Skip to content

Commit

Permalink
⭐ new(rule): add no-dynamic-keys rule
Browse files Browse the repository at this point in the history
  • Loading branch information
kazupon committed Mar 17, 2019
1 parent 18b77ed commit 7612dfd
Show file tree
Hide file tree
Showing 6 changed files with 238 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Details changes for each release are documented in the [CHANGELOG.md](https://gi

## :white_check_mark: TODO
- [x] no-missing-keys
- [ ] no-dynamic-keys
- [x] no-dynamic-keys
- [x] no-unused-keys
- [ ] no-raw-text
- [ ] valid-message-syntax
Expand Down
1 change: 1 addition & 0 deletions docs/rules/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@

| Rule ID | Description | |
|:--------|:------------|:---|
| [vue-i18n/<wbr>no-dynamic-keys](./no-dynamic-keys.html) | disallow localization dynamic keys at localization methods | |
| [vue-i18n/<wbr>no-unused-keys](./no-unused-keys.html) | disallow unused localization keys | |

99 changes: 99 additions & 0 deletions docs/rules/no-dynamic-keys.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# vue-i18n/no-dynamic-keys

> disallow localization dynamic keys at localization methods
This rule warns localization dynamic keys.

This role is useful for unifying all static keys in your application.

## :book: Rule Details

You can be detected with this rule the following:

- `$t`
- `t`
- `$tc`
- `tc`
- `v-t`

:-1: Examples of **incorrect** code for this rule:

locale messages:
```json
{
"hello": "Hello! DIO!"
}
```

localization codes:

```vue
<template>
<div class="app">
<!-- ✗ BAD -->
<p>{{ $t(msg) }}</p>
<!-- ✗ BAD -->
<p v-t="msg"></p>
</div>
</template>
```

```js
const i18n = new VueI18n({
locale: 'en',
messages: {
en: require('./locales/en.json')
}
})

const app = new Vue({
i18n,
data () {
return { msg: 'hello' }
}
})

/* ✗ BAD */
i18n.t(app.msg)
```

:+1: Examples of **correct** code for this rule:

locale messages:
```json
{
"hello": "Hello! DIO!"
}
```

localization codes:

```vue
<template>
<div class="app">
<!-- ✗ GOOD -->
<p>{{ $t('hello') }}</p>
<!-- ✗ GOOD -->
<p v-t="'hello'"></p>
</div>
</template>
```

```js
const i18n = new VueI18n({
locale: 'en',
messages: {
en: require('./locales/en.json')
}
})

const app = new Vue({
i18n,
data () {
return { msg: 'hello' }
}
})

/* ✓ GOOD */
i18n.t('hello')
```
1 change: 1 addition & 0 deletions lib/rules.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
'use strict'

module.exports = {
'no-dynamic-keys': require('./rules/no-dynamic-keys'),
'no-missing-keys': require('./rules/no-missing-keys'),
'no-unused-keys': require('./rules/no-unused-keys')
}
68 changes: 68 additions & 0 deletions lib/rules/no-dynamic-keys.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* @author kazuya kawaguchi (a.k.a. kazupon)
*/
'use strict'

const { defineTemplateBodyVisitor } = require('../utils/index')

function checkDirective (context, node) {
if ((node.value && node.value.type === 'VExpressionContainer') &&
(node.value.expression && node.value.expression.type === 'Identifier')) {
const name = node.value.expression.name
context.report({
node,
message: `'${name}' dynamic key is used'`
})
}
}

function checkCallExpression (context, node) {
const funcName = (node.callee.type === 'MemberExpression' && node.callee.property.name) || node.callee.name

if (!/^(\$t|t|\$tc|tc)$/.test(funcName) || !node.arguments || !node.arguments.length) {
return
}

const [keyNode] = node.arguments
if (keyNode.type === 'Identifier') {
const name = keyNode.name
context.report({
node,
message: `'${name}' dynamic key is used'`
})
}
}

function create (context) {
return defineTemplateBodyVisitor(context, {
"VAttribute[directive=true][key.name='t']" (node) {
checkDirective(context, node)
},

"VAttribute[directive=true][key.name.name='t']" (node) {
checkDirective(context, node)
},

CallExpression (node) {
checkCallExpression(context, node)
}
}, {
CallExpression (node) {
checkCallExpression(context, node)
}
})
}

module.exports = {
meta: {
type: 'suggestion',
docs: {
description: 'disallow localization dynamic keys at localization methods',
category: 'Best Practices',
recommended: false
},
fixable: null,
schema: []
},
create
}
68 changes: 68 additions & 0 deletions tests/lib/rules/no-dynamic-keys.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* @author kazuya kawaguchi (a.k.a. kazupon)
*/
'use strict'

const RuleTester = require('eslint').RuleTester
const rule = require('../../../lib/rules/no-dynamic-keys')

const tester = new RuleTester({
parser: 'vue-eslint-parser',
parserOptions: { ecmaVersion: 2015 }
})

tester.run('no-dynamic-keys', rule, {
valid: [{
// basic key
code: `$t('hello')`
}, {
// nested key
code: `t('messages.nested.hello')`
}, {
// linked key
code: `$tc('messages.hello.link')`
}, {
// hypened key
code: `tc('hello-dio')`
}, {
// key like the message
code: `$t('hello {name}')`
}, {
// instance member
code: `i18n.t('hello {name}')`
}, {
// using mustaches in template block
code: `<template>
<p>{{ $t('hello') }}</p>
</template>`
}, {
// using custom directive in template block
code: `<template>
<p v-t="'hello'"></p>
</template>`
}],

invalid: [{
// basic
code: `$t(missing)`,
errors: [
`'missing' dynamic key is used'`
]
}, {
// using mustaches in template block
code: `<template>
<p>{{ $t(missing) }}</p>
</template>`,
errors: [
`'missing' dynamic key is used'`
]
}, {
// using custom directive in template block
code: `<template>
<p v-t="missing"></p>
</template>`,
errors: [
`'missing' dynamic key is used'`
]
}]
})

0 comments on commit 7612dfd

Please sign in to comment.