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 = { pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy