From ecc0422c8c3fcd49a270155c0bbbb8ae7c92fa9e Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Sat, 7 Apr 2018 14:59:18 -0700 Subject: [PATCH] Move view config registry to shims This ensures that both Fabric and RN renderers share the same view config registry since it is stateful. I had to duplicate in the mocks for testing. --- .../src/ReactFabricRenderer.js | 2 +- .../src/ReactNativeBridgeEventPlugin.js | 2 +- .../src/ReactNativeFiberRenderer.js | 2 +- .../ReactNativeViewConfigRegistry.js | 21 ++-- .../src/createReactNativeComponentClass.js | 2 +- scripts/flow/react-native-host-hooks.js | 17 ++- scripts/rollup/bundles.js | 2 + .../ReactNativeViewConfigRegistry.js | 106 ++++++++++++++++++ 8 files changed, 136 insertions(+), 18 deletions(-) rename packages/react-native-renderer/src/{ => __mocks__}/ReactNativeViewConfigRegistry.js (84%) create mode 100644 scripts/rollup/shims/react-native/ReactNativeViewConfigRegistry.js diff --git a/packages/react-native-renderer/src/ReactFabricRenderer.js b/packages/react-native-renderer/src/ReactFabricRenderer.js index b19fd3f46a6f6..e9a1f2610fad7 100644 --- a/packages/react-native-renderer/src/ReactFabricRenderer.js +++ b/packages/react-native-renderer/src/ReactFabricRenderer.js @@ -18,7 +18,7 @@ import type { import {mountSafeCallback, warnForStyleProps} from './NativeMethodsMixinUtils'; import * as ReactNativeAttributePayload from './ReactNativeAttributePayload'; import * as ReactNativeFrameScheduling from './ReactNativeFrameScheduling'; -import * as ReactNativeViewConfigRegistry from './ReactNativeViewConfigRegistry'; +import * as ReactNativeViewConfigRegistry from 'ReactNativeViewConfigRegistry'; import ReactFiberReconciler from 'react-reconciler'; import ReactNativeTagHandles from './ReactNativeTagHandles'; diff --git a/packages/react-native-renderer/src/ReactNativeBridgeEventPlugin.js b/packages/react-native-renderer/src/ReactNativeBridgeEventPlugin.js index 19f6938d52648..69bc7a260a558 100644 --- a/packages/react-native-renderer/src/ReactNativeBridgeEventPlugin.js +++ b/packages/react-native-renderer/src/ReactNativeBridgeEventPlugin.js @@ -12,7 +12,7 @@ import { accumulateTwoPhaseDispatches, accumulateDirectDispatches, } from 'events/EventPropagators'; -import * as ReactNativeViewConfigRegistry from './ReactNativeViewConfigRegistry'; +import * as ReactNativeViewConfigRegistry from 'ReactNativeViewConfigRegistry'; import SyntheticEvent from 'events/SyntheticEvent'; import invariant from 'fbjs/lib/invariant'; diff --git a/packages/react-native-renderer/src/ReactNativeFiberRenderer.js b/packages/react-native-renderer/src/ReactNativeFiberRenderer.js index 956336c47b88a..e559fba86c574 100644 --- a/packages/react-native-renderer/src/ReactNativeFiberRenderer.js +++ b/packages/react-native-renderer/src/ReactNativeFiberRenderer.js @@ -16,7 +16,7 @@ import invariant from 'fbjs/lib/invariant'; import UIManager from 'UIManager'; import deepFreezeAndThrowOnMutationInDev from 'deepFreezeAndThrowOnMutationInDev'; -import * as ReactNativeViewConfigRegistry from './ReactNativeViewConfigRegistry'; +import * as ReactNativeViewConfigRegistry from 'ReactNativeViewConfigRegistry'; import * as ReactNativeAttributePayload from './ReactNativeAttributePayload'; import { precacheFiberNode, diff --git a/packages/react-native-renderer/src/ReactNativeViewConfigRegistry.js b/packages/react-native-renderer/src/__mocks__/ReactNativeViewConfigRegistry.js similarity index 84% rename from packages/react-native-renderer/src/ReactNativeViewConfigRegistry.js rename to packages/react-native-renderer/src/__mocks__/ReactNativeViewConfigRegistry.js index 929bd8d4e52da..c8ed940a86095 100644 --- a/packages/react-native-renderer/src/ReactNativeViewConfigRegistry.js +++ b/packages/react-native-renderer/src/__mocks__/ReactNativeViewConfigRegistry.js @@ -6,18 +6,23 @@ * * @flow */ +'use strict'; import type { ReactNativeBaseComponentViewConfig, ViewConfigGetter, } from './ReactNativeTypes'; -import invariant from 'fbjs/lib/invariant'; +const invariant = require('fbjs/lib/invariant'); // Event configs -export const customBubblingEventTypes = {}; -export const customDirectEventTypes = {}; -export const eventTypes = {}; +const customBubblingEventTypes = {}; +const customDirectEventTypes = {}; +const eventTypes = {}; + +exports.customBubblingEventTypes = customBubblingEventTypes; +exports.customDirectEventTypes = customDirectEventTypes; +exports.eventTypes = eventTypes; const viewConfigCallbacks = new Map(); const viewConfigs = new Map(); @@ -64,7 +69,7 @@ function processEventTypes( * The callback is deferred until the view is actually rendered. * This is done to avoid causing Prepack deopts. */ -export function register(name: string, callback: ViewConfigGetter): string { +exports.register = function(name: string, callback: ViewConfigGetter): string { invariant( !viewConfigCallbacks.has(name), 'Tried to register two views with the same name %s', @@ -72,14 +77,14 @@ export function register(name: string, callback: ViewConfigGetter): string { ); viewConfigCallbacks.set(name, callback); return name; -} +}; /** * Retrieves a config for the specified view. * If this is the first time the view has been used, * This configuration will be lazy-loaded from UIManager. */ -export function get(name: string): ReactNativeBaseComponentViewConfig { +exports.get = function(name: string): ReactNativeBaseComponentViewConfig { let viewConfig; if (!viewConfigs.has(name)) { const callback = viewConfigCallbacks.get(name); @@ -97,4 +102,4 @@ export function get(name: string): ReactNativeBaseComponentViewConfig { } invariant(viewConfig, 'View config not found for name %s', name); return viewConfig; -} +}; diff --git a/packages/react-native-renderer/src/createReactNativeComponentClass.js b/packages/react-native-renderer/src/createReactNativeComponentClass.js index d4308a2a715cc..a72bb4418c889 100644 --- a/packages/react-native-renderer/src/createReactNativeComponentClass.js +++ b/packages/react-native-renderer/src/createReactNativeComponentClass.js @@ -9,7 +9,7 @@ import type {ViewConfigGetter} from './ReactNativeTypes'; -import {register} from './ReactNativeViewConfigRegistry'; +import {register} from 'ReactNativeViewConfigRegistry'; /** * Creates a renderable ReactNative host component. diff --git a/scripts/flow/react-native-host-hooks.js b/scripts/flow/react-native-host-hooks.js index a6fe506c4ac34..de0bdbf012af8 100644 --- a/scripts/flow/react-native-host-hooks.js +++ b/scripts/flow/react-native-host-hooks.js @@ -9,6 +9,11 @@ /* eslint-disable */ +import type { + ReactNativeBaseComponentViewConfig, + ViewConfigGetter, +} from 'react-native-renderer/src/ReactNativeTypes'; + declare module 'deepDiffer' { declare module.exports: (one: any, two: any) => boolean; } @@ -142,11 +147,11 @@ declare module 'BatchedBridge' { declare function registerCallableModule(name: string, module: Object): void; } -declare module 'CSComponent' { - declare type Element = any; - declare type Options = any; -} +declare module 'ReactNativeViewConfigRegistry' { + declare var customBubblingEventTypes: Object; + declare var customDirectEventTypes: Object; + declare var eventTypes: Object; -declare module 'CSStatefulComponent' { - declare function CSStatefulComponent(spec: any): any; + declare function register(name: string, callback: ViewConfigGetter): string; + declare function get(name: string): ReactNativeBaseComponentViewConfig; } diff --git a/scripts/rollup/bundles.js b/scripts/rollup/bundles.js index 2108c442d650d..ccf668e76f375 100644 --- a/scripts/rollup/bundles.js +++ b/scripts/rollup/bundles.js @@ -128,6 +128,7 @@ const bundles = [ 'deepDiffer', 'deepFreezeAndThrowOnMutationInDev', 'flattenStyle', + 'ReactNativeViewConfigRegistry', ], }, @@ -150,6 +151,7 @@ const bundles = [ 'deepDiffer', 'deepFreezeAndThrowOnMutationInDev', 'flattenStyle', + 'ReactNativeViewConfigRegistry', ], }, diff --git a/scripts/rollup/shims/react-native/ReactNativeViewConfigRegistry.js b/scripts/rollup/shims/react-native/ReactNativeViewConfigRegistry.js new file mode 100644 index 0000000000000..ffb0fa213f3b3 --- /dev/null +++ b/scripts/rollup/shims/react-native/ReactNativeViewConfigRegistry.js @@ -0,0 +1,106 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @providesModule ReactNativeViewConfigRegistry + * @flow + */ +'use strict'; + +import type { + ReactNativeBaseComponentViewConfig, + ViewConfigGetter, +} from './ReactNativeTypes'; + +const invariant = require('fbjs/lib/invariant'); + +// Event configs +const customBubblingEventTypes = {}; +const customDirectEventTypes = {}; +const eventTypes = {}; + +exports.customBubblingEventTypes = customBubblingEventTypes; +exports.customDirectEventTypes = customDirectEventTypes; +exports.eventTypes = eventTypes; + +const viewConfigCallbacks = new Map(); +const viewConfigs = new Map(); + +function processEventTypes( + viewConfig: ReactNativeBaseComponentViewConfig, +): void { + const {bubblingEventTypes, directEventTypes} = viewConfig; + + if (__DEV__) { + if (bubblingEventTypes != null && directEventTypes != null) { + for (const topLevelType in directEventTypes) { + invariant( + bubblingEventTypes[topLevelType] == null, + 'Event cannot be both direct and bubbling: %s', + topLevelType, + ); + } + } + } + + if (bubblingEventTypes != null) { + for (const topLevelType in bubblingEventTypes) { + if (customBubblingEventTypes[topLevelType] == null) { + eventTypes[topLevelType] = customBubblingEventTypes[topLevelType] = + bubblingEventTypes[topLevelType]; + } + } + } + + if (directEventTypes != null) { + for (const topLevelType in directEventTypes) { + if (customDirectEventTypes[topLevelType] == null) { + eventTypes[topLevelType] = customDirectEventTypes[topLevelType] = + directEventTypes[topLevelType]; + } + } + } +} + +/** + * Registers a native view/component by name. + * A callback is provided to load the view config from UIManager. + * The callback is deferred until the view is actually rendered. + * This is done to avoid causing Prepack deopts. + */ +exports.register = function(name: string, callback: ViewConfigGetter): string { + invariant( + !viewConfigCallbacks.has(name), + 'Tried to register two views with the same name %s', + name, + ); + viewConfigCallbacks.set(name, callback); + return name; +}; + +/** + * Retrieves a config for the specified view. + * If this is the first time the view has been used, + * This configuration will be lazy-loaded from UIManager. + */ +exports.get = function(name: string): ReactNativeBaseComponentViewConfig { + let viewConfig; + if (!viewConfigs.has(name)) { + const callback = viewConfigCallbacks.get(name); + invariant( + typeof callback === 'function', + 'View config not found for name %s', + name, + ); + viewConfigCallbacks.set(name, null); + viewConfig = callback(); + processEventTypes(viewConfig); + viewConfigs.set(name, viewConfig); + } else { + viewConfig = viewConfigs.get(name); + } + invariant(viewConfig, 'View config not found for name %s', name); + return viewConfig; +};