eslint-plugin-i18next
Advanced tools
Comparing version 2.3.0 to 3.2.0
@@ -5,2 +5,56 @@ # Changelog | ||
## [3.2.0](https://github.com/edvardchen/eslint-plugin-i18next/compare/v3.1.1...v3.2.0) (2019-10-21) | ||
### Features | ||
* allow displayName property in classes ([5362281](https://github.com/edvardchen/eslint-plugin-i18next/commit/5362281)) | ||
### [3.1.1](https://github.com/edvardchen/eslint-plugin-i18next/compare/v3.1.0...v3.1.1) (2019-10-10) | ||
### Bug Fixes | ||
* add missing plugin in recommended config ([dde83ed](https://github.com/edvardchen/eslint-plugin-i18next/commit/dde83ed)) | ||
## [3.1.0](https://github.com/edvardchen/eslint-plugin-i18next/compare/v3.0.0...v3.1.0) (2019-10-10) | ||
### Features | ||
* ignore not-word string ([1752cbe](https://github.com/edvardchen/eslint-plugin-i18next/commit/1752cbe)) | ||
## [3.0.0](https://github.com/edvardchen/eslint-plugin-i18next/compare/v2.5.0...v3.0.0) (2019-10-09) | ||
### ⚠ BREAKING CHANGES | ||
* SInce the whitelist was cut short, it would complain when the removed attributes | ||
were added to custom component like <Foo src="hello" /> | ||
### Features | ||
* ignore most DOM attrs ([71483c2](https://github.com/edvardchen/eslint-plugin-i18next/commit/71483c2)) | ||
## [2.5.0](https://github.com/edvardchen/eslint-plugin-i18next/compare/v2.4.0...v2.5.0) (2019-10-08) | ||
### Features | ||
* add more ignored attributes and callee ([0f9e2ec](https://github.com/edvardchen/eslint-plugin-i18next/commit/0f9e2ec)) | ||
## [2.4.0](https://github.com/edvardchen/eslint-plugin-i18next/compare/v2.3.1...v2.4.0) (2019-10-08) | ||
### Features | ||
* add ignoreAttribute option ([c854313](https://github.com/edvardchen/eslint-plugin-i18next/commit/c854313)) | ||
### [2.3.1](https://github.com/edvardchen/eslint-plugin-i18next/compare/v2.3.0...v2.3.1) (2019-09-16) | ||
### Bug Fixes | ||
* whitelist addEventListener and few SVG attributes ([46241a6](https://github.com/edvardchen/eslint-plugin-i18next/commit/46241a6)) | ||
## [2.3.0](https://github.com/edvardchen/eslint-plugin-i18next/compare/v2.2.0...v2.3.0) (2019-07-26) | ||
@@ -7,0 +61,0 @@ |
@@ -0,1 +1,3 @@ | ||
const { DOM_TAGS, SVG_TAGS } = require('./constants'); | ||
function isUpperCase(str) { | ||
@@ -5,2 +7,20 @@ return /^[A-Z_-]+$/.test(str); | ||
function isNativeDOMTag(str) { | ||
return DOM_TAGS.includes(str); | ||
} | ||
function isSvgTag(str) { | ||
return SVG_TAGS.includes(str); | ||
} | ||
const blacklistAttrs = ['placeholder', 'alt', 'aria-label', 'value']; | ||
function isAllowedDOMAttr(tag, attr) { | ||
if (isSvgTag(tag)) return true; | ||
if (isNativeDOMTag(tag)) { | ||
return !blacklistAttrs.includes(attr); | ||
} | ||
return false; | ||
} | ||
exports.isUpperCase = isUpperCase; | ||
exports.isAllowedDOMAttr = isAllowedDOMAttr; |
@@ -22,2 +22,3 @@ /** | ||
recommended: { | ||
plugins: ['i18next'], | ||
rules: { | ||
@@ -24,0 +25,0 @@ 'i18next/no-literal-string': [2] |
@@ -7,3 +7,3 @@ /** | ||
const { isUpperCase } = require('../helper'); | ||
const { isUpperCase, isAllowedDOMAttr } = require('../helper'); | ||
// const { TypeFlags, SyntaxKind } = require('typescript'); | ||
@@ -37,2 +37,8 @@ | ||
} | ||
}, | ||
ignoreAttribute: { | ||
type: 'array', | ||
items: { | ||
type: 'string' | ||
} | ||
} | ||
@@ -51,5 +57,6 @@ }, | ||
} = context; | ||
const whitelists = ((option && option.ignore) || []).map( | ||
item => new RegExp(item) | ||
); | ||
const whitelists = [ | ||
/^[^A-Za-z]+$/, // ignore not-word string | ||
...((option && option.ignore) || []) | ||
].map(item => new RegExp(item)); | ||
@@ -78,8 +85,21 @@ const calleeWhitelists = generateCalleeWhitelists(option); | ||
if (calleeName === 'require') return true; | ||
return calleeWhitelists.simple.indexOf(calleeName) !== -1; | ||
} | ||
const atts = ['className', 'style', 'styleName', 'src', 'type', 'id']; | ||
const ignoredClassProperties = ['displayName']; | ||
const ignoredAttributes = (option && option.ignoreAttribute) || []; | ||
const userJSXAttrs = [ | ||
'className', | ||
'styleName', | ||
'type', | ||
'id', | ||
'width', | ||
'height', | ||
...ignoredAttributes | ||
]; | ||
function isValidAttrName(name) { | ||
return atts.includes(name); | ||
return userJSXAttrs.includes(name); | ||
} | ||
@@ -143,7 +163,15 @@ | ||
const parent = getNearestAncestor(node, 'JSXAttribute'); | ||
const attrName = parent.name.name; | ||
// allow <div className="active" /> | ||
if (isValidAttrName(parent.name.name)) { | ||
// allow <MyComponent className="active" /> | ||
if (isValidAttrName(attrName)) { | ||
visited.add(node); | ||
return; | ||
} | ||
const jsxElement = getNearestAncestor(node, 'JSXOpeningElement'); | ||
const tagName = jsxElement.name.name; | ||
if (isAllowedDOMAttr(tagName, attrName)) { | ||
visited.add(node); | ||
} | ||
}, | ||
@@ -174,2 +202,10 @@ | ||
'ClassProperty > Literal'(node) { | ||
const { parent } = node; | ||
if (parent.key && ignoredClassProperties.includes(parent.key.name)) { | ||
visited.add(node); | ||
} | ||
}, | ||
'VariableDeclarator > Literal'(node) { | ||
@@ -288,2 +324,6 @@ // allow statements like const A_B = "test" | ||
const popularCallee = [ | ||
'addEventListener', | ||
'removeEventListener', | ||
'postMessage', | ||
'getElementById', | ||
// | ||
@@ -297,3 +337,5 @@ // ─── VUEX CALLEE ──────────────────────────────────────────────────────────────── | ||
'includes', | ||
'indexOf' | ||
'indexOf', | ||
'endsWith', | ||
'startsWith' | ||
]; | ||
@@ -300,0 +342,0 @@ function generateCalleeWhitelists(option) { |
{ | ||
"name": "eslint-plugin-i18next", | ||
"version": "2.3.0", | ||
"version": "3.2.0", | ||
"description": "ESLint plugin for i18n", | ||
@@ -35,3 +35,5 @@ "keywords": [ | ||
"husky": "^1.3.1", | ||
"lint-staged": "^9.4.2", | ||
"mocha": "^6.1.4", | ||
"prettier": "^1.18.2", | ||
"typescript": "^3.5.2", | ||
@@ -43,4 +45,11 @@ "vue-eslint-parser": "^6.0.3" | ||
}, | ||
"lint-staged": { | ||
"*.js": [ | ||
"prettier --write", | ||
"git add" | ||
] | ||
}, | ||
"husky": { | ||
"hooks": { | ||
"pre-commit": "lint-staged", | ||
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS" | ||
@@ -47,0 +56,0 @@ } |
@@ -189,2 +189,16 @@ # eslint-plugin-i18next | ||
#### SwithCase | ||
Skip switchcase statement: | ||
```typescript | ||
// correct | ||
switch (type) { | ||
case 'foo': | ||
break; | ||
case 'bar': | ||
break; | ||
} | ||
``` | ||
### Options | ||
@@ -215,1 +229,12 @@ | ||
``` | ||
#### ignoreAttribute | ||
The `ignoreAttribute` option specifies exceptions not to check for JSX attributes that match one of ignored attributes. | ||
Examples of correct code for the `{ "ignoreAttribute": ["foo"] }` option: | ||
```jsx | ||
/*eslint i18next/no-literal-string: ["error", { "ignoreAttribute": ["foo"] }]*/ | ||
const element = <div foo="bar" />; | ||
``` |
@@ -38,4 +38,16 @@ /** | ||
{ code: 'a.includes("ios")' }, | ||
{ code: 'a.startsWith("ios")' }, | ||
{ code: 'a.endsWith("@gmail.com")' }, | ||
{ code: 'export * from "hello_export_all";' }, | ||
{ code: 'export { a } from "hello_export";' }, | ||
{ | ||
code: | ||
'document.addEventListener("click", (event) => { event.preventDefault() })' | ||
}, | ||
{ | ||
code: | ||
'document.removeEventListener("click", (event) => { event.preventDefault() })' | ||
}, | ||
{ code: 'window.postMessage("message", "*")' }, | ||
{ code: 'document.getElementById("some-id")' }, | ||
{ code: 'require("hello");' }, | ||
@@ -45,2 +57,4 @@ { code: 'const a = require(["hello"]);' }, | ||
{ code: 'const a = 1;' }, | ||
{ code: 'const a = "?";' }, | ||
{ code: `const a = "0123456789!@#$%^&*()_+|~-=\`[]{};':\\",./<>?";` }, | ||
{ code: 'i18n("hello");' }, | ||
@@ -59,6 +73,25 @@ { code: 'dispatch("hello");' }, | ||
{ code: 'var a = {foo: "FOO"};' }, | ||
{ code: 'class Form extends Component { displayName = "FormContainer" };' }, | ||
// JSX | ||
{ code: '<div className="primary"></div>' }, | ||
{ code: '<div className={a ? "active": "inactive"}></div>' }, | ||
{ code: '<div>{i18next.t("foo")}</div>' } | ||
{ code: '<div>{i18next.t("foo")}</div>' }, | ||
{ code: '<svg viewBox="0 0 20 40"></svg>' }, | ||
{ code: '<line x1="0" y1="0" x2="10" y2="20" />' }, | ||
{ code: '<path d="M10 10" />' }, | ||
{ | ||
code: | ||
'<circle width="16px" height="16px" cx="10" cy="10" r="2" fill="red" />' | ||
}, | ||
{ | ||
code: | ||
'<a href="https://google.com" target="_blank" rel="noreferrer noopener"></a>' | ||
}, | ||
{ | ||
code: '<div id="some-id" tabIndex="0" aria-labelledby="label-id"></div>' | ||
}, | ||
{ code: '<div role="button"></div>' }, | ||
{ code: '<img src="./image.png" />' }, | ||
{ code: '<button type="button" for="form-id" />' }, | ||
{ code: '<DIV foo="bar" />', options: [{ ignoreAttribute: ['foo'] }] } | ||
], | ||
@@ -77,5 +110,9 @@ | ||
{ code: 'const a = "afoo";', options: [{ ignore: ['^foo'] }], errors }, | ||
{ code: 'class Form extends Component { property = "Something" };', errors }, | ||
// JSX | ||
{ code: '<div>foo</div>', errors }, | ||
{ code: '<div>FOO</div>', errors } | ||
{ code: '<div>FOO</div>', errors }, | ||
{ code: '<DIV foo="bar" />', errors }, | ||
{ code: '<img src="./image.png" alt="some-image" />', errors }, | ||
{ code: '<button aria-label="Close" type="button" />', errors } | ||
] | ||
@@ -82,0 +119,0 @@ }); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
38334
11
768
239
11
178009