ReactNative flat renderer bundles (#9626)
* Split ReactNativeFiber into separate ReactNativeFiberRenderer module Hopefully this is sufficient to work around Rollup circular dependency problems. (To be seen in subsequent commits...) * Split findNodeHandle into findNodeHandleFiber + findNodeHandleStack This allowed me to remove the ReactNative -> findNodeHandle injections, which should in turn allow me to require a fully-functional findNodeHandle without going through ReactNative. This will hopefully allow ReactNativeBaseomponent to avoid a circular dependency. * Un-forked findNodeHandle in favor of just inlining the findNode function impl * takeSnapshot no longer requires/depends-on ReactNative for findNodeHandle Instead it uses the new, renderer-specific wrappers (eg findNodeHandleFiberWrapper and findNodeHandleStackWrapper) to ensure the returned value is numeric (or null). This avoids a circular dependency that would trip up Rollup. * NativeMethodsMixin requires findNodeHandler wrapper(s) directly rather than ReactNative This works around a potential circular dependency that would break the Rollup build * Add RN_* build targets to hash-finle-name check * Strip @providesModule annotations from headers for RN_* builds * Added process.env.REACT_NATIVE_USE_FIBER to ReactNativeFeatureFlags This is kind of a hacky solution, but it is temporary. It works around the fact that ReactNativeFeatureFlag values need to be set at build time in order to avoid a mismatch between runtime flag values. DOM avoids the need to do this by using injection but Native is not able to use this same approach due to circular dependency issues. * Moved a couple of SECRET exports to dev-only. Removed SyntheticEvent and PooledClass from SECRET exports. Converted Rollup helper function to use named params. * Split NativeMethodsMixins interface and object-type * Add @noflow header to flat-bundle template to avoid triggering Flow problems When Flow tries to infer such a large file, it consumes massive amounts of CPU/RAM and can often lead to programs crashing. It is better for such large files to use .flow.js types instead. * NativeMethodsMixin and ReactNativeFiberHostComponent now share the same Flow type * Collocated (externally exposed) ReactTypes and ReactNativeTypes into single files to be synced to fbsource. ReactNativeFiber and ReactNativeStack use ReactNativeType Flow type * Build script syncs RN types and PooledClass automatically * Added optional sync-RN step to Rollup build script * Added results.json for new RN bundles
This commit is contained in:
parent
6ac91d24c4
commit
c22b94f14a
|
@ -18,6 +18,7 @@ const Bundles = require('./bundles');
|
|||
const propertyMangleWhitelist = require('./mangle').propertyMangleWhitelist;
|
||||
const sizes = require('./plugins/sizes-plugin');
|
||||
const Stats = require('./stats');
|
||||
const syncReactNative = require('./sync').syncReactNative;
|
||||
const Packaging = require('./packaging');
|
||||
const Header = require('./header');
|
||||
|
||||
|
@ -37,6 +38,7 @@ const requestedBundleTypes = (argv.type || '')
|
|||
const requestedBundleNames = (argv._[0] || '')
|
||||
.split(',')
|
||||
.map(type => type.toLowerCase());
|
||||
const syncFbsource = argv['sync-fbsource'];
|
||||
|
||||
// used for when we property mangle with uglify/gcc
|
||||
const mangleRegex = new RegExp(
|
||||
|
@ -46,6 +48,32 @@ const mangleRegex = new RegExp(
|
|||
'g'
|
||||
);
|
||||
|
||||
function getHeaderSanityCheck(bundleType, hasteName) {
|
||||
switch (bundleType) {
|
||||
case FB_DEV:
|
||||
case FB_PROD:
|
||||
case RN_DEV:
|
||||
case RN_PROD:
|
||||
let hasteFinalName = hasteName;
|
||||
switch (bundleType) {
|
||||
case FB_DEV:
|
||||
case RN_DEV:
|
||||
hasteFinalName += '-dev';
|
||||
break;
|
||||
case FB_PROD:
|
||||
case RN_PROD:
|
||||
hasteFinalName += '-prod';
|
||||
break;
|
||||
}
|
||||
return hasteFinalName;
|
||||
case UMD_DEV:
|
||||
case UMD_PROD:
|
||||
return reactVersion;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function getBanner(bundleType, hasteName, filename) {
|
||||
switch (bundleType) {
|
||||
case FB_DEV:
|
||||
|
@ -55,9 +83,11 @@ function getBanner(bundleType, hasteName, filename) {
|
|||
let hasteFinalName = hasteName;
|
||||
switch (bundleType) {
|
||||
case FB_DEV:
|
||||
case RN_DEV:
|
||||
hasteFinalName += '-dev';
|
||||
break;
|
||||
case FB_PROD:
|
||||
case RN_PROD:
|
||||
hasteFinalName += '-prod';
|
||||
break;
|
||||
}
|
||||
|
@ -122,6 +152,12 @@ function updateBundleConfig(config, filename, format, bundleType, hasteName) {
|
|||
});
|
||||
}
|
||||
|
||||
function setReactNativeUseFiberEnvVariable(useFiber) {
|
||||
return {
|
||||
'process.env.REACT_NATIVE_USE_FIBER': useFiber,
|
||||
};
|
||||
}
|
||||
|
||||
function stripEnvVariables(production) {
|
||||
return {
|
||||
__DEV__: production ? 'false' : 'true',
|
||||
|
@ -165,7 +201,13 @@ function getFilename(name, hasteName, bundleType) {
|
|||
}
|
||||
}
|
||||
|
||||
function uglifyConfig(mangle, manglePropertiesOnProd, preserveVersionHeader) {
|
||||
function uglifyConfig({
|
||||
mangle,
|
||||
manglePropertiesOnProd,
|
||||
preserveVersionHeader,
|
||||
removeComments,
|
||||
headerSanityCheck,
|
||||
}) {
|
||||
return {
|
||||
warnings: false,
|
||||
compress: {
|
||||
|
@ -186,7 +228,10 @@ function uglifyConfig(mangle, manglePropertiesOnProd, preserveVersionHeader) {
|
|||
comments(node, comment) {
|
||||
if (preserveVersionHeader && comment.pos === 0 && comment.col === 0) {
|
||||
// Keep the very first comment (the bundle header) in prod bundles.
|
||||
if (comment.value.indexOf(reactVersion) === -1) {
|
||||
if (
|
||||
headerSanityCheck &&
|
||||
comment.value.indexOf(headerSanityCheck) === -1
|
||||
) {
|
||||
// Sanity check: this doesn't look like the bundle header!
|
||||
throw new Error(
|
||||
'Expected the first comment to be the file header but got: ' +
|
||||
|
@ -195,8 +240,7 @@ function uglifyConfig(mangle, manglePropertiesOnProd, preserveVersionHeader) {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
// Keep all comments in FB bundles.
|
||||
return !mangle;
|
||||
return !removeComments;
|
||||
},
|
||||
},
|
||||
mangleProperties: mangle && manglePropertiesOnProd
|
||||
|
@ -242,8 +286,10 @@ function getPlugins(
|
|||
paths,
|
||||
filename,
|
||||
bundleType,
|
||||
hasteName,
|
||||
isRenderer,
|
||||
manglePropertiesOnProd
|
||||
manglePropertiesOnProd,
|
||||
useFiber
|
||||
) {
|
||||
const plugins = [
|
||||
babel(updateBabelConfig(babelOpts, bundleType)),
|
||||
|
@ -259,11 +305,12 @@ function getPlugins(
|
|||
plugins.unshift(replace(replaceModules));
|
||||
}
|
||||
|
||||
const headerSanityCheck = getHeaderSanityCheck(bundleType, hasteName);
|
||||
|
||||
switch (bundleType) {
|
||||
case UMD_DEV:
|
||||
case NODE_DEV:
|
||||
case FB_DEV:
|
||||
case RN_DEV:
|
||||
plugins.push(
|
||||
replace(stripEnvVariables(false)),
|
||||
// needs to happen after strip env
|
||||
|
@ -273,17 +320,36 @@ function getPlugins(
|
|||
case UMD_PROD:
|
||||
case NODE_PROD:
|
||||
case FB_PROD:
|
||||
case RN_PROD:
|
||||
plugins.push(
|
||||
replace(stripEnvVariables(true)),
|
||||
// needs to happen after strip env
|
||||
commonjs(getCommonJsConfig(bundleType)),
|
||||
uglify(
|
||||
uglifyConfig(
|
||||
bundleType !== FB_PROD,
|
||||
uglifyConfig({
|
||||
mangle: bundleType !== FB_PROD,
|
||||
manglePropertiesOnProd,
|
||||
bundleType === UMD_PROD
|
||||
)
|
||||
preserveVersionHeader: bundleType === UMD_PROD,
|
||||
removeComments: bundleType === FB_PROD,
|
||||
headerSanityCheck,
|
||||
})
|
||||
)
|
||||
);
|
||||
break;
|
||||
case RN_DEV:
|
||||
case RN_PROD:
|
||||
plugins.push(
|
||||
replace(stripEnvVariables(bundleType === RN_PROD)),
|
||||
replace(setReactNativeUseFiberEnvVariable(useFiber)),
|
||||
// needs to happen after strip env
|
||||
commonjs(getCommonJsConfig(bundleType)),
|
||||
uglify(
|
||||
uglifyConfig({
|
||||
mangle: false,
|
||||
manglePropertiesOnProd,
|
||||
preserveVersionHeader: true,
|
||||
removeComments: true,
|
||||
headerSanityCheck,
|
||||
})
|
||||
)
|
||||
);
|
||||
break;
|
||||
|
@ -349,8 +415,10 @@ function createBundle(bundle, bundleType) {
|
|||
bundle.paths,
|
||||
filename,
|
||||
bundleType,
|
||||
bundle.hasteName,
|
||||
bundle.isRenderer,
|
||||
bundle.manglePropertiesOnProd
|
||||
bundle.manglePropertiesOnProd,
|
||||
bundle.useFiber
|
||||
),
|
||||
})
|
||||
.then(result =>
|
||||
|
@ -406,6 +474,11 @@ rimraf('build', () => {
|
|||
() => createBundle(bundle, RN_PROD)
|
||||
);
|
||||
}
|
||||
if (syncFbsource) {
|
||||
tasks.push(() =>
|
||||
syncReactNative(join('build', 'react-native'), syncFbsource)
|
||||
);
|
||||
}
|
||||
// rather than run concurently, opt to run them serially
|
||||
// this helps improve console/warning/error output
|
||||
// and fixes a bunch of IO failures that sometimes occured
|
||||
|
|
|
@ -19,8 +19,8 @@ const NODE_DEV = bundleTypes.NODE_DEV;
|
|||
const NODE_PROD = bundleTypes.NODE_PROD;
|
||||
const FB_DEV = bundleTypes.FB_DEV;
|
||||
const FB_PROD = bundleTypes.FB_PROD;
|
||||
// const RN_DEV = bundleTypes.RN_DEV;
|
||||
// const RN_PROD = bundleTypes.RN_PROD;
|
||||
const RN_DEV = bundleTypes.RN_DEV;
|
||||
const RN_PROD = bundleTypes.RN_PROD;
|
||||
|
||||
const babelOptsReact = {
|
||||
exclude: 'node_modules/**',
|
||||
|
@ -284,9 +284,7 @@ const bundles = [
|
|||
/******* React Native *******/
|
||||
{
|
||||
babelOpts: babelOptsReact,
|
||||
bundleTypes: [
|
||||
/* RN_DEV, RN_PROD */
|
||||
],
|
||||
bundleTypes: [RN_DEV, RN_PROD],
|
||||
config: {
|
||||
destDir: 'build/',
|
||||
moduleName: 'ReactNativeStack',
|
||||
|
@ -304,6 +302,7 @@ const bundles = [
|
|||
'deepDiffer',
|
||||
'deepFreezeAndThrowOnMutationInDev',
|
||||
'flattenStyle',
|
||||
'prop-types/checkPropTypes',
|
||||
],
|
||||
hasteName: 'ReactNativeStack',
|
||||
isRenderer: true,
|
||||
|
@ -317,12 +316,11 @@ const bundles = [
|
|||
'src/ReactVersion.js',
|
||||
'src/shared/**/*.js',
|
||||
],
|
||||
useFiber: false,
|
||||
},
|
||||
{
|
||||
babelOpts: babelOptsReact,
|
||||
bundleTypes: [
|
||||
/* RN_DEV, RN_PROD */
|
||||
],
|
||||
bundleTypes: [RN_DEV, RN_PROD],
|
||||
config: {
|
||||
destDir: 'build/',
|
||||
moduleName: 'ReactNativeFiber',
|
||||
|
@ -340,6 +338,7 @@ const bundles = [
|
|||
'deepDiffer',
|
||||
'deepFreezeAndThrowOnMutationInDev',
|
||||
'flattenStyle',
|
||||
'prop-types/checkPropTypes',
|
||||
],
|
||||
hasteName: 'ReactNativeFiber',
|
||||
isRenderer: true,
|
||||
|
@ -353,6 +352,7 @@ const bundles = [
|
|||
'src/ReactVersion.js',
|
||||
'src/shared/**/*.js',
|
||||
],
|
||||
useFiber: true,
|
||||
},
|
||||
|
||||
/******* React Test Renderer *******/
|
||||
|
|
|
@ -11,6 +11,7 @@ function getProvidesHeader(hasteFinalName, bundleType, fbDevCode) {
|
|||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @noflow
|
||||
* @providesModule ${hasteFinalName}
|
||||
*/${bundleType === FB_DEV ? fbDevCode : ''}
|
||||
`;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
'use strict';
|
||||
|
||||
const basename = require('path').basename;
|
||||
const ncp = require('ncp').ncp;
|
||||
const fs = require('fs');
|
||||
const join = require('path').join;
|
||||
const resolve = require('path').resolve;
|
||||
const Bundles = require('./bundles');
|
||||
const asyncCopyTo = require('./utils').asyncCopyTo;
|
||||
|
||||
const UMD_DEV = Bundles.bundleTypes.UMD_DEV;
|
||||
const UMD_PROD = Bundles.bundleTypes.UMD_PROD;
|
||||
|
@ -23,17 +23,12 @@ const facebookWWWSrcDependencies = [
|
|||
'src/renderers/dom/shared/eventPlugins/TapEventPlugin.js',
|
||||
];
|
||||
|
||||
function asyncCopyTo(from, to) {
|
||||
return new Promise(_resolve => {
|
||||
ncp(from, to, error => {
|
||||
if (error) {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
}
|
||||
_resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
// these files need to be copied to the react-native build
|
||||
const reactNativeSrcDependencies = [
|
||||
'src/shared/utils/PooledClass.js',
|
||||
'src/renderers/shared/fiber/isomorphic/ReactTypes.js',
|
||||
'src/renderers/native/ReactNativeTypes.js',
|
||||
];
|
||||
|
||||
function getPackageName(name) {
|
||||
if (name.indexOf('/') !== -1) {
|
||||
|
@ -51,7 +46,17 @@ function createReactNativeBuild() {
|
|||
const from = join('scripts', 'rollup', 'shims', 'react-native');
|
||||
const to = join('build', 'react-native', 'shims');
|
||||
|
||||
return asyncCopyTo(from, to);
|
||||
return asyncCopyTo(from, to).then(() => {
|
||||
let promises = [];
|
||||
// we also need to copy over some specific files from src
|
||||
// defined in reactNativeSrcDependencies
|
||||
for (const srcDependency of reactNativeSrcDependencies) {
|
||||
promises.push(
|
||||
asyncCopyTo(resolve(srcDependency), join(to, basename(srcDependency)))
|
||||
);
|
||||
}
|
||||
return Promise.all(promises);
|
||||
});
|
||||
}
|
||||
|
||||
function createFacebookWWWBuild() {
|
||||
|
|
|
@ -163,6 +163,22 @@
|
|||
"ReactDOMServerStream-prod.js (FB_PROD)": {
|
||||
"size": 345920,
|
||||
"gzip": 83497
|
||||
},
|
||||
"ReactNativeStack-dev.js (RN_DEV)": {
|
||||
"size": 350743,
|
||||
"gzip": 63769
|
||||
},
|
||||
"ReactNativeStack-prod.js (RN_PROD)": {
|
||||
"size": 269602,
|
||||
"gzip": 46634
|
||||
},
|
||||
"ReactNativeFiber-dev.js (RN_DEV)": {
|
||||
"size": 327677,
|
||||
"gzip": 59441
|
||||
},
|
||||
"ReactNativeFiber-prod.js (RN_PROD)": {
|
||||
"size": 248866,
|
||||
"gzip": 43107
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,5 +16,8 @@ const {
|
|||
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
|
||||
} = require('ReactNative');
|
||||
|
||||
module.exports =
|
||||
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.NativeMethodsMixin;
|
||||
import type {NativeMethodsMixinType} from 'ReactNativeTypes';
|
||||
|
||||
const {NativeMethodsMixin} = __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
|
||||
|
||||
module.exports = ((NativeMethodsMixin: any): $Exact<NativeMethodsMixinType>);
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule PooledClass
|
||||
* @providesModule ReactGlobalSharedState
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
@ -15,4 +15,5 @@ const {
|
|||
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
|
||||
} = require('ReactNative');
|
||||
|
||||
module.exports = __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.PooledClass;
|
||||
module.exports =
|
||||
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactGlobalSharedState;
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactNative
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const ReactNativeFeatureFlags = require('ReactNativeFeatureFlags');
|
||||
|
||||
import type {ReactNativeType} from 'ReactNativeTypes';
|
||||
|
||||
let ReactNative;
|
||||
|
||||
if (__DEV__) {
|
||||
ReactNative = ReactNativeFeatureFlags.useFiber
|
||||
? require('ReactNativeFiber-dev')
|
||||
: require('ReactNativeStack-dev');
|
||||
} else {
|
||||
ReactNative = ReactNativeFeatureFlags.useFiber
|
||||
? require('ReactNativeFiber-prod')
|
||||
: require('ReactNativeStack-prod');
|
||||
}
|
||||
|
||||
module.exports = (ReactNative: ReactNativeType);
|
|
@ -0,0 +1,19 @@
|
|||
/**
|
||||
* Copyright 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactNativeFeatureFlags
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var ReactNativeFeatureFlags = {
|
||||
useFiber: false,
|
||||
};
|
||||
|
||||
module.exports = ReactNativeFeatureFlags;
|
|
@ -1,30 +0,0 @@
|
|||
/**
|
||||
* Copyright 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule findNodeHandle
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// While ReactNative renderer bundle is initializing, some
|
||||
// code (e.g. UIManager) imports from ReactNative.
|
||||
|
||||
// We use an indirection to avoid a circular dependency.
|
||||
|
||||
let realFindNodeHandle = null;
|
||||
|
||||
function findNodeHandle(componentOrHandle: any): ?number {
|
||||
if (realFindNodeHandle === null) {
|
||||
realFindNodeHandle = require('ReactNative')
|
||||
.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.findNodeHandle;
|
||||
}
|
||||
return realFindNodeHandle(componentOrHandle);
|
||||
}
|
||||
|
||||
module.exports = findNodeHandle;
|
|
@ -6,7 +6,7 @@
|
|||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactErrorUtils
|
||||
* @providesModule takeSnapshot
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
@ -16,4 +16,4 @@ const {
|
|||
} = require('ReactNative');
|
||||
|
||||
module.exports =
|
||||
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactErrorUtils;
|
||||
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.takeSnapshot;
|
|
@ -0,0 +1,37 @@
|
|||
'use strict';
|
||||
|
||||
const asyncCopyTo = require('./utils').asyncCopyTo;
|
||||
const chalk = require('chalk');
|
||||
const resolvePath = require('./utils').resolvePath;
|
||||
|
||||
const DEFAULT_FB_SOURCE_PATH = '~/fbsource/';
|
||||
const RELATIVE_RN_PATH = 'xplat/js/react-native-github/Libraries/Renderer/';
|
||||
|
||||
function syncReactNative(buildPath, fbSourcePath) {
|
||||
fbSourcePath = typeof fbSourcePath === 'string'
|
||||
? fbSourcePath
|
||||
: DEFAULT_FB_SOURCE_PATH;
|
||||
|
||||
if (fbSourcePath.charAt(fbSourcePath.length - 1) !== '/') {
|
||||
fbSourcePath += '/';
|
||||
}
|
||||
|
||||
const destPath = resolvePath(fbSourcePath + RELATIVE_RN_PATH);
|
||||
|
||||
console.log(
|
||||
`${chalk.bgYellow.black(' SYNCING ')} ReactNative to ${destPath}`
|
||||
);
|
||||
|
||||
const promise = asyncCopyTo(buildPath, destPath);
|
||||
promise.then(() => {
|
||||
console.log(
|
||||
`${chalk.bgGreen.black(' SYNCED ')} ReactNative to ${destPath}`
|
||||
);
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
syncReactNative: syncReactNative,
|
||||
};
|
|
@ -0,0 +1,30 @@
|
|||
'use strict';
|
||||
|
||||
const ncp = require('ncp').ncp;
|
||||
const join = require('path').join;
|
||||
const resolve = require('path').resolve;
|
||||
|
||||
function asyncCopyTo(from, to) {
|
||||
return new Promise(_resolve => {
|
||||
ncp(from, to, error => {
|
||||
if (error) {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
}
|
||||
_resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function resolvePath(path) {
|
||||
if (path[0] === '~') {
|
||||
return join(process.env.HOME, path.slice(1));
|
||||
} else {
|
||||
return resolve(path);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
asyncCopyTo: asyncCopyTo,
|
||||
resolvePath: resolvePath,
|
||||
};
|
|
@ -11,7 +11,6 @@
|
|||
*/
|
||||
'use strict';
|
||||
|
||||
var ReactNative = require('ReactNative');
|
||||
var ReactNativeFeatureFlags = require('ReactNativeFeatureFlags');
|
||||
var ReactNativeAttributePayload = require('ReactNativeAttributePayload');
|
||||
var TextInputState = require('TextInputState');
|
||||
|
@ -30,11 +29,16 @@ import type {
|
|||
MeasureInWindowOnSuccessCallback,
|
||||
MeasureLayoutOnSuccessCallback,
|
||||
MeasureOnSuccessCallback,
|
||||
} from 'NativeMethodsMixinUtils';
|
||||
NativeMethodsMixinType,
|
||||
} from 'ReactNativeTypes';
|
||||
import type {
|
||||
ReactNativeBaseComponentViewConfig,
|
||||
} from 'ReactNativeViewConfigRegistry';
|
||||
|
||||
const findNumericNodeHandle = ReactNativeFeatureFlags.useFiber
|
||||
? require('findNumericNodeHandleFiber')
|
||||
: require('findNumericNodeHandleStack');
|
||||
|
||||
/**
|
||||
* `NativeMethodsMixin` provides methods to access the underlying native
|
||||
* component directly. This can be useful in cases when you want to focus
|
||||
|
@ -46,12 +50,11 @@ import type {
|
|||
* generally include most components that you define in your own app. For more
|
||||
* information, see [Direct
|
||||
* Manipulation](docs/direct-manipulation.html).
|
||||
*
|
||||
* Note the Flow $Exact<> syntax is required to support mixins.
|
||||
* React createClass mixins can only be used with exact types.
|
||||
*/
|
||||
// TODO (bvaughn) Figure out how to use the NativeMethodsInterface type to-
|
||||
// ensure that these mixins and ReactNativeFiberHostComponent stay in sync.
|
||||
// Unfortunately, using it causes Flow to complain WRT createClass mixins:
|
||||
// "call of method `createClass`. Expected an exact object instead of ..."
|
||||
var NativeMethodsMixin = {
|
||||
var NativeMethodsMixin: $Exact<NativeMethodsMixinType> = {
|
||||
/**
|
||||
* Determines the location on screen, width, and height of the given view and
|
||||
* returns the values via an async callback. If successful, the callback will
|
||||
|
@ -71,7 +74,7 @@ var NativeMethodsMixin = {
|
|||
*/
|
||||
measure: function(callback: MeasureOnSuccessCallback) {
|
||||
UIManager.measure(
|
||||
ReactNative.findNodeHandle(this),
|
||||
findNumericNodeHandle(this),
|
||||
mountSafeCallback(this, callback),
|
||||
);
|
||||
},
|
||||
|
@ -93,7 +96,7 @@ var NativeMethodsMixin = {
|
|||
*/
|
||||
measureInWindow: function(callback: MeasureInWindowOnSuccessCallback) {
|
||||
UIManager.measureInWindow(
|
||||
ReactNative.findNodeHandle(this),
|
||||
findNumericNodeHandle(this),
|
||||
mountSafeCallback(this, callback),
|
||||
);
|
||||
},
|
||||
|
@ -104,7 +107,7 @@ var NativeMethodsMixin = {
|
|||
* are relative to the origin x, y of the ancestor view.
|
||||
*
|
||||
* As always, to obtain a native node handle for a component, you can use
|
||||
* `ReactNative.findNodeHandle(component)`.
|
||||
* `findNumericNodeHandle(component)`.
|
||||
*/
|
||||
measureLayout: function(
|
||||
relativeToNativeNode: number,
|
||||
|
@ -112,7 +115,7 @@ var NativeMethodsMixin = {
|
|||
onFail: () => void /* currently unused */,
|
||||
) {
|
||||
UIManager.measureLayout(
|
||||
ReactNative.findNodeHandle(this),
|
||||
findNumericNodeHandle(this),
|
||||
relativeToNativeNode,
|
||||
mountSafeCallback(this, onFail),
|
||||
mountSafeCallback(this, onSuccess),
|
||||
|
@ -126,14 +129,6 @@ var NativeMethodsMixin = {
|
|||
* Manipulation](docs/direct-manipulation.html)).
|
||||
*/
|
||||
setNativeProps: function(nativeProps: Object) {
|
||||
// Ensure ReactNative factory function has configured findNodeHandle.
|
||||
// Requiring it won't execute the factory function until first referenced.
|
||||
// It's possible for tests that use ReactTestRenderer to reach this point,
|
||||
// Without having executed ReactNative.
|
||||
// Defer the factory function until now to avoid a cycle with UIManager.
|
||||
// TODO (bvaughn) Remove this once ReactNativeStack is dropped.
|
||||
require('ReactNative');
|
||||
|
||||
injectedSetNativeProps(this, nativeProps);
|
||||
},
|
||||
|
||||
|
@ -142,14 +137,14 @@ var NativeMethodsMixin = {
|
|||
* will depend on the platform and type of view.
|
||||
*/
|
||||
focus: function() {
|
||||
TextInputState.focusTextInput(ReactNative.findNodeHandle(this));
|
||||
TextInputState.focusTextInput(findNumericNodeHandle(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes focus from an input or view. This is the opposite of `focus()`.
|
||||
*/
|
||||
blur: function() {
|
||||
TextInputState.blurTextInput(ReactNative.findNodeHandle(this));
|
||||
TextInputState.blurTextInput(findNumericNodeHandle(this));
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -158,7 +153,7 @@ function setNativePropsFiber(componentOrHandle: any, nativeProps: Object) {
|
|||
// Class components don't have viewConfig -> validateAttributes.
|
||||
// Nor does it make sense to set native props on a non-native component.
|
||||
// Instead, find the nearest host component and set props on it.
|
||||
// Use findNodeHandle() rather than ReactNative.findNodeHandle() because
|
||||
// Use findNodeHandle() rather than findNumericNodeHandle() because
|
||||
// We want the instance/wrapper (not the native tag).
|
||||
let maybeInstance;
|
||||
|
||||
|
@ -200,7 +195,7 @@ function setNativePropsStack(componentOrHandle: any, nativeProps: Object) {
|
|||
// Class components don't have viewConfig -> validateAttributes.
|
||||
// Nor does it make sense to set native props on a non-native component.
|
||||
// Instead, find the nearest host component and set props on it.
|
||||
// Use findNodeHandle() rather than ReactNative.findNodeHandle() because
|
||||
// Use findNodeHandle() rather than findNumericNodeHandle() because
|
||||
// We want the instance/wrapper (not the native tag).
|
||||
let maybeInstance = findNodeHandle(componentOrHandle);
|
||||
|
||||
|
|
|
@ -11,46 +11,6 @@
|
|||
*/
|
||||
'use strict';
|
||||
|
||||
export type MeasureOnSuccessCallback = (
|
||||
x: number,
|
||||
y: number,
|
||||
width: number,
|
||||
height: number,
|
||||
pageX: number,
|
||||
pageY: number,
|
||||
) => void;
|
||||
|
||||
export type MeasureInWindowOnSuccessCallback = (
|
||||
x: number,
|
||||
y: number,
|
||||
width: number,
|
||||
height: number,
|
||||
) => void;
|
||||
|
||||
export type MeasureLayoutOnSuccessCallback = (
|
||||
left: number,
|
||||
top: number,
|
||||
width: number,
|
||||
height: number,
|
||||
) => void;
|
||||
|
||||
/**
|
||||
* Shared between ReactNativeFiberHostComponent and NativeMethodsMixin to keep
|
||||
* API in sync.
|
||||
*/
|
||||
export interface NativeMethodsInterface {
|
||||
blur(): void,
|
||||
focus(): void,
|
||||
measure(callback: MeasureOnSuccessCallback): void,
|
||||
measureInWindow(callback: MeasureInWindowOnSuccessCallback): void,
|
||||
measureLayout(
|
||||
relativeToNativeNode: number,
|
||||
onSuccess: MeasureLayoutOnSuccessCallback,
|
||||
onFail: () => void,
|
||||
): void,
|
||||
setNativeProps(nativeProps: Object): void,
|
||||
}
|
||||
|
||||
/**
|
||||
* In the future, we should cleanup callbacks by cancelling them instead of
|
||||
* using this.
|
||||
|
|
|
@ -12,8 +12,10 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
// Read from process.env in order to support Rollup flat bundles.
|
||||
// Jest test script will also write this value for Fiber tests.
|
||||
var ReactNativeFeatureFlags = {
|
||||
useFiber: false,
|
||||
useFiber: process.env.REACT_NATIVE_USE_FIBER,
|
||||
};
|
||||
|
||||
module.exports = ReactNativeFeatureFlags;
|
||||
|
|
|
@ -13,412 +13,40 @@
|
|||
'use strict';
|
||||
|
||||
const ReactFiberErrorLogger = require('ReactFiberErrorLogger');
|
||||
const ReactFiberReconciler = require('ReactFiberReconciler');
|
||||
const ReactGenericBatching = require('ReactGenericBatching');
|
||||
const ReactNativeAttributePayload = require('ReactNativeAttributePayload');
|
||||
const ReactNativeComponentTree = require('ReactNativeComponentTree');
|
||||
const ReactNativeFiberErrorDialog = require('ReactNativeFiberErrorDialog');
|
||||
const ReactNativeFiberHostComponent = require('ReactNativeFiberHostComponent');
|
||||
const ReactNativeInjection = require('ReactNativeInjection');
|
||||
const ReactNativeTagHandles = require('ReactNativeTagHandles');
|
||||
const ReactNativeViewConfigRegistry = require('ReactNativeViewConfigRegistry');
|
||||
const ReactPortal = require('ReactPortal');
|
||||
const ReactNativeComponentTree = require('ReactNativeComponentTree');
|
||||
const ReactNativeFiberRenderer = require('ReactNativeFiberRenderer');
|
||||
const ReactNativeFiberInspector = require('ReactNativeFiberInspector');
|
||||
const ReactVersion = require('ReactVersion');
|
||||
const UIManager = require('UIManager');
|
||||
|
||||
const deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationInDev');
|
||||
const emptyObject = require('fbjs/lib/emptyObject');
|
||||
const findNodeHandle = require('findNodeHandle');
|
||||
const invariant = require('fbjs/lib/invariant');
|
||||
const findNumericNodeHandle = require('findNumericNodeHandleFiber');
|
||||
|
||||
const {injectInternals} = require('ReactFiberDevToolsHook');
|
||||
|
||||
import type {Element} from 'React';
|
||||
import type {Fiber} from 'ReactFiber';
|
||||
import type {
|
||||
ReactNativeBaseComponentViewConfig,
|
||||
} from 'ReactNativeViewConfigRegistry';
|
||||
import type {ReactNativeType} from 'ReactNativeTypes';
|
||||
import type {ReactNodeList} from 'ReactTypes';
|
||||
const {
|
||||
precacheFiberNode,
|
||||
uncacheFiberNode,
|
||||
updateFiberProps,
|
||||
} = ReactNativeComponentTree;
|
||||
|
||||
ReactNativeInjection.inject();
|
||||
|
||||
type Container = number;
|
||||
export type Instance = {
|
||||
_children: Array<Instance | number>,
|
||||
_nativeTag: number,
|
||||
viewConfig: ReactNativeBaseComponentViewConfig,
|
||||
};
|
||||
type Props = Object;
|
||||
type TextInstance = number;
|
||||
|
||||
function recursivelyUncacheFiberNode(node: Instance | TextInstance) {
|
||||
if (typeof node === 'number') {
|
||||
// Leaf node (eg text)
|
||||
uncacheFiberNode(node);
|
||||
} else {
|
||||
uncacheFiberNode((node: any)._nativeTag);
|
||||
|
||||
(node: any)._children.forEach(recursivelyUncacheFiberNode);
|
||||
}
|
||||
}
|
||||
|
||||
const NativeRenderer = ReactFiberReconciler({
|
||||
appendChild(
|
||||
parentInstance: Instance | Container,
|
||||
child: Instance | TextInstance,
|
||||
): void {
|
||||
const childTag = typeof child === 'number' ? child : child._nativeTag;
|
||||
|
||||
if (typeof parentInstance === 'number') {
|
||||
// Root container
|
||||
UIManager.setChildren(
|
||||
parentInstance, // containerTag
|
||||
[childTag], // reactTags
|
||||
);
|
||||
} else {
|
||||
const children = parentInstance._children;
|
||||
|
||||
const index = children.indexOf(child);
|
||||
|
||||
if (index >= 0) {
|
||||
children.splice(index, 1);
|
||||
children.push(child);
|
||||
|
||||
UIManager.manageChildren(
|
||||
parentInstance._nativeTag, // containerTag
|
||||
[index], // moveFromIndices
|
||||
[children.length - 1], // moveToIndices
|
||||
[], // addChildReactTags
|
||||
[], // addAtIndices
|
||||
[], // removeAtIndices
|
||||
);
|
||||
} else {
|
||||
children.push(child);
|
||||
|
||||
UIManager.manageChildren(
|
||||
parentInstance._nativeTag, // containerTag
|
||||
[], // moveFromIndices
|
||||
[], // moveToIndices
|
||||
[childTag], // addChildReactTags
|
||||
[children.length - 1], // addAtIndices
|
||||
[], // removeAtIndices
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
appendInitialChild(
|
||||
parentInstance: Instance,
|
||||
child: Instance | TextInstance,
|
||||
): void {
|
||||
parentInstance._children.push(child);
|
||||
},
|
||||
|
||||
commitTextUpdate(
|
||||
textInstance: TextInstance,
|
||||
oldText: string,
|
||||
newText: string,
|
||||
): void {
|
||||
UIManager.updateView(
|
||||
textInstance, // reactTag
|
||||
'RCTRawText', // viewName
|
||||
{text: newText}, // props
|
||||
);
|
||||
},
|
||||
|
||||
commitMount(
|
||||
instance: Instance,
|
||||
type: string,
|
||||
newProps: Props,
|
||||
internalInstanceHandle: Object,
|
||||
): void {
|
||||
// Noop
|
||||
},
|
||||
|
||||
commitUpdate(
|
||||
instance: Instance,
|
||||
updatePayloadTODO: Object,
|
||||
type: string,
|
||||
oldProps: Props,
|
||||
newProps: Props,
|
||||
internalInstanceHandle: Object,
|
||||
): void {
|
||||
const viewConfig = instance.viewConfig;
|
||||
|
||||
updateFiberProps(instance._nativeTag, newProps);
|
||||
|
||||
const updatePayload = ReactNativeAttributePayload.diff(
|
||||
oldProps,
|
||||
newProps,
|
||||
viewConfig.validAttributes,
|
||||
);
|
||||
|
||||
UIManager.updateView(
|
||||
instance._nativeTag, // reactTag
|
||||
viewConfig.uiViewClassName, // viewName
|
||||
updatePayload, // props
|
||||
);
|
||||
},
|
||||
|
||||
createInstance(
|
||||
type: string,
|
||||
props: Props,
|
||||
rootContainerInstance: Container,
|
||||
hostContext: {},
|
||||
internalInstanceHandle: Object,
|
||||
): Instance {
|
||||
const tag = ReactNativeTagHandles.allocateTag();
|
||||
const viewConfig = ReactNativeViewConfigRegistry.get(type);
|
||||
|
||||
if (__DEV__) {
|
||||
for (const key in viewConfig.validAttributes) {
|
||||
if (props.hasOwnProperty(key)) {
|
||||
deepFreezeAndThrowOnMutationInDev(props[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const updatePayload = ReactNativeAttributePayload.create(
|
||||
props,
|
||||
viewConfig.validAttributes,
|
||||
);
|
||||
|
||||
UIManager.createView(
|
||||
tag, // reactTag
|
||||
viewConfig.uiViewClassName, // viewName
|
||||
rootContainerInstance, // rootTag
|
||||
updatePayload, // props
|
||||
);
|
||||
|
||||
const component = new ReactNativeFiberHostComponent(tag, viewConfig);
|
||||
|
||||
precacheFiberNode(internalInstanceHandle, tag);
|
||||
updateFiberProps(tag, props);
|
||||
|
||||
// Not sure how to avoid this cast. Flow is okay if the component is defined
|
||||
// in the same file but if it's external it can't see the types.
|
||||
return ((component: any): Instance);
|
||||
},
|
||||
|
||||
createTextInstance(
|
||||
text: string,
|
||||
rootContainerInstance: Container,
|
||||
hostContext: {},
|
||||
internalInstanceHandle: Object,
|
||||
): TextInstance {
|
||||
const tag = ReactNativeTagHandles.allocateTag();
|
||||
|
||||
UIManager.createView(
|
||||
tag, // reactTag
|
||||
'RCTRawText', // viewName
|
||||
rootContainerInstance, // rootTag
|
||||
{text: text}, // props
|
||||
);
|
||||
|
||||
precacheFiberNode(internalInstanceHandle, tag);
|
||||
|
||||
return tag;
|
||||
},
|
||||
|
||||
finalizeInitialChildren(
|
||||
parentInstance: Instance,
|
||||
type: string,
|
||||
props: Props,
|
||||
rootContainerInstance: Container,
|
||||
): boolean {
|
||||
// Don't send a no-op message over the bridge.
|
||||
if (parentInstance._children.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Map from child objects to native tags.
|
||||
// Either way we need to pass a copy of the Array to prevent it from being frozen.
|
||||
const nativeTags = parentInstance._children.map(
|
||||
child =>
|
||||
(typeof child === 'number'
|
||||
? child // Leaf node (eg text)
|
||||
: child._nativeTag),
|
||||
);
|
||||
|
||||
UIManager.setChildren(
|
||||
parentInstance._nativeTag, // containerTag
|
||||
nativeTags, // reactTags
|
||||
);
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
getRootHostContext(): {} {
|
||||
return emptyObject;
|
||||
},
|
||||
|
||||
getChildHostContext(): {} {
|
||||
return emptyObject;
|
||||
},
|
||||
|
||||
getPublicInstance(instance) {
|
||||
return instance;
|
||||
},
|
||||
|
||||
insertBefore(
|
||||
parentInstance: Instance | Container,
|
||||
child: Instance | TextInstance,
|
||||
beforeChild: Instance | TextInstance,
|
||||
): void {
|
||||
// TODO (bvaughn): Remove this check when...
|
||||
// We create a wrapper object for the container in ReactNative render()
|
||||
// Or we refactor to remove wrapper objects entirely.
|
||||
// For more info on pros/cons see PR #8560 description.
|
||||
invariant(
|
||||
typeof parentInstance !== 'number',
|
||||
'Container does not support insertBefore operation',
|
||||
);
|
||||
|
||||
const children = (parentInstance: any)._children;
|
||||
|
||||
const index = children.indexOf(child);
|
||||
|
||||
// Move existing child or add new child?
|
||||
if (index >= 0) {
|
||||
children.splice(index, 1);
|
||||
const beforeChildIndex = children.indexOf(beforeChild);
|
||||
children.splice(beforeChildIndex, 0, child);
|
||||
|
||||
UIManager.manageChildren(
|
||||
(parentInstance: any)._nativeTag, // containerID
|
||||
[index], // moveFromIndices
|
||||
[beforeChildIndex], // moveToIndices
|
||||
[], // addChildReactTags
|
||||
[], // addAtIndices
|
||||
[], // removeAtIndices
|
||||
);
|
||||
} else {
|
||||
const beforeChildIndex = children.indexOf(beforeChild);
|
||||
children.splice(beforeChildIndex, 0, child);
|
||||
|
||||
const childTag = typeof child === 'number' ? child : child._nativeTag;
|
||||
|
||||
UIManager.manageChildren(
|
||||
(parentInstance: any)._nativeTag, // containerID
|
||||
[], // moveFromIndices
|
||||
[], // moveToIndices
|
||||
[childTag], // addChildReactTags
|
||||
[beforeChildIndex], // addAtIndices
|
||||
[], // removeAtIndices
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
prepareForCommit(): void {
|
||||
// Noop
|
||||
},
|
||||
|
||||
prepareUpdate(
|
||||
instance: Instance,
|
||||
type: string,
|
||||
oldProps: Props,
|
||||
newProps: Props,
|
||||
rootContainerInstance: Container,
|
||||
hostContext: {},
|
||||
): null | Object {
|
||||
return emptyObject;
|
||||
},
|
||||
|
||||
removeChild(
|
||||
parentInstance: Instance | Container,
|
||||
child: Instance | TextInstance,
|
||||
): void {
|
||||
recursivelyUncacheFiberNode(child);
|
||||
|
||||
if (typeof parentInstance === 'number') {
|
||||
UIManager.manageChildren(
|
||||
parentInstance, // containerID
|
||||
[], // moveFromIndices
|
||||
[], // moveToIndices
|
||||
[], // addChildReactTags
|
||||
[], // addAtIndices
|
||||
[0], // removeAtIndices
|
||||
);
|
||||
} else {
|
||||
const children = parentInstance._children;
|
||||
const index = children.indexOf(child);
|
||||
|
||||
children.splice(index, 1);
|
||||
|
||||
UIManager.manageChildren(
|
||||
parentInstance._nativeTag, // containerID
|
||||
[], // moveFromIndices
|
||||
[], // moveToIndices
|
||||
[], // addChildReactTags
|
||||
[], // addAtIndices
|
||||
[index], // removeAtIndices
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
resetAfterCommit(): void {
|
||||
// Noop
|
||||
},
|
||||
|
||||
resetTextContent(instance: Instance): void {
|
||||
// Noop
|
||||
},
|
||||
|
||||
shouldDeprioritizeSubtree(type: string, props: Props): boolean {
|
||||
return false;
|
||||
},
|
||||
|
||||
scheduleAnimationCallback: global.requestAnimationFrame,
|
||||
|
||||
scheduleDeferredCallback: global.requestIdleCallback,
|
||||
|
||||
shouldSetTextContent(props: Props): boolean {
|
||||
// TODO (bvaughn) Revisit this decision.
|
||||
// Always returning false simplifies the createInstance() implementation,
|
||||
// But creates an additional child Fiber for raw text children.
|
||||
// No additional native views are created though.
|
||||
// It's not clear to me which is better so I'm deferring for now.
|
||||
// More context @ github.com/facebook/react/pull/8560#discussion_r92111303
|
||||
return false;
|
||||
},
|
||||
|
||||
useSyncScheduling: true,
|
||||
});
|
||||
|
||||
ReactGenericBatching.injection.injectFiberBatchedUpdates(
|
||||
NativeRenderer.batchedUpdates,
|
||||
ReactNativeFiberRenderer.batchedUpdates,
|
||||
);
|
||||
|
||||
const roots = new Map();
|
||||
|
||||
findNodeHandle.injection.injectFindNode((fiber: Fiber) =>
|
||||
NativeRenderer.findHostInstance(fiber),
|
||||
);
|
||||
findNodeHandle.injection.injectFindRootNodeID(instance => instance);
|
||||
|
||||
// Intercept lifecycle errors and ensure they are shown with the correct stack
|
||||
// trace within the native redbox component.
|
||||
ReactFiberErrorLogger.injection.injectDialog(
|
||||
ReactNativeFiberErrorDialog.showDialog,
|
||||
);
|
||||
|
||||
const ReactNative = {
|
||||
// External users of findNodeHandle() expect the host tag number return type.
|
||||
// The injected findNodeHandle() strategy returns the instance wrapper though.
|
||||
// See NativeMethodsMixin#setNativeProps for more info on why this is done.
|
||||
findNodeHandle(componentOrHandle: any): ?number {
|
||||
const instance: any = findNodeHandle(componentOrHandle);
|
||||
if (instance == null || typeof instance === 'number') {
|
||||
return instance;
|
||||
}
|
||||
return instance._nativeTag;
|
||||
},
|
||||
var ReactNative: ReactNativeType = {
|
||||
findNodeHandle: findNumericNodeHandle,
|
||||
|
||||
render(element: Element<any>, containerTag: any, callback: ?Function) {
|
||||
let root = roots.get(containerTag);
|
||||
|
@ -426,19 +54,19 @@ const ReactNative = {
|
|||
if (!root) {
|
||||
// TODO (bvaughn): If we decide to keep the wrapper component,
|
||||
// We could create a wrapper for containerTag as well to reduce special casing.
|
||||
root = NativeRenderer.createContainer(containerTag);
|
||||
root = ReactNativeFiberRenderer.createContainer(containerTag);
|
||||
roots.set(containerTag, root);
|
||||
}
|
||||
NativeRenderer.updateContainer(element, root, null, callback);
|
||||
ReactNativeFiberRenderer.updateContainer(element, root, null, callback);
|
||||
|
||||
return NativeRenderer.getPublicRootInstance(root);
|
||||
return ReactNativeFiberRenderer.getPublicRootInstance(root);
|
||||
},
|
||||
|
||||
unmountComponentAtNode(containerTag: number) {
|
||||
const root = roots.get(containerTag);
|
||||
if (root) {
|
||||
// TODO: Is it safe to reset this now or should I wait since this unmount could be deferred?
|
||||
NativeRenderer.updateContainer(null, root, null, () => {
|
||||
ReactNativeFiberRenderer.updateContainer(null, root, null, () => {
|
||||
roots.delete(containerTag);
|
||||
});
|
||||
}
|
||||
|
@ -460,12 +88,36 @@ const ReactNative = {
|
|||
},
|
||||
|
||||
unstable_batchedUpdates: ReactGenericBatching.batchedUpdates,
|
||||
|
||||
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
|
||||
// Used as a mixin in many createClass-based components
|
||||
NativeMethodsMixin: require('NativeMethodsMixin'),
|
||||
|
||||
// Used by react-native-github/Libraries/ components
|
||||
ReactGlobalSharedState: require('ReactGlobalSharedState'), // Systrace
|
||||
ReactNativeComponentTree: require('ReactNativeComponentTree'), // InspectorUtils, ScrollResponder
|
||||
ReactNativePropRegistry: require('ReactNativePropRegistry'), // flattenStyle, Stylesheet
|
||||
TouchHistoryMath: require('TouchHistoryMath'), // PanResponder
|
||||
createReactNativeComponentClass: require('createReactNativeComponentClass'), // eg Text
|
||||
takeSnapshot: require('takeSnapshot'), // react-native-implementation
|
||||
},
|
||||
};
|
||||
|
||||
if (__DEV__) {
|
||||
// $FlowFixMe
|
||||
Object.assign(
|
||||
ReactNative.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
|
||||
{
|
||||
ReactDebugTool: require('ReactDebugTool'), // RCTRenderingPerf, Systrace
|
||||
ReactPerf: require('ReactPerf'), // ReactPerfStallHandler, RCTRenderingPerf
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof injectInternals === 'function') {
|
||||
injectInternals({
|
||||
findFiberByHostInstance: ReactNativeComponentTree.getClosestInstanceFromNode,
|
||||
findHostInstanceByFiber: NativeRenderer.findHostInstance,
|
||||
findHostInstanceByFiber: ReactNativeFiberRenderer.findHostInstance,
|
||||
getInspectorDataForViewTag: ReactNativeFiberInspector.getInspectorDataForViewTag,
|
||||
// This is an enum because we may add more (e.g. profiler build)
|
||||
bundleType: __DEV__ ? 1 : 0,
|
||||
|
|
|
@ -23,9 +23,9 @@ import type {
|
|||
MeasureInWindowOnSuccessCallback,
|
||||
MeasureLayoutOnSuccessCallback,
|
||||
MeasureOnSuccessCallback,
|
||||
NativeMethodsInterface,
|
||||
} from 'NativeMethodsMixinUtils';
|
||||
import type {Instance} from 'ReactNativeFiber';
|
||||
NativeMethodsMixinType,
|
||||
} from 'ReactNativeTypes';
|
||||
import type {Instance} from 'ReactNativeFiberRenderer';
|
||||
import type {
|
||||
ReactNativeBaseComponentViewConfig,
|
||||
} from 'ReactNativeViewConfigRegistry';
|
||||
|
@ -37,7 +37,7 @@ import type {
|
|||
* ReactNativeFiber depends on this component and NativeMethodsMixin depends on
|
||||
* ReactNativeFiber).
|
||||
*/
|
||||
class ReactNativeFiberHostComponent implements NativeMethodsInterface {
|
||||
class ReactNativeFiberHostComponent {
|
||||
_children: Array<Instance | number>;
|
||||
_nativeTag: number;
|
||||
viewConfig: ReactNativeBaseComponentViewConfig;
|
||||
|
@ -98,4 +98,7 @@ class ReactNativeFiberHostComponent implements NativeMethodsInterface {
|
|||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
(ReactNativeFiberHostComponent.prototype: NativeMethodsMixinType);
|
||||
|
||||
module.exports = ReactNativeFiberHostComponent;
|
||||
|
|
|
@ -0,0 +1,380 @@
|
|||
/**
|
||||
* Copyright 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactNativeFiberRenderer
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const ReactFiberReconciler = require('ReactFiberReconciler');
|
||||
const ReactNativeAttributePayload = require('ReactNativeAttributePayload');
|
||||
const ReactNativeComponentTree = require('ReactNativeComponentTree');
|
||||
const ReactNativeFiberHostComponent = require('ReactNativeFiberHostComponent');
|
||||
const ReactNativeTagHandles = require('ReactNativeTagHandles');
|
||||
const ReactNativeViewConfigRegistry = require('ReactNativeViewConfigRegistry');
|
||||
const UIManager = require('UIManager');
|
||||
|
||||
const deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationInDev');
|
||||
const emptyObject = require('fbjs/lib/emptyObject');
|
||||
const invariant = require('fbjs/lib/invariant');
|
||||
|
||||
import type {
|
||||
ReactNativeBaseComponentViewConfig,
|
||||
} from 'ReactNativeViewConfigRegistry';
|
||||
|
||||
export type Container = number;
|
||||
export type Instance = {
|
||||
_children: Array<Instance | number>,
|
||||
_nativeTag: number,
|
||||
viewConfig: ReactNativeBaseComponentViewConfig,
|
||||
};
|
||||
export type Props = Object;
|
||||
export type TextInstance = number;
|
||||
|
||||
const {
|
||||
precacheFiberNode,
|
||||
uncacheFiberNode,
|
||||
updateFiberProps,
|
||||
} = ReactNativeComponentTree;
|
||||
|
||||
function recursivelyUncacheFiberNode(node: Instance | TextInstance) {
|
||||
if (typeof node === 'number') {
|
||||
// Leaf node (eg text)
|
||||
uncacheFiberNode(node);
|
||||
} else {
|
||||
uncacheFiberNode((node: any)._nativeTag);
|
||||
|
||||
(node: any)._children.forEach(recursivelyUncacheFiberNode);
|
||||
}
|
||||
}
|
||||
|
||||
const NativeRenderer = ReactFiberReconciler({
|
||||
appendChild(
|
||||
parentInstance: Instance | Container,
|
||||
child: Instance | TextInstance,
|
||||
): void {
|
||||
const childTag = typeof child === 'number' ? child : child._nativeTag;
|
||||
|
||||
if (typeof parentInstance === 'number') {
|
||||
// Root container
|
||||
UIManager.setChildren(
|
||||
parentInstance, // containerTag
|
||||
[childTag], // reactTags
|
||||
);
|
||||
} else {
|
||||
const children = parentInstance._children;
|
||||
|
||||
const index = children.indexOf(child);
|
||||
|
||||
if (index >= 0) {
|
||||
children.splice(index, 1);
|
||||
children.push(child);
|
||||
|
||||
UIManager.manageChildren(
|
||||
parentInstance._nativeTag, // containerTag
|
||||
[index], // moveFromIndices
|
||||
[children.length - 1], // moveToIndices
|
||||
[], // addChildReactTags
|
||||
[], // addAtIndices
|
||||
[], // removeAtIndices
|
||||
);
|
||||
} else {
|
||||
children.push(child);
|
||||
|
||||
UIManager.manageChildren(
|
||||
parentInstance._nativeTag, // containerTag
|
||||
[], // moveFromIndices
|
||||
[], // moveToIndices
|
||||
[childTag], // addChildReactTags
|
||||
[children.length - 1], // addAtIndices
|
||||
[], // removeAtIndices
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
appendInitialChild(
|
||||
parentInstance: Instance,
|
||||
child: Instance | TextInstance,
|
||||
): void {
|
||||
parentInstance._children.push(child);
|
||||
},
|
||||
|
||||
commitTextUpdate(
|
||||
textInstance: TextInstance,
|
||||
oldText: string,
|
||||
newText: string,
|
||||
): void {
|
||||
UIManager.updateView(
|
||||
textInstance, // reactTag
|
||||
'RCTRawText', // viewName
|
||||
{text: newText}, // props
|
||||
);
|
||||
},
|
||||
|
||||
commitMount(
|
||||
instance: Instance,
|
||||
type: string,
|
||||
newProps: Props,
|
||||
internalInstanceHandle: Object,
|
||||
): void {
|
||||
// Noop
|
||||
},
|
||||
|
||||
commitUpdate(
|
||||
instance: Instance,
|
||||
updatePayloadTODO: Object,
|
||||
type: string,
|
||||
oldProps: Props,
|
||||
newProps: Props,
|
||||
internalInstanceHandle: Object,
|
||||
): void {
|
||||
const viewConfig = instance.viewConfig;
|
||||
|
||||
updateFiberProps(instance._nativeTag, newProps);
|
||||
|
||||
const updatePayload = ReactNativeAttributePayload.diff(
|
||||
oldProps,
|
||||
newProps,
|
||||
viewConfig.validAttributes,
|
||||
);
|
||||
|
||||
UIManager.updateView(
|
||||
instance._nativeTag, // reactTag
|
||||
viewConfig.uiViewClassName, // viewName
|
||||
updatePayload, // props
|
||||
);
|
||||
},
|
||||
|
||||
createInstance(
|
||||
type: string,
|
||||
props: Props,
|
||||
rootContainerInstance: Container,
|
||||
hostContext: {},
|
||||
internalInstanceHandle: Object,
|
||||
): Instance {
|
||||
const tag = ReactNativeTagHandles.allocateTag();
|
||||
const viewConfig = ReactNativeViewConfigRegistry.get(type);
|
||||
|
||||
if (__DEV__) {
|
||||
for (const key in viewConfig.validAttributes) {
|
||||
if (props.hasOwnProperty(key)) {
|
||||
deepFreezeAndThrowOnMutationInDev(props[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const updatePayload = ReactNativeAttributePayload.create(
|
||||
props,
|
||||
viewConfig.validAttributes,
|
||||
);
|
||||
|
||||
UIManager.createView(
|
||||
tag, // reactTag
|
||||
viewConfig.uiViewClassName, // viewName
|
||||
rootContainerInstance, // rootTag
|
||||
updatePayload, // props
|
||||
);
|
||||
|
||||
const component = new ReactNativeFiberHostComponent(tag, viewConfig);
|
||||
|
||||
precacheFiberNode(internalInstanceHandle, tag);
|
||||
updateFiberProps(tag, props);
|
||||
|
||||
// Not sure how to avoid this cast. Flow is okay if the component is defined
|
||||
// in the same file but if it's external it can't see the types.
|
||||
return ((component: any): Instance);
|
||||
},
|
||||
|
||||
createTextInstance(
|
||||
text: string,
|
||||
rootContainerInstance: Container,
|
||||
hostContext: {},
|
||||
internalInstanceHandle: Object,
|
||||
): TextInstance {
|
||||
const tag = ReactNativeTagHandles.allocateTag();
|
||||
|
||||
UIManager.createView(
|
||||
tag, // reactTag
|
||||
'RCTRawText', // viewName
|
||||
rootContainerInstance, // rootTag
|
||||
{text: text}, // props
|
||||
);
|
||||
|
||||
precacheFiberNode(internalInstanceHandle, tag);
|
||||
|
||||
return tag;
|
||||
},
|
||||
|
||||
finalizeInitialChildren(
|
||||
parentInstance: Instance,
|
||||
type: string,
|
||||
props: Props,
|
||||
rootContainerInstance: Container,
|
||||
): boolean {
|
||||
// Don't send a no-op message over the bridge.
|
||||
if (parentInstance._children.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Map from child objects to native tags.
|
||||
// Either way we need to pass a copy of the Array to prevent it from being frozen.
|
||||
const nativeTags = parentInstance._children.map(
|
||||
child =>
|
||||
(typeof child === 'number'
|
||||
? child // Leaf node (eg text)
|
||||
: child._nativeTag),
|
||||
);
|
||||
|
||||
UIManager.setChildren(
|
||||
parentInstance._nativeTag, // containerTag
|
||||
nativeTags, // reactTags
|
||||
);
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
getRootHostContext(): {} {
|
||||
return emptyObject;
|
||||
},
|
||||
|
||||
getChildHostContext(): {} {
|
||||
return emptyObject;
|
||||
},
|
||||
|
||||
getPublicInstance(instance) {
|
||||
return instance;
|
||||
},
|
||||
|
||||
insertBefore(
|
||||
parentInstance: Instance | Container,
|
||||
child: Instance | TextInstance,
|
||||
beforeChild: Instance | TextInstance,
|
||||
): void {
|
||||
// TODO (bvaughn): Remove this check when...
|
||||
// We create a wrapper object for the container in ReactNative render()
|
||||
// Or we refactor to remove wrapper objects entirely.
|
||||
// For more info on pros/cons see PR #8560 description.
|
||||
invariant(
|
||||
typeof parentInstance !== 'number',
|
||||
'Container does not support insertBefore operation',
|
||||
);
|
||||
|
||||
const children = (parentInstance: any)._children;
|
||||
|
||||
const index = children.indexOf(child);
|
||||
|
||||
// Move existing child or add new child?
|
||||
if (index >= 0) {
|
||||
children.splice(index, 1);
|
||||
const beforeChildIndex = children.indexOf(beforeChild);
|
||||
children.splice(beforeChildIndex, 0, child);
|
||||
|
||||
UIManager.manageChildren(
|
||||
(parentInstance: any)._nativeTag, // containerID
|
||||
[index], // moveFromIndices
|
||||
[beforeChildIndex], // moveToIndices
|
||||
[], // addChildReactTags
|
||||
[], // addAtIndices
|
||||
[], // removeAtIndices
|
||||
);
|
||||
} else {
|
||||
const beforeChildIndex = children.indexOf(beforeChild);
|
||||
children.splice(beforeChildIndex, 0, child);
|
||||
|
||||
const childTag = typeof child === 'number' ? child : child._nativeTag;
|
||||
|
||||
UIManager.manageChildren(
|
||||
(parentInstance: any)._nativeTag, // containerID
|
||||
[], // moveFromIndices
|
||||
[], // moveToIndices
|
||||
[childTag], // addChildReactTags
|
||||
[beforeChildIndex], // addAtIndices
|
||||
[], // removeAtIndices
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
prepareForCommit(): void {
|
||||
// Noop
|
||||
},
|
||||
|
||||
prepareUpdate(
|
||||
instance: Instance,
|
||||
type: string,
|
||||
oldProps: Props,
|
||||
newProps: Props,
|
||||
rootContainerInstance: Container,
|
||||
hostContext: {},
|
||||
): null | Object {
|
||||
return emptyObject;
|
||||
},
|
||||
|
||||
removeChild(
|
||||
parentInstance: Instance | Container,
|
||||
child: Instance | TextInstance,
|
||||
): void {
|
||||
recursivelyUncacheFiberNode(child);
|
||||
|
||||
if (typeof parentInstance === 'number') {
|
||||
UIManager.manageChildren(
|
||||
parentInstance, // containerID
|
||||
[], // moveFromIndices
|
||||
[], // moveToIndices
|
||||
[], // addChildReactTags
|
||||
[], // addAtIndices
|
||||
[0], // removeAtIndices
|
||||
);
|
||||
} else {
|
||||
const children = parentInstance._children;
|
||||
const index = children.indexOf(child);
|
||||
|
||||
children.splice(index, 1);
|
||||
|
||||
UIManager.manageChildren(
|
||||
parentInstance._nativeTag, // containerID
|
||||
[], // moveFromIndices
|
||||
[], // moveToIndices
|
||||
[], // addChildReactTags
|
||||
[], // addAtIndices
|
||||
[index], // removeAtIndices
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
resetAfterCommit(): void {
|
||||
// Noop
|
||||
},
|
||||
|
||||
resetTextContent(instance: Instance): void {
|
||||
// Noop
|
||||
},
|
||||
|
||||
shouldDeprioritizeSubtree(type: string, props: Props): boolean {
|
||||
return false;
|
||||
},
|
||||
|
||||
scheduleAnimationCallback: global.requestAnimationFrame,
|
||||
|
||||
scheduleDeferredCallback: global.requestIdleCallback,
|
||||
|
||||
shouldSetTextContent(props: Props): boolean {
|
||||
// TODO (bvaughn) Revisit this decision.
|
||||
// Always returning false simplifies the createInstance() implementation,
|
||||
// But creates an additional child Fiber for raw text children.
|
||||
// No additional native views are created though.
|
||||
// It's not clear to me which is better so I'm deferring for now.
|
||||
// More context @ github.com/facebook/react/pull/8560#discussion_r92111303
|
||||
return false;
|
||||
},
|
||||
|
||||
useSyncScheduling: true,
|
||||
});
|
||||
|
||||
module.exports = NativeRenderer;
|
|
@ -18,7 +18,9 @@ var ReactNativeStackInjection = require('ReactNativeStackInjection');
|
|||
var ReactUpdates = require('ReactUpdates');
|
||||
var ReactNativeStackInspector = require('ReactNativeStackInspector');
|
||||
|
||||
var findNodeHandle = require('findNodeHandle');
|
||||
var findNumericNodeHandle = require('findNumericNodeHandleStack');
|
||||
|
||||
import type {ReactNativeType} from 'ReactNativeTypes';
|
||||
|
||||
ReactNativeInjection.inject();
|
||||
ReactNativeStackInjection.inject();
|
||||
|
@ -31,19 +33,10 @@ var render = function(
|
|||
return ReactNativeMount.renderComponent(element, mountInto, callback);
|
||||
};
|
||||
|
||||
var ReactNative = {
|
||||
var ReactNative: ReactNativeType = {
|
||||
hasReactNativeInitialized: false,
|
||||
|
||||
// External users of findNodeHandle() expect the host tag number return type.
|
||||
// The injected findNodeHandle() strategy returns the instance wrapper though.
|
||||
// See NativeMethodsMixin#setNativeProps for more info on why this is done.
|
||||
findNodeHandle(componentOrHandle: any): ?number {
|
||||
const nodeHandle = findNodeHandle(componentOrHandle);
|
||||
if (nodeHandle == null || typeof nodeHandle === 'number') {
|
||||
return nodeHandle;
|
||||
}
|
||||
return nodeHandle.getHostNode();
|
||||
},
|
||||
findNodeHandle: findNumericNodeHandle,
|
||||
|
||||
render: render,
|
||||
|
||||
|
@ -54,8 +47,32 @@ var ReactNative = {
|
|||
/* eslint-enable camelcase */
|
||||
|
||||
unmountComponentAtNodeAndRemoveContainer: ReactNativeMount.unmountComponentAtNodeAndRemoveContainer,
|
||||
|
||||
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
|
||||
// Used as a mixin in many createClass-based components
|
||||
NativeMethodsMixin: require('NativeMethodsMixin'),
|
||||
|
||||
// Used by react-native-github/Libraries/ components
|
||||
ReactGlobalSharedState: require('ReactGlobalSharedState'), // Systrace
|
||||
ReactNativeComponentTree: require('ReactNativeComponentTree'), // InspectorUtils, ScrollResponder
|
||||
ReactNativePropRegistry: require('ReactNativePropRegistry'), // flattenStyle, Stylesheet
|
||||
TouchHistoryMath: require('TouchHistoryMath'), // PanResponder
|
||||
createReactNativeComponentClass: require('createReactNativeComponentClass'), // eg Text
|
||||
takeSnapshot: require('takeSnapshot'), // react-native-implementation
|
||||
},
|
||||
};
|
||||
|
||||
if (__DEV__) {
|
||||
// $FlowFixMe
|
||||
Object.assign(
|
||||
ReactNative.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
|
||||
{
|
||||
ReactDebugTool: require('ReactDebugTool'), // RCTRenderingPerf, Systrace
|
||||
ReactPerf: require('ReactPerf'), // ReactPerfStallHandler, RCTRenderingPerf
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Inject the runtime into a devtools global hook regardless of browser.
|
||||
// Allows for debugging when the hook is injected on the page.
|
||||
/* globals __REACT_DEVTOOLS_GLOBAL_HOOK__ */
|
||||
|
|
|
@ -30,7 +30,6 @@ var ReactNativeTextComponent = require('ReactNativeTextComponent');
|
|||
var ReactSimpleEmptyComponent = require('ReactSimpleEmptyComponent');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
|
||||
var findNodeHandle = require('findNodeHandle');
|
||||
var invariant = require('fbjs/lib/invariant');
|
||||
|
||||
function inject() {
|
||||
|
@ -60,9 +59,6 @@ function inject() {
|
|||
);
|
||||
};
|
||||
|
||||
findNodeHandle.injection.injectFindNode(instance => instance);
|
||||
findNodeHandle.injection.injectFindRootNodeID(instance => instance);
|
||||
|
||||
ReactEmptyComponent.injection.injectEmptyComponentFactory(EmptyComponent);
|
||||
|
||||
ReactHostComponent.injection.injectTextComponentClass(
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactNativeTypes
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
import type React from 'react';
|
||||
|
||||
export type MeasureOnSuccessCallback = (
|
||||
x: number,
|
||||
y: number,
|
||||
width: number,
|
||||
height: number,
|
||||
pageX: number,
|
||||
pageY: number,
|
||||
) => void;
|
||||
|
||||
export type MeasureInWindowOnSuccessCallback = (
|
||||
x: number,
|
||||
y: number,
|
||||
width: number,
|
||||
height: number,
|
||||
) => void;
|
||||
|
||||
export type MeasureLayoutOnSuccessCallback = (
|
||||
left: number,
|
||||
top: number,
|
||||
width: number,
|
||||
height: number,
|
||||
) => void;
|
||||
|
||||
/**
|
||||
* This type keeps ReactNativeFiberHostComponent and NativeMethodsMixin in sync.
|
||||
* It can also provide types for ReactNative applications that use NMM or refs.
|
||||
*/
|
||||
export type NativeMethodsMixinType = {
|
||||
blur(): void,
|
||||
focus(): void,
|
||||
measure(callback: MeasureOnSuccessCallback): void,
|
||||
measureInWindow(callback: MeasureInWindowOnSuccessCallback): void,
|
||||
measureLayout(
|
||||
relativeToNativeNode: number,
|
||||
onSuccess: MeasureLayoutOnSuccessCallback,
|
||||
onFail: () => void,
|
||||
): void,
|
||||
setNativeProps(nativeProps: Object): void,
|
||||
};
|
||||
|
||||
type ReactNativeBaseComponentViewConfig = {
|
||||
validAttributes: Object,
|
||||
uiViewClassName: string,
|
||||
propTypes?: Object,
|
||||
};
|
||||
|
||||
type SecretInternalsType = {
|
||||
NativeMethodsMixin: NativeMethodsMixinType,
|
||||
createReactNativeComponentClass(
|
||||
viewConfig: ReactNativeBaseComponentViewConfig,
|
||||
): any,
|
||||
ReactNativeComponentTree: any,
|
||||
ReactNativePropRegistry: any,
|
||||
// TODO (bvaughn) Decide which additional types to expose here?
|
||||
// And how much information to fill in for the above types.
|
||||
};
|
||||
|
||||
/**
|
||||
* Flat ReactNative renderer bundles are too big for Flow to parse effeciently.
|
||||
* Provide minimal Flow typing for the high-level RN API and call it a day.
|
||||
*/
|
||||
export type ReactNativeType = {
|
||||
findNodeHandle(componentOrHandle: any): ?number,
|
||||
render(
|
||||
element: React.Element<any>,
|
||||
containerTag: any,
|
||||
callback: ?Function,
|
||||
): any,
|
||||
unmountComponentAtNode(containerTag: number): any,
|
||||
unmountComponentAtNodeAndRemoveContainer(containerTag: number): any,
|
||||
unstable_batchedUpdates: any, // TODO (bvaughn) Add types
|
||||
|
||||
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: SecretInternalsType,
|
||||
};
|
|
@ -13,11 +13,14 @@
|
|||
'use strict';
|
||||
|
||||
var ReactInstanceMap = require('ReactInstanceMap');
|
||||
var ReactNativeFeatureFlags = require('ReactNativeFeatureFlags');
|
||||
var ReactNativeFiberRenderer = require('ReactNativeFiberRenderer');
|
||||
var {ReactCurrentOwner} = require('ReactGlobalSharedState');
|
||||
|
||||
var invariant = require('fbjs/lib/invariant');
|
||||
var warning = require('fbjs/lib/warning');
|
||||
|
||||
import type {Fiber} from 'ReactFiber';
|
||||
import type {ReactInstance} from 'ReactInstanceType';
|
||||
|
||||
/**
|
||||
|
@ -50,8 +53,10 @@ import type {ReactInstance} from 'ReactInstanceType';
|
|||
* nodeHandle N/A rootNodeID tag
|
||||
*/
|
||||
|
||||
let injectedFindNode;
|
||||
let injectedFindRootNodeID;
|
||||
// Rollup will strip the ReactNativeFiberRenderer from the Stack build.
|
||||
const injectedFindNode = ReactNativeFeatureFlags.useFiber
|
||||
? (fiber: Fiber) => ReactNativeFiberRenderer.findHostInstance(fiber)
|
||||
: instance => instance;
|
||||
|
||||
// TODO (bvaughn) Rename the findNodeHandle module to something more descriptive
|
||||
// eg findInternalHostInstance. This will reduce the likelihood of someone
|
||||
|
@ -90,9 +95,8 @@ function findNodeHandle(componentOrHandle: any): any {
|
|||
if (internalInstance) {
|
||||
return injectedFindNode(internalInstance);
|
||||
} else {
|
||||
var rootNodeID = injectedFindRootNodeID(component);
|
||||
if (rootNodeID) {
|
||||
return rootNodeID;
|
||||
if (component) {
|
||||
return component;
|
||||
} else {
|
||||
invariant(
|
||||
// Native
|
||||
|
@ -115,14 +119,4 @@ function findNodeHandle(componentOrHandle: any): any {
|
|||
}
|
||||
}
|
||||
|
||||
// Fiber and stack implementations differ; each must inject a strategy
|
||||
findNodeHandle.injection = {
|
||||
injectFindNode(findNode) {
|
||||
injectedFindNode = findNode;
|
||||
},
|
||||
injectFindRootNodeID(findRootNodeID) {
|
||||
injectedFindRootNodeID = findRootNodeID;
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = findNodeHandle;
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule findNumericNodeHandleFiber
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var findNodeHandle = require('findNodeHandle');
|
||||
|
||||
/**
|
||||
* External users of findNodeHandle() expect the host tag number return type.
|
||||
* The injected findNodeHandle() strategy returns the instance wrapper though.
|
||||
* See NativeMethodsMixin#setNativeProps for more info on why this is done.
|
||||
*/
|
||||
module.exports = function findNumericNodeHandleFiber(
|
||||
componentOrHandle: any,
|
||||
): ?number {
|
||||
const instance: any = findNodeHandle(componentOrHandle);
|
||||
if (instance == null || typeof instance === 'number') {
|
||||
return instance;
|
||||
}
|
||||
return instance._nativeTag;
|
||||
};
|
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule findNumericNodeHandleStack
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var findNodeHandle = require('findNodeHandle');
|
||||
|
||||
/**
|
||||
* External users of findNodeHandle() expect the host tag number return type.
|
||||
* The injected findNodeHandle() strategy returns the instance wrapper though.
|
||||
* See NativeMethodsMixin#setNativeProps for more info on why this is done.
|
||||
*/
|
||||
module.exports = function findNumericNodeHandleStack(
|
||||
componentOrHandle: any,
|
||||
): ?number {
|
||||
const nodeHandle = findNodeHandle(componentOrHandle);
|
||||
if (nodeHandle == null || typeof nodeHandle === 'number') {
|
||||
return nodeHandle;
|
||||
}
|
||||
return nodeHandle.getHostNode();
|
||||
};
|
|
@ -11,11 +11,15 @@
|
|||
*/
|
||||
'use strict';
|
||||
|
||||
var ReactNative = require('ReactNative');
|
||||
var UIManager = require('UIManager');
|
||||
const ReactNativeFeatureFlags = require('ReactNativeFeatureFlags');
|
||||
const UIManager = require('UIManager');
|
||||
|
||||
import type {Element} from 'React';
|
||||
|
||||
const findNumericNodeHandle = ReactNativeFeatureFlags.useFiber
|
||||
? require('findNumericNodeHandleFiber')
|
||||
: require('findNumericNodeHandleStack');
|
||||
|
||||
/**
|
||||
* Capture an image of the screen, window or an individual view. The image
|
||||
* will be stored in a temporary file that will only exist for as long as the
|
||||
|
@ -43,7 +47,7 @@ function takeSnapshot(
|
|||
},
|
||||
): Promise<any> {
|
||||
if (typeof view !== 'number' && view !== 'window') {
|
||||
view = ReactNative.findNodeHandle(view) || 'window';
|
||||
view = findNumericNodeHandle(view) || 'window';
|
||||
}
|
||||
|
||||
// Call the hidden '__takeSnapshot' method; the main one throws an error to
|
||||
|
|
|
@ -13,8 +13,7 @@
|
|||
'use strict';
|
||||
|
||||
import type {ReactElement} from 'ReactElementType';
|
||||
import type {ReactCoroutine, ReactYield} from 'ReactCoroutine';
|
||||
import type {ReactPortal} from 'ReactPortal';
|
||||
import type {ReactCoroutine, ReactPortal, ReactYield} from 'ReactTypes';
|
||||
import type {Fiber} from 'ReactFiber';
|
||||
import type {ReactInstance} from 'ReactInstanceType';
|
||||
import type {PriorityLevel} from 'ReactPriorityLevel';
|
||||
|
|
|
@ -14,9 +14,12 @@
|
|||
|
||||
import type {ReactElement, Source} from 'ReactElementType';
|
||||
import type {ReactInstance, DebugID} from 'ReactInstanceType';
|
||||
import type {ReactFragment} from 'ReactTypes';
|
||||
import type {ReactCoroutine, ReactYield} from 'ReactCoroutine';
|
||||
import type {ReactPortal} from 'ReactPortal';
|
||||
import type {
|
||||
ReactCoroutine,
|
||||
ReactFragment,
|
||||
ReactPortal,
|
||||
ReactYield,
|
||||
} from 'ReactTypes';
|
||||
import type {TypeOfWork} from 'ReactTypeOfWork';
|
||||
import type {TypeOfInternalContext} from 'ReactTypeOfInternalContext';
|
||||
import type {TypeOfSideEffect} from 'ReactTypeOfSideEffect';
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
import type {ReactCoroutine} from 'ReactCoroutine';
|
||||
import type {ReactCoroutine} from 'ReactTypes';
|
||||
import type {Fiber} from 'ReactFiber';
|
||||
import type {HostContext} from 'ReactFiberHostContext';
|
||||
import type {FiberRoot} from 'ReactFiberRoot';
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
import type {ReactCoroutine} from 'ReactCoroutine';
|
||||
import type {ReactCoroutine} from 'ReactTypes';
|
||||
import type {Fiber} from 'ReactFiber';
|
||||
import type {HostContext} from 'ReactFiberHostContext';
|
||||
import type {FiberRoot} from 'ReactFiberRoot';
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
import type {ReactNodeList} from 'ReactTypes';
|
||||
import type {ReactCoroutine, ReactNodeList, ReactYield} from 'ReactTypes';
|
||||
|
||||
// The Symbol used to tag the special React types. If there is no native Symbol
|
||||
// nor polyfill, then a plain number is used for performance.
|
||||
|
@ -28,19 +28,6 @@ if (typeof Symbol === 'function' && Symbol.for) {
|
|||
|
||||
type CoroutineHandler<T> = (props: T, yields: Array<mixed>) => ReactNodeList;
|
||||
|
||||
export type ReactCoroutine = {
|
||||
$$typeof: Symbol | number,
|
||||
key: null | string,
|
||||
children: any,
|
||||
// This should be a more specific CoroutineHandler
|
||||
handler: (props: any, yields: Array<mixed>) => ReactNodeList,
|
||||
props: any,
|
||||
};
|
||||
export type ReactYield = {
|
||||
$$typeof: Symbol | number,
|
||||
value: mixed,
|
||||
};
|
||||
|
||||
exports.createCoroutine = function<T>(
|
||||
children: mixed,
|
||||
handler: CoroutineHandler<T>,
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
import type {ReactNodeList} from 'ReactTypes';
|
||||
import type {ReactNodeList, ReactPortal} from 'ReactTypes';
|
||||
|
||||
// The Symbol used to tag the special React types. If there is no native Symbol
|
||||
// nor polyfill, then a plain number is used for performance.
|
||||
|
@ -20,15 +20,6 @@ var REACT_PORTAL_TYPE =
|
|||
(typeof Symbol === 'function' && Symbol.for && Symbol.for('react.portal')) ||
|
||||
0xeaca;
|
||||
|
||||
export type ReactPortal = {
|
||||
$$typeof: Symbol | number,
|
||||
key: null | string,
|
||||
containerInfo: any,
|
||||
children: ReactNodeList,
|
||||
// TODO: figure out the API for cross-renderer implementation.
|
||||
implementation: any,
|
||||
};
|
||||
|
||||
exports.createPortal = function(
|
||||
children: ReactNodeList,
|
||||
containerInfo: any,
|
||||
|
|
|
@ -12,9 +12,6 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
import type {ReactCoroutine, ReactYield} from 'ReactCoroutine';
|
||||
import type {ReactPortal} from 'ReactPortal';
|
||||
|
||||
export type ReactNode =
|
||||
| ReactElement<any>
|
||||
| ReactCoroutine
|
||||
|
@ -30,3 +27,26 @@ export type ReactNodeList = ReactEmpty | ReactNode;
|
|||
export type ReactText = string | number;
|
||||
|
||||
export type ReactEmpty = null | void | boolean;
|
||||
|
||||
export type ReactCoroutine = {
|
||||
$$typeof: Symbol | number,
|
||||
key: null | string,
|
||||
children: any,
|
||||
// This should be a more specific CoroutineHandler
|
||||
handler: (props: any, yields: Array<mixed>) => ReactNodeList,
|
||||
props: any,
|
||||
};
|
||||
|
||||
export type ReactYield = {
|
||||
$$typeof: Symbol | number,
|
||||
value: mixed,
|
||||
};
|
||||
|
||||
export type ReactPortal = {
|
||||
$$typeof: Symbol | number,
|
||||
key: null | string,
|
||||
containerInfo: any,
|
||||
children: ReactNodeList,
|
||||
// TODO: figure out the API for cross-renderer implementation.
|
||||
implementation: any,
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue