Skip to content

Commit

Permalink
feat: support option jsx-attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
edvardchen committed May 7, 2022
1 parent 484fcfb commit 08ae48b
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 65 deletions.
17 changes: 7 additions & 10 deletions lib/helper/shouldSkip.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
const matchPatterns = require('./matchPatterns');

module.exports = function shouldSkip({ exclude = [], include = [] }, text) {
let skipped = 0;
if (exclude.length) {
if (matchPatterns(exclude, text)) {
skipped++;
if (include.length || exclude.length) {
if (
(!exclude.length || matchPatterns(exclude, text)) &&
!matchPatterns(include, text)
) {
return true;
}
}
if (include.length) {
if (matchPatterns(include, text)) {
skipped--;
}
}
return skipped > 0;
return false;
};
45 changes: 5 additions & 40 deletions lib/rules/no-literal-string.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,7 @@
'use strict';

const _ = require('lodash');
const {
isUpperCase,
generateFullMatchRegExp,
isAllowedDOMAttr,
shouldSkip,
} = require('../helper');
const { isUpperCase, isAllowedDOMAttr, shouldSkip } = require('../helper');

//------------------------------------------------------------------------------
// Rule Definition
Expand All @@ -29,20 +24,16 @@ module.exports = {
create(context) {
// variables should be defined here
const { parserServices } = context;
const options = _.defaultsDeep(
const options = _.defaults(
{},
context.options[0],
require('../options/defaults.json')
);

const {
onlyAttribute = [],
markupOnly: _markupOnly,
validateTemplate,
ignoreComponent = [],
ignoreAttribute = [],
ignoreProperty = [],
ignoreCallee = [],
} = options;

const message = 'disallow literal string';
Expand All @@ -64,44 +55,19 @@ module.exports = {
}

function isValidFunctionCall({ callee }) {
let calleeName = callee.name;
if (callee.type === 'Import') return true;

const sourceText = context.getSourceCode().getText(callee);

return shouldSkip(options.callees, sourceText);
}

const ignoredClassProperties = ['displayName'];

const userJSXAttrs = [
'className',
'styleName',
'style',
'type',
'key',
'id',
'width',
'height',

...ignoreAttribute,
];
function isValidAttrName(name) {
if (onlyAttribute.length) {
// only validate those attributes in onlyAttribute option
return !onlyAttribute.includes(name);
}
return userJSXAttrs.includes(name);
}

// Ignore the Trans component for react-i18next compatibility
const ignoredComponents = ['Trans', ...ignoreComponent];

//----------------------------------------------------------------------
// Public
//----------------------------------------------------------------------
const visited = new WeakSet();

function getNearestAncestor(node, type) {
let temp = node.parent;
while (temp) {
Expand Down Expand Up @@ -164,8 +130,7 @@ module.exports = {
context.report({ node, message });
}

// onlyAttribute would turn on markOnly
const markupOnly = _markupOnly || !!onlyAttribute.length;
const markupOnly = _markupOnly;

function endIndicator() {
indicatorStack.pop();
Expand Down Expand Up @@ -224,7 +189,7 @@ module.exports = {
const attrName = node.name.name;

// allow <MyComponent className="active" />
if (isValidAttrName(attrName)) {
if (shouldSkip(options['jsx-attributes'], attrName)) {
indicatorStack.push(true);
return;
}
Expand Down Expand Up @@ -286,7 +251,7 @@ module.exports = {

ClassProperty(node) {
indicatorStack.push(
!!(node.key && ignoredClassProperties.includes(node.key.name))
!!(node.key && shouldSkip(options['class-properties'], node.key.name))
);
},
'ClassProperty:exit': endIndicator,
Expand Down
36 changes: 21 additions & 15 deletions tests/lib/rules/no-literal-string.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,30 +57,34 @@ ruleTester.run('no-literal-string', rule, {
encoding: 'utf8',
}),
},
{ code: '<DIV foo="bar" />', options: [{ ignoreAttribute: ['foo'] }] },
{
code: '<DIV foo="bar" />',
options: [{ 'jsx-attributes': { exclude: ['foo'] } }],
},
{ code: '<Trans>foo</Trans>', options: [{ ignoreComponent: ['Icon'] }] },
{ code: '<Icon>arrow</Icon>', options: [{ ignoreComponent: ['Icon'] }] },
{
code: `a + "b"
const c = <div>{import("abc")}</div>
const d = <div>{[].map(item=>"abc")}</div>
const e = <div>{"hello" + "world"}</div>
const f = <DIV foo="FOO" />
`,
const c = <div>{import("abc")}</div>
const d = <div>{[].map(item=>"abc")}</div>
const e = <div>{"hello" + "world"}</div>
const f = <DIV foo="FOO" />
`,
options: [{ markupOnly: true }],
},
{
code: '<DIV foo="bar" />',
options: [{ markupOnly: true, ignoreAttribute: ['foo'] }],
code: '<DIV foo="bar1" />',
options: [{ markupOnly: true, 'jsx-attributes': { exclude: ['foo'] } }],
},
{
code: '<DIV foo="bar2" />',
options: [{ 'jsx-attributes': { include: ['bar'] } }],
},
// when onlyAttribute was configured, the markOnly would be treated as true
{ code: 'const a = "foo";', options: [{ onlyAttribute: ['bar'] }] },
{ code: '<DIV foo="bar" />', options: [{ onlyAttribute: ['bar'] }] },
{
code: `import(\`hello\`);
var a = \`12345\`
var a = \`\`
`,
var a = \`12345\`
var a = \`\`
`,
options: [{ validateTemplate: true }],
},
],
Expand Down Expand Up @@ -175,7 +179,9 @@ const tsTester = new RuleTester({

tsTester.run('no-literal-string', rule, {
valid: [
{ code: `declare module 'country-emoji' {}` },
{
code: `declare module 'country-emoji' {}`,
},
{ code: '<div className="hello"></div>', filename: 'a.tsx' },
{ code: "var a: Element['nodeName']" },
{ code: "var a: Omit<T, 'af'>" },
Expand Down

0 comments on commit 08ae48b

Please sign in to comment.