diff --git a/Gruntfile.js b/Gruntfile.js index 0932add89c47f..20df7c9b85239 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -117,12 +117,31 @@ module.exports = function(grunt) { 'build-modules', 'browserify:addonsMin', ]); + grunt.registerTask('build:dom', [ + 'build-modules', + 'version-check', + 'browserify:dom', + ]); + grunt.registerTask('build:dom-min', [ + 'build-modules', + 'version-check', + 'browserify:domMin', + ]); + grunt.registerTask('build:dom-server', [ + 'build-modules', + 'version-check', + 'browserify:domServer', + ]); + grunt.registerTask('build:dom-server-min', [ + 'build-modules', + 'version-check', + 'browserify:domServerMin', + ]); grunt.registerTask('build:npm-react', [ 'version-check', 'build-modules', 'npm-react:release', ]); - grunt.registerTask('build:react-dom', require('./grunt/tasks/react-dom')); var jestTasks = require('./grunt/tasks/jest'); grunt.registerTask('jest:normal', jestTasks.normal); @@ -141,7 +160,10 @@ module.exports = function(grunt) { 'browserify:addons', 'browserify:min', 'browserify:addonsMin', - 'build:react-dom', + 'browserify:dom', + 'browserify:domMin', + 'browserify:domServer', + 'browserify:domServerMin', 'npm-react:release', 'npm-react:pack', 'npm-react-dom:release', diff --git a/grunt/config/browserify.js b/grunt/config/browserify.js index bfcb01ddda4e6..e61fba9bc986f 100644 --- a/grunt/config/browserify.js +++ b/grunt/config/browserify.js @@ -7,11 +7,30 @@ var grunt = require('grunt'); var UglifyJS = require('uglify-js'); var uglifyify = require('uglifyify'); var derequire = require('derequire'); +var aliasify = require('aliasify'); +var globalShim = require('browserify-global-shim'); var collapser = require('bundle-collapser/plugin'); var envifyDev = envify({NODE_ENV: process.env.NODE_ENV || 'development'}); var envifyProd = envify({NODE_ENV: process.env.NODE_ENV || 'production'}); +var SECRET_INTERNALS_NAME = 'React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED'; + +var shimSharedModules = globalShim.configure({ + './ReactCurrentOwner': SECRET_INTERNALS_NAME + '.ReactCurrentOwner', + './ReactComponentTreeHook': SECRET_INTERNALS_NAME + '.ReactComponentTreeHook', + // All these methods are shared are exposed. + './ReactElement': 'React', + './ReactPropTypes': 'React.PropTypes', + './ReactChildren': 'React.Children', +}); + +var shimDOMModules = aliasify.configure({ + 'aliases': { + './ReactAddonsDOMDependencies': './build/modules/ReactAddonsDOMDependenciesUMDShim.js', + }, +}); + var SIMPLE_TEMPLATE = grunt.file.read('./grunt/data/header-template-short.txt'); @@ -87,6 +106,7 @@ var addons = { debug: false, standalone: 'React', packageName: 'React (with addons)', + transforms: [shimDOMModules], globalTransforms: [envifyDev], plugins: [collapser], after: [derequire, simpleBannerify], @@ -100,7 +120,72 @@ var addonsMin = { debug: false, standalone: 'React', packageName: 'React (with addons)', - transforms: [envifyProd, uglifyify], + transforms: [shimDOMModules, envifyProd, uglifyify], + globalTransforms: [envifyProd], + plugins: [collapser], + // No need to derequire because the minifier will mangle + // the "require" calls. + + after: [minify, bannerify], +}; + +// The DOM Builds +var dom = { + entries: [ + './build/modules/ReactDOMUMDEntry.js', + ], + outfile: './build/react-dom.js', + debug: false, + standalone: 'ReactDOM', + // Apply as global transform so that we also envify fbjs and any other deps + transforms: [shimSharedModules], + globalTransforms: [envifyDev], + plugins: [collapser], + after: [derequire, simpleBannerify], +}; + +var domMin = { + entries: [ + './build/modules/ReactDOMUMDEntry.js', + ], + outfile: './build/react-dom.min.js', + debug: false, + standalone: 'ReactDOM', + // Envify twice. The first ensures that when we uglifyify, we have the right + // conditions to exclude requires. The global transform runs on deps. + transforms: [shimSharedModules, envifyProd, uglifyify], + globalTransforms: [envifyProd], + plugins: [collapser], + // No need to derequire because the minifier will mangle + // the "require" calls. + + after: [minify, bannerify], +}; + +var domServer = { + entries: [ + './build/modules/ReactDOMServerUMDEntry.js', + ], + outfile: './build/react-dom-server.js', + debug: false, + standalone: 'ReactDOMServer', + // Apply as global transform so that we also envify fbjs and any other deps + transforms: [shimSharedModules], + globalTransforms: [envifyDev], + plugins: [collapser], + after: [derequire, simpleBannerify], +}; + +var domServerMin = { + entries: [ + './build/modules/ReactDOMServerUMDEntry.js', + ], + outfile: './build/react-dom-server.min.js', + debug: false, + standalone: 'ReactDOMServer', + // Envify twice. The first ensures that when we uglifyify, we have the right + // conditions to exclude requires. The global transform runs on deps. + transforms: [shimSharedModules, envifyProd, uglifyify], globalTransforms: [envifyProd], plugins: [collapser], // No need to derequire because the minifier will mangle @@ -114,4 +199,8 @@ module.exports = { min: min, addons: addons, addonsMin: addonsMin, + dom: dom, + domMin: domMin, + domServer: domServer, + domServerMin: domServerMin, }; diff --git a/grunt/tasks/react-dom.js b/grunt/tasks/react-dom.js deleted file mode 100644 index 77a68d0656ace..0000000000000 --- a/grunt/tasks/react-dom.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict'; - -var grunt = require('grunt'); -var UglifyJS = require('uglify-js'); - -var LICENSE_TEMPLATE = - grunt.file.read('./grunt/data/header-template-extended.txt'); - -function build(name, filename) { - var srcFile = `vendor/${filename}.js`; - var destFile = `build/${filename}.js`; - var destFileMin = `build/${filename}.min.js`; - var templateData = { - package: name, - version: grunt.config.data.pkg.version, - }; - var header = grunt.template.process( - LICENSE_TEMPLATE, - {data: templateData} - ); - var src = grunt.file.read(srcFile); - var srcMin = UglifyJS.minify(src, {fromString: true}).code; - grunt.file.write(destFile, header + src); - grunt.file.write(destFileMin, header + srcMin); -} - -module.exports = function() { - build('ReactDOM', 'react-dom'); - build('ReactDOMServer', 'react-dom-server'); -}; diff --git a/package.json b/package.json index 85bc4a1101a54..fd5c05e0ca427 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "private": true, "version": "16.0.0-alpha", "devDependencies": { + "aliasify": "^2.0.0", "art": "^0.10.1", "async": "^1.5.0", "babel-cli": "^6.6.5", @@ -33,6 +34,7 @@ "babel-traverse": "^6.9.0", "babylon": "6.8.0", "browserify": "^13.0.0", + "browserify-global-shim": "^1.0.3", "bundle-collapser": "^1.1.1", "coffee-script": "^1.8.0", "core-js": "^2.2.1", diff --git a/src/addons/ReactAddonsDOMDependencies.js b/src/addons/ReactAddonsDOMDependencies.js new file mode 100644 index 0000000000000..e0b5eb45a5e42 --- /dev/null +++ b/src/addons/ReactAddonsDOMDependencies.js @@ -0,0 +1,36 @@ +/** + * 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 ReactAddonsDOMDependencies + */ + +'use strict'; + +var ReactDOM = require('ReactDOM'); +var ReactInstanceMap = require('ReactInstanceMap'); + +exports.getReactDOM = function() { + return ReactDOM; +}; + +exports.getReactInstanceMap = function() { + return ReactInstanceMap; +}; + +if (__DEV__) { + var ReactPerf = require('ReactPerf'); + var ReactTestUtils = require('ReactTestUtils'); + + exports.getReactPerf = function() { + return ReactPerf; + }; + + exports.getReactTestUtils = function() { + return ReactTestUtils; + }; +} diff --git a/src/addons/ReactWithAddons.js b/src/addons/ReactWithAddons.js index 94102c5c2e8b6..4d556e9223488 100644 --- a/src/addons/ReactWithAddons.js +++ b/src/addons/ReactWithAddons.js @@ -13,6 +13,7 @@ var LinkedStateMixin = require('LinkedStateMixin'); var React = require('React'); +var ReactAddonsDOMDependencies = require('ReactAddonsDOMDependencies'); var ReactComponentWithPureRenderMixin = require('ReactComponentWithPureRenderMixin'); var ReactCSSTransitionGroup = require('ReactCSSTransitionGroup'); @@ -34,8 +35,20 @@ React.addons = { }; if (__DEV__) { - React.addons.Perf = require('ReactPerf'); - React.addons.TestUtils = require('ReactTestUtils'); + // For the UMD build we get these lazily from the global since they're tied + // to the DOM renderer and it hasn't loaded yet. + Object.defineProperty(React.addons, 'Perf', { + enumerable: true, + get: function() { + return ReactAddonsDOMDependencies.getReactPerf(); + }, + }); + Object.defineProperty(React.addons, 'TestUtils', { + enumerable: true, + get: function() { + return ReactAddonsDOMDependencies.getReactTestUtils(); + }, + }); } module.exports = React; diff --git a/src/addons/transitions/ReactCSSTransitionGroupChild.js b/src/addons/transitions/ReactCSSTransitionGroupChild.js index 0412895ba468d..4ac8233f68467 100644 --- a/src/addons/transitions/ReactCSSTransitionGroupChild.js +++ b/src/addons/transitions/ReactCSSTransitionGroupChild.js @@ -12,7 +12,7 @@ 'use strict'; var React = require('React'); -var ReactDOM = require('ReactDOM'); +var ReactAddonsDOMDependencies = require('ReactAddonsDOMDependencies'); var CSSCore = require('CSSCore'); var ReactTransitionEvents = require('ReactTransitionEvents'); @@ -54,7 +54,7 @@ var ReactCSSTransitionGroupChild = React.createClass({ }, transition: function(animationType, finishCallback, userSpecifiedDelay) { - var node = ReactDOM.findDOMNode(this); + var node = ReactAddonsDOMDependencies.getReactDOM().findDOMNode(this); if (!node) { if (finishCallback) { diff --git a/src/addons/transitions/ReactTransitionGroup.js b/src/addons/transitions/ReactTransitionGroup.js index f33a8f3d5d725..23121a334ac41 100644 --- a/src/addons/transitions/ReactTransitionGroup.js +++ b/src/addons/transitions/ReactTransitionGroup.js @@ -12,7 +12,7 @@ 'use strict'; var React = require('React'); -var ReactInstanceMap = require('ReactInstanceMap'); +var ReactAddonsDOMDependencies = require('ReactAddonsDOMDependencies'); var ReactTransitionChildMapping = require('ReactTransitionChildMapping'); var emptyFunction = require('emptyFunction'); @@ -64,7 +64,7 @@ var ReactTransitionGroup = React.createClass({ if (__DEV__) { nextChildMapping = ReactTransitionChildMapping.getChildMapping( nextProps.children, - ReactInstanceMap.get(this)._debugID + ReactAddonsDOMDependencies.getReactInstanceMap().get(this)._debugID ); } else { nextChildMapping = ReactTransitionChildMapping.getChildMapping( @@ -137,7 +137,7 @@ var ReactTransitionGroup = React.createClass({ if (__DEV__) { currentChildMapping = ReactTransitionChildMapping.getChildMapping( this.props.children, - ReactInstanceMap.get(this)._debugID + ReactAddonsDOMDependencies.getReactInstanceMap().get(this)._debugID ); } else { currentChildMapping = ReactTransitionChildMapping.getChildMapping( @@ -177,7 +177,7 @@ var ReactTransitionGroup = React.createClass({ if (__DEV__) { currentChildMapping = ReactTransitionChildMapping.getChildMapping( this.props.children, - ReactInstanceMap.get(this)._debugID + ReactAddonsDOMDependencies.getReactInstanceMap().get(this)._debugID ); } else { currentChildMapping = ReactTransitionChildMapping.getChildMapping( @@ -218,7 +218,7 @@ var ReactTransitionGroup = React.createClass({ if (__DEV__) { currentChildMapping = ReactTransitionChildMapping.getChildMapping( this.props.children, - ReactInstanceMap.get(this)._debugID + ReactAddonsDOMDependencies.getReactInstanceMap().get(this)._debugID ); } else { currentChildMapping = ReactTransitionChildMapping.getChildMapping( diff --git a/src/renderers/dom/client/ReactMount.js b/src/renderers/dom/client/ReactMount.js index 42c4a676f96cb..af0a9cd57ba94 100644 --- a/src/renderers/dom/client/ReactMount.js +++ b/src/renderers/dom/client/ReactMount.js @@ -100,7 +100,7 @@ function mountComponentIntoNode( ) { var markerName; if (ReactFeatureFlags.logTopLevelRenders) { - var wrappedElement = wrapperInstance._currentElement.props; + var wrappedElement = wrapperInstance._currentElement.props.child; var type = wrappedElement.type; markerName = 'React mount: ' + ( typeof type === 'string' ? type : @@ -235,8 +235,7 @@ if (__DEV__) { TopLevelWrapper.displayName = 'TopLevelWrapper'; } TopLevelWrapper.prototype.render = function() { - // this.props is actually a ReactElement - return this.props; + return this.props.child; }; /** @@ -421,14 +420,9 @@ var ReactMount = { 'for your app.' ); - var nextWrappedElement = ReactElement( + var nextWrappedElement = ReactElement.createElement( TopLevelWrapper, - null, - null, - null, - null, - null, - nextElement + { child: nextElement } ); var nextContext; @@ -443,7 +437,7 @@ var ReactMount = { if (prevComponent) { var prevWrappedElement = prevComponent._currentElement; - var prevElement = prevWrappedElement.props; + var prevElement = prevWrappedElement.props.child; if (shouldUpdateReactComponent(prevElement, nextElement)) { var publicInst = prevComponent._renderedComponent.getPublicInstance(); var updatedCallback = callback && function() { diff --git a/src/addons/renderSubtreeIntoContainer.js b/src/renderers/dom/client/renderSubtreeIntoContainer.js similarity index 100% rename from src/addons/renderSubtreeIntoContainer.js rename to src/renderers/dom/client/renderSubtreeIntoContainer.js diff --git a/src/renderers/dom/shared/ReactInjection.js b/src/renderers/dom/shared/ReactInjection.js index bd9d1a527d3a6..25ef6325b96b1 100644 --- a/src/renderers/dom/shared/ReactInjection.js +++ b/src/renderers/dom/shared/ReactInjection.js @@ -15,7 +15,6 @@ var DOMProperty = require('DOMProperty'); var EventPluginHub = require('EventPluginHub'); var EventPluginUtils = require('EventPluginUtils'); var ReactComponentEnvironment = require('ReactComponentEnvironment'); -var ReactClass = require('ReactClass'); var ReactEmptyComponent = require('ReactEmptyComponent'); var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter'); var ReactHostComponent = require('ReactHostComponent'); @@ -23,7 +22,6 @@ var ReactUpdates = require('ReactUpdates'); var ReactInjection = { Component: ReactComponentEnvironment.injection, - Class: ReactClass.injection, DOMProperty: DOMProperty.injection, EmptyComponent: ReactEmptyComponent.injection, EventPluginHub: EventPluginHub.injection, diff --git a/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js b/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js index 8c50058f1dbc9..3c442cba60083 100644 --- a/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js +++ b/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js @@ -18,10 +18,13 @@ var ReactErrorUtils = require('ReactErrorUtils'); var ReactInstanceMap = require('ReactInstanceMap'); var ReactInstrumentation = require('ReactInstrumentation'); var ReactNodeTypes = require('ReactNodeTypes'); -var ReactPropTypeLocations = require('ReactPropTypeLocations'); var ReactReconciler = require('ReactReconciler'); -var checkReactTypeSpec = require('checkReactTypeSpec'); +if (__DEV__) { + var ReactPropTypeLocations = require('ReactPropTypeLocations'); + var checkReactTypeSpec = require('checkReactTypeSpec'); +} + var emptyObject = require('emptyObject'); var invariant = require('invariant'); var shallowEqual = require('shallowEqual'); @@ -729,14 +732,16 @@ var ReactCompositeComponentMixin = { * @private */ _checkContextTypes: function(typeSpecs, values, location) { - checkReactTypeSpec( - typeSpecs, - values, - location, - this.getName(), - null, - this._debugID - ); + if (__DEV__) { + checkReactTypeSpec( + typeSpecs, + values, + location, + this.getName(), + null, + this._debugID + ); + } }, receiveComponent: function(nextElement, transaction, nextContext) { diff --git a/src/renderers/shared/stack/reconciler/ReactUpdates.js b/src/renderers/shared/stack/reconciler/ReactUpdates.js index 2009fb3134726..b0077ea0432b9 100644 --- a/src/renderers/shared/stack/reconciler/ReactUpdates.js +++ b/src/renderers/shared/stack/reconciler/ReactUpdates.js @@ -161,7 +161,7 @@ function runBatchedUpdates(transaction) { var namedComponent = component; // Duck type TopLevelWrapper. This is probably always true. if ( - component._currentElement.props === + component._currentElement.props.child === component._renderedComponent._currentElement ) { namedComponent = component._renderedComponent; diff --git a/src/umd/ReactDOMServerUMDEntry.js b/src/umd/ReactDOMServerUMDEntry.js new file mode 100644 index 0000000000000..7a55540e2b729 --- /dev/null +++ b/src/umd/ReactDOMServerUMDEntry.js @@ -0,0 +1,16 @@ +/** + * 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 ReactDOMServerUMDEntry + */ + +'use strict'; + +var ReactDOMServer = require('ReactDOMServer'); + +module.exports = ReactDOMServer; diff --git a/src/umd/ReactDOMUMDEntry.js b/src/umd/ReactDOMUMDEntry.js new file mode 100644 index 0000000000000..59daf4bef9c61 --- /dev/null +++ b/src/umd/ReactDOMUMDEntry.js @@ -0,0 +1,34 @@ +/** + * 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 ReactDOMUMDEntry + */ + +'use strict'; + +var ReactDOM = require('ReactDOM'); + +var ReactDOMUMDEntry = Object.assign({ + __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: { + ReactInstanceMap: require('ReactInstanceMap'), + }, +}, ReactDOM); + +if (__DEV__) { + Object.assign( + ReactDOMUMDEntry.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, + { + // ReactPerf and ReactTestUtils currently only work with the DOM renderer + // so we expose them from here, but only in DEV mode. + ReactPerf: require('ReactPerf'), + ReactTestUtils: require('ReactTestUtils'), + } + ); +} + +module.exports = ReactDOMUMDEntry; diff --git a/src/umd/ReactUMDEntry.js b/src/umd/ReactUMDEntry.js index dda6487594732..4984d842d4989 100644 --- a/src/umd/ReactUMDEntry.js +++ b/src/umd/ReactUMDEntry.js @@ -11,15 +11,14 @@ 'use strict'; -var ReactDOM = require('ReactDOM'); -var ReactDOMServer = require('ReactDOMServer'); var React = require('React'); - -// `version` will be added here by ReactIsomorphic. +// `version` will be added here by the React module. var ReactUMDEntry = Object.assign({ - __SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactDOM, - __SECRET_DOM_SERVER_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactDOMServer, + __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: { + ReactCurrentOwner: require('ReactCurrentOwner'), + ReactComponentTreeHook: require('ReactComponentTreeHook'), + }, }, React); module.exports = ReactUMDEntry; diff --git a/src/umd/ReactWithAddonsUMDEntry.js b/src/umd/ReactWithAddonsUMDEntry.js index cf59eb9c85eda..a2828fa70070d 100644 --- a/src/umd/ReactWithAddonsUMDEntry.js +++ b/src/umd/ReactWithAddonsUMDEntry.js @@ -11,15 +11,14 @@ 'use strict'; -var ReactDOM = require('ReactDOM'); -var ReactDOMServer = require('ReactDOMServer'); var ReactWithAddons = require('ReactWithAddons'); - -// `version` will be added here by ReactIsomorphic. +// `version` will be added here by the React module. var ReactWithAddonsUMDEntry = Object.assign({ - __SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactDOM, - __SECRET_DOM_SERVER_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactDOMServer, + __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: { + ReactCurrentOwner: require('ReactCurrentOwner'), + ReactComponentTreeHook: require('ReactComponentTreeHook'), + }, }, ReactWithAddons); module.exports = ReactWithAddonsUMDEntry; diff --git a/src/umd/shims/ReactAddonsDOMDependenciesUMDShim.js b/src/umd/shims/ReactAddonsDOMDependenciesUMDShim.js new file mode 100644 index 0000000000000..2aa7b886b544b --- /dev/null +++ b/src/umd/shims/ReactAddonsDOMDependenciesUMDShim.js @@ -0,0 +1,32 @@ +/** + * 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 ReactAddonsDOMDependenciesUMDShim + */ + +/* globals ReactDOM */ + +'use strict'; + +exports.getReactDOM = function() { + return ReactDOM; +}; + +exports.getReactInstanceMap = function() { + return ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactInstanceMap; +}; + +if (__DEV__) { + exports.getReactPerf = function() { + return ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactPerf; + }; + + exports.getReactTestUtils = function() { + return ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactTestUtils; + }; +} diff --git a/vendor/react-dom-server.js b/vendor/react-dom-server.js deleted file mode 100644 index 783456c54bb2d..0000000000000 --- a/vendor/react-dom-server.js +++ /dev/null @@ -1,31 +0,0 @@ -// Based off https://github.com/ForbesLindesay/umd/blob/master/template.js -;(function(f) { - // CommonJS - if (typeof exports === "object" && typeof module !== "undefined") { - module.exports = f(require('react')); - - // RequireJS - } else if (typeof define === "function" && define.amd) { - define(['react'], f); - - //