diff --git a/docs/rules/split-platform-components.md b/docs/rules/split-platform-components.md index 039cb8b..9d2dcc8 100644 --- a/docs/rules/split-platform-components.md +++ b/docs/rules/split-platform-components.md @@ -52,6 +52,16 @@ const Hello = React.createClass({ }); ``` +Using `import` declaration pattern: Hello.js +```js +import React from 'react' +import { ActivityIndicatiorIOS } from 'react-native' + +export default function Hello() { + return +} +``` + The following patterns are not considered warnings: filename: Hello.ios.js @@ -81,3 +91,13 @@ const Hello = React.createClass({ } }); ``` + +Using `import` declaration pattern: Hello.ios.js +```js +import React from 'react' +import { ActivityIndicatiorIOS } from 'react-native' + +export default function Hello() { + return +} +``` diff --git a/lib/rules/split-platform-components.js b/lib/rules/split-platform-components.js index 100307e..4fa5916 100644 --- a/lib/rules/split-platform-components.js +++ b/lib/rules/split-platform-components.js @@ -12,14 +12,18 @@ module.exports = function (context) { const iosMessage = 'IOS components should be placed in ios files'; const conflictMessage = 'IOS and Android components can\'t be mixed'; - function getKeyValue(node) { - const key = node.key || node.argument; - return key.type === 'Identifier' ? key.name : key.value; + function getName(node) { + if (node.type === 'Property') { + const key = node.key || node.argument; + return key.type === 'Identifier' ? key.name : key.value; + } else if (node.type === 'Identifier') { + return node.name; + } } function hasNodeWithName(nodes, name) { return nodes.some((node) => { - const nodeName = getKeyValue(node); + const nodeName = getName(node); return nodeName && nodeName.includes(name); }); } @@ -31,7 +35,7 @@ module.exports = function (context) { ); components.forEach((node) => { - const propName = getKeyValue(node); + const propName = getName(node); if (propName.includes('IOS') && !filename.endsWith('.ios.js')) { context.report(node, containsAndroidAndIOS ? conflictMessage : iosMessage); @@ -51,6 +55,15 @@ module.exports = function (context) { reactComponents = reactComponents.concat(node.id.properties); } }, + ImportDeclaration: function (node) { + if (node.source.value === 'react-native') { + node.specifiers.forEach((importSpecifier) => { + if (importSpecifier.type === 'ImportSpecifier') { + reactComponents = reactComponents.concat(importSpecifier.imported); + } + }); + } + }, 'Program:exit': function () { const filename = context.getFilename(); reportErrors(reactComponents, filename); diff --git a/tests/lib/rules/split-platform-components.js b/tests/lib/rules/split-platform-components.js index 7e4eb5c..10d8a8f 100644 --- a/tests/lib/rules/split-platform-components.js +++ b/tests/lib/rules/split-platform-components.js @@ -20,7 +20,7 @@ require('babel-eslint'); // ------------------------------------------------------------------------------ const ruleTester = new RuleTester(); -ruleTester.run('no-unused-styles', rule, { +ruleTester.run('split-platform-components', rule, { valid: [{ code: [ @@ -76,6 +76,42 @@ ruleTester.run('no-unused-styles', rule, { classes: true, jsx: true, }, + }, { + code: [ + 'import {', + ' ActivityIndicatiorIOS,', + '} from \'react-native\'', + ].join('\n'), + filename: 'Hello.ios.js', + parser: 'babel-eslint', + ecmaFeatures: { + classes: true, + jsx: true, + }, + }, { + code: [ + 'import {', + ' ProgressBarAndroid,', + '} from \'react-native\'', + ].join('\n'), + parser: 'babel-eslint', + filename: 'Hello.android.js', + ecmaFeatures: { + classes: true, + jsx: true, + }, + }, { + code: [ + 'import {', + ' View,', + '} from \'react-native\'', + ].join('\n'), + parser: 'babel-eslint', + filename: 'Hello.js', + ecmaFeatures: { + classes: true, + jsx: true, + }, }], invalid: [{ @@ -144,5 +180,53 @@ ruleTester.run('no-unused-styles', rule, { }, { message: 'IOS and Android components can\'t be mixed', }], + }, { + code: [ + 'import {', + ' ProgressBarAndroid,', + '} from \'react-native\'', + ].join('\n'), + parser: 'babel-eslint', + filename: 'Hello.js', + ecmaFeatures: { + classes: true, + jsx: true, + }, + errors: [{ + message: 'Android components should be placed in android files', + }], + }, { + code: [ + 'import {', + ' ActivityIndicatiorIOS,', + '} from \'react-native\'', + ].join('\n'), + parser: 'babel-eslint', + filename: 'Hello.js', + ecmaFeatures: { + classes: true, + jsx: true, + }, + errors: [{ + message: 'IOS components should be placed in ios files', + }], + }, { + code: [ + 'import {', + ' ActivityIndicatiorIOS,', + ' ProgressBarAndroid,', + '} from \'react-native\'', + ].join('\n'), + parser: 'babel-eslint', + filename: 'Hello.js', + ecmaFeatures: { + classes: true, + jsx: true, + }, + errors: [{ + message: 'IOS and Android components can\'t be mixed', + }, { + message: 'IOS and Android components can\'t be mixed', + }], }], });