diff --git a/README.md b/README.md index 17f4ad6..3be4cf0 100644 --- a/README.md +++ b/README.md @@ -250,6 +250,26 @@ switch (type) { ### Options +### markupOnly + +If `markupOnly` option turn on, only JSX text and strings used as JSX attributes will be validated. + +JSX text: + +```jsx +// incorrect +
hello world
+
{"hello world"}
+``` + +Strings as JSX attribute: + +```jsx +// incorrect +
+
+``` + #### ignore The `ignore` option specifies exceptions not to check for diff --git a/lib/rules/no-literal-string.js b/lib/rules/no-literal-string.js index c98cd14..0218efc 100644 --- a/lib/rules/no-literal-string.js +++ b/lib/rules/no-literal-string.js @@ -45,6 +45,9 @@ module.exports = { items: { type: 'string' } + }, + markupOnly: { + type: 'boolean' } }, additionalProperties: false @@ -152,6 +155,47 @@ module.exports = { if (program && esTreeNodeToTSNodeMap) typeChecker = program.getTypeChecker(); + function validateLiteral(node) { + // visited and passed linting + if (visited.has(node)) return; + const trimed = node.value.trim(); + if (!trimed) return; + + const { parent } = node; + + // allow statements like const a = "FOO" + if (isUpperCase(trimed)) return; + + if (match(trimed)) return; + + // + // TYPESCRIPT + // + + if (typeChecker) { + const tsNode = esTreeNodeToTSNodeMap.get(node); + const typeObj = typeChecker.getTypeAtLocation(tsNode.parent); + + // var a: 'abc' = 'abc' + if (typeObj.isStringLiteral()) { + return; + } + + // var a: 'abc' | 'name' = 'abc' + if (typeObj.isUnion()) { + const found = typeObj.types.some(item => { + if (item.isStringLiteral() && item.value === node.value) { + return true; + } + }); + if (found) return; + } + } + // • • • • • + + context.report({ node, message }); + } + const scriptVisitor = { // // ─── EXPORT AND IMPORT ─────────────────────────────────────────── @@ -186,6 +230,18 @@ module.exports = { scriptVisitor.JSXText(node); }, + 'JSXExpressionContainer > Literal:exit'(node) { + if (option && option.markupOnly) { + validateLiteral(node); + } + }, + + 'JSXAttribute > Literal:exit'(node) { + if (option && option.markupOnly) { + validateLiteral(node); + } + }, + 'JSXAttribute Literal'(node) { const parent = getNearestAncestor(node, 'JSXAttribute'); const attrName = parent.name.name; @@ -283,44 +339,10 @@ module.exports = { }, 'Literal:exit'(node) { - // visited and passed linting - if (visited.has(node)) return; - const trimed = node.value.trim(); - if (!trimed) return; - - const { parent } = node; - - // allow statements like const a = "FOO" - if (isUpperCase(trimed)) return; - - if (match(trimed)) return; - - // - // TYPESCRIPT - // - - if (typeChecker) { - const tsNode = esTreeNodeToTSNodeMap.get(node); - const typeObj = typeChecker.getTypeAtLocation(tsNode.parent); - - // var a: 'abc' = 'abc' - if (typeObj.isStringLiteral()) { - return; - } - - // var a: 'abc' | 'name' = 'abc' - if (typeObj.isUnion()) { - const found = typeObj.types.some(item => { - if (item.isStringLiteral() && item.value === node.value) { - return true; - } - }); - if (found) return; - } + if (option && option.markupOnly) { + return; } - // • • • • • - - context.report({ node, message }); + validateLiteral(node); } }; diff --git a/tests/lib/rules/no-literal-string.js b/tests/lib/rules/no-literal-string.js index 76e9020..e96e03b 100644 --- a/tests/lib/rules/no-literal-string.js +++ b/tests/lib/rules/no-literal-string.js @@ -90,11 +90,23 @@ ruleTester.run('no-literal-string', rule, { }, { code: '
' }, { code: '' }, - { code: '', errors }, + { code: '' }, { code: '