diff --git a/lib/rules/no-unused-styles.js b/lib/rules/no-unused-styles.js
index f91d501..ac82f2f 100644
--- a/lib/rules/no-unused-styles.js
+++ b/lib/rules/no-unused-styles.js
@@ -20,12 +20,7 @@ module.exports = Components.detect((context, components) => {
if ({}.hasOwnProperty.call(unusedStyles, key)) {
const styles = unusedStyles[key];
styles.forEach((node) => {
- const message = [
- 'Unused style detected: ',
- key,
- '.',
- node.key.name,
- ].join('');
+ const message = ['Unused style detected: ', key, '.', node.key.name].join('');
context.report(node, message);
});
@@ -41,6 +36,11 @@ module.exports = Components.detect((context, components) => {
}
},
+ VariableDeclarator: function (node) {
+ const styleRefs = astHelpers.getPotentialDestructuredStyleReferences(node);
+ styleRefs.forEach(styleReferences.add, styleReferences);
+ },
+
CallExpression: function (node) {
if (astHelpers.isStyleSheetDeclaration(node, context.settings)) {
const styleSheetName = astHelpers.getStyleSheetName(node);
@@ -53,9 +53,7 @@ module.exports = Components.detect((context, components) => {
'Program:exit': function () {
const list = components.all();
if (Object.keys(list).length > 0) {
- styleReferences.forEach((reference) => {
- styleSheets.markAsUsed(reference);
- });
+ styleReferences.forEach(styleSheets.markAsUsed, styleSheets);
reportUnusedStyles(styleSheets.getUnusedReferences());
}
},
diff --git a/lib/util/stylesheet.js b/lib/util/stylesheet.js
index ff8da41..11c0409 100644
--- a/lib/util/stylesheet.js
+++ b/lib/util/stylesheet.js
@@ -1,4 +1,3 @@
-
'use strict';
/**
@@ -32,9 +31,9 @@ StyleSheets.prototype.markAsUsed = function (fullyQualifiedName) {
const styleSheetProperty = nameSplit[1];
if (this.styleSheets[styleSheetName]) {
- this.styleSheets[styleSheetName] = this
- .styleSheets[styleSheetName]
- .filter((property) => property.key.name !== styleSheetProperty);
+ this.styleSheets[styleSheetName] = this.styleSheets[styleSheetName].filter(
+ (property) => property.key.name !== styleSheetProperty
+ );
}
};
@@ -86,11 +85,8 @@ StyleSheets.prototype.getObjectExpressions = function () {
return this.objectExpressions;
};
-
let currentContent;
-const getSourceCode = (node) => currentContent
- .getSourceCode(node)
- .getText(node);
+const getSourceCode = (node) => currentContent.getSourceCode(node).getText(node);
const getStyleSheetObjectNames = (settings) => settings['react-native/style-sheet-object-names'] || ['StyleSheet'];
@@ -98,20 +94,17 @@ const astHelpers = {
containsStyleSheetObject: function (node, objectNames) {
return Boolean(
node
- && node.type === 'CallExpression'
- && node.callee
- && node.callee.object
- && node.callee.object.name
- && objectNames.includes(node.callee.object.name)
+ && node.type === 'CallExpression'
+ && node.callee
+ && node.callee.object
+ && node.callee.object.name
+ && objectNames.includes(node.callee.object.name)
);
},
containsCreateCall: function (node) {
return Boolean(
- node
- && node.callee
- && node.callee.property
- && node.callee.property.name === 'create'
+ node && node.callee && node.callee.property && node.callee.property.name === 'create'
);
},
@@ -119,11 +112,18 @@ const astHelpers = {
const objectNames = getStyleSheetObjectNames(settings);
return Boolean(
- astHelpers.containsStyleSheetObject(node, objectNames)
- && astHelpers.containsCreateCall(node)
+ astHelpers.containsStyleSheetObject(node, objectNames) && astHelpers.containsCreateCall(node)
);
},
+ getDestructuringAssignmentParts: function (node) {
+ if (node && node.id && node.id.type === 'ObjectPattern' && node.id.properties && node.init) {
+ return [node.init.name, node.id.properties];
+ }
+
+ return [null, null];
+ },
+
getStyleSheetName: function (node) {
if (node && node.parent && node.parent.id) {
return node.parent.id.name;
@@ -200,10 +200,12 @@ const astHelpers = {
case 'Literal':
return node.value;
case 'TemplateLiteral':
- return node.quasis.reduce((result, quasi, index) => result
- + quasi.value.cooked
- + astHelpers.getExpressionIdentifier(node.expressions[index]),
- '');
+ return node.quasis.reduce(
+ (result, quasi, index) => result
+ + quasi.value.cooked
+ + astHelpers.getExpressionIdentifier(node.expressions[index]),
+ ''
+ );
default:
return '';
}
@@ -213,10 +215,7 @@ const astHelpers = {
},
getStylePropertyIdentifier: function (node) {
- if (
- node
- && node.key
- ) {
+ if (node && node.key) {
return astHelpers.getExpressionIdentifier(node.key);
}
},
@@ -224,23 +223,20 @@ const astHelpers = {
isStyleAttribute: function (node) {
return Boolean(
node.type === 'JSXAttribute'
- && node.name
- && node.name.name
- && node.name.name.toLowerCase().includes('style')
+ && node.name
+ && node.name.name
+ && node.name.name.toLowerCase().includes('style')
);
},
collectStyleObjectExpressions: function (node, context) {
currentContent = context;
if (astHelpers.hasArrayOfStyleReferences(node)) {
- const styleReferenceContainers = node
- .expression
- .elements;
+ const styleReferenceContainers = node.expression.elements;
- return astHelpers.collectStyleObjectExpressionFromContainers(
- styleReferenceContainers
- );
- } if (node && node.expression) {
+ return astHelpers.collectStyleObjectExpressionFromContainers(styleReferenceContainers);
+ }
+ if (node && node.expression) {
return astHelpers.getStyleObjectExpressionFromNode(node.expression);
}
@@ -254,13 +250,9 @@ const astHelpers = {
currentContent = context;
if (astHelpers.hasArrayOfStyleReferences(node)) {
- const styleReferenceContainers = node
- .expression
- .elements;
+ const styleReferenceContainers = node.expression.elements;
- return astHelpers.collectColorLiteralsFromContainers(
- styleReferenceContainers
- );
+ return astHelpers.collectColorLiteralsFromContainers(styleReferenceContainers);
}
if (node.type === 'ObjectExpression') {
@@ -273,8 +265,9 @@ const astHelpers = {
collectStyleObjectExpressionFromContainers: function (nodes) {
let objectExpressions = [];
nodes.forEach((node) => {
- objectExpressions = objectExpressions
- .concat(astHelpers.getStyleObjectExpressionFromNode(node));
+ objectExpressions = objectExpressions.concat(
+ astHelpers.getStyleObjectExpressionFromNode(node)
+ );
});
return objectExpressions;
@@ -283,8 +276,7 @@ const astHelpers = {
collectColorLiteralsFromContainers: function (nodes) {
let colorLiterals = [];
nodes.forEach((node) => {
- colorLiterals = colorLiterals
- .concat(astHelpers.getColorLiteralsFromNode(node));
+ colorLiterals = colorLiterals.concat(astHelpers.getColorLiteralsFromNode(node));
});
return colorLiterals;
@@ -369,10 +361,13 @@ const astHelpers = {
},
hasArrayOfStyleReferences: function (node) {
- return node && Boolean(
- node.type === 'JSXExpressionContainer'
- && node.expression
- && node.expression.type === 'ArrayExpression'
+ return (
+ node
+ && Boolean(
+ node.type === 'JSXExpressionContainer'
+ && node.expression
+ && node.expression.type === 'ArrayExpression'
+ )
);
},
@@ -408,10 +403,18 @@ const astHelpers = {
invalid = true;
obj[p.key.name] = getSourceCode(innerNode);
}
- } else if (p.value.type === 'UnaryExpression' && p.value.operator === '-' && p.value.argument.type === 'Literal') {
+ } else if (
+ p.value.type === 'UnaryExpression'
+ && p.value.operator === '-'
+ && p.value.argument.type === 'Literal'
+ ) {
invalid = true;
obj[p.key.name] = -1 * p.value.argument.value;
- } else if (p.value.type === 'UnaryExpression' && p.value.operator === '+' && p.value.argument.type === 'Literal') {
+ } else if (
+ p.value.type === 'UnaryExpression'
+ && p.value.operator === '+'
+ && p.value.argument.type === 'Literal'
+ ) {
invalid = true;
obj[p.key.name] = p.value.argument.value;
}
@@ -443,21 +446,13 @@ const astHelpers = {
},
getObjectName: function (node) {
- if (
- node
- && node.object
- && node.object.name
- ) {
+ if (node && node.object && node.object.name) {
return node.object.name;
}
},
getPropertyName: function (node) {
- if (
- node
- && node.property
- && node.property.name
- ) {
+ if (node && node.property && node.property.name) {
return node.property.name;
}
},
@@ -477,11 +472,22 @@ const astHelpers = {
}
},
+ getPotentialDestructuredStyleReferences: function (node) {
+ const [styleSheetName, properties] = this.getDestructuringAssignmentParts(node);
+
+ return styleSheetName && properties
+ ? properties.flatMap((property) => (property.key && property.key.type === 'Identifier' && property.key.name
+ ? `${styleSheetName}.${property.key.name}`
+ : []))
+ : [];
+ },
+
isEitherShortHand: function (property1, property2) {
const shorthands = ['margin', 'padding', 'border', 'flex'];
if (shorthands.includes(property1)) {
return property2.startsWith(property1);
- } if (shorthands.includes(property2)) {
+ }
+ if (shorthands.includes(property2)) {
return property1.startsWith(property2);
}
return false;
diff --git a/tests/lib/rules/no-color-literals.js b/tests/lib/rules/no-color-literals.js
index 987c4bf..185b652 100644
--- a/tests/lib/rules/no-color-literals.js
+++ b/tests/lib/rules/no-color-literals.js
@@ -37,7 +37,7 @@ const tests = {
export default class MyComponent extends Component {
render() {
const isDanger = true;
- return ;
}
@@ -57,11 +57,11 @@ const tests = {
export default class MyComponent extends Component {
render() {
const trueColor = '#fff';
- const falseColor = '#000'
- return ;
}
}
@@ -79,9 +79,11 @@ const tests = {
}
});
`,
- errors: [{
- message: 'Color literal: { backgroundColor: \'#FFFFFF\' }',
- }],
+ errors: [
+ {
+ message: "Color literal: { backgroundColor: '#FFFFFF' }",
+ },
+ ],
},
{
code: `
@@ -93,9 +95,11 @@ const tests = {
}
});
`,
- errors: [{
- message: 'Color literal: { backgroundColor: \'#FFFFFF\' }',
- }],
+ errors: [
+ {
+ message: "Color literal: { backgroundColor: '#FFFFFF' }",
+ },
+ ],
},
{
code: `
@@ -110,9 +114,11 @@ const tests = {
}
});
`,
- errors: [{
- message: 'Color literal: { fontColor: \'#000\' }',
- }],
+ errors: [
+ {
+ message: "Color literal: { fontColor: '#000' }",
+ },
+ ],
},
{
code: `
@@ -124,24 +130,28 @@ const tests = {
}
});
`,
- errors: [{
- message: 'Color literal: { backgroundColor: \'#FFFFFF\' }',
- }],
+ errors: [
+ {
+ message: "Color literal: { backgroundColor: '#FFFFFF' }",
+ },
+ ],
},
{
code: `
const Hello = React.createClass({
render: function() {
- const someBoolean = false;
+ const someBoolean = false;
return
Hello {this.props.name}
;
}
});
`,
- errors: [{
- message: 'Color literal: { backgroundColor: \'#FFFFFF\' }',
- }],
+ errors: [
+ {
+ message: "Color literal: { backgroundColor: '#FFFFFF' }",
+ },
+ ],
},
{
code: `
@@ -156,23 +166,23 @@ const tests = {
});
export default class MyComponent extends Component {
render() {
- return ;
}
}
`,
errors: [
{
- message: 'Color literal: { color: \'red\' }',
+ message: "Color literal: { color: 'red' }",
},
{
- message: 'Color literal: { borderBottomColor: \'blue\' }',
+ message: "Color literal: { borderBottomColor: 'blue' }",
},
{
- message: 'Color literal: { backgroundColor: \'someBoolean ? \\\'#fff\\\' : \\\'#000\\\'\' }', //eslint-disable-line
+ message: `Color literal: { backgroundColor: "someBoolean ? '#fff' : '#000'" }`, //eslint-disable-line
},
],
},
diff --git a/tests/lib/rules/no-inline-styles.js b/tests/lib/rules/no-inline-styles.js
index 0259ab8..d61e2f5 100644
--- a/tests/lib/rules/no-inline-styles.js
+++ b/tests/lib/rules/no-inline-styles.js
@@ -56,11 +56,11 @@ const tests = {
});
export default class MyComponent extends Component {
render() {
- const trueColor = '#fff'; const falseColor = '#000'
- return ;
}
}
@@ -154,7 +154,7 @@ const tests = {
code: `
const Hello = React.createClass({
render: function() {
- const someBoolean = false;
+ const someBoolean = false;
return
Hello {this.props.name}
;
@@ -177,16 +177,16 @@ const tests = {
});
export default class MyComponent extends Component {
render() {
- return ;
}
}
`,
errors: [{
- message: 'Inline style: { backgroundColor: \'someBoolean ? \\\'#fff\\\' : \\\'#000\\\'\' }', //eslint-disable-line
+ message: `Inline style: { backgroundColor: "someBoolean ? '#fff' : '#000'" }`, //eslint-disable-line
}],
},
],
diff --git a/tests/lib/rules/no-unused-styles.js b/tests/lib/rules/no-unused-styles.js
index 9681b33..a9b468d 100644
--- a/tests/lib/rules/no-unused-styles.js
+++ b/tests/lib/rules/no-unused-styles.js
@@ -20,8 +20,9 @@ require('babel-eslint');
const ruleTester = new RuleTester();
const tests = {
- valid: [{
- code: `
+ valid: [
+ {
+ code: `
const styles = StyleSheet.create({
name: {}
});
@@ -31,8 +32,22 @@ const tests = {
}
});
`,
- }, {
- code: `
+ },
+ {
+ code: `
+ const styles = StyleSheet.create({
+ name: {}
+ });
+ const Hello = React.createClass({
+ render: function() {
+ const { name } = styles;
+ return Hello {this.props.name};
+ }
+ });
+ `,
+ },
+ {
+ code: `
const Hello = React.createClass({
render: function() {
return Hello {this.props.name};
@@ -41,37 +56,68 @@ const tests = {
const styles = StyleSheet.create({
name: {}
});
+ `,
+ },
+ {
+ code: `
+ const Hello = React.createClass({
+ render: function() {
+ const { name } = styles;
+ return Hello {this.props.name};
+ }
+ });
+ const styles = StyleSheet.create({
+ name: {}
+ });
`,
- }, {
- code: `
+ },
+ {
+ code: `
const styles = StyleSheet.create({
name: {}
+ })
+ `,
+ },
+ {
+ code: `
+ const styles = StyleSheet.create({
+ name: {},
+ welcome: {}
});
const Hello = React.createClass({
render: function() {
return Hello {this.props.name};
}
});
+ const Welcome = React.createClass({
+ render: function() {
+ return Welcome;
+ }
+ });
`,
- }, {
- code: `
+ },
+ {
+ code: `
const styles = StyleSheet.create({
name: {},
welcome: {}
});
const Hello = React.createClass({
render: function() {
- return Hello {this.props.name};
+ const { name } = styles;
+ return Hello {this.props.name};
}
});
const Welcome = React.createClass({
render: function() {
- return Welcome;
+ const { welcome } = styles;
+ return Welcome;
}
});
`,
- }, {
- code: `
+ },
+ {
+ code: `
const styles = StyleSheet.create({
text: {}
})
@@ -84,8 +130,25 @@ const tests = {
}
});
`,
- }, {
- code: `
+ },
+ {
+ code: `
+ const styles = StyleSheet.create({
+ text: {}
+ })
+ const Hello = React.createClass({
+ propTypes: {
+ textStyle: Text.propTypes.style,
+ },
+ render: function() {
+ const { text } = styles;
+ return Hello {this.props.name};
+ }
+ });
+ `,
+ },
+ {
+ code: `
const styles = StyleSheet.create({
text: {}
})
@@ -105,14 +168,39 @@ const tests = {
}
});
`,
- }, {
- code: `
+ },
+ {
+ code: `
+ const styles = StyleSheet.create({
+ text: {}
+ })
+ const styles2 = StyleSheet.create({
+ text: {}
+ })
+ const Hello = React.createClass({
+ propTypes: {
+ textStyle: Text.propTypes.style,
+ },
+ render: function() {
+ const { text } = styles;
+ const { text: text2 } = styles2;
+ return (
+
+ Hello {this.props.name}
+
+ );
+ }
+ });
+ `,
+ },
+ {
+ code: `
const styles = StyleSheet.create({
text: {}
});
const Hello = React.createClass({
getInitialState: function() {
- return { condition: true, condition2: true };
+ return { condition: true, condition2: true };
},
render: function() {
return (
@@ -127,15 +215,40 @@ const tests = {
}
});
`,
- }, {
- code: `
+ },
+ {
+ code: `
+ const styles = StyleSheet.create({
+ text: {}
+ });
+ const Hello = React.createClass({
+ getInitialState: function() {
+ return { condition: true, condition2: true };
+ },
+ render: function() {
+ const { text } = styles;
+ return (
+
+ Hello {this.props.name}
+
+ );
+ }
+ });
+ `,
+ },
+ {
+ code: `
const styles = StyleSheet.create({
text: {},
text2: {},
});
const Hello = React.createClass({
getInitialState: function() {
- return { condition: true };
+ return { condition: true };
},
render: function() {
return (
@@ -146,8 +259,30 @@ const tests = {
}
});
`,
- }, {
- code: `
+ },
+ {
+ code: `
+ const styles = StyleSheet.create({
+ text: {},
+ text2: {},
+ });
+ const Hello = React.createClass({
+ getInitialState: function() {
+ return { condition: true };
+ },
+ render: function() {
+ const { text, text2 } = styles;
+ return (
+
+ Hello {this.props.name}
+
+ );
+ }
+ });
+ `,
+ },
+ {
+ code: `
const styles = StyleSheet.create({
style1: {
color: 'red',
@@ -165,17 +300,40 @@ const tests = {
}
}
`,
- }, {
- code: `
+ },
+ {
+ code: `
+ const styles = StyleSheet.create({
+ style1: {
+ color: 'red',
+ },
+ style2: {
+ color: 'blue',
+ }
+ });
+ export default class MyComponent extends Component {
+ static propTypes = {
+ isDanger: PropTypes.bool
+ };
+ render() {
+ const { style1, style2 } = styles;
+ return ;
+ }
+ }
+ `,
+ },
+ {
+ code: `
const styles = StyleSheet.create({
text: {}
})
`,
- }, {
- code: `
+ },
+ {
+ code: `
const Hello = React.createClass({
getInitialState: function() {
- return { condition: true };
+ return { condition: true };
},
render: function() {
const myStyle = this.state.condition ? styles.text : styles.text2;
@@ -191,8 +349,31 @@ const tests = {
text2: {},
});
`,
- }, {
- code: `
+ },
+ {
+ code: `
+ const Hello = React.createClass({
+ getInitialState: function() {
+ return { condition: true };
+ },
+ render: function() {
+ const { text, text2 } = styles;
+ const myStyle = this.state.condition ? text : text2;
+ return (
+
+ Hello {this.props.name}
+
+ );
+ }
+ });
+ const styles = StyleSheet.create({
+ text: {},
+ text2: {},
+ });
+ `,
+ },
+ {
+ code: `
const additionalStyles = {};
const styles = StyleSheet.create({
name: {},
@@ -204,8 +385,24 @@ const tests = {
}
});
`,
- }, {
- code: `
+ },
+ {
+ code: `
+ const additionalStyles = {};
+ const styles = StyleSheet.create({
+ name: {},
+ ...additionalStyles
+ });
+ const Hello = React.createClass({
+ render: function() {
+ const { name } = styles;
+ return Hello {this.props.name};
+ }
+ });
+ `,
+ },
+ {
+ code: `
const styles = OtherStyleSheet.create({
name: {},
});
@@ -215,10 +412,25 @@ const tests = {
}
});
`,
- }],
+ },
+ {
+ code: `
+ const styles = OtherStyleSheet.create({
+ name: {},
+ });
+ const Hello = React.createClass({
+ render: function() {
+ const { name } = styles;
+ return Hello {this.props.name};
+ }
+ });
+ `,
+ },
+ ],
- invalid: [{
- code: `
+ invalid: [
+ {
+ code: `
const styles = StyleSheet.create({
text: {}
})
@@ -228,11 +440,32 @@ const tests = {
}
});
`,
- errors: [{
- message: 'Unused style detected: styles.text',
- }],
- }, {
- code: `
+ errors: [
+ {
+ message: 'Unused style detected: styles.text',
+ },
+ ],
+ },
+ {
+ code: `
+ const styles = StyleSheet.create({
+ text: {}
+ })
+ const Hello = React.createClass({
+ render: function() {
+ const { b } = styles;
+ return Hello {this.props.name};
+ }
+ });
+ `,
+ errors: [
+ {
+ message: 'Unused style detected: styles.text',
+ },
+ ],
+ },
+ {
+ code: `
const styles = StyleSheet.create({
foo: {},
bar: {},
@@ -243,11 +476,33 @@ const tests = {
}
}
`,
- errors: [{
- message: 'Unused style detected: styles.bar',
- }],
- }, {
- code: `
+ errors: [
+ {
+ message: 'Unused style detected: styles.bar',
+ },
+ ],
+ },
+ {
+ code: `
+ const styles = StyleSheet.create({
+ foo: {},
+ bar: {},
+ })
+ class Foo extends React.Component {
+ render() {
+ const { foo } = styles;
+ return ;
+ }
+ }
+ `,
+ errors: [
+ {
+ message: 'Unused style detected: styles.bar',
+ },
+ ],
+ },
+ {
+ code: `
const styles = StyleSheet.create({
foo: {},
bar: {},
@@ -258,11 +513,33 @@ const tests = {
}
}
`,
- errors: [{
- message: 'Unused style detected: styles.bar',
- }],
- }, {
- code: `
+ errors: [
+ {
+ message: 'Unused style detected: styles.bar',
+ },
+ ],
+ },
+ {
+ code: `
+ const styles = StyleSheet.create({
+ foo: {},
+ bar: {},
+ })
+ class Foo extends React.PureComponent {
+ render() {
+ const { foo } = styles;
+ return ;
+ }
+ }
+ `,
+ errors: [
+ {
+ message: 'Unused style detected: styles.bar',
+ },
+ ],
+ },
+ {
+ code: `
const styles = OtherStyleSheet.create({
foo: {},
bar: {},
@@ -273,20 +550,61 @@ const tests = {
}
}
`,
- errors: [{
- message: 'Unused style detected: styles.bar',
- }],
- }, {
- code: `
+ errors: [
+ {
+ message: 'Unused style detected: styles.bar',
+ },
+ ],
+ },
+ {
+ code: `
+ const styles = OtherStyleSheet.create({
+ foo: {},
+ bar: {},
+ })
+ class Foo extends React.PureComponent {
+ render() {
+ const { foo } = styles;
+ return ;
+ }
+ }
+ `,
+ errors: [
+ {
+ message: 'Unused style detected: styles.bar',
+ },
+ ],
+ },
+ {
+ code: `
const styles = StyleSheet.create({
text: {}
})
const Hello = () => (<>Hello>);
`,
- errors: [{
- message: 'Unused style detected: styles.text',
- }],
- }],
+ errors: [
+ {
+ message: 'Unused style detected: styles.text',
+ },
+ ],
+ },
+ {
+ code: `
+ const styles = StyleSheet.create({
+ text: {}
+ })
+ const Hello = () => {
+ const { b } = styles;
+ return <>Hello>;
+ }
+ `,
+ errors: [
+ {
+ message: 'Unused style detected: styles.text',
+ },
+ ],
+ },
+ ],
};
const config = {