diff --git a/lib/rules/no-literal-string.js b/lib/rules/no-literal-string.js index 0218efc..351a87d 100644 --- a/lib/rules/no-literal-string.js +++ b/lib/rules/no-literal-string.js @@ -23,10 +23,8 @@ module.exports = { type: 'object', properties: { ignore: { - type: 'array', - items: { - type: 'string' - } + type: 'array' + // string or regexp }, ignoreAttribute: { type: 'array', @@ -35,10 +33,8 @@ module.exports = { } }, ignoreCallee: { - type: 'array', - items: { - type: 'string' - } + type: 'array' + // string or regexp }, ignoreProperty: { type: 'array', @@ -66,7 +62,6 @@ module.exports = { ...((option && option.ignore) || []) ].map(item => new RegExp(item)); - const calleeWhitelists = generateCalleeWhitelists(option); const message = 'disallow literal string'; //---------------------------------------------------------------------- // Helpers @@ -75,21 +70,47 @@ module.exports = { return whitelists.some(item => item.test(str)); } + const popularCallee = [ + /^i18n(ext)?$/, + 't', + 'require', + 'addEventListener', + 'removeEventListener', + 'postMessage', + 'getElementById', + // + // ─── VUEX CALLEE ──────────────────────────────────────────────────────────────── + // + 'dispatch', + 'commit', + // ──────────────────────────────────────────────────────────────────────────────── + + 'includes', + 'indexOf', + 'endsWith', + 'startsWith' + ]; + + const validCalleeList = [ + ...popularCallee, + ...((option && option.ignoreCallee) || []) + ].map(item => { + if (item instanceof RegExp) { + return item; + } + // allow dot ahead + return new RegExp(`(^|\\.)${item}${item.endsWith('$') ? '' : '$'}`); + }); + function isValidFunctionCall({ callee }) { let calleeName = callee.name; if (callee.type === 'Import') return true; - if (callee.type === 'MemberExpression') { - if (calleeWhitelists.simple.indexOf(callee.property.name) !== -1) - return true; - - calleeName = `${callee.object.name}.${callee.property.name}`; - return calleeWhitelists.complex.indexOf(calleeName) !== -1; - } - - if (calleeName === 'require') return true; + const sourceText = context.getSourceCode().getText(callee); - return calleeWhitelists.simple.indexOf(calleeName) !== -1; + return validCalleeList.some(item => { + return item.test(sourceText); + }); } const ignoredObjectProperties = (option && option.ignoreProperty) || []; @@ -380,36 +401,3 @@ module.exports = { ); } }; - -const popularCallee = [ - 'addEventListener', - 'removeEventListener', - 'postMessage', - 'getElementById', - // - // ─── VUEX CALLEE ──────────────────────────────────────────────────────────────── - // - 'dispatch', - 'commit', - // ──────────────────────────────────────────────────────────────────────────────── - - 'includes', - 'indexOf', - 'endsWith', - 'startsWith' -]; -function generateCalleeWhitelists(option) { - const ignoreCallee = (option && option.ignoreCallee) || []; - const result = { - simple: ['i18n', 'i18next', ...popularCallee], - complex: ['i18n.t', 'i18next.t'] - }; - ignoreCallee.forEach(item => { - if (item.indexOf('.') !== -1) { - result.complex.push(item); - } else { - result.simple.push(item); - } - }); - return result; -} diff --git a/tests/lib/rules/no-literal-string.js b/tests/lib/rules/no-literal-string.js index e96e03b..51b48a4 100644 --- a/tests/lib/rules/no-literal-string.js +++ b/tests/lib/rules/no-literal-string.js @@ -61,7 +61,15 @@ ruleTester.run('no-literal-string', rule, { { code: 'store.commit("hello");' }, { code: 'i18n.t("hello");' }, { code: 'const a = "absfoo";', options: [{ ignore: ['foo'] }] }, - { code: 'const a = "fooabc";', options: [{ ignore: ['^foo'] }] }, + { code: 'const a = "fooabc";', options: [{ ignore: [/^foo/] }] }, + { + code: 'foo.bar("taa");', + options: [ + { + ignoreCallee: [/foo.+/] + } + ] + }, { code: 'const a = "FOO";' }, { code: 'var A_B = "world";' }, { code: 'var a = {["A_B"]: "hello world"};' }, @@ -110,6 +118,7 @@ ruleTester.run('no-literal-string', rule, { ], invalid: [ + { code: 'i18nextXt("taa");', errors }, { code: 'a + "b"', errors }, { code: "switch(a){ case 'a': var a ='b'; break; default: break;}",