diff --git a/docs/rules/no-raw-text.md b/docs/rules/no-raw-text.md
index 29727096..51883955 100644
--- a/docs/rules/no-raw-text.md
+++ b/docs/rules/no-raw-text.md
@@ -10,7 +10,10 @@ since: v0.2.0
- :star: The `"extends": "plugin:@intlify/vue-i18n/recommended"` property in a configuration file enables this rule.
-This rule warns the usage of string literal.
+This rule warns the usage of literal the bellow:
+
+- string literal
+- template literals (no epressions, plain text only)
This rule encourage i18n in about the application needs to be localized.
diff --git a/lib/rules/no-raw-text.ts b/lib/rules/no-raw-text.ts
index f87eff6d..3ade123a 100644
--- a/lib/rules/no-raw-text.ts
+++ b/lib/rules/no-raw-text.ts
@@ -11,18 +11,27 @@ import type {
SourceLocation
} from '../types'
-type AnyValue = VAST.ESLintLiteral['value']
+type AnyValue =
+ | VAST.ESLintLiteral['value']
+ | VAST.ESLintTemplateElement['value']
const config: {
ignorePattern: RegExp
ignoreNodes: string[]
ignoreText: string[]
} = { ignorePattern: /^[^\S\s]$/, ignoreNodes: [], ignoreText: [] }
const hasOnlyWhitespace = (value: string) => /^[\r\n\s\t\f\v]+$/.test(value)
+const hasTemplateElementValue = (
+ value: any // eslint-disable-line @typescript-eslint/no-explicit-any
+): value is { raw: string; cooked: string } =>
+ 'raw' in value &&
+ typeof value.raw === 'string' &&
+ 'cooked' in value &&
+ typeof value.cooked === 'string'
const INNER_START_OFFSET = ''.length
function calculateLoc(
- node: VAST.ESLintLiteral,
- base: VAST.ESLintLiteral | null = null
+ node: VAST.ESLintLiteral | VAST.ESLintTemplateElement,
+ base: VAST.ESLintLiteral | VAST.ESLintTemplateElement | null = null
) {
return !base
? node.loc
@@ -40,15 +49,24 @@ function calculateLoc(
}
}
-function testValue(value: AnyValue) {
+function testTextable(value: string): boolean {
return (
- typeof value !== 'string' ||
hasOnlyWhitespace(value) ||
config.ignorePattern.test(value.trim()) ||
config.ignoreText.includes(value.trim())
)
}
+function testValue(value: AnyValue): boolean {
+ if (typeof value === 'string') {
+ return testTextable(value)
+ } else if (hasTemplateElementValue(value)) {
+ return testTextable(value.raw)
+ } else {
+ return false
+ }
+}
+
// parent is directive (e.g )
function checkVAttributeDirective(
context: RuleContext,
@@ -65,29 +83,17 @@ function checkVAttributeDirective(
(attrNode.key.name === 'text' ||
// for vue-eslint-parser v6+
attrNode.key.name.name === 'text') &&
- node.expression &&
- node.expression.type === 'Literal'
+ node.expression
) {
- const literalNode = node.expression
- const value = literalNode.value
-
- if (testValue(value)) {
- return
- }
-
- const loc = calculateLoc(literalNode, baseNode)
- context.report({
- loc,
- message: `raw text '${literalNode.value}' is used`
- })
+ checkExpressionContainerText(context, node.expression, baseNode)
}
}
}
-function checkVExpressionContainerText(
+function checkVExpressionContainer(
context: RuleContext,
node: VAST.VExpressionContainer,
- baseNode: VAST.ESLintLiteral | null = null
+ baseNode: VAST.ESLintLiteral | VAST.ESLintTemplateElement | null = null
) {
if (!node.expression) {
return
@@ -95,33 +101,7 @@ function checkVExpressionContainerText(
if (node.parent && node.parent.type === 'VElement') {
// parent is element (e.g. {{ ... }} {{ $t('hello') }} {{ $t('click') }}{{ $t('here') }}{{ $t('terminal') }} {{ $t(\`click\`) }}{{ $t('here') }}{{ $t('terminal') }}
${msg}
` }' } ], @@ -157,6 +161,20 @@ tester.run('no-raw-text', rule as never, { } ] }, + { + // directly specify template literal in mustache + code: ` + +{{ \`hello\` }}
+ + `, + errors: [ + { + message: `raw text 'hello' is used`, + line: 3 + } + ] + }, { // javascript expression specify string literal in mustache code: ` @@ -175,6 +193,24 @@ tester.run('no-raw-text', rule as never, { } ] }, + { + // javascript expression specify template literal in mustache + code: ` + +{{ ok ? \`hello\` : \`world\` }}
+ + `, + errors: [ + { + message: `raw text 'hello' is used`, + line: 3 + }, + { + message: `raw text 'world' is used`, + line: 3 + } + ] + }, { // directly specify string literal in v-text code: ` @@ -189,6 +225,20 @@ tester.run('no-raw-text', rule as never, { } ] }, + { + // directly specify tempate literal in v-text + code: ` + + + + `, + errors: [ + { + message: `raw text 'hello' is used`, + line: 3 + } + ] + }, { // directly specify string literal to `template` component option at export default object code: ` @@ -203,6 +253,20 @@ tester.run('no-raw-text', rule as never, { } ] }, + { + // directly specify template literal to `template` component option at export default object + code: ` + export default { + template: \`hello
\` + } + `, + errors: [ + { + message: `raw text 'hello' is used`, + line: 3 + } + ] + }, { // directly specify string literal to `template` component option at variable code: ` @@ -217,6 +281,20 @@ tester.run('no-raw-text', rule as never, { } ] }, + { + // directly specify template literal to `template` component option at variable + code: ` + const Component = { + template: \`hello
\` + } + `, + errors: [ + { + message: `raw text 'hello' is used`, + line: 3 + } + ] + }, { // directly specify string literal to `template` variable code: ` @@ -232,6 +310,21 @@ tester.run('no-raw-text', rule as never, { } ] }, + { + // directly specify templtea literal to `template` variable + code: ` + const template = \`hello
\` + const Component = { + template + } + `, + errors: [ + { + message: `raw text 'hello' is used`, + line: 2 + } + ] + }, { // directly specify string literal to `template` variable code: ` @@ -248,6 +341,22 @@ tester.run('no-raw-text', rule as never, { } ] }, + { + // directly specify string literal to `template` variable + code: ` + const template = '{{ \`hello\` }}
' + const Component = { + template + } + `, + errors: [ + { + message: `raw text 'hello' is used`, + line: 2, + column: 30 + } + ] + }, { // javascript expression specify string literal to `template` variable in mustache code: ` @@ -269,6 +378,27 @@ tester.run('no-raw-text', rule as never, { } ] }, + { + // javascript expression specify tempalte literal to `template` variable in mustache + code: ` + const template = '{{ ok ? \`hello\` : \`world\` }}
' + const Component = { + template + } + `, + errors: [ + { + message: `raw text 'hello' is used`, + line: 2, + column: 35 + }, + { + message: `raw text 'world' is used`, + line: 2, + column: 45 + } + ] + }, { // directly specify string literal to JSX with `render` code: ` @@ -304,11 +434,11 @@ tester.run('no-raw-text', rule as never, { { code: ` -{{ $t('foo') }}: {{ $t('bar') }}
+{{ $t(\`foo\`) }}: {{ $t('bar') }}
hello
-
@
-{{ true ? $t('ok') : ' - ' }}
+{{ true ? $t(\`ok\`) : ' - ' }}
{{ true ? $t('ok') : '@' }}
`, @@ -334,7 +464,7 @@ tester.run('no-raw-text', rule as never, { code: `