From b9d848ea433dd684ed59e21c21359e98fc5c11a5 Mon Sep 17 00:00:00 2001 From: Axel Eirola Date: Thu, 2 May 2019 17:31:22 +0300 Subject: [PATCH 1/3] Add no-unused-styles default import support --- lib/util/stylesheet.js | 57 +++++++++++++++++++++++++++-- tests/lib/rules/no-unused-styles.js | 41 +++++++++++++++++++++ 2 files changed, 95 insertions(+), 3 deletions(-) diff --git a/lib/util/stylesheet.js b/lib/util/stylesheet.js index 0b8a6ef..b7f2c53 100644 --- a/lib/util/stylesheet.js +++ b/lib/util/stylesheet.js @@ -101,9 +101,16 @@ const astHelpers = { node && node.type === 'CallExpression' && node.callee && - node.callee.object && - node.callee.object.name && - objectNames.includes(node.callee.object.name) + node.callee.object && (( + node.callee.object.type === 'Identifier' && + node.callee.object.name && + objectNames.includes(node.callee.object.name) + ) || ( + node.callee.object.type === 'MemberExpression' && + node.callee.object.object.type === 'Identifier' && + astHelpers.getImportSource(node.callee.object.object) === 'react-native' && + objectNames.includes(node.callee.object.property.name) + )) ); }, @@ -463,6 +470,50 @@ const astHelpers = { } }, + getRoot: function (node) { + let currentNode = node; + while (currentNode && currentNode.parent) { + currentNode = currentNode.parent; + } + + return currentNode; + }, + + getImportVariable: function (node) { + if (node && node.type === 'ImportDeclaration') { + for (let i = 0; node.specifiers; i += 1) { + const specifier = node.specifiers[i]; + if ( + specifier && ( + specifier.type === 'ImportDefaultSpecifier' || + specifier.type === 'ImportNamespaceSpecifier' + ) && + specifier.local && + specifier.local.type === 'Identifier' + ) { + return specifier.local.name; + } + } + } + }, + + getImportSource: function (node) { + if (node && node.type === 'Identifier' && node.name) { + const rootNode = astHelpers.getRoot(node); + if (rootNode && rootNode.body) { + for (let i = 0; rootNode.body.length; i += 1) { + const bodyNode = rootNode.body[i]; + if ( + astHelpers.getImportVariable(bodyNode) === node.name && + bodyNode.source && bodyNode.source.type === 'Literal' + ) { + return bodyNode.source.value; + } + } + } + } + }, + getPotentialStyleReferenceFromMemberExpression: function (node) { if ( node && diff --git a/tests/lib/rules/no-unused-styles.js b/tests/lib/rules/no-unused-styles.js index c99d3e6..ee6d4f7 100644 --- a/tests/lib/rules/no-unused-styles.js +++ b/tests/lib/rules/no-unused-styles.js @@ -215,6 +215,31 @@ const tests = { } }); `, + }, { + code: ` + import RN from 'react-native'; + const styles = RN.StyleSheet.create({ + name: {} + }); + const Hello = React.createClass({ + render: function() { + return Hello {this.props.name}; + } + }); + `, + }, { + code: ` + import React from 'react'; + import RN from 'react-native'; + const styles = RN.StyleSheet.create({ + name: {} + }); + const Hello = React.createClass({ + render: function() { + return Hello {this.props.name}; + } + }); + `, }], invalid: [{ @@ -276,6 +301,22 @@ const tests = { errors: [{ message: 'Unused style detected: styles.bar', }], + }, { + code: ` + import RN from 'react-native'; + const styles = RN.StyleSheet.create({ + name: {} + }); + const Hello = React.createClass({ + render: function() { + return Hello {this.props.name}; + } + }); + `, + errors: [{ + message: 'Unused style detected: styles.name', + }], + }], }; From 437ab6ac0e90b462a978d278cdea6163b40d0427 Mon Sep 17 00:00:00 2001 From: Axel Eirola Date: Mon, 6 May 2019 11:24:52 +0300 Subject: [PATCH 2/3] Move expensive check last --- lib/util/stylesheet.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/util/stylesheet.js b/lib/util/stylesheet.js index b7f2c53..8adf504 100644 --- a/lib/util/stylesheet.js +++ b/lib/util/stylesheet.js @@ -108,8 +108,8 @@ const astHelpers = { ) || ( node.callee.object.type === 'MemberExpression' && node.callee.object.object.type === 'Identifier' && - astHelpers.getImportSource(node.callee.object.object) === 'react-native' && - objectNames.includes(node.callee.object.property.name) + objectNames.includes(node.callee.object.property.name) && + astHelpers.getImportSource(node.callee.object.object) === 'react-native' )) ); }, From 1e915e8293ce63c8f8b85987289fbaa3571f8e50 Mon Sep 17 00:00:00 2001 From: Axel Eirola Date: Tue, 7 May 2019 16:05:43 +0300 Subject: [PATCH 3/3] Fix loop style --- lib/util/stylesheet.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/util/stylesheet.js b/lib/util/stylesheet.js index 8adf504..0edc46e 100644 --- a/lib/util/stylesheet.js +++ b/lib/util/stylesheet.js @@ -481,7 +481,7 @@ const astHelpers = { getImportVariable: function (node) { if (node && node.type === 'ImportDeclaration') { - for (let i = 0; node.specifiers; i += 1) { + for (let i = 0; i < node.specifiers.length; i += 1) { const specifier = node.specifiers[i]; if ( specifier && ( @@ -501,7 +501,7 @@ const astHelpers = { if (node && node.type === 'Identifier' && node.name) { const rootNode = astHelpers.getRoot(node); if (rootNode && rootNode.body) { - for (let i = 0; rootNode.body.length; i += 1) { + for (let i = 0; i < rootNode.body.length; i += 1) { const bodyNode = rootNode.body[i]; if ( astHelpers.getImportVariable(bodyNode) === node.name &&