diff --git a/package.json b/package.json index c019a7d717f..c831eee727c 100644 --- a/package.json +++ b/package.json @@ -108,9 +108,40 @@ "version-check": "node ./scripts/tasks/version-check.js" }, "jest": { - "projects": [ - "/scripts/jest/stack.config.json", - "/scripts/jest/fiber.config.json" - ] + "modulePathIgnorePatterns": [ + "/.module-cache/", + "/build/", + "/scripts/rollup/shims/", + "/scripts/bench/" + ], + "transform": { + ".*": "./scripts/jest/preprocessor.js" + }, + "setupFiles": [ + "./scripts/jest/setup.js", + "./scripts/jest/environment.js" + ], + "setupTestFrameworkScriptFile": "./scripts/jest/test-framework-setup.js", + "testRegex": "/__tests__/.*(\\.js|coffee|ts)$", + "moduleFileExtensions": [ + "js", + "json", + "node", + "coffee", + "ts" + ], + "roots": [ + "/eslint-rules", + "/mocks", + "/scripts", + "/src", + "node_modules/fbjs" + ], + "collectCoverageFrom": [ + "src/**/*.js", + "!src/__mocks__/vendor/third_party/*.js", + "!src/test/*.js" + ], + "timers": "fake" } } diff --git a/scripts/jest/fiber.config.json b/scripts/jest/fiber.config.json deleted file mode 100644 index c6f47bf86cd..00000000000 --- a/scripts/jest/fiber.config.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "modulePathIgnorePatterns": [ - "/.module-cache/", - "/build/", - "/scripts/rollup/shims/", - "/scripts/bench/" - ], - "rootDir": "../../", - "transform": { - ".*": "./scripts/jest/preprocessor.js" - }, - "setupFiles": [ - "./scripts/jest/fiber.setup.js", - "./scripts/jest/environment.js" - ], - "setupTestFrameworkScriptFile": "./scripts/jest/test-framework-setup.js", - "testRegex": "/__tests__/.*(\\.js|coffee|ts)$", - "moduleFileExtensions": [ - "js", - "json", - "node", - "coffee", - "ts" - ], - "roots": [ - "/eslint-rules", - "/mocks", - "/scripts", - "/src", - "node_modules/fbjs" - ], - "collectCoverageFrom": [ - "src/**/*.js", - "!src/__mocks__/vendor/third_party/*.js", - "!src/test/*.js" - ], - "timers": "fake" -} diff --git a/scripts/jest/fiber.setup.js b/scripts/jest/setup.js similarity index 74% rename from scripts/jest/fiber.setup.js rename to scripts/jest/setup.js index 9972f16fff8..a0fbd78530a 100644 --- a/scripts/jest/fiber.setup.js +++ b/scripts/jest/setup.js @@ -2,12 +2,6 @@ // We want to globally mock this but jest doesn't let us do that by default // for a file that already exists. So we have to explicitly mock it. -jest.mock('ReactDOMFeatureFlags', () => { - const flags = require.requireActual('ReactDOMFeatureFlags'); - return Object.assign({}, flags, { - useFiber: true, - }); -}); jest.mock('ReactFeatureFlags', () => { const flags = require.requireActual('ReactFeatureFlags'); return Object.assign({}, flags, { diff --git a/scripts/jest/stack.config.json b/scripts/jest/stack.config.json deleted file mode 100644 index 77972ca00cb..00000000000 --- a/scripts/jest/stack.config.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "modulePathIgnorePatterns": [ - "/.module-cache/", - "/build/", - "/scripts/rollup/shims/", - "/scripts/bench/" - ], - "rootDir": "../../", - "transform": { - ".*": "./scripts/jest/preprocessor.js" - }, - "setupFiles": [ - "./scripts/jest/stack.setup.js", - "./scripts/jest/environment.js" - ], - "setupTestFrameworkScriptFile": "./scripts/jest/test-framework-setup.js", - "testRegex": "/__tests__/.*(\\.js|coffee|ts)$", - "moduleFileExtensions": [ - "js", - "json", - "node", - "coffee", - "ts" - ], - "roots": [ - "/eslint-rules", - "/mocks", - "/scripts", - "/src", - "node_modules/fbjs" - ], - "collectCoverageFrom": [ - "src/**/*.js", - "!src/__mocks__/vendor/third_party/*.js", - "!src/test/*.js" - ], - "timers": "fake" -} diff --git a/scripts/jest/stack.setup.js b/scripts/jest/stack.setup.js deleted file mode 100644 index 820ad8c5c0e..00000000000 --- a/scripts/jest/stack.setup.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -// We want to globally mock this but jest doesn't let us do that by default -// for a file that already exists. So we have to explicitly mock it. -jest.mock('ReactDOMFeatureFlags', () => { - const flags = require.requireActual('ReactDOMFeatureFlags'); - return Object.assign({}, flags, { - useFiber: false, - }); -}); - -// Error logging varies between Fiber and Stack; -// Rather than fork dozens of tests, mock the error-logging file by default. -jest.mock('ReactFiberErrorLogger'); diff --git a/scripts/rollup/packaging.js b/scripts/rollup/packaging.js index e5cf6f7eae6..caa09834c50 100644 --- a/scripts/rollup/packaging.js +++ b/scripts/rollup/packaging.js @@ -25,7 +25,7 @@ const facebookWWWSrcDependencies = [ // these files need to be copied to the react-native build const reactNativeSrcDependencies = [ // TODO: copy this to RN repository and delete from React - 'src/renderers/shared/stack/PooledClass.js', + 'src/renderers/native/PooledClass.js', 'src/renderers/shared/fiber/isomorphic/ReactTypes.js', 'src/renderers/native/ReactNativeTypes.js', ]; diff --git a/src/isomorphic/ReactEntry.js b/src/isomorphic/ReactEntry.js index 65aaa829344..028f11de8fc 100644 --- a/src/isomorphic/ReactEntry.js +++ b/src/isomorphic/ReactEntry.js @@ -60,7 +60,6 @@ var React = { if (__DEV__) { Object.assign(React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, { // These should not be included in production. - ReactComponentTreeHook: require('ReactComponentTreeHook'), ReactDebugCurrentFrame: require('ReactDebugCurrentFrame'), }); } diff --git a/src/isomorphic/hooks/ReactComponentTreeHook.js b/src/isomorphic/hooks/ReactComponentTreeHook.js deleted file mode 100644 index 08c107e5791..00000000000 --- a/src/isomorphic/hooks/ReactComponentTreeHook.js +++ /dev/null @@ -1,398 +0,0 @@ -/** - * Copyright 2016-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. - * - * @flow - * @providesModule ReactComponentTreeHook - */ - -'use strict'; - -var ReactCurrentOwner = require('ReactCurrentOwner'); -var invariant = require('fbjs/lib/invariant'); -var describeComponentFrame = require('describeComponentFrame'); - -if (__DEV__) { - var warning = require('fbjs/lib/warning'); -} - -import type {ReactElement, Source} from 'ReactElementType'; -import type {DebugID} from 'ReactInstanceType'; - -function isNative(fn) { - // Based on isNative() from Lodash - var funcToString = Function.prototype.toString; - var reIsNative = RegExp( - '^' + - funcToString - // Take an example native function source for comparison - .call(Object.prototype.hasOwnProperty) - // Strip regex characters so we can use it for regex - .replace(/[\\^$.*+?()[\]{}|]/g, '\\$&') - // Remove hasOwnProperty from the template to make it generic - .replace( - /hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, - '$1.*?', - ) + - '$', - ); - try { - var source = funcToString.call(fn); - return reIsNative.test(source); - } catch (err) { - return false; - } -} - -var canUseCollections = - // Array.from - typeof Array.from === 'function' && - // Map - typeof Map === 'function' && - isNative(Map) && - // Map.prototype.keys - Map.prototype != null && - typeof Map.prototype.keys === 'function' && - isNative(Map.prototype.keys) && - // Set - typeof Set === 'function' && - isNative(Set) && - // Set.prototype.keys - Set.prototype != null && - typeof Set.prototype.keys === 'function' && - isNative(Set.prototype.keys); - -var setItem; -var getItem; -var removeItem; -var getItemIDs; -var addRoot; -var removeRoot; -var getRootIDs; - -if (canUseCollections) { - var itemMap = new Map(); - var rootIDSet = new Set(); - - setItem = function(id, item) { - itemMap.set(id, item); - }; - getItem = function(id) { - return itemMap.get(id); - }; - removeItem = function(id) { - itemMap.delete(id); - }; - getItemIDs = function() { - return Array.from(itemMap.keys()); - }; - - addRoot = function(id) { - rootIDSet.add(id); - }; - removeRoot = function(id) { - rootIDSet.delete(id); - }; - getRootIDs = function() { - return Array.from(rootIDSet.keys()); - }; -} else { - var itemByKey = {}; - var rootByKey = {}; - - // Use non-numeric keys to prevent V8 performance issues: - // https://github.com/facebook/react/pull/7232 - var getKeyFromID = function(id: DebugID): string { - return '.' + id; - }; - var getIDFromKey = function(key: string): DebugID { - return parseInt(key.substr(1), 10); - }; - - setItem = function(id, item) { - var key = getKeyFromID(id); - itemByKey[key] = item; - }; - getItem = function(id) { - var key = getKeyFromID(id); - return itemByKey[key]; - }; - removeItem = function(id) { - var key = getKeyFromID(id); - delete itemByKey[key]; - }; - getItemIDs = function() { - return Object.keys(itemByKey).map(getIDFromKey); - }; - - addRoot = function(id) { - var key = getKeyFromID(id); - rootByKey[key] = true; - }; - removeRoot = function(id) { - var key = getKeyFromID(id); - delete rootByKey[key]; - }; - getRootIDs = function() { - return Object.keys(rootByKey).map(getIDFromKey); - }; -} - -var unmountedIDs: Array = []; - -function purgeDeep(id) { - var item = getItem(id); - if (item) { - var {childIDs} = item; - removeItem(id); - childIDs.forEach(purgeDeep); - } -} - -function getDisplayName(element: ?ReactElement): string { - if (element == null) { - return '#empty'; - } else if (typeof element === 'string' || typeof element === 'number') { - return '#text'; - } else if (typeof element.type === 'string') { - return element.type; - } else { - return element.type.displayName || element.type.name || 'Unknown'; - } -} - -function describeID(id: DebugID): string { - const name = ReactComponentTreeHook.getDisplayName(id); - const element = ReactComponentTreeHook.getElement(id); - const ownerID = ReactComponentTreeHook.getOwnerID(id); - let ownerName; - - if (ownerID) { - ownerName = ReactComponentTreeHook.getDisplayName(ownerID); - } - warning( - element, - 'ReactComponentTreeHook: Missing React element for debugID %s when ' + - 'building stack', - id, - ); - return describeComponentFrame( - name || '', - element && element._source, - ownerName || '', - ); -} - -var ReactComponentTreeHook = { - onSetChildren(id: DebugID, nextChildIDs: Array): void { - var item = getItem(id); - invariant(item, 'Item must have been set'); - item.childIDs = nextChildIDs; - - for (var i = 0; i < nextChildIDs.length; i++) { - var nextChildID = nextChildIDs[i]; - var nextChild = getItem(nextChildID); - invariant( - nextChild, - 'Expected hook events to fire for the child ' + - 'before its parent includes it in onSetChildren().', - ); - invariant( - nextChild.childIDs != null || - typeof nextChild.element !== 'object' || - nextChild.element == null, - 'Expected onSetChildren() to fire for a container child ' + - 'before its parent includes it in onSetChildren().', - ); - invariant( - nextChild.isMounted, - 'Expected onMountComponent() to fire for the child ' + - 'before its parent includes it in onSetChildren().', - ); - if (nextChild.parentID == null) { - nextChild.parentID = id; - // TODO: This shouldn't be necessary but mounting a new root during in - // componentWillMount currently causes not-yet-mounted components to - // be purged from our tree data so their parent id is missing. - } - invariant( - nextChild.parentID === id, - 'Expected onBeforeMountComponent() parent and onSetChildren() to ' + - 'be consistent (%s has parents %s and %s).', - nextChildID, - nextChild.parentID, - id, - ); - } - }, - - onBeforeMountComponent( - id: DebugID, - element: ReactElement, - parentID: DebugID, - ): void { - var item = { - element, - parentID, - text: null, - childIDs: [], - isMounted: false, - updateCount: 0, - }; - setItem(id, item); - }, - - onBeforeUpdateComponent(id: DebugID, element: ReactElement): void { - var item = getItem(id); - if (!item || !item.isMounted) { - // We may end up here as a result of setState() in componentWillUnmount(). - // In this case, ignore the element. - return; - } - item.element = element; - }, - - onMountComponent(id: DebugID): void { - var item = getItem(id); - invariant(item, 'Item must have been set'); - item.isMounted = true; - var isRoot = item.parentID === 0; - if (isRoot) { - addRoot(id); - } - }, - - onUpdateComponent(id: DebugID): void { - var item = getItem(id); - if (!item || !item.isMounted) { - // We may end up here as a result of setState() in componentWillUnmount(). - // In this case, ignore the element. - return; - } - item.updateCount++; - }, - - onUnmountComponent(id: DebugID): void { - var item = getItem(id); - if (item) { - // We need to check if it exists. - // `item` might not exist if it is inside an error boundary, and a sibling - // error boundary child threw while mounting. Then this instance never - // got a chance to mount, but it still gets an unmounting event during - // the error boundary cleanup. - item.isMounted = false; - var isRoot = item.parentID === 0; - if (isRoot) { - removeRoot(id); - } - } - unmountedIDs.push(id); - }, - - purgeUnmountedComponents(): void { - if (ReactComponentTreeHook._preventPurging) { - // Should only be used for testing. - return; - } - - for (var i = 0; i < unmountedIDs.length; i++) { - var id = unmountedIDs[i]; - purgeDeep(id); - } - unmountedIDs.length = 0; - }, - - isMounted(id: DebugID): boolean { - var item = getItem(id); - return item ? item.isMounted : false; - }, - - getCurrentStackAddendum(): string { - var info = ''; - var currentOwner = ReactCurrentOwner.current; - if (currentOwner) { - invariant( - typeof currentOwner.tag !== 'number', - 'Fiber owners should not show up in Stack stack traces.', - ); - if (typeof currentOwner._debugID === 'number') { - info += ReactComponentTreeHook.getStackAddendumByID( - currentOwner._debugID, - ); - } - } - return info; - }, - - getStackAddendumByID(id: ?DebugID): string { - var info = ''; - while (id) { - info += describeID(id); - id = ReactComponentTreeHook.getParentID(id); - } - return info; - }, - - getChildIDs(id: DebugID): Array { - var item = getItem(id); - return item ? item.childIDs : []; - }, - - getDisplayName(id: DebugID): ?string { - var element = ReactComponentTreeHook.getElement(id); - if (!element) { - return null; - } - return getDisplayName(element); - }, - - getElement(id: DebugID): ?ReactElement { - var item = getItem(id); - return item ? item.element : null; - }, - - getOwnerID(id: DebugID): ?DebugID { - var element = ReactComponentTreeHook.getElement(id); - if (!element || !element._owner) { - return null; - } - return element._owner._debugID; - }, - - getParentID(id: DebugID): ?DebugID { - var item = getItem(id); - return item ? item.parentID : null; - }, - - getSource(id: DebugID): ?Source { - var item = getItem(id); - var element = item ? item.element : null; - var source = element != null ? element._source : null; - return source; - }, - - getText(id: DebugID): ?string { - var element = ReactComponentTreeHook.getElement(id); - if (typeof element === 'string') { - return element; - } else if (typeof element === 'number') { - return '' + element; - } else { - return null; - } - }, - - getUpdateCount(id: DebugID): number { - var item = getItem(id); - return item ? item.updateCount : 0; - }, - - getRootIDs, - getRegisteredIDs: getItemIDs, -}; - -module.exports = ReactComponentTreeHook; diff --git a/src/node_modules/react-dom/index.js b/src/node_modules/react-dom/index.js index 9e3e32c735b..509750de3b7 100644 --- a/src/node_modules/react-dom/index.js +++ b/src/node_modules/react-dom/index.js @@ -6,9 +6,4 @@ 'use strict'; -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); -var useFiber = ReactDOMFeatureFlags.useFiber; - -module.exports = useFiber - ? require('ReactDOMFiberEntry') - : require('ReactDOMStackEntry'); +module.exports = require('ReactDOMFiberEntry'); diff --git a/src/node_modules/react-dom/server.browser.js b/src/node_modules/react-dom/server.browser.js index 8c08b1d6768..bc357430978 100644 --- a/src/node_modules/react-dom/server.browser.js +++ b/src/node_modules/react-dom/server.browser.js @@ -6,9 +6,4 @@ 'use strict'; -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); -var useFiber = ReactDOMFeatureFlags.useFiber; - -module.exports = useFiber - ? require('ReactDOMServerBrowserEntry') - : require('ReactDOMServerStackEntry'); +module.exports = require('ReactDOMServerBrowserEntry'); diff --git a/src/node_modules/react-dom/server.js b/src/node_modules/react-dom/server.js index fa6285bfefc..f19a4b36a91 100644 --- a/src/node_modules/react-dom/server.js +++ b/src/node_modules/react-dom/server.js @@ -6,9 +6,4 @@ 'use strict'; -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); -var useFiber = ReactDOMFeatureFlags.useFiber; - -module.exports = useFiber - ? require('ReactDOMServerNodeEntry') - : require('ReactDOMServerStackEntry'); +module.exports = require('ReactDOMServerNodeEntry'); diff --git a/src/renderers/__tests__/ReactComponentTreeHook-test.js b/src/renderers/__tests__/ReactComponentTreeHook-test.js deleted file mode 100644 index c7ca3644ecd..00000000000 --- a/src/renderers/__tests__/ReactComponentTreeHook-test.js +++ /dev/null @@ -1,2217 +0,0 @@ -/** - * Copyright 2016-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. - * - * @emails react-core - */ - -'use strict'; - -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); -var describeStack = ReactDOMFeatureFlags.useFiber ? describe.skip : describe; - -describe('ReactComponentTreeHook', () => { - var React; - var ReactDOM; - var ReactDOMServer; - var ReactInstanceMap; - var ReactComponentTreeHook; - var ReactDebugCurrentFiber; - var ReactComponentTreeTestUtils; - - beforeEach(() => { - jest.resetModules(); - - React = require('react'); - ReactDOM = require('react-dom'); - ReactDOMServer = require('react-dom/server'); - ReactInstanceMap = require('ReactInstanceMap'); - ReactDebugCurrentFiber = require('ReactDebugCurrentFiber'); - ReactComponentTreeHook = require('ReactComponentTreeHook'); - ReactComponentTreeTestUtils = require('ReactComponentTreeTestUtils'); - }); - - // This is the only part used both by Stack and Fiber. - describe('stack addenda', () => { - it('gets created', () => { - function getAddendum(element) { - var addendum = ReactDOMFeatureFlags.useFiber - ? ReactDebugCurrentFiber.getCurrentFiberStackAddendum() || '' - : ReactComponentTreeHook.getCurrentStackAddendum(); - return addendum.replace(/\(at .+?:\d+\)/g, '(at **)'); - } - - function Anon() { - return null; - } - Object.defineProperty(Anon, 'name', { - value: null, - }); - // function Orange() { - // return null; - // } - - expectDev(getAddendum()).toBe(''); - // expectDev(getAddendum(
)).toBe('\n in div (at **)'); - // expectDev(getAddendum()).toBe('\n in Unknown (at **)'); - // expectDev(getAddendum()).toBe('\n in Orange (at **)'); - // expectDev(getAddendum(React.createElement(Orange))).toBe( - // '\n in Orange', - // ); - - var renders = 0; - //var rOwnedByQ; - - function Q() { - return /*rOwnedByQ =*/ React.createElement(R); - } - function R() { - return
; - } - class S extends React.Component { - componentDidMount() { - // Check that the parent path is still fetched when only S itself is on - // the stack. - this.forceUpdate(); - } - render() { - expectDev(getAddendum()).toBe( - '\n in S (at **)' + - '\n in div (at **)' + - '\n in R (created by Q)' + - '\n in Q (at **)', - ); - expectDev(getAddendum()).toBe( - // '\n in span (at **)' + - '\n in S (at **)' + - '\n in div (at **)' + - '\n in R (created by Q)' + - '\n in Q (at **)', - ); - expectDev(getAddendum(React.createElement('span'))).toBe( - // '\n in span (created by S)' + - '\n in S (at **)' + - '\n in div (at **)' + - '\n in R (created by Q)' + - '\n in Q (at **)', - ); - renders++; - return null; - } - } - ReactDOM.render(, document.createElement('div')); - expectDev(renders).toBe(2); - - // Make sure owner is fetched for the top element too. - // expectDev(getAddendum(rOwnedByQ)).toBe('\n in R (created by Q)'); - }); - - // These are features and regression tests that only affect - // the Stack implementation of the stack addendum. - if (!ReactDOMFeatureFlags.useFiber) { - it('can be retrieved by ID', () => { - function getAddendum(id) { - var addendum = ReactComponentTreeHook.getStackAddendumByID(id); - return addendum.replace(/\(at .+?:\d+\)/g, '(at **)'); - } - - class Q extends React.Component { - render() { - return null; - } - } - - var q = ReactDOM.render(, document.createElement('div')); - expectDev(getAddendum(ReactInstanceMap.get(q)._debugID)).toBe( - '\n in Q (at **)', - ); - - spyOn(console, 'error'); - getAddendum(-17); - expectDev(console.error.calls.count()).toBe(1); - expectDev(console.error.calls.argsFor(0)[0]).toBe( - 'Warning: ReactComponentTreeHook: Missing React element for ' + - 'debugID -17 when building stack', - ); - }); - - it('is created during mounting', () => { - // https://github.com/facebook/react/issues/7187 - var el = document.createElement('div'); - var portalEl = document.createElement('div'); - class Foo extends React.Component { - componentWillMount() { - ReactDOM.render(
, portalEl); - } - render() { - return
; - } - } - ReactDOM.render(, el); - }); - - it('is created when calling renderToString during render', () => { - // https://github.com/facebook/react/issues/7190 - var el = document.createElement('div'); - class Foo extends React.Component { - render() { - return ( -
-
- {ReactDOMServer.renderToString(
)} -
-
- ); - } - } - ReactDOM.render(, el); - }); - } - }); - - // The rest of this file is not relevant for Fiber. - // TODO: remove tests below when we delete Stack. - - function assertTreeMatches(pairs) { - if (!Array.isArray(pairs[0])) { - pairs = [pairs]; - } - - var node = document.createElement('div'); - var currentElement; - var rootInstance; - - class Wrapper extends React.Component { - render() { - rootInstance = ReactInstanceMap.get(this); - return currentElement; - } - } - - function expectWrapperTreeToEqual(expectedTree, andStayMounted) { - ReactComponentTreeTestUtils.expectTree(rootInstance._debugID, { - displayName: 'Wrapper', - children: expectedTree ? [expectedTree] : [], - }); - var rootDisplayNames = ReactComponentTreeTestUtils.getRootDisplayNames(); - var registeredDisplayNames = ReactComponentTreeTestUtils.getRegisteredDisplayNames(); - if (!expectedTree) { - expectDev(rootDisplayNames).toEqual([]); - expectDev(registeredDisplayNames).toEqual([]); - } else if (andStayMounted) { - expectDev(rootDisplayNames).toContain('Wrapper'); - expectDev(registeredDisplayNames).toContain('Wrapper'); - } - } - - // Mount once, render updates, then unmount. - // Ensure the tree is correct on every step. - pairs.forEach(([element, expectedTree]) => { - currentElement = element; - - // Mount a new tree or update the existing tree. - ReactDOM.render(, node); - expectWrapperTreeToEqual(expectedTree, true); - - // Purging should have no effect - // on the tree we expect to see. - ReactComponentTreeHook.purgeUnmountedComponents(); - expectWrapperTreeToEqual(expectedTree, true); - }); - - // Unmounting the root node should purge - // the whole subtree automatically. - ReactDOM.unmountComponentAtNode(node); - expectWrapperTreeToEqual(null); - } - - describeStack('mount', () => { - it('uses displayName or Unknown for classic components', () => { - class Foo extends React.Component { - render() { - return null; - } - } - - Foo.displayName = 'Bar'; - - class Baz extends React.Component { - render() { - return null; - } - } - - class Qux extends React.Component { - render() { - return null; - } - } - - delete Qux.displayName; - - var element =
; - var tree = { - displayName: 'div', - children: [ - { - displayName: 'Bar', - children: [], - }, - { - displayName: 'Baz', - children: [], - }, - { - displayName: 'Unknown', - children: [], - }, - ], - }; - assertTreeMatches([element, tree]); - }); - - it('uses displayName, name, or ReactComponent for modern components', () => { - class Foo extends React.Component { - render() { - return null; - } - } - Foo.displayName = 'Bar'; - class Baz extends React.Component { - render() { - return null; - } - } - class Qux extends React.Component { - render() { - return null; - } - } - delete Qux.name; - - var element =
; - var tree = { - displayName: 'div', - element, - children: [ - { - displayName: 'Bar', - children: [], - }, - { - displayName: 'Baz', - children: [], - }, - { - // Note: Ideally fallback name should be consistent (e.g. "Unknown") - displayName: 'ReactComponent', - children: [], - }, - ], - }; - assertTreeMatches([element, tree]); - }); - - it('uses displayName, name, or Object for factory components', () => { - function Foo() { - return { - render() { - return null; - }, - }; - } - Foo.displayName = 'Bar'; - function Baz() { - return { - render() { - return null; - }, - }; - } - function Qux() { - return { - render() { - return null; - }, - }; - } - delete Qux.name; - - var element =
; - var tree = { - displayName: 'div', - element, - children: [ - { - displayName: 'Bar', - children: [], - }, - { - displayName: 'Baz', - children: [], - }, - { - displayName: 'Unknown', - children: [], - }, - ], - }; - assertTreeMatches([element, tree]); - }); - - it('uses displayName, name, or StatelessComponent for functional components', () => { - function Foo() { - return null; - } - Foo.displayName = 'Bar'; - function Baz() { - return null; - } - function Qux() { - return null; - } - delete Qux.name; - - var element =
; - var tree = { - displayName: 'div', - element, - children: [ - { - displayName: 'Bar', - children: [], - }, - { - displayName: 'Baz', - children: [], - }, - { - displayName: 'Unknown', - children: [], - }, - ], - }; - assertTreeMatches([element, tree]); - }); - - it('reports a host tree correctly', () => { - var element = ( -
-

- - Hi! - - Wow. -

-
-
- ); - var tree = { - displayName: 'div', - children: [ - { - displayName: 'p', - children: [ - { - displayName: 'span', - children: [ - { - displayName: '#text', - text: 'Hi!', - }, - ], - }, - { - displayName: '#text', - text: 'Wow.', - }, - ], - }, - { - displayName: 'hr', - element:
, - children: [], - }, - ], - }; - assertTreeMatches([element, tree]); - }); - - it('reports a simple tree with composites correctly', () => { - class Foo extends React.Component { - render() { - return
; - } - } - - var element = ; - var tree = { - displayName: 'Foo', - element, - children: [ - { - displayName: 'div', - element:
, - children: [], - }, - ], - }; - assertTreeMatches([element, tree]); - }); - - it('reports a tree with composites correctly', () => { - class Qux extends React.Component { - render() { - return null; - } - } - - function Foo() { - return { - render() { - return ; - }, - }; - } - function Bar({children}) { - return

{children}

; - } - class Baz extends React.Component { - render() { - return ( -
- - - Hi, - Mom - - Click me. -
- ); - } - } - - var element = ; - var tree = { - displayName: 'Baz', - element, - children: [ - { - displayName: 'div', - children: [ - { - displayName: 'Foo', - element: , - children: [ - { - displayName: 'Qux', - element: , - children: [], - }, - ], - }, - { - displayName: 'Bar', - children: [ - { - displayName: 'h1', - children: [ - { - displayName: 'span', - children: [ - { - displayName: '#text', - element: 'Hi,', - text: 'Hi,', - }, - ], - }, - { - displayName: '#text', - text: 'Mom', - element: 'Mom', - }, - ], - }, - ], - }, - { - displayName: 'a', - children: [ - { - displayName: '#text', - text: 'Click me.', - element: 'Click me.', - }, - ], - }, - ], - }, - ], - }; - assertTreeMatches([element, tree]); - }); - - it('ignores null children', () => { - class Foo extends React.Component { - render() { - return null; - } - } - var element = ; - var tree = { - displayName: 'Foo', - children: [], - }; - assertTreeMatches([element, tree]); - }); - - it('ignores false children', () => { - class Foo extends React.Component { - render() { - return false; - } - } - var element = ; - var tree = { - displayName: 'Foo', - children: [], - }; - assertTreeMatches([element, tree]); - }); - - it('reports text nodes as children', () => { - var element =
{'1'}{2}
; - var tree = { - displayName: 'div', - element, - children: [ - { - displayName: '#text', - text: '1', - }, - { - displayName: '#text', - text: '2', - }, - ], - }; - assertTreeMatches([element, tree]); - }); - - it('reports a single text node as a child', () => { - var element =
{'1'}
; - var tree = { - displayName: 'div', - element, - children: [ - { - displayName: '#text', - text: '1', - }, - ], - }; - assertTreeMatches([element, tree]); - }); - - it('reports a single number node as a child', () => { - var element =
{42}
; - var tree = { - displayName: 'div', - element, - children: [ - { - displayName: '#text', - text: '42', - }, - ], - }; - assertTreeMatches([element, tree]); - }); - - it('reports a zero as a child', () => { - var element =
{0}
; - var tree = { - displayName: 'div', - element, - children: [ - { - displayName: '#text', - text: '0', - }, - ], - }; - assertTreeMatches([element, tree]); - }); - - it('skips empty nodes for multiple children', () => { - function Foo() { - return
; - } - var element = ( -
- {'hi'} - {false} - {42} - {null} - -
- ); - var tree = { - displayName: 'div', - element, - children: [ - { - displayName: '#text', - text: 'hi', - element: 'hi', - }, - { - displayName: '#text', - text: '42', - element: 42, - }, - { - displayName: 'Foo', - element: , - children: [ - { - displayName: 'div', - element:
, - children: [], - }, - ], - }, - ], - }; - assertTreeMatches([element, tree]); - }); - - it('reports html content as no children', () => { - var element =
; - var tree = { - displayName: 'div', - children: [], - }; - assertTreeMatches([element, tree]); - }); - }); - - describeStack('update', () => { - describe('host component', () => { - it('updates text of a single text child', () => { - var elementBefore =
Hi.
; - var treeBefore = { - displayName: 'div', - children: [ - { - displayName: '#text', - text: 'Hi.', - }, - ], - }; - - var elementAfter =
Bye.
; - var treeAfter = { - displayName: 'div', - children: [ - { - displayName: '#text', - text: 'Bye.', - }, - ], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates from no children to a single text child', () => { - var elementBefore =
; - var treeBefore = { - displayName: 'div', - children: [], - }; - - var elementAfter =
Hi.
; - var treeAfter = { - displayName: 'div', - children: [ - { - displayName: '#text', - text: 'Hi.', - }, - ], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates from a single text child to no children', () => { - var elementBefore =
Hi.
; - var treeBefore = { - displayName: 'div', - children: [ - { - displayName: '#text', - text: 'Hi.', - }, - ], - }; - - var elementAfter =
; - var treeAfter = { - displayName: 'div', - children: [], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates from html content to a single text child', () => { - var elementBefore =
; - var treeBefore = { - displayName: 'div', - children: [], - }; - - var elementAfter =
Hi.
; - var treeAfter = { - displayName: 'div', - children: [ - { - displayName: '#text', - text: 'Hi.', - }, - ], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates from a single text child to html content', () => { - var elementBefore =
Hi.
; - var treeBefore = { - displayName: 'div', - children: [ - { - displayName: '#text', - text: 'Hi.', - }, - ], - }; - - var elementAfter =
; - var treeAfter = { - displayName: 'div', - children: [], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates from no children to multiple text children', () => { - var elementBefore =
; - var treeBefore = { - displayName: 'div', - children: [], - }; - - var elementAfter =
{'Hi.'}{'Bye.'}
; - var treeAfter = { - displayName: 'div', - children: [ - { - displayName: '#text', - text: 'Hi.', - }, - { - displayName: '#text', - text: 'Bye.', - }, - ], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates from multiple text children to no children', () => { - var elementBefore =
{'Hi.'}{'Bye.'}
; - var treeBefore = { - displayName: 'div', - children: [ - { - displayName: '#text', - text: 'Hi.', - }, - { - displayName: '#text', - text: 'Bye.', - }, - ], - }; - - var elementAfter =
; - var treeAfter = { - displayName: 'div', - children: [], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates from html content to multiple text children', () => { - var elementBefore =
; - var treeBefore = { - displayName: 'div', - children: [], - }; - - var elementAfter =
{'Hi.'}{'Bye.'}
; - var treeAfter = { - displayName: 'div', - children: [ - { - displayName: '#text', - text: 'Hi.', - }, - { - displayName: '#text', - text: 'Bye.', - }, - ], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates from multiple text children to html content', () => { - var elementBefore =
{'Hi.'}{'Bye.'}
; - var treeBefore = { - displayName: 'div', - children: [ - { - displayName: '#text', - text: 'Hi.', - }, - { - displayName: '#text', - text: 'Bye.', - }, - ], - }; - - var elementAfter =
; - var treeAfter = { - displayName: 'div', - children: [], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates from html content to no children', () => { - var elementBefore =
; - var treeBefore = { - displayName: 'div', - children: [], - }; - - var elementAfter =
; - var treeAfter = { - displayName: 'div', - children: [], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates from no children to html content', () => { - var elementBefore =
; - var treeBefore = { - displayName: 'div', - children: [], - }; - - var elementAfter =
; - var treeAfter = { - displayName: 'div', - children: [], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates from one text child to multiple text children', () => { - var elementBefore =
Hi.
; - var treeBefore = { - displayName: 'div', - children: [ - { - displayName: '#text', - text: 'Hi.', - }, - ], - }; - - var elementAfter =
{'Hi.'}{'Bye.'}
; - var treeAfter = { - displayName: 'div', - children: [ - { - displayName: '#text', - text: 'Hi.', - }, - { - displayName: '#text', - text: 'Bye.', - }, - ], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates from multiple text children to one text child', () => { - var elementBefore =
{'Hi.'}{'Bye.'}
; - var treeBefore = { - displayName: 'div', - children: [ - { - displayName: '#text', - text: 'Hi.', - }, - { - displayName: '#text', - text: 'Bye.', - }, - ], - }; - - var elementAfter =
Hi.
; - var treeAfter = { - displayName: 'div', - children: [ - { - displayName: '#text', - text: 'Hi.', - }, - ], - }; - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates text nodes when reordering', () => { - var elementBefore =
{'Hi.'}{'Bye.'}
; - var treeBefore = { - displayName: 'div', - children: [ - { - displayName: '#text', - text: 'Hi.', - }, - { - displayName: '#text', - text: 'Bye.', - }, - ], - }; - - var elementAfter =
{'Bye.'}{'Hi.'}
; - var treeAfter = { - displayName: 'div', - children: [ - { - displayName: '#text', - text: 'Bye.', - }, - { - displayName: '#text', - text: 'Hi.', - }, - ], - }; - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates host nodes when reordering with keys', () => { - var elementBefore = ( -
-
Hi.
-
Bye.
-
- ); - var treeBefore = { - displayName: 'div', - children: [ - { - displayName: 'div', - children: [ - { - displayName: '#text', - text: 'Hi.', - }, - ], - }, - { - displayName: 'div', - children: [ - { - displayName: '#text', - text: 'Bye.', - }, - ], - }, - ], - }; - - var elementAfter = ( -
-
Bye.
-
Hi.
-
- ); - var treeAfter = { - displayName: 'div', - children: [ - { - displayName: 'div', - children: [ - { - displayName: '#text', - text: 'Bye.', - }, - ], - }, - { - displayName: 'div', - children: [ - { - displayName: '#text', - text: 'Hi.', - }, - ], - }, - ], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates host nodes when reordering without keys', () => { - var elementBefore = ( -
-
Hi.
-
Bye.
-
- ); - var treeBefore = { - displayName: 'div', - children: [ - { - displayName: 'div', - children: [ - { - displayName: '#text', - text: 'Hi.', - }, - ], - }, - { - displayName: 'div', - children: [ - { - displayName: '#text', - text: 'Bye.', - }, - ], - }, - ], - }; - - var elementAfter = ( -
-
Bye.
-
Hi.
-
- ); - var treeAfter = { - displayName: 'div', - children: [ - { - displayName: 'div', - children: [ - { - displayName: '#text', - text: 'Bye.', - }, - ], - }, - { - displayName: 'div', - children: [ - { - displayName: '#text', - text: 'Hi.', - }, - ], - }, - ], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates a single composite child of a different type', () => { - function Foo() { - return null; - } - - function Bar() { - return null; - } - - var elementBefore =
; - var treeBefore = { - displayName: 'div', - children: [ - { - displayName: 'Foo', - children: [], - }, - ], - }; - - var elementAfter =
; - var treeAfter = { - displayName: 'div', - children: [ - { - displayName: 'Bar', - children: [], - }, - ], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates a single composite child of the same type', () => { - function Foo({children}) { - return children; - } - - var elementBefore =
; - var treeBefore = { - displayName: 'div', - children: [ - { - displayName: 'Foo', - children: [ - { - displayName: 'div', - children: [], - }, - ], - }, - ], - }; - - var elementAfter =
; - var treeAfter = { - displayName: 'div', - children: [ - { - displayName: 'Foo', - children: [ - { - displayName: 'span', - children: [], - }, - ], - }, - ], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates from no children to a single composite child', () => { - function Foo() { - return null; - } - - var elementBefore =
; - var treeBefore = { - displayName: 'div', - children: [], - }; - - var elementAfter =
; - var treeAfter = { - displayName: 'div', - children: [ - { - displayName: 'Foo', - children: [], - }, - ], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates from a single composite child to no children', () => { - function Foo() { - return null; - } - - var elementBefore =
; - var treeBefore = { - displayName: 'div', - children: [ - { - displayName: 'Foo', - children: [], - }, - ], - }; - - var elementAfter =
; - var treeAfter = { - displayName: 'div', - children: [], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates mixed children', () => { - function Foo() { - return
; - } - var element1 = ( -
- {'hi'} - {false} - {42} - {null} - -
- ); - var tree1 = { - displayName: 'div', - children: [ - { - displayName: '#text', - text: 'hi', - }, - { - displayName: '#text', - text: '42', - }, - { - displayName: 'Foo', - children: [ - { - displayName: 'div', - children: [], - }, - ], - }, - ], - }; - - var element2 = ( -
- - {false} - {'hi'} - {null} -
- ); - var tree2 = { - displayName: 'div', - children: [ - { - displayName: 'Foo', - children: [ - { - displayName: 'div', - children: [], - }, - ], - }, - { - displayName: '#text', - text: 'hi', - }, - ], - }; - - var element3 = ( -
- -
- ); - var tree3 = { - displayName: 'div', - children: [ - { - displayName: 'Foo', - children: [ - { - displayName: 'div', - children: [], - }, - ], - }, - ], - }; - - assertTreeMatches([ - [element1, tree1], - [element2, tree2], - [element3, tree3], - ]); - }); - }); - - describe('functional component', () => { - it('updates with a host child', () => { - function Foo({children}) { - return children; - } - - var elementBefore =
; - var treeBefore = { - displayName: 'Foo', - children: [ - { - displayName: 'div', - children: [], - }, - ], - }; - - var elementAfter = ; - var treeAfter = { - displayName: 'Foo', - children: [ - { - displayName: 'span', - children: [], - }, - ], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates from null to a host child', () => { - function Foo({children}) { - return children; - } - - var elementBefore = {null}; - var treeBefore = { - displayName: 'Foo', - children: [], - }; - - var elementAfter =
; - var treeAfter = { - displayName: 'Foo', - children: [ - { - displayName: 'div', - children: [], - }, - ], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates from a host child to null', () => { - function Foo({children}) { - return children; - } - - var elementBefore =
; - var treeBefore = { - displayName: 'Foo', - children: [ - { - displayName: 'div', - children: [], - }, - ], - }; - - var elementAfter = {null}; - var treeAfter = { - displayName: 'Foo', - children: [], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates from a host child to a composite child', () => { - function Bar() { - return null; - } - - function Foo({children}) { - return children; - } - - var elementBefore =
; - var treeBefore = { - displayName: 'Foo', - children: [ - { - displayName: 'div', - children: [], - }, - ], - }; - - var elementAfter = ; - var treeAfter = { - displayName: 'Foo', - children: [ - { - displayName: 'Bar', - children: [], - }, - ], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates from a composite child to a host child', () => { - function Bar() { - return null; - } - - function Foo({children}) { - return children; - } - - var elementBefore = ; - var treeBefore = { - displayName: 'Foo', - children: [ - { - displayName: 'Bar', - children: [], - }, - ], - }; - - var elementAfter =
; - var treeAfter = { - displayName: 'Foo', - children: [ - { - displayName: 'div', - children: [], - }, - ], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates from null to a composite child', () => { - function Bar() { - return null; - } - - function Foo({children}) { - return children; - } - - var elementBefore = {null}; - var treeBefore = { - displayName: 'Foo', - children: [], - }; - - var elementAfter = ; - var treeAfter = { - displayName: 'Foo', - children: [ - { - displayName: 'Bar', - children: [], - }, - ], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates from a composite child to null', () => { - function Bar() { - return null; - } - - function Foo({children}) { - return children; - } - - var elementBefore = ; - var treeBefore = { - displayName: 'Foo', - children: [ - { - displayName: 'Bar', - children: [], - }, - ], - }; - - var elementAfter = {null}; - var treeAfter = { - displayName: 'Foo', - children: [], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - }); - - describe('class component', () => { - it('updates with a host child', () => { - class Foo extends React.Component { - render() { - return this.props.children; - } - } - - var elementBefore =
; - var treeBefore = { - displayName: 'Foo', - children: [ - { - displayName: 'div', - children: [], - }, - ], - }; - - var elementAfter = ; - var treeAfter = { - displayName: 'Foo', - children: [ - { - displayName: 'span', - children: [], - }, - ], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates from null to a host child', () => { - class Foo extends React.Component { - render() { - return this.props.children; - } - } - - var elementBefore = {null}; - var treeBefore = { - displayName: 'Foo', - children: [], - }; - - var elementAfter =
; - var treeAfter = { - displayName: 'Foo', - children: [ - { - displayName: 'div', - children: [], - }, - ], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates from a host child to null', () => { - class Foo extends React.Component { - render() { - return this.props.children; - } - } - - var elementBefore =
; - var treeBefore = { - displayName: 'Foo', - children: [ - { - displayName: 'div', - children: [], - }, - ], - }; - - var elementAfter = {null}; - var treeAfter = { - displayName: 'Foo', - children: [], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates from a host child to a composite child', () => { - class Bar extends React.Component { - render() { - return null; - } - } - - class Foo extends React.Component { - render() { - return this.props.children; - } - } - - var elementBefore =
; - var treeBefore = { - displayName: 'Foo', - children: [ - { - displayName: 'div', - children: [], - }, - ], - }; - - var elementAfter = ; - var treeAfter = { - displayName: 'Foo', - children: [ - { - displayName: 'Bar', - children: [], - }, - ], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates from a composite child to a host child', () => { - class Bar extends React.Component { - render() { - return null; - } - } - - class Foo extends React.Component { - render() { - return this.props.children; - } - } - - var elementBefore = ; - var treeBefore = { - displayName: 'Foo', - children: [ - { - displayName: 'Bar', - children: [], - }, - ], - }; - - var elementAfter =
; - var treeAfter = { - displayName: 'Foo', - children: [ - { - displayName: 'div', - children: [], - }, - ], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates from null to a composite child', () => { - class Bar extends React.Component { - render() { - return null; - } - } - - class Foo extends React.Component { - render() { - return this.props.children; - } - } - - var elementBefore = {null}; - var treeBefore = { - displayName: 'Foo', - children: [], - }; - - var elementAfter = ; - var treeAfter = { - displayName: 'Foo', - children: [ - { - displayName: 'Bar', - children: [], - }, - ], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - - it('updates from a composite child to null', () => { - class Bar extends React.Component { - render() { - return null; - } - } - - class Foo extends React.Component { - render() { - return this.props.children; - } - } - - var elementBefore = ; - var treeBefore = { - displayName: 'Foo', - children: [ - { - displayName: 'Bar', - children: [], - }, - ], - }; - - var elementAfter = {null}; - var treeAfter = { - displayName: 'Foo', - children: [], - }; - - assertTreeMatches([ - [elementBefore, treeBefore], - [elementAfter, treeAfter], - ]); - }); - }); - }); - - describeStack('misc', () => { - it('tracks owner correctly', () => { - class Foo extends React.Component { - render() { - return

Hi.

; - } - } - function Bar({children}) { - return
{children} Mom
; - } - - // Note that owner is not calculated for text nodes - // because they are not created from real elements. - var element =
; - var tree = { - displayName: 'article', - children: [ - { - displayName: 'Foo', - children: [ - { - displayName: 'Bar', - ownerDisplayName: 'Foo', - children: [ - { - displayName: 'div', - ownerDisplayName: 'Bar', - children: [ - { - displayName: 'h1', - ownerDisplayName: 'Foo', - children: [ - { - displayName: '#text', - text: 'Hi.', - }, - ], - }, - { - displayName: '#text', - text: ' Mom', - }, - ], - }, - ], - }, - ], - }, - ], - }; - assertTreeMatches([element, tree]); - }); - - it('purges unmounted components automatically', () => { - var node = document.createElement('div'); - var renderBar = true; - var fooInstance; - var barInstance; - - class Foo extends React.Component { - render() { - fooInstance = ReactInstanceMap.get(this); - return renderBar ? : null; - } - } - - class Bar extends React.Component { - render() { - barInstance = ReactInstanceMap.get(this); - return null; - } - } - - ReactDOM.render(, node); - ReactComponentTreeTestUtils.expectTree( - barInstance._debugID, - { - displayName: 'Bar', - parentDisplayName: 'Foo', - parentID: fooInstance._debugID, - children: [], - }, - 'Foo', - ); - - renderBar = false; - ReactDOM.render(, node); - ReactDOM.render(, node); - ReactComponentTreeTestUtils.expectTree( - barInstance._debugID, - { - displayName: 'Unknown', - children: [], - parentID: null, - }, - 'Foo', - ); - - ReactDOM.unmountComponentAtNode(node); - ReactComponentTreeTestUtils.expectTree( - barInstance._debugID, - { - displayName: 'Unknown', - children: [], - parentID: null, - }, - 'Foo', - ); - }); - - it('reports update counts', () => { - var node = document.createElement('div'); - - ReactDOM.render(
, node); - var divID = ReactComponentTreeHook.getRootIDs()[0]; - expectDev(ReactComponentTreeHook.getUpdateCount(divID)).toEqual(0); - - ReactDOM.render(, node); - var spanID = ReactComponentTreeHook.getRootIDs()[0]; - expectDev(ReactComponentTreeHook.getUpdateCount(divID)).toEqual(0); - expectDev(ReactComponentTreeHook.getUpdateCount(spanID)).toEqual(0); - - ReactDOM.render(, node); - expectDev(ReactComponentTreeHook.getUpdateCount(divID)).toEqual(0); - expectDev(ReactComponentTreeHook.getUpdateCount(spanID)).toEqual(1); - - ReactDOM.render(, node); - expectDev(ReactComponentTreeHook.getUpdateCount(divID)).toEqual(0); - expectDev(ReactComponentTreeHook.getUpdateCount(spanID)).toEqual(2); - - ReactDOM.unmountComponentAtNode(node); - expectDev(ReactComponentTreeHook.getUpdateCount(divID)).toEqual(0); - expectDev(ReactComponentTreeHook.getUpdateCount(spanID)).toEqual(0); - }); - - it('does not report top-level wrapper as a root', () => { - var node = document.createElement('div'); - - ReactDOM.render(
, node); - expectDev(ReactComponentTreeTestUtils.getRootDisplayNames()).toEqual([ - 'div', - ]); - - ReactDOM.render(
, node); - expectDev(ReactComponentTreeTestUtils.getRootDisplayNames()).toEqual([ - 'div', - ]); - - ReactDOM.unmountComponentAtNode(node); - expectDev(ReactComponentTreeTestUtils.getRootDisplayNames()).toEqual([]); - expectDev( - ReactComponentTreeTestUtils.getRegisteredDisplayNames(), - ).toEqual([]); - }); - - it('registers inlined text nodes', () => { - var node = document.createElement('div'); - - ReactDOM.render(
hi
, node); - expectDev( - ReactComponentTreeTestUtils.getRegisteredDisplayNames(), - ).toEqual(['div', '#text']); - - ReactDOM.unmountComponentAtNode(node); - expectDev( - ReactComponentTreeTestUtils.getRegisteredDisplayNames(), - ).toEqual([]); - }); - }); - - describeStack('in environment without Map, Set and Array.from', () => { - var realMap; - var realSet; - var realArrayFrom; - - beforeEach(() => { - realMap = global.Map; - realSet = global.Set; - realArrayFrom = Array.from; - - global.Map = undefined; - global.Set = undefined; - Array.from = undefined; - - jest.resetModules(); - - React = require('react'); - ReactDOM = require('react-dom'); - ReactDOMServer = require('react-dom/server'); - ReactInstanceMap = require('ReactInstanceMap'); - ReactComponentTreeHook = require('ReactComponentTreeHook'); - ReactComponentTreeTestUtils = require('ReactComponentTreeTestUtils'); - }); - - afterEach(() => { - global.Map = realMap; - global.Set = realSet; - Array.from = realArrayFrom; - }); - - it('works', () => { - class Qux extends React.Component { - render() { - return null; - } - } - - function Foo() { - return { - render() { - return ; - }, - }; - } - function Bar({children}) { - return

{children}

; - } - class Baz extends React.Component { - render() { - return ( -
- - - Hi, - Mom - - Click me. -
- ); - } - } - - var element = ; - var tree = { - displayName: 'Baz', - element, - children: [ - { - displayName: 'div', - children: [ - { - displayName: 'Foo', - element: , - children: [ - { - displayName: 'Qux', - element: , - children: [], - }, - ], - }, - { - displayName: 'Bar', - children: [ - { - displayName: 'h1', - children: [ - { - displayName: 'span', - children: [ - { - displayName: '#text', - element: 'Hi,', - text: 'Hi,', - }, - ], - }, - { - displayName: '#text', - text: 'Mom', - element: 'Mom', - }, - ], - }, - ], - }, - { - displayName: 'a', - children: [ - { - displayName: '#text', - text: 'Click me.', - element: 'Click me.', - }, - ], - }, - ], - }, - ], - }; - assertTreeMatches([element, tree]); - }); - }); -}); diff --git a/src/renderers/__tests__/ReactHostOperationHistoryHook-test.js b/src/renderers/__tests__/ReactHostOperationHistoryHook-test.js deleted file mode 100644 index fa43369f8df..00000000000 --- a/src/renderers/__tests__/ReactHostOperationHistoryHook-test.js +++ /dev/null @@ -1,781 +0,0 @@ -/** - * Copyright 2016-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. - * - * @emails react-core - */ - -'use strict'; - -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); -var describeStack = ReactDOMFeatureFlags.useFiber ? describe.skip : describe; - -// This is only used by ReactPerf which is currently not supported on Fiber. -// Use browser timeline integration instead. -describeStack('ReactHostOperationHistoryHook', () => { - var React; - var ReactPerf; - var ReactDOM; - var ReactDOMComponentTree; - var ReactHostOperationHistoryHook; - - beforeEach(() => { - jest.resetModules(); - - React = require('react'); - ReactPerf = require('ReactPerf'); - ReactDOM = require('react-dom'); - ReactDOMComponentTree = require('ReactDOMComponentTree'); - ReactHostOperationHistoryHook = require('ReactHostOperationHistoryHook'); - - ReactPerf.start(); - }); - - afterEach(() => { - ReactPerf.stop(); - }); - - function assertHistoryMatches(expectedHistory) { - var actualHistory = ReactHostOperationHistoryHook.getHistory(); - expectDev(actualHistory).toEqual(expectedHistory); - } - - describe('mount', () => { - it('gets recorded for host roots', () => { - var node = document.createElement('div'); - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render(

Hi.

, node); - - var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild); - assertHistoryMatches([ - { - instanceID: inst._debugID, - type: 'mount', - payload: 'DIV', - }, - ]); - }); - - it('gets recorded for composite roots', () => { - function Foo() { - return

Hi.

; - } - var node = document.createElement('div'); - - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render(, node); - - var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild); - assertHistoryMatches([ - { - instanceID: inst._debugID, - type: 'mount', - payload: 'DIV', - }, - ]); - }); - - it('gets ignored for composite roots that return null', () => { - function Foo() { - return null; - } - var node = document.createElement('div'); - - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render(, node); - - // Empty DOM components should be invisible to hooks. - assertHistoryMatches([]); - }); - - it('gets recorded when a native is mounted deeply instead of null', () => { - var element; - function Foo() { - return element; - } - - ReactHostOperationHistoryHook._preventClearing = true; - - var node = document.createElement('div'); - element = null; - ReactDOM.render(, node); - - element = ; - ReactDOM.render(, node); - var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild); - - // Since empty components should be invisible to hooks, - // we record a "mount" event rather than a "replace with". - assertHistoryMatches([ - { - instanceID: inst._debugID, - type: 'mount', - payload: 'SPAN', - }, - ]); - }); - }); - - describe('update styles', () => { - it('gets recorded during mount', () => { - var node = document.createElement('div'); - - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render( -
, - node, - ); - - var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild); - assertHistoryMatches([ - { - instanceID: inst._debugID, - type: 'update styles', - payload: { - color: 'red', - backgroundColor: 'yellow', - }, - }, - { - instanceID: inst._debugID, - type: 'mount', - payload: 'DIV', - }, - ]); - }); - - it('gets recorded during an update', () => { - var node = document.createElement('div'); - ReactDOM.render(
, node); - var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild); - - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render(
, node); - ReactDOM.render( -
, - node, - ); - ReactDOM.render(
, node); - ReactDOM.render(
, node); - - assertHistoryMatches([ - { - instanceID: inst._debugID, - type: 'update styles', - payload: {color: 'red'}, - }, - { - instanceID: inst._debugID, - type: 'update styles', - payload: {color: 'blue', backgroundColor: 'yellow'}, - }, - { - instanceID: inst._debugID, - type: 'update styles', - payload: {color: '', backgroundColor: 'green'}, - }, - { - instanceID: inst._debugID, - type: 'update styles', - payload: {backgroundColor: ''}, - }, - ]); - }); - - it('gets ignored if the styles are shallowly equal', () => { - var node = document.createElement('div'); - ReactDOM.render(
, node); - var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild); - - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render( -
, - node, - ); - ReactDOM.render( -
, - node, - ); - - assertHistoryMatches([ - { - instanceID: inst._debugID, - type: 'update styles', - payload: { - color: 'red', - backgroundColor: 'yellow', - }, - }, - ]); - }); - }); - - describe('update attribute', () => { - describe('simple attribute', () => { - it('gets recorded during mount', () => { - var node = document.createElement('div'); - - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render(
, node); - - var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild); - assertHistoryMatches([ - { - instanceID: inst._debugID, - type: 'update attribute', - payload: {className: 'rad'}, - }, - { - instanceID: inst._debugID, - type: 'update attribute', - payload: {tabIndex: 42}, - }, - { - instanceID: inst._debugID, - type: 'mount', - payload: 'DIV', - }, - ]); - }); - - it('gets recorded during an update', () => { - var node = document.createElement('div'); - ReactDOM.render(
, node); - var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild); - - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render(
, node); - ReactDOM.render(
, node); - ReactDOM.render(
, node); - - assertHistoryMatches([ - { - instanceID: inst._debugID, - type: 'update attribute', - payload: {className: 'rad'}, - }, - { - instanceID: inst._debugID, - type: 'update attribute', - payload: {className: 'mad'}, - }, - { - instanceID: inst._debugID, - type: 'update attribute', - payload: {tabIndex: 42}, - }, - { - instanceID: inst._debugID, - type: 'remove attribute', - payload: 'className', - }, - { - instanceID: inst._debugID, - type: 'update attribute', - payload: {tabIndex: 43}, - }, - ]); - }); - }); - - describe('attribute that gets removed with certain values', () => { - it('gets recorded as a removal during an update', () => { - var node = document.createElement('div'); - ReactDOM.render(
, node); - var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild); - - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render(
, node); - ReactDOM.render(
, node); - - assertHistoryMatches([ - { - instanceID: inst._debugID, - type: 'update attribute', - payload: {disabled: true}, - }, - { - instanceID: inst._debugID, - type: 'remove attribute', - payload: 'disabled', - }, - ]); - }); - }); - - describe('custom attribute', () => { - it('gets recorded during mount', () => { - var node = document.createElement('div'); - - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render(
, node); - - var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild); - assertHistoryMatches([ - { - instanceID: inst._debugID, - type: 'update attribute', - payload: {'data-x': 'rad'}, - }, - { - instanceID: inst._debugID, - type: 'update attribute', - payload: {'data-y': 42}, - }, - { - instanceID: inst._debugID, - type: 'mount', - payload: 'DIV', - }, - ]); - }); - - it('gets recorded during an update', () => { - var node = document.createElement('div'); - ReactDOM.render(
, node); - var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild); - - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render(
, node); - ReactDOM.render(
, node); - ReactDOM.render(
, node); - - assertHistoryMatches([ - { - instanceID: inst._debugID, - type: 'update attribute', - payload: {'data-x': 'rad'}, - }, - { - instanceID: inst._debugID, - type: 'update attribute', - payload: {'data-x': 'mad'}, - }, - { - instanceID: inst._debugID, - type: 'update attribute', - payload: {'data-y': 42}, - }, - { - instanceID: inst._debugID, - type: 'remove attribute', - payload: 'data-x', - }, - { - instanceID: inst._debugID, - type: 'update attribute', - payload: {'data-y': 43}, - }, - ]); - }); - }); - - describe('attribute on a web component', () => { - it('gets recorded during mount', () => { - var node = document.createElement('div'); - - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render(, node); - - var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild); - assertHistoryMatches([ - { - instanceID: inst._debugID, - type: 'update attribute', - payload: {className: 'rad'}, - }, - { - instanceID: inst._debugID, - type: 'update attribute', - payload: {tabIndex: 42}, - }, - { - instanceID: inst._debugID, - type: 'mount', - payload: 'MY-COMPONENT', - }, - ]); - }); - - it('gets recorded during an update', () => { - var node = document.createElement('div'); - ReactDOM.render(, node); - var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild); - - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render(, node); - ReactDOM.render(, node); - ReactDOM.render(, node); - - assertHistoryMatches([ - { - instanceID: inst._debugID, - type: 'update attribute', - payload: {className: 'rad'}, - }, - { - instanceID: inst._debugID, - type: 'update attribute', - payload: {className: 'mad'}, - }, - { - instanceID: inst._debugID, - type: 'update attribute', - payload: {tabIndex: 42}, - }, - { - instanceID: inst._debugID, - type: 'remove attribute', - payload: 'className', - }, - { - instanceID: inst._debugID, - type: 'update attribute', - payload: {tabIndex: 43}, - }, - ]); - }); - }); - }); - - describe('replace text', () => { - describe('text content', () => { - it('gets recorded during an update from text content', () => { - var node = document.createElement('div'); - ReactDOM.render(
Hi.
, node); - var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild); - - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render(
Bye.
, node); - - assertHistoryMatches([ - { - instanceID: inst._debugID, - type: 'replace text', - payload: 'Bye.', - }, - ]); - }); - - it('gets recorded during an update from html', () => { - var node = document.createElement('div'); - ReactDOM.render( -
, - node, - ); - var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild); - - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render(
Bye.
, node); - - assertHistoryMatches([ - { - instanceID: inst._debugID, - type: 'replace text', - payload: 'Bye.', - }, - ]); - }); - - it('gets recorded during an update from children', () => { - var node = document.createElement('div'); - ReactDOM.render(

, node); - var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild); - - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render(
Bye.
, node); - - assertHistoryMatches([ - { - instanceID: inst._debugID, - type: 'remove child', - payload: {fromIndex: 0}, - }, - { - instanceID: inst._debugID, - type: 'remove child', - payload: {fromIndex: 1}, - }, - { - instanceID: inst._debugID, - type: 'replace text', - payload: 'Bye.', - }, - ]); - }); - - it('gets ignored if new text is equal', () => { - var node = document.createElement('div'); - ReactDOM.render(
Hi.
, node); - - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render(
Hi.
, node); - - assertHistoryMatches([]); - }); - }); - - describe('text node', () => { - it('gets recorded during an update', () => { - var node = document.createElement('div'); - ReactDOM.render(
{'Hi.'}{42}
, node); - var inst1 = ReactDOMComponentTree.getInstanceFromNode( - node.firstChild.childNodes[0], - ); - var inst2 = ReactDOMComponentTree.getInstanceFromNode( - node.firstChild.childNodes[3], - ); - - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render(
{'Bye.'}{43}
, node); - - assertHistoryMatches([ - { - instanceID: inst1._debugID, - type: 'replace text', - payload: 'Bye.', - }, - { - instanceID: inst2._debugID, - type: 'replace text', - payload: '43', - }, - ]); - }); - - it('gets ignored if new text is equal', () => { - var node = document.createElement('div'); - ReactDOM.render(
{'Hi.'}{42}
, node); - - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render(
{'Hi.'}{42}
, node); - - assertHistoryMatches([]); - }); - }); - }); - - describe('replace with', () => { - it('gets recorded when composite renders to a different type', () => { - var element; - function Foo() { - return element; - } - - var node = document.createElement('div'); - element =
; - ReactDOM.render(, node); - var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild); - - element = ; - - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render(, node); - - assertHistoryMatches([ - { - instanceID: inst._debugID, - type: 'replace with', - payload: 'SPAN', - }, - ]); - }); - - it('gets recorded when composite renders to null after a native', () => { - var element; - function Foo() { - return element; - } - - var node = document.createElement('div'); - element = ; - ReactDOM.render(, node); - - var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild); - element = null; - - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render(, node); - - assertHistoryMatches([ - { - instanceID: inst._debugID, - type: 'replace with', - payload: '#comment', - }, - ]); - }); - - it('gets ignored if the type has not changed', () => { - var element; - function Foo() { - return element; - } - - var node = document.createElement('div'); - element =
; - ReactDOM.render(, node); - - element =
; - - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render(, node); - - assertHistoryMatches([]); - }); - }); - - describe('replace children', () => { - it('gets recorded during an update from text content', () => { - var node = document.createElement('div'); - ReactDOM.render(
Hi.
, node); - var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild); - - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render(
, node); - - assertHistoryMatches([ - { - instanceID: inst._debugID, - type: 'replace children', - payload: 'Bye.', - }, - ]); - }); - - it('gets recorded during an update from html', () => { - var node = document.createElement('div'); - ReactDOM.render(
, node); - var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild); - - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render(
, node); - - assertHistoryMatches([ - { - instanceID: inst._debugID, - type: 'replace children', - payload: 'Bye.', - }, - ]); - }); - - it('gets recorded during an update from children', () => { - var node = document.createElement('div'); - ReactDOM.render(

, node); - var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild); - - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render(
, node); - - assertHistoryMatches([ - { - instanceID: inst._debugID, - type: 'remove child', - payload: {fromIndex: 0}, - }, - { - instanceID: inst._debugID, - type: 'remove child', - payload: {fromIndex: 1}, - }, - { - instanceID: inst._debugID, - type: 'replace children', - payload: 'Hi.', - }, - ]); - }); - - it('gets ignored if new html is equal', () => { - var node = document.createElement('div'); - ReactDOM.render(
, node); - - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render(
, node); - - assertHistoryMatches([]); - }); - }); - - describe('insert child', () => { - it('gets reported when a child is inserted', () => { - var node = document.createElement('div'); - ReactDOM.render(
, node); - var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild); - - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render(

, node); - - assertHistoryMatches([ - { - instanceID: inst._debugID, - type: 'insert child', - payload: {toIndex: 1, content: 'P'}, - }, - ]); - }); - }); - - describe('move child', () => { - it('gets reported when a child is inserted', () => { - var node = document.createElement('div'); - ReactDOM.render(

, node); - var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild); - - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render(

, node); - - assertHistoryMatches([ - { - instanceID: inst._debugID, - type: 'move child', - payload: {fromIndex: 0, toIndex: 1}, - }, - ]); - }); - }); - - describe('remove child', () => { - it('gets reported when a child is removed', () => { - var node = document.createElement('div'); - ReactDOM.render(

, node); - var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild); - - ReactHostOperationHistoryHook._preventClearing = true; - ReactDOM.render(
, node); - - assertHistoryMatches([ - { - instanceID: inst._debugID, - type: 'remove child', - payload: {fromIndex: 1}, - }, - ]); - }); - }); -}); diff --git a/src/renderers/__tests__/ReactPerf-test.js b/src/renderers/__tests__/ReactPerf-test.js deleted file mode 100644 index 40d76b81207..00000000000 --- a/src/renderers/__tests__/ReactPerf-test.js +++ /dev/null @@ -1,746 +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. - * - * @emails react-core - */ - -'use strict'; - -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); -var describeStack = ReactDOMFeatureFlags.useFiber ? describe.skip : describe; - -// ReactPerf is currently not supported on Fiber. -// Use browser timeline integration instead. -describeStack('ReactPerf', () => { - var React; - var ReactDOM; - var ReactPerf; - var ReactTestUtils; - var emptyFunction; - - var App; - var Box; - var Div; - var LifeCycle; - - beforeEach(() => { - var now = 0; - jest.setMock('fbjs/lib/performanceNow', function() { - return now++; - }); - - if (typeof console.table !== 'function') { - console.table = () => {}; - console.table.isFake = true; - } - - React = require('react'); - ReactDOM = require('react-dom'); - ReactPerf = require('ReactPerf'); - ReactTestUtils = require('react-dom/test-utils'); - emptyFunction = require('fbjs/lib/emptyFunction'); - - App = class extends React.Component { - render() { - return
; - } - }; - - Box = class extends React.Component { - render() { - return
; - } - }; - - // ReactPerf only measures composites, so we put everything in one. - Div = class extends React.Component { - render() { - return
; - } - }; - - LifeCycle = class extends React.Component { - shouldComponentUpdate = emptyFunction.thatReturnsTrue; - componentWillMount = emptyFunction; - componentDidMount = emptyFunction; - componentWillReceiveProps = emptyFunction; - componentWillUpdate = emptyFunction; - componentDidUpdate = emptyFunction; - componentWillUnmount = emptyFunction; - render = emptyFunction.thatReturnsNull; - }; - }); - - afterEach(() => { - if (console.table.isFake) { - delete console.table; - } - }); - - function measure(fn) { - ReactPerf.start(); - fn(); - ReactPerf.stop(); - - // Make sure none of the methods crash. - ReactPerf.getWasted(); - ReactPerf.getInclusive(); - ReactPerf.getExclusive(); - ReactPerf.getOperations(); - - return ReactPerf.getLastMeasurements(); - } - - it('should count no-op update as waste', () => { - var container = document.createElement('div'); - ReactDOM.render(, container); - var measurements = measure(() => { - ReactDOM.render(, container); - }); - - var summary = ReactPerf.getWasted(measurements); - expect(summary).toEqual([ - { - key: 'App', - instanceCount: 1, - inclusiveRenderDuration: 3, - renderCount: 1, - }, - { - key: 'App > Box', - instanceCount: 2, - inclusiveRenderDuration: 2, - renderCount: 2, - }, - ]); - }); - - it('should count no-op update in child as waste', () => { - var container = document.createElement('div'); - ReactDOM.render(, container); - - // Here, we add a Box -- two of the updates are wasted time (but the - // addition of the third is not) - var measurements = measure(() => { - ReactDOM.render(, container); - }); - - var summary = ReactPerf.getWasted(measurements); - expect(summary).toEqual([ - { - key: 'App > Box', - instanceCount: 1, - inclusiveRenderDuration: 1, - renderCount: 1, - }, - ]); - }); - - function expectNoWaste(fn) { - var measurements = measure(fn); - var summary = ReactPerf.getWasted(measurements); - expect(summary).toEqual([]); - } - - it('should not count initial render as waste', () => { - expectNoWaste(() => { - ReactTestUtils.renderIntoDocument(); - }); - }); - - it('should not count unmount as waste', () => { - var container = document.createElement('div'); - ReactDOM.render(
hello
, container); - expectNoWaste(() => { - ReactDOM.unmountComponentAtNode(container); - }); - }); - - it('should not count content update as waste', () => { - var container = document.createElement('div'); - ReactDOM.render(
hello
, container); - expectNoWaste(() => { - ReactDOM.render(
hello world
, container); - }); - }); - - it('should not count child addition as waste', () => { - var container = document.createElement('div'); - ReactDOM.render(
, container); - expectNoWaste(() => { - ReactDOM.render(
, container); - }); - }); - - it('should not count child removal as waste', () => { - var container = document.createElement('div'); - ReactDOM.render(
, container); - expectNoWaste(() => { - ReactDOM.render(
, container); - }); - }); - - it('should not count property update as waste', () => { - var container = document.createElement('div'); - ReactDOM.render(
hey
, container); - expectNoWaste(() => { - ReactDOM.render(
hey
, container); - }); - }); - - it('should not count style update as waste', () => { - var container = document.createElement('div'); - ReactDOM.render(
hey
, container); - expectNoWaste(() => { - ReactDOM.render(
hey
, container); - }); - }); - - it('should not count property removal as waste', () => { - var container = document.createElement('div'); - ReactDOM.render(
hey
, container); - expectNoWaste(() => { - ReactDOM.render(
hey
, container); - }); - }); - - it('should not count raw HTML update as waste', () => { - var container = document.createElement('div'); - ReactDOM.render( -
, - container, - ); - expectNoWaste(() => { - ReactDOM.render( -
, - container, - ); - }); - }); - - it('should not count child reordering as waste', () => { - var container = document.createElement('div'); - ReactDOM.render(
, container); - expectNoWaste(() => { - ReactDOM.render(
, container); - }); - }); - - it('should not count text update as waste', () => { - var container = document.createElement('div'); - ReactDOM.render(
{'hello'}{'world'}
, container); - expectNoWaste(() => { - ReactDOM.render(
{'hello'}{'friend'}
, container); - }); - }); - - it('should not count replacing null with a host as waste', () => { - var element = null; - function Foo() { - return element; - } - var container = document.createElement('div'); - ReactDOM.render(, container); - expectNoWaste(() => { - element =
; - ReactDOM.render(, container); - }); - }); - - it('should not count replacing a host with null as waste', () => { - var element =
; - function Foo() { - return element; - } - var container = document.createElement('div'); - ReactDOM.render(, container); - expectNoWaste(() => { - element = null; - ReactDOM.render(, container); - }); - }); - - it('should include stats for components unmounted during measurement', () => { - var container = document.createElement('div'); - var measurements = measure(() => { - ReactDOM.render(
, container); - ReactDOM.render(
, container); - }); - expect(ReactPerf.getExclusive(measurements)).toEqual([ - { - key: 'Div', - instanceCount: 3, - counts: {ctor: 3, render: 4}, - durations: {ctor: 3, render: 4}, - totalDuration: 7, - }, - ]); - }); - - it('should include lifecycle methods in measurements', () => { - var container = document.createElement('div'); - var measurements = measure(() => { - var instance = ReactDOM.render(, container); - ReactDOM.render(, container); - instance.setState({}); - ReactDOM.unmountComponentAtNode(container); - }); - expect(ReactPerf.getExclusive(measurements)).toEqual([ - { - key: 'LifeCycle', - instanceCount: 1, - totalDuration: 14, - counts: { - ctor: 1, - shouldComponentUpdate: 2, - componentWillMount: 1, - componentDidMount: 1, - componentWillReceiveProps: 1, - componentWillUpdate: 2, - componentDidUpdate: 2, - componentWillUnmount: 1, - render: 3, - }, - durations: { - ctor: 1, - shouldComponentUpdate: 2, - componentWillMount: 1, - componentDidMount: 1, - componentWillReceiveProps: 1, - componentWillUpdate: 2, - componentDidUpdate: 2, - componentWillUnmount: 1, - render: 3, - }, - }, - ]); - }); - - it('should include render time of functional components', () => { - function Foo() { - return null; - } - - var container = document.createElement('div'); - var measurements = measure(() => { - ReactDOM.render(, container); - }); - expect(ReactPerf.getExclusive(measurements)).toEqual([ - { - key: 'Foo', - instanceCount: 1, - totalDuration: 1, - counts: { - render: 1, - }, - durations: { - render: 1, - }, - }, - ]); - }); - - it('should not count time in a portal towards lifecycle method', () => { - function Foo() { - return null; - } - - var portalContainer = document.createElement('div'); - class Portal extends React.Component { - componentDidMount() { - ReactDOM.render(, portalContainer); - } - render() { - return null; - } - } - - var container = document.createElement('div'); - var measurements = measure(() => { - ReactDOM.render(, container); - }); - - expect(ReactPerf.getExclusive(measurements)).toEqual([ - { - key: 'Portal', - instanceCount: 1, - totalDuration: 6, - counts: { - ctor: 1, - componentDidMount: 1, - render: 1, - }, - durations: { - ctor: 1, - // We want to exclude nested imperative ReactDOM.render() from lifecycle hook's own time. - // Otherwise it would artificially float to the top even though its exclusive time is small. - // This is how we get 4 as a number with the performanceNow() mock: - // - we capture the time we enter componentDidMount (n = 0) - // - we capture the time when we enter a nested flush (n = 1) - // - in the nested flush, we call it twice: before and after rendering. (n = 3) - // - we capture the time when we exit a nested flush (n = 4) - // - we capture the time we exit componentDidMount (n = 5) - // Time spent in componentDidMount = (5 - 0 - (4 - 3)) = 4. - componentDidMount: 4, - render: 1, - }, - }, - { - key: 'Foo', - instanceCount: 1, - totalDuration: 1, - counts: { - render: 1, - }, - durations: { - render: 1, - }, - }, - ]); - }); - - it('warns once when using getMeasurementsSummaryMap', () => { - var measurements = measure(() => {}); - spyOn(console, 'warn'); - ReactPerf.getMeasurementsSummaryMap(measurements); - expectDev(console.warn.calls.count()).toBe(1); - expectDev(console.warn.calls.argsFor(0)[0]).toContain( - '`ReactPerf.getMeasurementsSummaryMap(...)` is deprecated. Use ' + - '`ReactPerf.getWasted(...)` instead.', - ); - - ReactPerf.getMeasurementsSummaryMap(measurements); - expectDev(console.warn.calls.count()).toBe(1); - }); - - it('warns once when using printDOM', () => { - var measurements = measure(() => {}); - spyOn(console, 'warn'); - ReactPerf.printDOM(measurements); - expectDev(console.warn.calls.count()).toBe(1); - expectDev(console.warn.calls.argsFor(0)[0]).toContain( - '`ReactPerf.printDOM(...)` is deprecated. Use ' + - '`ReactPerf.printOperations(...)` instead.', - ); - - ReactPerf.printDOM(measurements); - expectDev(console.warn.calls.count()).toBe(1); - }); - - it('returns isRunning state', () => { - expect(ReactPerf.isRunning()).toBe(false); - - ReactPerf.start(); - expect(ReactPerf.isRunning()).toBe(true); - - ReactPerf.stop(); - expect(ReactPerf.isRunning()).toBe(false); - }); - - it('start has no effect when already running', () => { - expect(ReactPerf.isRunning()).toBe(false); - - ReactPerf.start(); - expect(ReactPerf.isRunning()).toBe(true); - - ReactPerf.start(); - expect(ReactPerf.isRunning()).toBe(true); - - ReactPerf.stop(); - expect(ReactPerf.isRunning()).toBe(false); - }); - - it('stop has no effect when already stopped', () => { - expect(ReactPerf.isRunning()).toBe(false); - - ReactPerf.stop(); - expect(ReactPerf.isRunning()).toBe(false); - - ReactPerf.stop(); - expect(ReactPerf.isRunning()).toBe(false); - }); - - it('should print console error only once', () => { - __DEV__ = false; - - spyOn(console, 'error'); - - expect(ReactPerf.getLastMeasurements()).toEqual([]); - expect(ReactPerf.getExclusive()).toEqual([]); - expect(ReactPerf.getInclusive()).toEqual([]); - expect(ReactPerf.getWasted()).toEqual([]); - expect(ReactPerf.getOperations()).toEqual([]); - expect(ReactPerf.printExclusive()).toEqual(undefined); - expect(ReactPerf.printInclusive()).toEqual(undefined); - expect(ReactPerf.printWasted()).toEqual(undefined); - expect(ReactPerf.printOperations()).toEqual(undefined); - expect(ReactPerf.start()).toBe(undefined); - expect(ReactPerf.stop()).toBe(undefined); - expect(ReactPerf.isRunning()).toBe(false); - - expectDev(console.error.calls.count()).toBe(1); - - __DEV__ = true; - }); - - it('should work when measurement starts during reconciliation', () => { - // https://github.com/facebook/react/issues/6949#issuecomment-230371009 - class Measurer extends React.Component { - componentWillMount() { - ReactPerf.start(); - } - - componentDidMount() { - ReactPerf.stop(); - } - - componentWillUpdate() { - ReactPerf.start(); - } - - componentDidUpdate() { - ReactPerf.stop(); - } - - render() { - // Force reconciliation despite constant element - return React.cloneElement(this.props.children); - } - } - - var container = document.createElement('div'); - ReactDOM.render(, container); - expect(ReactPerf.getWasted()).toEqual([]); - - ReactDOM.render(, container); - expect(ReactPerf.getWasted()).toEqual([ - { - key: 'Measurer', - instanceCount: 1, - inclusiveRenderDuration: 4, - renderCount: 1, - }, - { - key: 'App', - instanceCount: 1, - inclusiveRenderDuration: 3, - renderCount: 1, - }, - { - key: 'App > Box', - instanceCount: 2, - inclusiveRenderDuration: 2, - renderCount: 2, - }, - ]); - }); - - it('should not print errant warnings if render() throws', () => { - var container = document.createElement('div'); - var thrownErr = new Error('Muhaha!'); - - class Evil extends React.Component { - render() { - throw thrownErr; - } - } - - ReactPerf.start(); - try { - ReactDOM.render( -
- - -
, - container, - ); - } catch (err) { - if (err !== thrownErr) { - throw err; - } - } - ReactPerf.stop(); - }); - - it('should not print errant warnings if componentWillMount() throws', () => { - var container = document.createElement('div'); - var thrownErr = new Error('Muhaha!'); - - class Evil extends React.Component { - componentWillMount() { - throw thrownErr; - } - render() { - return
; - } - } - - ReactPerf.start(); - try { - ReactDOM.render( -
- - -
, - container, - ); - } catch (err) { - if (err !== thrownErr) { - throw err; - } - } - ReactPerf.stop(); - }); - - it('should not print errant warnings if componentDidMount() throws', () => { - var container = document.createElement('div'); - var thrownErr = new Error('Muhaha!'); - - class Evil extends React.Component { - componentDidMount() { - throw thrownErr; - } - render() { - return
; - } - } - - ReactPerf.start(); - try { - ReactDOM.render( -
- - -
, - container, - ); - } catch (err) { - if (err !== thrownErr) { - throw err; - } - } - ReactPerf.stop(); - }); - - it('should not print errant warnings if portal throws in render()', () => { - var container = document.createElement('div'); - var thrownErr = new Error('Muhaha!'); - - class Evil extends React.Component { - render() { - throw thrownErr; - } - } - class EvilPortal extends React.Component { - componentDidMount() { - var portalContainer = document.createElement('div'); - ReactDOM.render(, portalContainer); - } - render() { - return
; - } - } - - ReactPerf.start(); - try { - ReactDOM.render( -
- - -
, - container, - ); - } catch (err) { - if (err !== thrownErr) { - throw err; - } - } - ReactDOM.unmountComponentAtNode(container); - ReactPerf.stop(); - }); - - it('should not print errant warnings if portal throws in componentWillMount()', () => { - var container = document.createElement('div'); - var thrownErr = new Error('Muhaha!'); - - class Evil extends React.Component { - componentWillMount() { - throw thrownErr; - } - render() { - return
; - } - } - class EvilPortal extends React.Component { - componentWillMount() { - var portalContainer = document.createElement('div'); - ReactDOM.render(, portalContainer); - } - render() { - return
; - } - } - - ReactPerf.start(); - try { - ReactDOM.render( -
- - -
, - container, - ); - } catch (err) { - if (err !== thrownErr) { - throw err; - } - } - ReactDOM.unmountComponentAtNode(container); - ReactPerf.stop(); - }); - - it('should not print errant warnings if portal throws in componentDidMount()', () => { - var container = document.createElement('div'); - var thrownErr = new Error('Muhaha!'); - - class Evil extends React.Component { - componentDidMount() { - throw thrownErr; - } - render() { - return
; - } - } - class EvilPortal extends React.Component { - componentDidMount() { - var portalContainer = document.createElement('div'); - ReactDOM.render(, portalContainer); - } - render() { - return
; - } - } - - ReactPerf.start(); - try { - ReactDOM.render( -
- - -
, - container, - ); - } catch (err) { - if (err !== thrownErr) { - throw err; - } - } - ReactDOM.unmountComponentAtNode(container); - ReactPerf.stop(); - }); -}); diff --git a/src/renderers/__tests__/refs-test.js b/src/renderers/__tests__/refs-test.js index 3c920a3c500..f127a343d60 100644 --- a/src/renderers/__tests__/refs-test.js +++ b/src/renderers/__tests__/refs-test.js @@ -308,91 +308,6 @@ describe('ref swapping', () => { }); }); -describe('string refs between fiber and stack', () => { - beforeEach(() => { - jest.resetModules(); - React = require('react'); - ReactTestUtils = require('react-dom/test-utils'); - }); - - it('attaches, detaches from fiber component with stack layer', () => { - const ReactCurrentOwner = require('ReactCurrentOwner'); - - const ReactDOMStack = require('ReactDOMStackEntry'); - const ReactDOMFiber = require('ReactDOMFiberEntry'); - const ReactInstanceMap = require('ReactInstanceMap'); - let layerMounted = false; - class A extends React.Component { - render() { - return
; - } - componentDidMount() { - // ReactLayeredComponentMixin sets ReactCurrentOwner manually - ReactCurrentOwner.current = ReactInstanceMap.get(this); - const span = ; - ReactCurrentOwner.current = null; - - ReactDOMStack.unstable_renderSubtreeIntoContainer( - this, - span, - (this._container = document.createElement('div')), - () => { - expect(this.refs.span.nodeName).toBe('SPAN'); - layerMounted = true; - }, - ); - } - componentWillUnmount() { - ReactDOMStack.unmountComponentAtNode(this._container); - } - } - const container = document.createElement('div'); - const a = ReactDOMFiber.render(, container); - expect(a.refs.span).toBeTruthy(); - ReactDOMFiber.unmountComponentAtNode(container); - expect(a.refs.span).toBe(undefined); - expect(layerMounted).toBe(true); - }); - - it('attaches, detaches from stack component with fiber layer', () => { - const ReactCurrentOwner = require('ReactCurrentOwner'); - const ReactDOM = require('ReactDOMStackEntry'); - const ReactDOMFiber = require('ReactDOMFiberEntry'); - const ReactInstanceMap = require('ReactInstanceMap'); - let layerMounted = false; - class A extends React.Component { - render() { - return
; - } - componentDidMount() { - // ReactLayeredComponentMixin sets ReactCurrentOwner manually - ReactCurrentOwner.current = ReactInstanceMap.get(this); - const span = ; - ReactCurrentOwner.current = null; - - ReactDOMFiber.unstable_renderSubtreeIntoContainer( - this, - span, - (this._container = document.createElement('div')), - () => { - expect(this.refs.span.nodeName).toBe('SPAN'); - layerMounted = true; - }, - ); - } - componentWillUnmount() { - ReactDOMFiber.unmountComponentAtNode(this._container); - } - } - const container = document.createElement('div'); - const a = ReactDOM.render(, container); - expect(a.refs.span).toBeTruthy(); - ReactDOM.unmountComponentAtNode(container); - expect(a.refs.span).toBe(undefined); - expect(layerMounted).toBe(true); - }); -}); - describe('root level refs', () => { it('attaches and detaches root refs', () => { var ReactDOM = require('react-dom'); diff --git a/src/renderers/dom/ReactDOMServerStackEntry.js b/src/renderers/dom/ReactDOMServerStackEntry.js deleted file mode 100644 index 67ca724da8f..00000000000 --- a/src/renderers/dom/ReactDOMServerStackEntry.js +++ /dev/null @@ -1,37 +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 ReactDOMServerStackEntry - */ - -'use strict'; - -var ReactServerRendering = require('ReactServerRendering'); -var ReactVersion = require('ReactVersion'); - -require('ReactDOMInjection'); -require('ReactDOMStackInjection'); - -var ReactDOMServerStack = { - renderToString: ReactServerRendering.renderToString, - renderToStaticMarkup: ReactServerRendering.renderToStaticMarkup, - version: ReactVersion, -}; - -if (__DEV__) { - var ReactInstrumentation = require('ReactInstrumentation'); - var ReactDOMUnknownPropertyHook = require('ReactDOMUnknownPropertyHook'); - var ReactDOMNullInputValuePropHook = require('ReactDOMNullInputValuePropHook'); - var ReactDOMInvalidARIAHook = require('ReactDOMInvalidARIAHook'); - - ReactInstrumentation.debugTool.addHook(ReactDOMUnknownPropertyHook); - ReactInstrumentation.debugTool.addHook(ReactDOMNullInputValuePropHook); - ReactInstrumentation.debugTool.addHook(ReactDOMInvalidARIAHook); -} - -module.exports = ReactDOMServerStack; diff --git a/src/renderers/dom/ReactDOMStackEntry.js b/src/renderers/dom/ReactDOMStackEntry.js deleted file mode 100644 index fe12a60eb07..00000000000 --- a/src/renderers/dom/ReactDOMStackEntry.js +++ /dev/null @@ -1,170 +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 ReactDOMStackEntry - */ - -/* globals __REACT_DEVTOOLS_GLOBAL_HOOK__*/ - -'use strict'; - -require('checkReact'); -var ReactDOMComponentTree = require('ReactDOMComponentTree'); -var ReactGenericBatching = require('ReactGenericBatching'); -var ReactMount = require('ReactMount'); -var ReactUpdates = require('ReactUpdates'); -var ReactReconciler = require('ReactReconciler'); -var ReactVersion = require('ReactVersion'); - -var findDOMNode = require('findDOMNode'); -var getHostComponentFromComposite = require('getHostComponentFromComposite'); - -if (__DEV__) { - var warning = require('fbjs/lib/warning'); -} - -require('ReactDOMInjection'); -require('ReactDOMClientInjection'); -require('ReactDOMStackInjection'); - -var ReactDOMStack = { - findDOMNode: findDOMNode, - render: ReactMount.render, - unmountComponentAtNode: ReactMount.unmountComponentAtNode, - version: ReactVersion, - - /* eslint-disable camelcase */ - unstable_batchedUpdates: ReactGenericBatching.batchedUpdates, - unstable_renderSubtreeIntoContainer: ReactMount.renderSubtreeIntoContainer, - /* eslint-enable camelcase */ - - __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: { - // For TapEventPlugin which is popular in open source - EventPluginHub: require('EventPluginHub'), - // Used by test-utils - EventPluginRegistry: require('EventPluginRegistry'), - EventPropagators: require('EventPropagators'), - ReactControlledComponent: require('ReactControlledComponent'), - ReactDOMComponentTree, - ReactDOMEventListener: require('ReactDOMEventListener'), - ReactUpdates: ReactUpdates, - }, -}; - -// Inject the runtime into a devtools global hook regardless of browser. -// Allows for debugging when the hook is injected on the page. -if ( - typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' && - typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject === 'function' -) { - __REACT_DEVTOOLS_GLOBAL_HOOK__.inject({ - ComponentTree: { - getClosestInstanceFromNode: ReactDOMComponentTree.getClosestInstanceFromNode, - getNodeFromInstance: function(inst) { - // inst is an internal instance (but could be a composite) - if (inst._renderedComponent) { - inst = getHostComponentFromComposite(inst); - } - if (inst) { - return ReactDOMComponentTree.getNodeFromInstance(inst); - } else { - return null; - } - }, - }, - Mount: ReactMount, - Reconciler: ReactReconciler, - rendererPackageName: 'react-dom', - }); -} - -if (__DEV__) { - var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment'); - if (ExecutionEnvironment.canUseDOM && window.top === window.self) { - // First check if devtools is not installed - if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined') { - // If we're in Chrome or Firefox, provide a download link if not installed. - if ( - (navigator.userAgent.indexOf('Chrome') > -1 && - navigator.userAgent.indexOf('Edge') === -1) || - navigator.userAgent.indexOf('Firefox') > -1 - ) { - // Firefox does not have the issue with devtools loaded over file:// - var showFileUrlMessage = - window.location.protocol.indexOf('http') === -1 && - navigator.userAgent.indexOf('Firefox') === -1; - console.debug( - 'Download the React DevTools ' + - (showFileUrlMessage - ? 'and use an HTTP server (instead of a file: URL) ' - : '') + - 'for a better development experience: ' + - 'https://fb.me/react-devtools', - ); - } - } - - var testFunc = function testFn() {}; - warning( - (testFunc.name || testFunc.toString()).indexOf('testFn') !== -1, - "It looks like you're using a minified copy of the development build " + - 'of React. When deploying React apps to production, make sure to use ' + - 'the production build which skips development warnings and is faster. ' + - 'See https://fb.me/react-minification for more details.', - ); - - // If we're in IE8, check to see if we are in compatibility mode and provide - // information on preventing compatibility mode - var ieCompatibilityMode = - document.documentMode && document.documentMode < 8; - - warning( - !ieCompatibilityMode, - 'Internet Explorer is running in compatibility mode; please add the ' + - 'following tag to your HTML to prevent this from happening: ' + - '', - ); - - var expectedFeatures = [ - // shims - Array.isArray, - Array.prototype.every, - Array.prototype.forEach, - Array.prototype.indexOf, - Array.prototype.map, - Date.now, - Function.prototype.bind, - Object.keys, - String.prototype.trim, - ]; - - for (var i = 0; i < expectedFeatures.length; i++) { - if (!expectedFeatures[i]) { - warning( - false, - 'One or more ES5 shims expected by React are not available: ' + - 'https://fb.me/react-warning-polyfills', - ); - break; - } - } - } -} - -if (__DEV__) { - var ReactInstrumentation = require('ReactInstrumentation'); - var ReactDOMUnknownPropertyHook = require('ReactDOMUnknownPropertyHook'); - var ReactDOMNullInputValuePropHook = require('ReactDOMNullInputValuePropHook'); - var ReactDOMInvalidARIAHook = require('ReactDOMInvalidARIAHook'); - - ReactInstrumentation.debugTool.addHook(ReactDOMUnknownPropertyHook); - ReactInstrumentation.debugTool.addHook(ReactDOMNullInputValuePropHook); - ReactInstrumentation.debugTool.addHook(ReactDOMInvalidARIAHook); -} - -module.exports = ReactDOMStack; diff --git a/src/renderers/dom/shared/DOMPropertyOperations.js b/src/renderers/dom/shared/DOMPropertyOperations.js index 96dead745ba..7079b722ef9 100644 --- a/src/renderers/dom/shared/DOMPropertyOperations.js +++ b/src/renderers/dom/shared/DOMPropertyOperations.js @@ -12,8 +12,6 @@ 'use strict'; var DOMProperty = require('DOMProperty'); -var ReactDOMComponentTree = require('ReactDOMComponentTree'); -var ReactInstrumentation = require('ReactInstrumentation'); if (__DEV__) { var warning = require('fbjs/lib/warning'); @@ -203,11 +201,6 @@ var DOMPropertyOperations = { if (__DEV__) { var payload = {}; payload[name] = value; - ReactInstrumentation.debugTool.onHostOperation({ - instanceID: ReactDOMComponentTree.getInstanceFromNode(node)._debugID, - type: 'update attribute', - payload: payload, - }); } }, @@ -224,11 +217,6 @@ var DOMPropertyOperations = { if (__DEV__) { var payload = {}; payload[name] = value; - ReactInstrumentation.debugTool.onHostOperation({ - instanceID: ReactDOMComponentTree.getInstanceFromNode(node)._debugID, - type: 'update attribute', - payload: payload, - }); } }, @@ -240,13 +228,6 @@ var DOMPropertyOperations = { */ deleteValueForAttribute: function(node, name) { node.removeAttribute(name); - if (__DEV__) { - ReactInstrumentation.debugTool.onHostOperation({ - instanceID: ReactDOMComponentTree.getInstanceFromNode(node)._debugID, - type: 'remove attribute', - payload: name, - }); - } }, /** @@ -274,14 +255,6 @@ var DOMPropertyOperations = { } else { node.removeAttribute(name); } - - if (__DEV__) { - ReactInstrumentation.debugTool.onHostOperation({ - instanceID: ReactDOMComponentTree.getInstanceFromNode(node)._debugID, - type: 'remove attribute', - payload: name, - }); - } }, }; diff --git a/src/renderers/dom/shared/__tests__/ReactServerRendering-test.js b/src/renderers/dom/shared/__tests__/ReactServerRendering-test.js index 1e273f8ecb4..39d17b5f5cd 100644 --- a/src/renderers/dom/shared/__tests__/ReactServerRendering-test.js +++ b/src/renderers/dom/shared/__tests__/ReactServerRendering-test.js @@ -15,8 +15,6 @@ var ExecutionEnvironment; var React; var ReactDOM; var ReactDOMServer; -var ReactMarkupChecksum; -var ReactReconcileTransaction; var ReactTestUtils; var PropTypes; @@ -31,8 +29,6 @@ describe('ReactDOMServer', () => { React = require('react'); ReactDOM = require('react-dom'); ReactTestUtils = require('react-dom/test-utils'); - ReactMarkupChecksum = require('ReactMarkupChecksum'); - ReactReconcileTransaction = require('ReactReconcileTransaction'); PropTypes = require('prop-types'); ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment'); @@ -49,16 +45,7 @@ describe('ReactDOMServer', () => { var response = ReactDOMServer.renderToString(hello world); expect(response).toMatch( new RegExp( - 'hello world', + 'hello world', ), ); }); @@ -66,18 +53,7 @@ describe('ReactDOMServer', () => { it('should generate simple markup for self-closing tags', () => { var response = ReactDOMServer.renderToString(); expect(response).toMatch( - new RegExp( - '', - ), + new RegExp(''), ); }); @@ -85,16 +61,7 @@ describe('ReactDOMServer', () => { var response = ReactDOMServer.renderToString(); expect(response).toMatch( new RegExp( - '', + '', ), ); }); @@ -135,17 +102,8 @@ describe('ReactDOMServer', () => { '
' + '' + (ReactDOMFeatureFlags.useFiber ? 'My name is child' @@ -212,9 +170,6 @@ describe('ReactDOMServer', () => { (ReactDOMFeatureFlags.useFiber ? '' : ' ' + ID_ATTRIBUTE_NAME + '="[^"]*"') + - (ReactDOMFeatureFlags.useFiber - ? '' - : ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+"') + '>' + (ReactDOMFeatureFlags.useFiber ? 'Component name: TestComponent' @@ -578,11 +533,6 @@ describe('ReactDOMServer', () => { return
{this.state.text}
; } } - - ReactReconcileTransaction.prototype.perform = function() { - // We shouldn't ever be calling this on the server - throw new Error('Browser reconcile transaction should not be used'); - }; var markup = ReactDOMServer.renderToString(); expect(markup).toContain('hello, world'); }); @@ -602,11 +552,6 @@ describe('ReactDOMServer', () => { return
{this.state.text}
; } } - - ReactReconcileTransaction.prototype.perform = function() { - // We shouldn't ever be calling this on the server - throw new Error('Browser reconcile transaction should not be used'); - }; var markup = ReactDOMServer.renderToString(); expect(markup).toContain('hello, world'); }); diff --git a/src/renderers/dom/shared/hooks/ReactDOMInvalidARIAHook.js b/src/renderers/dom/shared/hooks/ReactDOMInvalidARIAHook.js index 705c5aa207d..341b7e2cedc 100644 --- a/src/renderers/dom/shared/hooks/ReactDOMInvalidARIAHook.js +++ b/src/renderers/dom/shared/hooks/ReactDOMInvalidARIAHook.js @@ -24,27 +24,17 @@ var hasOwnProperty = Object.prototype.hasOwnProperty; if (__DEV__) { var warning = require('fbjs/lib/warning'); - var { - ReactComponentTreeHook, - ReactDebugCurrentFrame, - } = require('ReactGlobalSharedState'); - var {getStackAddendumByID} = ReactComponentTreeHook; + var {ReactDebugCurrentFrame} = require('ReactGlobalSharedState'); var validAriaProperties = require('./validAriaProperties'); } -function getStackAddendum(debugID) { - if (debugID != null) { - // This can only happen on Stack - return getStackAddendumByID(debugID); - } else { - // This can only happen on Fiber / Server - var stack = ReactDebugCurrentFrame.getStackAddendum(); - return stack != null ? stack : ''; - } +function getStackAddendum() { + var stack = ReactDebugCurrentFrame.getStackAddendum(); + return stack != null ? stack : ''; } -function validateProperty(tagName, name, debugID) { +function validateProperty(tagName, name) { if (hasOwnProperty.call(warnedProperties, name) && warnedProperties[name]) { return true; } @@ -62,7 +52,7 @@ function validateProperty(tagName, name, debugID) { false, 'Invalid ARIA attribute `%s`. ARIA attributes follow the pattern aria-* and must be lowercase.%s', name, - getStackAddendum(debugID), + getStackAddendum(), ); warnedProperties[name] = true; return true; @@ -74,7 +64,7 @@ function validateProperty(tagName, name, debugID) { 'Invalid ARIA attribute `%s`. Did you mean `%s`?%s', name, correctName, - getStackAddendum(debugID), + getStackAddendum(), ); warnedProperties[name] = true; return true; @@ -100,7 +90,7 @@ function validateProperty(tagName, name, debugID) { 'Unknown ARIA attribute `%s`. Did you mean `%s`?%s', name, standardName, - getStackAddendum(debugID), + getStackAddendum(), ); warnedProperties[name] = true; return true; @@ -110,11 +100,11 @@ function validateProperty(tagName, name, debugID) { return true; } -function warnInvalidARIAProps(type, props, debugID) { +function warnInvalidARIAProps(type, props) { const invalidProps = []; for (var key in props) { - var isValid = validateProperty(type, key, debugID); + var isValid = validateProperty(type, key); if (!isValid) { invalidProps.push(key); } @@ -131,7 +121,7 @@ function warnInvalidARIAProps(type, props, debugID) { 'For details, see https://fb.me/invalid-aria-prop%s', unknownPropString, type, - getStackAddendum(debugID), + getStackAddendum(), ); } else if (invalidProps.length > 1) { warning( @@ -140,32 +130,20 @@ function warnInvalidARIAProps(type, props, debugID) { 'For details, see https://fb.me/invalid-aria-prop%s', unknownPropString, type, - getStackAddendum(debugID), + getStackAddendum(), ); } } -function validateProperties(type, props, debugID /* Stack only */) { +function validateProperties(type, props) { if (isCustomComponent(type, props)) { return; } - warnInvalidARIAProps(type, props, debugID); + warnInvalidARIAProps(type, props); } var ReactDOMInvalidARIAHook = { - // Fiber validateProperties, - // Stack - onBeforeMountComponent(debugID, element) { - if (__DEV__ && element != null && typeof element.type === 'string') { - validateProperties(element.type, element.props, debugID); - } - }, - onBeforeUpdateComponent(debugID, element) { - if (__DEV__ && element != null && typeof element.type === 'string') { - validateProperties(element.type, element.props, debugID); - } - }, }; module.exports = ReactDOMInvalidARIAHook; diff --git a/src/renderers/dom/shared/hooks/ReactDOMNullInputValuePropHook.js b/src/renderers/dom/shared/hooks/ReactDOMNullInputValuePropHook.js index 905e30b13e4..77b0b702a3f 100644 --- a/src/renderers/dom/shared/hooks/ReactDOMNullInputValuePropHook.js +++ b/src/renderers/dom/shared/hooks/ReactDOMNullInputValuePropHook.js @@ -13,27 +13,17 @@ if (__DEV__) { var warning = require('fbjs/lib/warning'); - var { - ReactComponentTreeHook, - ReactDebugCurrentFrame, - } = require('ReactGlobalSharedState'); - var {getStackAddendumByID} = ReactComponentTreeHook; + var {ReactDebugCurrentFrame} = require('ReactGlobalSharedState'); } var didWarnValueNull = false; -function getStackAddendum(debugID) { - if (debugID != null) { - // This can only happen on Stack - return getStackAddendumByID(debugID); - } else { - // This can only happen on Fiber / Server - var stack = ReactDebugCurrentFrame.getStackAddendum(); - return stack != null ? stack : ''; - } +function getStackAddendum() { + var stack = ReactDebugCurrentFrame.getStackAddendum(); + return stack != null ? stack : ''; } -function validateProperties(type, props, debugID /* Stack only */) { +function validateProperties(type, props) { if (type !== 'input' && type !== 'textarea' && type !== 'select') { return; } @@ -44,7 +34,7 @@ function validateProperties(type, props, debugID /* Stack only */) { 'Consider using the empty string to clear the component or `undefined` ' + 'for uncontrolled components.%s', type, - getStackAddendum(debugID), + getStackAddendum(), ); didWarnValueNull = true; @@ -52,19 +42,7 @@ function validateProperties(type, props, debugID /* Stack only */) { } var ReactDOMNullInputValuePropHook = { - // Fiber validateProperties, - // Stack - onBeforeMountComponent(debugID, element) { - if (__DEV__ && element != null && typeof element.type === 'string') { - validateProperties(element.type, element.props, debugID); - } - }, - onBeforeUpdateComponent(debugID, element) { - if (__DEV__ && element != null && typeof element.type === 'string') { - validateProperties(element.type, element.props, debugID); - } - }, }; module.exports = ReactDOMNullInputValuePropHook; diff --git a/src/renderers/dom/shared/hooks/ReactDOMUnknownPropertyHook.js b/src/renderers/dom/shared/hooks/ReactDOMUnknownPropertyHook.js index f49442ccf3d..9db27edab0d 100644 --- a/src/renderers/dom/shared/hooks/ReactDOMUnknownPropertyHook.js +++ b/src/renderers/dom/shared/hooks/ReactDOMUnknownPropertyHook.js @@ -17,22 +17,12 @@ var isCustomComponent = require('isCustomComponent'); if (__DEV__) { var warning = require('fbjs/lib/warning'); - var { - ReactComponentTreeHook, - ReactDebugCurrentFrame, - } = require('ReactGlobalSharedState'); - var {getStackAddendumByID} = ReactComponentTreeHook; + var {ReactDebugCurrentFrame} = require('ReactGlobalSharedState'); } -function getStackAddendum(debugID) { - if (debugID != null) { - // This can only happen on Stack - return getStackAddendumByID(debugID); - } else { - // This can only happen on Fiber / Server - var stack = ReactDebugCurrentFrame.getStackAddendum(); - return stack != null ? stack : ''; - } +function getStackAddendum() { + var stack = ReactDebugCurrentFrame.getStackAddendum(); + return stack != null ? stack : ''; } if (__DEV__) { @@ -45,7 +35,7 @@ if (__DEV__) { ); var possibleStandardNames = require('possibleStandardNames'); - var validateProperty = function(tagName, name, value, debugID) { + var validateProperty = function(tagName, name, value) { if (hasOwnProperty.call(warnedProperties, name) && warnedProperties[name]) { return true; } @@ -76,7 +66,7 @@ if (__DEV__) { 'Unknown event handler property `%s`. Did you mean `%s`?%s', name, registrationName, - getStackAddendum(debugID), + getStackAddendum(), ); warnedProperties[name] = true; return true; @@ -87,7 +77,7 @@ if (__DEV__) { false, 'Unknown event handler property `%s`. It will be ignored.%s', name, - getStackAddendum(debugID), + getStackAddendum(), ); warnedProperties[name] = true; return true; @@ -140,7 +130,7 @@ if (__DEV__) { 'Received a `%s` for string attribute `is`. If this is expected, cast ' + 'the value to a string.%s', typeof value, - getStackAddendum(debugID), + getStackAddendum(), ); warnedProperties[name] = true; return true; @@ -152,7 +142,7 @@ if (__DEV__) { 'Received NaN for numeric attribute `%s`. If this is expected, cast ' + 'the value to a string.%s', name, - getStackAddendum(debugID), + getStackAddendum(), ); warnedProperties[name] = true; return true; @@ -169,7 +159,7 @@ if (__DEV__) { 'Invalid DOM property `%s`. Did you mean `%s`?%s', name, standardName, - getStackAddendum(debugID), + getStackAddendum(), ); warnedProperties[name] = true; return true; @@ -186,7 +176,7 @@ if (__DEV__) { 'it from the DOM element.%s', name, lowerCasedName, - getStackAddendum(debugID), + getStackAddendum(), ); warnedProperties[name] = true; return true; @@ -199,7 +189,7 @@ if (__DEV__) { 'the value to a string.%s', value, name, - getStackAddendum(debugID), + getStackAddendum(), ); warnedProperties[name] = true; return true; @@ -221,10 +211,10 @@ if (__DEV__) { }; } -var warnUnknownProperties = function(type, props, debugID) { +var warnUnknownProperties = function(type, props) { var unknownProps = []; for (var key in props) { - var isValid = validateProperty(type, key, props[key], debugID); + var isValid = validateProperty(type, key, props[key]); if (!isValid) { unknownProps.push(key); } @@ -239,7 +229,7 @@ var warnUnknownProperties = function(type, props, debugID) { 'For details, see https://fb.me/react-unknown-prop%s', unknownPropString, type, - getStackAddendum(debugID), + getStackAddendum(), ); } else if (unknownProps.length > 1) { warning( @@ -249,32 +239,20 @@ var warnUnknownProperties = function(type, props, debugID) { 'For details, see https://fb.me/react-unknown-prop%s', unknownPropString, type, - getStackAddendum(debugID), + getStackAddendum(), ); } }; -function validateProperties(type, props, debugID /* Stack only */) { +function validateProperties(type, props) { if (isCustomComponent(type, props)) { return; } - warnUnknownProperties(type, props, debugID); + warnUnknownProperties(type, props); } var ReactDOMUnknownPropertyHook = { - // Fiber validateProperties, - // Stack - onBeforeMountComponent(debugID, element) { - if (__DEV__ && element != null && typeof element.type === 'string') { - validateProperties(element.type, element.props, debugID); - } - }, - onBeforeUpdateComponent(debugID, element) { - if (__DEV__ && element != null && typeof element.type === 'string') { - validateProperties(element.type, element.props, debugID); - } - }, }; module.exports = ReactDOMUnknownPropertyHook; diff --git a/src/renderers/dom/stack/client/DOMChildrenOperations.js b/src/renderers/dom/stack/client/DOMChildrenOperations.js deleted file mode 100644 index b7e4e1e105d..00000000000 --- a/src/renderers/dom/stack/client/DOMChildrenOperations.js +++ /dev/null @@ -1,250 +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 DOMChildrenOperations - */ - -'use strict'; - -var DOMLazyTree = require('DOMLazyTree'); -var Danger = require('Danger'); -var ReactDOMComponentTree = require('ReactDOMComponentTree'); -var ReactInstrumentation = require('ReactInstrumentation'); - -var createMicrosoftUnsafeLocalFunction = require('createMicrosoftUnsafeLocalFunction'); -var setInnerHTML = require('setInnerHTML'); -var setTextContent = require('setTextContent'); - -function getNodeAfter(parentNode, node) { - // Special case for text components, which return [open, close] comments - // from getHostNode. - if (Array.isArray(node)) { - node = node[1]; - } - return node ? node.nextSibling : parentNode.firstChild; -} - -/** - * Inserts `childNode` as a child of `parentNode` at the `index`. - * - * @param {DOMElement} parentNode Parent node in which to insert. - * @param {DOMElement} childNode Child node to insert. - * @param {number} index Index at which to insert the child. - * @internal - */ -var insertChildAt = createMicrosoftUnsafeLocalFunction(function( - parentNode, - childNode, - referenceNode, -) { - // We rely exclusively on `insertBefore(node, null)` instead of also using - // `appendChild(node)`. (Using `undefined` is not allowed by all browsers so - // we are careful to use `null`.) - parentNode.insertBefore(childNode, referenceNode); -}); - -function insertLazyTreeChildAt(parentNode, childTree, referenceNode) { - DOMLazyTree.insertTreeBefore(parentNode, childTree, referenceNode); -} - -function moveChild(parentNode, childNode, referenceNode) { - if (Array.isArray(childNode)) { - moveDelimitedText(parentNode, childNode[0], childNode[1], referenceNode); - } else { - insertChildAt(parentNode, childNode, referenceNode); - } -} - -function removeChild(parentNode, childNode) { - if (Array.isArray(childNode)) { - var closingComment = childNode[1]; - childNode = childNode[0]; - removeDelimitedText(parentNode, childNode, closingComment); - parentNode.removeChild(closingComment); - } - parentNode.removeChild(childNode); -} - -function moveDelimitedText( - parentNode, - openingComment, - closingComment, - referenceNode, -) { - var node = openingComment; - while (true) { - var nextNode = node.nextSibling; - insertChildAt(parentNode, node, referenceNode); - if (node === closingComment) { - break; - } - node = nextNode; - } -} - -function removeDelimitedText(parentNode, startNode, closingComment) { - while (true) { - var node = startNode.nextSibling; - if (node === closingComment) { - // The closing comment is removed by ReactMultiChild. - break; - } else { - parentNode.removeChild(node); - } - } -} - -function replaceDelimitedText(openingComment, closingComment, stringText) { - var parentNode = openingComment.parentNode; - var nodeAfterComment = openingComment.nextSibling; - if (nodeAfterComment === closingComment) { - // There are no text nodes between the opening and closing comments; insert - // a new one if stringText isn't empty. - if (stringText) { - insertChildAt( - parentNode, - document.createTextNode(stringText), - nodeAfterComment, - ); - } - } else { - if (stringText) { - // Set the text content of the first node after the opening comment, and - // remove all following nodes up until the closing comment. - setTextContent(nodeAfterComment, stringText); - removeDelimitedText(parentNode, nodeAfterComment, closingComment); - } else { - removeDelimitedText(parentNode, openingComment, closingComment); - } - } - - if (__DEV__) { - ReactInstrumentation.debugTool.onHostOperation({ - instanceID: ReactDOMComponentTree.getInstanceFromNode(openingComment) - ._debugID, - type: 'replace text', - payload: stringText, - }); - } -} - -var dangerouslyReplaceNodeWithMarkup = Danger.dangerouslyReplaceNodeWithMarkup; -if (__DEV__) { - dangerouslyReplaceNodeWithMarkup = function(oldChild, markup, prevInstance) { - Danger.dangerouslyReplaceNodeWithMarkup(oldChild, markup); - if (prevInstance._debugID !== 0) { - ReactInstrumentation.debugTool.onHostOperation({ - instanceID: prevInstance._debugID, - type: 'replace with', - payload: markup.toString(), - }); - } else { - var nextInstance = ReactDOMComponentTree.getInstanceFromNode(markup.node); - if (nextInstance._debugID !== 0) { - ReactInstrumentation.debugTool.onHostOperation({ - instanceID: nextInstance._debugID, - type: 'mount', - payload: markup.toString(), - }); - } - } - }; -} - -/** - * Operations for updating with DOM children. - */ -var DOMChildrenOperations = { - dangerouslyReplaceNodeWithMarkup: dangerouslyReplaceNodeWithMarkup, - - replaceDelimitedText: replaceDelimitedText, - - /** - * Updates a component's children by processing a series of updates. The - * update configurations are each expected to have a `parentNode` property. - * - * @param {array} updates List of update configurations. - * @internal - */ - processUpdates: function(parentNode, updates) { - if (__DEV__) { - var parentNodeDebugID = ReactDOMComponentTree.getInstanceFromNode( - parentNode, - )._debugID; - } - - for (var k = 0; k < updates.length; k++) { - var update = updates[k]; - switch (update.type) { - case 'INSERT_MARKUP': - insertLazyTreeChildAt( - parentNode, - update.content, - getNodeAfter(parentNode, update.afterNode), - ); - if (__DEV__) { - ReactInstrumentation.debugTool.onHostOperation({ - instanceID: parentNodeDebugID, - type: 'insert child', - payload: { - toIndex: update.toIndex, - content: update.content.toString(), - }, - }); - } - break; - case 'MOVE_EXISTING': - moveChild( - parentNode, - update.fromNode, - getNodeAfter(parentNode, update.afterNode), - ); - if (__DEV__) { - ReactInstrumentation.debugTool.onHostOperation({ - instanceID: parentNodeDebugID, - type: 'move child', - payload: {fromIndex: update.fromIndex, toIndex: update.toIndex}, - }); - } - break; - case 'SET_MARKUP': - setInnerHTML(parentNode, update.content); - if (__DEV__) { - ReactInstrumentation.debugTool.onHostOperation({ - instanceID: parentNodeDebugID, - type: 'replace children', - payload: update.content.toString(), - }); - } - break; - case 'TEXT_CONTENT': - setTextContent(parentNode, update.content); - if (__DEV__) { - ReactInstrumentation.debugTool.onHostOperation({ - instanceID: parentNodeDebugID, - type: 'replace text', - payload: update.content.toString(), - }); - } - break; - case 'REMOVE_NODE': - removeChild(parentNode, update.fromNode); - if (__DEV__) { - ReactInstrumentation.debugTool.onHostOperation({ - instanceID: parentNodeDebugID, - type: 'remove child', - payload: {fromIndex: update.fromIndex}, - }); - } - break; - } - } - }, -}; - -module.exports = DOMChildrenOperations; diff --git a/src/renderers/dom/stack/client/DOMLazyTree.js b/src/renderers/dom/stack/client/DOMLazyTree.js deleted file mode 100644 index c1533ace2fa..00000000000 --- a/src/renderers/dom/stack/client/DOMLazyTree.js +++ /dev/null @@ -1,130 +0,0 @@ -/** - * Copyright 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 DOMLazyTree - */ - -'use strict'; - -var Namespaces = require('DOMNamespaces').Namespaces; -var setInnerHTML = require('setInnerHTML'); -var {DOCUMENT_FRAGMENT_NODE, ELEMENT_NODE} = require('HTMLNodeType'); -var createMicrosoftUnsafeLocalFunction = require('createMicrosoftUnsafeLocalFunction'); -var setTextContent = require('setTextContent'); - -/** - * In IE (8-11) and Edge, appending nodes with no children is dramatically - * faster than appending a full subtree, so we essentially queue up the - * .appendChild calls here and apply them so each node is added to its parent - * before any children are added. - * - * In other browsers, doing so is slower or neutral compared to the other order - * (in Firefox, twice as slow) so we only do this inversion in IE. - * - * See https://github.com/sophiebits/innerhtml-vs-createelement-vs-clonenode. - */ -var enableLazy = - (typeof document !== 'undefined' && - typeof document.documentMode === 'number') || - (typeof navigator !== 'undefined' && - typeof navigator.userAgent === 'string' && - /\bEdge\/\d/.test(navigator.userAgent)); - -function insertTreeChildren(tree) { - if (!enableLazy) { - return; - } - var node = tree.node; - var children = tree.children; - if (children.length) { - for (var i = 0; i < children.length; i++) { - insertTreeBefore(node, children[i], null); - } - } else if (tree.html != null) { - setInnerHTML(node, tree.html); - } else if (tree.text != null) { - setTextContent(node, tree.text); - } -} - -var insertTreeBefore = createMicrosoftUnsafeLocalFunction(function( - parentNode, - tree, - referenceNode, -) { - // DocumentFragments aren't actually part of the DOM after insertion so - // appending children won't update the DOM. We need to ensure the fragment - // is properly populated first, breaking out of our lazy approach for just - // this level. Also, some plugins (like Flash Player) will read - // nodes immediately upon insertion into the DOM, so - // must also be populated prior to insertion into the DOM. - if ( - tree.node.nodeType === DOCUMENT_FRAGMENT_NODE || - (tree.node.nodeType === ELEMENT_NODE && - tree.node.nodeName.toLowerCase() === 'object' && - (tree.node.namespaceURI == null || - tree.node.namespaceURI === Namespaces.html)) - ) { - insertTreeChildren(tree); - parentNode.insertBefore(tree.node, referenceNode); - } else { - parentNode.insertBefore(tree.node, referenceNode); - insertTreeChildren(tree); - } -}); - -function replaceChildWithTree(oldNode, newTree) { - oldNode.parentNode.replaceChild(newTree.node, oldNode); - insertTreeChildren(newTree); -} - -function queueChild(parentTree, childTree) { - if (enableLazy) { - parentTree.children.push(childTree); - } else { - parentTree.node.appendChild(childTree.node); - } -} - -function queueHTML(tree, html) { - if (enableLazy) { - tree.html = html; - } else { - setInnerHTML(tree.node, html); - } -} - -function queueText(tree, text) { - if (enableLazy) { - tree.text = text; - } else { - setTextContent(tree.node, text); - } -} - -function toString() { - return this.node.nodeName; -} - -function DOMLazyTree(node) { - return { - node: node, - children: [], - html: null, - text: null, - toString, - }; -} - -DOMLazyTree.insertTreeBefore = insertTreeBefore; -DOMLazyTree.replaceChildWithTree = replaceChildWithTree; -DOMLazyTree.queueChild = queueChild; -DOMLazyTree.queueHTML = queueHTML; -DOMLazyTree.queueText = queueText; - -module.exports = DOMLazyTree; diff --git a/src/renderers/dom/stack/client/Danger.js b/src/renderers/dom/stack/client/Danger.js deleted file mode 100644 index 77542e52c88..00000000000 --- a/src/renderers/dom/stack/client/Danger.js +++ /dev/null @@ -1,56 +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 Danger - */ - -'use strict'; - -var DOMLazyTree = require('DOMLazyTree'); -var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment'); - -var createNodesFromMarkup = require('fbjs/lib/createNodesFromMarkup'); -var emptyFunction = require('fbjs/lib/emptyFunction'); -var invariant = require('fbjs/lib/invariant'); - -var Danger = { - /** - * Replaces a node with a string of markup at its current position within its - * parent. The markup must render into a single root node. - * - * @param {DOMElement} oldChild Child node to replace. - * @param {string} markup Markup to render in place of the child node. - * @internal - */ - dangerouslyReplaceNodeWithMarkup: function(oldChild, markup) { - invariant( - ExecutionEnvironment.canUseDOM, - 'dangerouslyReplaceNodeWithMarkup(...): Cannot render markup in a ' + - 'worker thread. Make sure `window` and `document` are available ' + - 'globally before requiring React when unit testing or use ' + - 'ReactDOMServer.renderToString() for server rendering.', - ); - invariant(markup, 'dangerouslyReplaceNodeWithMarkup(...): Missing markup.'); - invariant( - oldChild.nodeName !== 'HTML', - 'dangerouslyReplaceNodeWithMarkup(...): Cannot replace markup of the ' + - ' node. This is because browser quirks make this unreliable ' + - 'and/or slow. If you want to render to the root you must use ' + - 'server rendering. See ReactDOMServer.renderToString().', - ); - - if (typeof markup === 'string') { - var newChild = createNodesFromMarkup(markup, emptyFunction)[0]; - oldChild.parentNode.replaceChild(newChild, oldChild); - } else { - DOMLazyTree.replaceChildWithTree(oldChild, markup); - } - }, -}; - -module.exports = Danger; diff --git a/src/renderers/dom/stack/client/ReactComponentBrowserEnvironment.js b/src/renderers/dom/stack/client/ReactComponentBrowserEnvironment.js deleted file mode 100644 index fe874cdc6c4..00000000000 --- a/src/renderers/dom/stack/client/ReactComponentBrowserEnvironment.js +++ /dev/null @@ -1,28 +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 ReactComponentBrowserEnvironment - */ - -'use strict'; - -var DOMChildrenOperations = require('DOMChildrenOperations'); -var ReactDOMIDOperations = require('ReactDOMIDOperations'); - -/** - * Abstracts away all functionality of the reconciler that requires knowledge of - * the browser context. TODO: These callers should be refactored to avoid the - * need for this injection. - */ -var ReactComponentBrowserEnvironment = { - processChildrenUpdates: ReactDOMIDOperations.dangerouslyProcessChildrenUpdates, - - replaceNodeWithMarkup: DOMChildrenOperations.dangerouslyReplaceNodeWithMarkup, -}; - -module.exports = ReactComponentBrowserEnvironment; diff --git a/src/renderers/dom/stack/client/ReactDOMComponent.js b/src/renderers/dom/stack/client/ReactDOMComponent.js deleted file mode 100644 index daf427fcc9e..00000000000 --- a/src/renderers/dom/stack/client/ReactDOMComponent.js +++ /dev/null @@ -1,1197 +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 ReactDOMComponent - */ - -'use strict'; - -var AutoFocusUtils = require('AutoFocusUtils'); -var CSSPropertyOperations = require('CSSPropertyOperations'); -var DOMLazyTree = require('DOMLazyTree'); -var Namespaces = require('DOMNamespaces').Namespaces; -var DOMMarkupOperations = require('DOMMarkupOperations'); -var DOMProperty = require('DOMProperty'); -var DOMPropertyOperations = require('DOMPropertyOperations'); -var EventPluginRegistry = require('EventPluginRegistry'); -var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter'); -var ReactDOMComponentFlags = require('ReactDOMComponentFlags'); -var ReactDOMComponentTree = require('ReactDOMComponentTree'); -var ReactDOMInput = require('ReactDOMInput'); -var ReactDOMOption = require('ReactDOMOption'); -var ReactDOMSelect = require('ReactDOMSelect'); -var ReactDOMTextarea = require('ReactDOMTextarea'); -var ReactInstrumentation = require('ReactInstrumentation'); -var ReactMultiChild = require('ReactMultiChild'); -var ReactServerRenderingTransaction = require('ReactServerRenderingTransaction'); -var {DOCUMENT_FRAGMENT_NODE} = require('HTMLNodeType'); - -var dangerousStyleValue = require('dangerousStyleValue'); -var emptyFunction = require('fbjs/lib/emptyFunction'); -var escapeTextContentForBrowser = require('escapeTextContentForBrowser'); -var hyphenateStyleName = require('fbjs/lib/hyphenateStyleName'); -var inputValueTracking = require('inputValueTracking'); -var invariant = require('fbjs/lib/invariant'); -var isCustomComponent = require('isCustomComponent'); -var memoizeStringOnly = require('fbjs/lib/memoizeStringOnly'); -var omittedCloseTags = require('omittedCloseTags'); -var validateDOMNesting = require('validateDOMNesting'); -var voidElementTags = require('voidElementTags'); -var warnValidStyle = require('warnValidStyle'); - -if (__DEV__) { - var warning = require('fbjs/lib/warning'); -} - -var didWarnShadyDOM = false; - -var Flags = ReactDOMComponentFlags; -var getNode = ReactDOMComponentTree.getNodeFromInstance; -var listenTo = ReactBrowserEventEmitter.listenTo; -var registrationNameModules = EventPluginRegistry.registrationNameModules; - -// For quickly matching children type, to test if can be treated as content. -var CONTENT_TYPES = {string: true, number: true}; - -var STYLE = 'style'; -var HTML = '__html'; - -function getDeclarationErrorAddendum(internalInstance) { - if (internalInstance) { - var owner = internalInstance._currentElement._owner || null; - if (owner) { - var name = owner.getName(); - if (name) { - return '\n\nThis DOM node was rendered by `' + name + '`.'; - } - } - } - return ''; -} - -/** - * @param {object} component - * @param {?object} props - */ -function assertValidProps(component, props) { - if (!props) { - return; - } - // Note the use of `==` which checks for null or undefined. - if (voidElementTags[component._tag]) { - invariant( - props.children == null && props.dangerouslySetInnerHTML == null, - '%s is a void element tag and must neither have `children` nor ' + - 'use `dangerouslySetInnerHTML`.%s', - component._tag, - getDeclarationErrorAddendum(component), - ); - } - if (props.dangerouslySetInnerHTML != null) { - invariant( - props.children == null, - 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.', - ); - invariant( - typeof props.dangerouslySetInnerHTML === 'object' && - HTML in props.dangerouslySetInnerHTML, - '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' + - 'Please visit https://fb.me/react-invariant-dangerously-set-inner-html ' + - 'for more information.', - ); - } - if (__DEV__) { - warning( - props.suppressContentEditableWarning || - !props.contentEditable || - props.children == null, - 'A component is `contentEditable` and contains `children` managed by ' + - 'React. It is now your responsibility to guarantee that none of ' + - 'those nodes are unexpectedly modified or duplicated. This is ' + - 'probably not intentional.', - ); - } - invariant( - props.style == null || typeof props.style === 'object', - 'The `style` prop expects a mapping from style properties to values, ' + - "not a string. For example, style={{marginRight: spacing + 'em'}} when " + - 'using JSX.%s', - getDeclarationErrorAddendum(component), - ); -} - -function ensureListeningTo(inst, registrationName, transaction) { - if (transaction instanceof ReactServerRenderingTransaction) { - return; - } - var containerInfo = inst._hostContainerInfo; - var isDocumentFragment = - containerInfo._node && - containerInfo._node.nodeType === DOCUMENT_FRAGMENT_NODE; - var doc = isDocumentFragment - ? containerInfo._node - : containerInfo._ownerDocument; - listenTo(registrationName, doc); -} - -function inputPostMount() { - var inst = this; - ReactDOMInput.postMountWrapper(inst); -} - -function textareaPostMount() { - var inst = this; - ReactDOMTextarea.postMountWrapper(inst); -} - -function optionPostMount() { - var inst = this; - ReactDOMOption.postMountWrapper(inst); -} - -var processStyleName = memoizeStringOnly(function(styleName) { - return hyphenateStyleName(styleName); -}); - -function createMarkupForStyles(styles, component) { - var serialized = ''; - var delimiter = ''; - for (var styleName in styles) { - if (!styles.hasOwnProperty(styleName)) { - continue; - } - var isCustomProperty = styleName.indexOf('--') === 0; - var styleValue = styles[styleName]; - if (__DEV__) { - if (!isCustomProperty) { - warnValidStyle(styleName, styleValue, component); - } - } - if (styleValue != null) { - serialized += delimiter + processStyleName(styleName) + ':'; - serialized += dangerousStyleValue( - styleName, - styleValue, - isCustomProperty, - ); - - delimiter = ';'; - } - } - return serialized || null; -} - -var setAndValidateContentChildDev = emptyFunction; -if (__DEV__) { - setAndValidateContentChildDev = function(content) { - var hasExistingContent = this._contentDebugID != null; - var debugID = this._debugID; - // This ID represents the inlined child that has no backing instance: - var contentDebugID = -debugID; - - if (content == null) { - if (hasExistingContent) { - ReactInstrumentation.debugTool.onUnmountComponent(this._contentDebugID); - } - this._contentDebugID = null; - return; - } - - validateDOMNesting(null, '' + content, this, this._ancestorInfo); - this._contentDebugID = contentDebugID; - if (hasExistingContent) { - ReactInstrumentation.debugTool.onBeforeUpdateComponent( - contentDebugID, - content, - ); - ReactInstrumentation.debugTool.onUpdateComponent(contentDebugID); - } else { - ReactInstrumentation.debugTool.onBeforeMountComponent( - contentDebugID, - content, - debugID, - ); - ReactInstrumentation.debugTool.onMountComponent(contentDebugID); - ReactInstrumentation.debugTool.onSetChildren(debugID, [contentDebugID]); - } - }; -} - -// There are so many media events, it makes sense to just -// maintain a list rather than create a `trapBubbledEvent` for each -var mediaEvents = { - topAbort: 'abort', - topCanPlay: 'canplay', - topCanPlayThrough: 'canplaythrough', - topDurationChange: 'durationchange', - topEmptied: 'emptied', - topEncrypted: 'encrypted', - topEnded: 'ended', - topError: 'error', - topLoadedData: 'loadeddata', - topLoadedMetadata: 'loadedmetadata', - topLoadStart: 'loadstart', - topPause: 'pause', - topPlay: 'play', - topPlaying: 'playing', - topProgress: 'progress', - topRateChange: 'ratechange', - topSeeked: 'seeked', - topSeeking: 'seeking', - topStalled: 'stalled', - topSuspend: 'suspend', - topTimeUpdate: 'timeupdate', - topVolumeChange: 'volumechange', - topWaiting: 'waiting', -}; - -function trackInputValue() { - inputValueTracking.track(getNode(this)); -} - -function trapClickOnNonInteractiveElement() { - // Mobile Safari does not fire properly bubble click events on - // non-interactive elements, which means delegated click listeners do not - // fire. The workaround for this bug involves attaching an empty click - // listener on the target node. - // http://www.quirksmode.org/blog/archives/2010/09/click_event_del.html - // Just set it using the onclick property so that we don't have to manage any - // bookkeeping for it. Not sure if we need to clear it when the listener is - // removed. - // TODO: Only do this for the relevant Safaris maybe? - var node = getNode(this); - node.onclick = emptyFunction; -} - -function trapBubbledEventsLocal() { - var inst = this; - // If a component renders to null or if another component fatals and causes - // the state of the tree to be corrupted, `node` here can be null. - invariant(inst._rootNodeID, 'Must be mounted to trap events'); - var node = getNode(inst); - invariant(node, 'trapBubbledEvent(...): Requires node to be rendered.'); - - switch (inst._tag) { - case 'iframe': - case 'object': - inst._wrapperState.listeners = [ - ReactBrowserEventEmitter.trapBubbledEvent('topLoad', 'load', node), - ]; - break; - case 'video': - case 'audio': - inst._wrapperState.listeners = []; - // Create listener for each media event - for (var event in mediaEvents) { - if (mediaEvents.hasOwnProperty(event)) { - inst._wrapperState.listeners.push( - ReactBrowserEventEmitter.trapBubbledEvent( - event, - mediaEvents[event], - node, - ), - ); - } - } - break; - case 'source': - inst._wrapperState.listeners = [ - ReactBrowserEventEmitter.trapBubbledEvent('topError', 'error', node), - ]; - break; - case 'img': - case 'image': - inst._wrapperState.listeners = [ - ReactBrowserEventEmitter.trapBubbledEvent('topError', 'error', node), - ReactBrowserEventEmitter.trapBubbledEvent('topLoad', 'load', node), - ]; - break; - case 'form': - inst._wrapperState.listeners = [ - ReactBrowserEventEmitter.trapBubbledEvent('topReset', 'reset', node), - ReactBrowserEventEmitter.trapBubbledEvent('topSubmit', 'submit', node), - ]; - break; - case 'input': - case 'select': - case 'textarea': - inst._wrapperState.listeners = [ - ReactBrowserEventEmitter.trapBubbledEvent( - 'topInvalid', - 'invalid', - node, - ), - ]; - break; - case 'details': - inst._wrapperState.listeners = [ - ReactBrowserEventEmitter.trapBubbledEvent('topToggle', 'toggle', node), - ]; - break; - } -} - -function postUpdateSelectWrapper() { - ReactDOMSelect.postUpdateWrapper(this); -} - -var newlineEatingTags = { - listing: true, - pre: true, - textarea: true, -}; - -// We accept any tag to be rendered but since this gets injected into arbitrary -// HTML, we want to make sure that it's a safe tag. -// http://www.w3.org/TR/REC-xml/#NT-Name - -var VALID_TAG_REGEX = /^[a-zA-Z][a-zA-Z:_\.\-\d]*$/; // Simplified subset -var validatedTagCache = {}; - -function validateDangerousTag(tag) { - if (!validatedTagCache.hasOwnProperty(tag)) { - invariant(VALID_TAG_REGEX.test(tag), 'Invalid tag: %s', tag); - validatedTagCache[tag] = true; - } -} - -var globalIdCounter = 1; -var warnedUnknownTags = { - // Chrome is the only major browser not shipping