diff --git a/Libraries/Animated/src/NativeAnimatedModule.js b/Libraries/Animated/src/NativeAnimatedModule.js index b13d6f33191bc4..80ece9d69d04cf 100644 --- a/Libraries/Animated/src/NativeAnimatedModule.js +++ b/Libraries/Animated/src/NativeAnimatedModule.js @@ -10,8 +10,8 @@ 'use strict'; -import type {TurboModule} from 'RCTExport'; -import * as TurboModuleRegistry from 'TurboModuleRegistry'; +import type {TurboModule} from '../../TurboModule/RCTExport'; +import * as TurboModuleRegistry from '../../TurboModule/TurboModuleRegistry'; type EndResult = {finished: boolean}; type EndCallback = (result: EndResult) => void; diff --git a/Libraries/BatchedBridge/__tests__/MessageQueue-test.js b/Libraries/BatchedBridge/__tests__/MessageQueue-test.js index f8568cae72d172..0c2db74b5266e5 100644 --- a/Libraries/BatchedBridge/__tests__/MessageQueue-test.js +++ b/Libraries/BatchedBridge/__tests__/MessageQueue-test.js @@ -33,7 +33,7 @@ describe('MessageQueue', function() { beforeEach(function() { jest.resetModules(); MessageQueue = require('../MessageQueue'); - MessageQueueTestModule = require('MessageQueueTestModule'); + MessageQueueTestModule = require('../__mocks__/MessageQueueTestModule'); queue = new MessageQueue(); queue.registerCallableModule( 'MessageQueueTestModule', diff --git a/Libraries/BatchedBridge/__tests__/NativeModules-test.js b/Libraries/BatchedBridge/__tests__/NativeModules-test.js index 6457385f233acb..3bc364694ed2ac 100644 --- a/Libraries/BatchedBridge/__tests__/NativeModules-test.js +++ b/Libraries/BatchedBridge/__tests__/NativeModules-test.js @@ -40,7 +40,7 @@ describe('MessageQueue', function() { beforeEach(function() { jest.resetModules(); - global.__fbBatchedBridgeConfig = require('MessageQueueTestConfig'); + global.__fbBatchedBridgeConfig = require('../__mocks__/MessageQueueTestConfig'); BatchedBridge = require('../BatchedBridge'); NativeModules = require('../NativeModules'); }); diff --git a/Libraries/Components/ActivityIndicator/ActivityIndicatorViewNativeViewConfig.js b/Libraries/Components/ActivityIndicator/ActivityIndicatorViewNativeViewConfig.js index 0e448d1d21f24e..608f02673b8625 100644 --- a/Libraries/Components/ActivityIndicator/ActivityIndicatorViewNativeViewConfig.js +++ b/Libraries/Components/ActivityIndicator/ActivityIndicatorViewNativeViewConfig.js @@ -10,9 +10,9 @@ 'use strict'; -const ReactNativeViewConfigRegistry = require('ReactNativeViewConfigRegistry'); -const ReactNativeViewViewConfig = require('ReactNativeViewViewConfig'); -const verifyComponentAttributeEquivalence = require('verifyComponentAttributeEquivalence'); +const ReactNativeViewConfigRegistry = require('../../Renderer/shims/ReactNativeViewConfigRegistry'); +const ReactNativeViewViewConfig = require('../View/ReactNativeViewViewConfig'); +const verifyComponentAttributeEquivalence = require('../../Utilities/verifyComponentAttributeEquivalence'); const ActivityIndicatorViewViewConfig = { uiViewClassName: 'RCTActivityIndicatorView', @@ -30,7 +30,7 @@ const ActivityIndicatorViewViewConfig = { ...ReactNativeViewViewConfig.validAttributes, hidesWhenStopped: true, animating: true, - color: { process: require('processColor') }, + color: { process: require('../../StyleSheet/processColor') }, size: true, }, }; diff --git a/Libraries/Components/RefreshControl/PullToRefreshViewNativeViewConfig.js b/Libraries/Components/RefreshControl/PullToRefreshViewNativeViewConfig.js index 27a1071076e540..f76c8e02dd2140 100644 --- a/Libraries/Components/RefreshControl/PullToRefreshViewNativeViewConfig.js +++ b/Libraries/Components/RefreshControl/PullToRefreshViewNativeViewConfig.js @@ -10,9 +10,9 @@ 'use strict'; -const ReactNativeViewConfigRegistry = require('ReactNativeViewConfigRegistry'); -const ReactNativeViewViewConfig = require('ReactNativeViewViewConfig'); -const verifyComponentAttributeEquivalence = require('verifyComponentAttributeEquivalence'); +const ReactNativeViewConfigRegistry = require('../../Renderer/shims/ReactNativeViewConfigRegistry'); +const ReactNativeViewViewConfig = require('../View/ReactNativeViewViewConfig'); +const verifyComponentAttributeEquivalence = require('../../Utilities/verifyComponentAttributeEquivalence'); const PullToRefreshViewViewConfig = { uiViewClassName: 'PullToRefreshView', @@ -35,8 +35,8 @@ const PullToRefreshViewViewConfig = { validAttributes: { ...ReactNativeViewViewConfig.validAttributes, - tintColor: { process: require('processColor') }, - titleColor: { process: require('processColor') }, + tintColor: { process: require('../../StyleSheet/processColor') }, + titleColor: { process: require('../../StyleSheet/processColor') }, title: true, refreshing: true, onRefresh: true, diff --git a/Libraries/Components/Slider/SliderNativeComponent.js b/Libraries/Components/Slider/SliderNativeComponent.js index a100656c2702c3..69c659d76b3dbd 100644 --- a/Libraries/Components/Slider/SliderNativeComponent.js +++ b/Libraries/Components/Slider/SliderNativeComponent.js @@ -59,4 +59,4 @@ type Options = { type SliderType = CodegenNativeComponent<'Slider', NativeProps, Options>; -module.exports = ((require('SliderNativeViewConfig'): any): SliderType); +module.exports = ((require('./SliderNativeViewConfig'): any): SliderType); diff --git a/Libraries/Components/Slider/SliderNativeViewConfig.js b/Libraries/Components/Slider/SliderNativeViewConfig.js index 92abe056836b21..166b011ec54a93 100644 --- a/Libraries/Components/Slider/SliderNativeViewConfig.js +++ b/Libraries/Components/Slider/SliderNativeViewConfig.js @@ -10,9 +10,9 @@ 'use strict'; -const ReactNativeViewConfigRegistry = require('ReactNativeViewConfigRegistry'); -const ReactNativeViewViewConfig = require('ReactNativeViewViewConfig'); -const verifyComponentAttributeEquivalence = require('verifyComponentAttributeEquivalence'); +const ReactNativeViewConfigRegistry = require('../../Renderer/shims/ReactNativeViewConfigRegistry'); +const ReactNativeViewViewConfig = require('../View/ReactNativeViewViewConfig'); +const verifyComponentAttributeEquivalence = require('../../Utilities/verifyComponentAttributeEquivalence'); const SliderViewConfig = { uiViewClassName: 'RCTSlider', @@ -48,17 +48,17 @@ const SliderViewConfig = { ...ReactNativeViewViewConfig.validAttributes, disabled: true, enabled: true, - maximumTrackImage: { process: require('resolveAssetSource') }, - maximumTrackTintColor: { process: require('processColor') }, + maximumTrackImage: { process: require('../../Image/resolveAssetSource') }, + maximumTrackTintColor: { process: require('../../StyleSheet/processColor') }, maximumValue: true, - minimumTrackImage: { process: require('resolveAssetSource') }, - minimumTrackTintColor: { process: require('processColor') }, + minimumTrackImage: { process: require('../../Image/resolveAssetSource') }, + minimumTrackTintColor: { process: require('../../StyleSheet/processColor') }, minimumValue: true, step: true, testID: true, - thumbImage: { process: require('resolveAssetSource') }, - thumbTintColor: { process: require('processColor') }, - trackImage: { process: require('resolveAssetSource') }, + thumbImage: { process: require('../../Image/resolveAssetSource') }, + thumbTintColor: { process: require('../../StyleSheet/processColor') }, + trackImage: { process: require('../../Image/resolveAssetSource') }, value: true, onChange: true, onValueChange: true, diff --git a/Libraries/NativeModules/specs/NativeDialogManagerAndroid.js b/Libraries/NativeModules/specs/NativeDialogManagerAndroid.js index 1ffb71c8ce8260..d1e1f9a7c83962 100644 --- a/Libraries/NativeModules/specs/NativeDialogManagerAndroid.js +++ b/Libraries/NativeModules/specs/NativeDialogManagerAndroid.js @@ -10,8 +10,8 @@ 'use strict'; -import type {TurboModule} from 'RCTExport'; -import * as TurboModuleRegistry from 'TurboModuleRegistry'; +import type {TurboModule} from '../../TurboModule/RCTExport'; +import * as TurboModuleRegistry from '../../TurboModule/TurboModuleRegistry'; /* 'buttonClicked' | 'dismissed' */ type DialogAction = string; diff --git a/Libraries/react-native/react-native-implementation.js b/Libraries/react-native/react-native-implementation.js index c1bfca14181584..3b3de8234e333c 100644 --- a/Libraries/react-native/react-native-implementation.js +++ b/Libraries/react-native/react-native-implementation.js @@ -11,9 +11,10 @@ 'use strict'; const invariant = require('invariant'); -const warnOnce = require('warnOnce'); +const warnOnce = require('../Utilities/warnOnce'); // Export React, plus some native additions. +/* eslint-disable @react-native-community/no-haste-imports */ module.exports = { // Components get AccessibilityInfo() { diff --git a/ReactAndroid/src/androidTest/js/ScrollViewTestModule.js b/ReactAndroid/src/androidTest/js/ScrollViewTestModule.js index 5da2b7686caa9c..3e097245800083 100644 --- a/ReactAndroid/src/androidTest/js/ScrollViewTestModule.js +++ b/ReactAndroid/src/androidTest/js/ScrollViewTestModule.js @@ -25,7 +25,7 @@ const {ScrollListener} = NativeModules; const NUM_ITEMS = 100; -import type {PressEvent} from 'CoreEventTypes'; +import type {PressEvent} from 'react-native/Libraries/Types/CoreEventTypes'; // Shared by integration tests for ScrollView and HorizontalScrollView diff --git a/package.json b/package.json index 38ae1245970930..a69cda1f1036f2 100644 --- a/package.json +++ b/package.json @@ -85,6 +85,7 @@ "@react-native-community/cli": "2.0.0-alpha.20", "@react-native-community/cli-platform-android": "2.0.0-alpha.20", "@react-native-community/cli-platform-ios": "2.0.0-alpha.20", + "@react-native-community/eslint-plugin": "file:./packages/eslint-plugin-react-native-community", "abort-controller": "^3.0.0", "art": "^0.10.0", "base64-js": "^1.1.2", diff --git a/packages/eslint-config-react-native-community/index.js b/packages/eslint-config-react-native-community/index.js index d1cfc6948713e2..e7d5c723db0a16 100644 --- a/packages/eslint-config-react-native-community/index.js +++ b/packages/eslint-config-react-native-community/index.js @@ -22,6 +22,7 @@ module.exports = { 'react', 'react-hooks', 'react-native', + '@react-native-community', 'jest', ], @@ -303,5 +304,7 @@ module.exports = { 'jest/no-focused-tests': 1, 'jest/no-identical-title': 1, 'jest/valid-expect': 1, + + '@react-native-community/no-haste-imports': 2, }, }; diff --git a/packages/eslint-config-react-native-community/package.json b/packages/eslint-config-react-native-community/package.json index 265be462a3e140..2b744b409ca74d 100644 --- a/packages/eslint-config-react-native-community/package.json +++ b/packages/eslint-config-react-native-community/package.json @@ -8,6 +8,7 @@ "url": "git@github.com:facebook/react-native.git" }, "dependencies": { + "@react-native-community/eslint-plugin": "file:../eslint-plugin-react-native-community", "@typescript-eslint/eslint-plugin": "^1.5.0", "@typescript-eslint/parser": "^1.5.0", "babel-eslint": "10.0.1", diff --git a/packages/eslint-config-react-native-community/yarn.lock b/packages/eslint-config-react-native-community/yarn.lock index e237c83e1c3f54..23df9054644744 100644 --- a/packages/eslint-config-react-native-community/yarn.lock +++ b/packages/eslint-config-react-native-community/yarn.lock @@ -90,6 +90,9 @@ lodash "^4.17.11" to-fast-properties "^2.0.0" +"@react-native-community/eslint-plugin@file:../eslint-plugin-react-native-community": + version "1.0.0" + "@typescript-eslint/eslint-plugin@^1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.5.0.tgz#85c509bcfc2eb35f37958fa677379c80b7a8f66f" diff --git a/packages/eslint-plugin-react-native-community/README.md b/packages/eslint-plugin-react-native-community/README.md new file mode 100644 index 00000000000000..262c7e92171884 --- /dev/null +++ b/packages/eslint-plugin-react-native-community/README.md @@ -0,0 +1,21 @@ +# eslint-plugin-react-native-community + +This plugin is intended to be used in `@react-native-community/eslint-plugin`. You probably want to install that package instead. + +## Installation + +``` +yarn add --dev eslint @react-native-community/eslint-plugin +``` + +*Note: We're using `yarn` to install deps. Feel free to change commands to use `npm` 3+ and `npx` if you like* + +## Usage + +Add to your eslint config (`.eslintrc`, or `eslintConfig` field in `package.json`): + +```json +{ + "plugins": ["@react-native-community"] +} +``` diff --git a/packages/eslint-plugin-react-native-community/index.js b/packages/eslint-plugin-react-native-community/index.js new file mode 100644 index 00000000000000..2cc6bbd9d87d95 --- /dev/null +++ b/packages/eslint-plugin-react-native-community/index.js @@ -0,0 +1,3 @@ +exports.rules = { + 'no-haste-imports': require('./no-haste-imports'), +}; diff --git a/packages/eslint-plugin-react-native-community/no-haste-imports.js b/packages/eslint-plugin-react-native-community/no-haste-imports.js new file mode 100644 index 00000000000000..0b4353711e0482 --- /dev/null +++ b/packages/eslint-plugin-react-native-community/no-haste-imports.js @@ -0,0 +1,64 @@ +module.exports = { + meta: { + type: 'problem', + docs: { + description: + 'disallow Haste module names in import statements and require calls', + }, + schema: [], + }, + + create(context) { + return { + ImportDeclaration(node) { + checkImportForHaste(context, node.source.value, node.source); + }, + CallExpression(node) { + if (isStaticRequireCall(node)) { + const [firstArgument] = node.arguments; + checkImportForHaste(context, firstArgument.value, firstArgument); + } + }, + }; + }, +}; + +function checkImportForHaste(context, importPath, node) { + if (isLikelyHasteModuleName(importPath)) { + context.report({ + node, + message: `"${importPath}" appears to be a Haste module name. Use path-based imports instead.`, + }); + } +} + +function isLikelyHasteModuleName(importPath) { + // Our heuristic assumes an import path is a Haste module name if it is not a + // path and doesn't appear to be an npm package. For several years, npm has + // disallowed uppercase characters in package names. + // + // This heuristic has a ~1% false negative rate for the filenames in React + // Native, which is acceptable since the linter will not complain wrongly and + // the rate is so low. False negatives that slip through will be caught by + // tests with Haste disabled. + return ( + // Exclude relative paths + !importPath.startsWith('.') && + // Exclude package-internal paths and scoped packages + !importPath.includes('/') && + // Include camelCase and UpperCamelCase + /[A-Z]/.test(importPath) + ); +} + +function isStaticRequireCall(node) { + return ( + node && + node.callee && + node.callee.type === 'Identifier' && + node.callee.name === 'require' && + node.arguments.length === 1 && + node.arguments[0].type === 'Literal' && + typeof node.arguments[0].value === 'string' + ); +} diff --git a/packages/eslint-plugin-react-native-community/package.json b/packages/eslint-plugin-react-native-community/package.json new file mode 100644 index 00000000000000..5a6c28f26d0281 --- /dev/null +++ b/packages/eslint-plugin-react-native-community/package.json @@ -0,0 +1,11 @@ +{ + "name": "@react-native-community/eslint-plugin", + "version": "1.0.0", + "description": "ESLint rules for @react-native-community/eslint-config", + "main": "index.js", + "repository": { + "type": "git", + "url": "git@github.com:facebook/react-native.git" + }, + "license": "MIT" +} diff --git a/yarn.lock b/yarn.lock index 000f16ca52c9c9..2c994daaa9bed6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1160,6 +1160,9 @@ shell-quote "1.6.1" ws "^1.1.0" +"@react-native-community/eslint-plugin@file:./packages/eslint-plugin-react-native-community": + version "1.0.0" + "@reactions/component@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@reactions/component/-/component-2.0.2.tgz#40f8c1c2c37baabe57a0c944edb9310dc1ec6642"