diff --git a/Gruntfile.js b/Gruntfile.js index b4013a8e66f1e..87208c011c99e 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -9,6 +9,7 @@ var webdriverJasmineTasks = require('./grunt/tasks/webdriver-jasmine'); var sauceTunnelTask = require('./grunt/tasks/sauce-tunnel'); var npmTask = require('./grunt/tasks/npm'); var releaseTasks = require('./grunt/tasks/release'); +var reactCoreTasks = require('./grunt/tasks/react-core'); module.exports = function(grunt) { @@ -51,17 +52,33 @@ module.exports = function(grunt) { grunt.registerMultiTask('npm', npmTask); + grunt.registerTask('react-core:release', reactCoreTasks.buildRelease); + // Check that the version we're exporting is the same one we expect in the // package. This is not an ideal way to do this, but makes sure that we keep // them in sync. var reactVersionExp = /\bReact\.version\s*=\s*['"]([^'"]+)['"];/; grunt.registerTask('version-check', function() { - var version = reactVersionExp.exec( + var reactVersion = reactVersionExp.exec( grunt.file.read('./build/modules/React.js') )[1]; - var expectedVersion = grunt.config.data.pkg.version; - if (version !== expectedVersion) { - grunt.log.error('Versions do not match. Expected %s, saw %s', expectedVersion, version); + var reactCoreVersion = grunt.file.readJSON('./npm-react-core/package.json').version; + var reactToolsVersion = grunt.config.data.pkg.version; + + if (reactVersion !== reactToolsVersion) { + grunt.log.error( + 'React version does not match react-tools version. Expected %s, saw %s', + reactToolsVersion, + reactVersion + ); + return false; + } + if (reactCoreVersion !== reactToolsVersion) { + grunt.log.error( + 'react-core version does not match react-tools veersion. Expected %s, saw %s', + reactToolsVersion, + reactCoreVersion + ); return false; } }); @@ -81,6 +98,7 @@ module.exports = function(grunt) { 'version-check', 'populist:test' ]); + grunt.registerTask('build:react-core', ['version-check', 'jsx:release', 'react-core:release']); grunt.registerTask('webdriver-phantomjs', webdriverPhantomJSTask); diff --git a/README.md b/README.md index 157457406c3af..4f3d7d89d0779 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,10 @@ React is a JavaScript library for building user interfaces. [Learn how to use React in your own project.](http://facebook.github.io/react/docs/getting-started.html) +## The `react` npm package has recently changed! + +If you're looking for jeffbski's [React.js](https://github.com/jeffbski/react) project, it's now in `npm` as `reactjs` rather than `react`. + ## Examples We have several examples [on the website](http://facebook.github.io/react/). Here is the first one to get you started: diff --git a/grunt/config/browserify.js b/grunt/config/browserify.js index 5bf86631f1855..57ff1b25d5b10 100644 --- a/grunt/config/browserify.js +++ b/grunt/config/browserify.js @@ -3,6 +3,7 @@ 'use strict'; +var envify = require('envify/custom'); var grunt = require('grunt'); var UglifyJS = require('uglify-js'); @@ -59,12 +60,14 @@ var basic = { outfile: './build/react.js', debug: false, standalone: 'React', + transforms: [envify({NODE_ENV: 'development'})], after: [simpleBannerify] }; var min = grunt.util._.merge({}, basic, { outfile: './build/react.min.js', debug: false, + transforms: [envify({NODE_ENV: 'production'})], after: [minify, bannerify] }); @@ -85,6 +88,7 @@ var addons = { outfile: './build/react-with-addons.js', debug: false, standalone: 'React', + transforms: [envify({NODE_ENV: 'development'})], packageName: 'React (with addons)', after: [simpleBannerify] }; @@ -92,6 +96,7 @@ var addons = { var addonsMin = grunt.util._.merge({}, addons, { outfile: './build/react-with-addons.min.js', debug: false, + transforms: [envify({NODE_ENV: 'production'})], after: [minify, bannerify] }); @@ -103,6 +108,7 @@ var withCodeCoverageLogging = { debug: true, standalone: 'React', transforms: [ + envify({NODE_ENV: 'development'}), require('coverify') ] }; diff --git a/grunt/tasks/populist.js b/grunt/tasks/populist.js index 6efa1e5132af6..7ea837d130c52 100644 --- a/grunt/tasks/populist.js +++ b/grunt/tasks/populist.js @@ -32,7 +32,7 @@ module.exports = function() { rootDirectory: config.rootDirectory, args: args }).then(function(output) { - grunt.file.write(config.outfile, output); + grunt.file.write(config.outfile, 'process = {env: {}};' + output); theFilesToTestScript.end(); theFilesToTestScript.once('close', done); }); diff --git a/grunt/tasks/react-core.js b/grunt/tasks/react-core.js new file mode 100644 index 0000000000000..e9abe3b269726 --- /dev/null +++ b/grunt/tasks/react-core.js @@ -0,0 +1,47 @@ +'use strict'; + +var grunt = require('grunt'); + +var src = 'npm-react-core/'; +var dest = 'build/react-core/'; +var modSrc = 'build/modules/'; +var lib = dest + 'lib/'; + +function buildRelease() { + // delete build/react-core for fresh start + grunt.file.exists(dest) && grunt.file.delete(dest); + + // mkdir -p build/react-core/lib + grunt.file.mkdir(lib); + + // Copy everything over + // console.log(grunt.file.expandMapping(src + '**/*', dest, {flatten: true})); + grunt.file.expandMapping(src + '**/*', dest, {flatten: true}).forEach(function(mapping) { + var src = mapping.src[0]; + var dest = mapping.dest; + if (grunt.file.isDir(src)) { + grunt.file.mkdir(dest); + } else { + grunt.file.copy(src, dest); + } + }); + + // copy build/modules/*.js to build/react-core/lib + grunt.file.expandMapping(modSrc + '*.js', lib, { flatten: true }).forEach(function(mapping) { + grunt.file.copy(mapping.src[0], mapping.dest); + }); + + // modify build/react-core/package.json to set version ## + var pkg = grunt.file.readJSON(dest + 'package.json'); + pkg.version = grunt.config.data.pkg.version; + grunt.file.write(dest + 'package.json', JSON.stringify(pkg, null, 2)); +} + +function buildDev() { + // TODO: same as above except different destination +} + +module.exports = { + buildRelease: buildRelease, + buildDev: buildDev +}; diff --git a/npm-react-core/README.md b/npm-react-core/README.md new file mode 100644 index 0000000000000..94849586b9d18 --- /dev/null +++ b/npm-react-core/README.md @@ -0,0 +1,21 @@ +# react-core + +An npm package to get you immediate access to [React](http://facebook.github.io/react/), +without also requiring the JSX transformer. This is especially useful for cases where you +want to [`browserify`](https://github.com/substack/node-browserify) your module using +`React`. + +## Example Usage + +```js + +// Previously, you might access React with react-tools. +var React = require('react-tools').React; + +// Now you can access React directly with react-core. +var React = require('react-core'); + +// You can also access ReactWithAddons. +var React = require('react-core/addons'); +``` + diff --git a/npm-react-core/ReactJSErrors.js b/npm-react-core/ReactJSErrors.js new file mode 100644 index 0000000000000..e2ef349bb085c --- /dev/null +++ b/npm-react-core/ReactJSErrors.js @@ -0,0 +1,38 @@ +var copyProperties = require('./lib/copyProperties'); + +var WARNING_MESSAGE = ( + 'It looks like you\'re trying to use jeffbski\'s React.js project.\n' + + 'The `react` npm package now points to the React JavaScript library for ' + + 'building user interfaces, not the React.js project for managing asynchronous ' + + 'control flow. If you\'re looking for that library, please npm install reactjs.' +); + +function error() { + throw new Error(WARNING_MESSAGE); +} + +// Model the React.js project's public interface exactly. + +function ReactJSShim() { + error(); +}; + +ReactJSShim.logEvents = error; +ReactJSShim.resolvePromises = error; +ReactJSShim.trackTasks = error; +ReactJSShim.createEventCollector = error; + +// These could throw using defineProperty() but supporting older browsers will +// be painful. Additionally any error messages around this will contain the string +// so I think this is sufficient. +ReactJSShim.options = WARNING_MESSAGE; +ReactJSShim.events = WARNING_MESSAGE; + +var ReactJSErrors = { + wrap: function(module) { + copyProperties(ReactJSShim, module); + return ReactJSShim; + } +}; + +module.exports = ReactJSErrors; \ No newline at end of file diff --git a/npm-react-core/addons.js b/npm-react-core/addons.js new file mode 100644 index 0000000000000..e74522f76db1f --- /dev/null +++ b/npm-react-core/addons.js @@ -0,0 +1,4 @@ +module.exports = require('./lib/ReactWithAddons'); +if ('production' !== process.env.NODE_ENV) { + module.exports = require('./ReactJSErrors').wrap(module.exports); +} diff --git a/npm-react-core/package.json b/npm-react-core/package.json new file mode 100644 index 0000000000000..b37973dcda293 --- /dev/null +++ b/npm-react-core/package.json @@ -0,0 +1,36 @@ +{ + "name": "react", + "version": "0.8.0-alpha", + "keywords": [ + "react" + ], + "homepage": "https://github.com/facebook/react/tree/master/npm-react-core", + "bugs": "https://github.com/facebook/react/issues?labels=react-core", + "licenses": [ + { + "type": "Apache-2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0" + } + ], + "files": [ + "README.md", + "addons.js", + "react.js", + "ReactJSErrors.js", + "lib/" + ], + "main": "react.js", + "repository": { + "type": "git", + "url": "https://github.com/facebook/react" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "envify": "~0.2.0" + }, + "browserify": { + "transform": ["envify"] + } +} diff --git a/npm-react-core/react.js b/npm-react-core/react.js new file mode 100644 index 0000000000000..b682b0397f41c --- /dev/null +++ b/npm-react-core/react.js @@ -0,0 +1,4 @@ +module.exports = require('./lib/React'); +if ('production' !== process.env.NODE_ENV) { + module.exports = require('./ReactJSErrors').wrap(module.exports); +} diff --git a/package.json b/package.json index 4c80a0172390d..18b8f53fcd0dc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-tools", - "version": "0.6.0-alpha", + "version": "0.8.0-alpha", "keywords": [ "react", "jsx", @@ -39,6 +39,8 @@ }, "devDependencies": { "browserify": "~2.36.1", + "browserify": "~2.34.1", + "envify": "~0.2.0", "populist": "~0.1.5", "grunt-cli": "~0.1.9", "grunt": "~0.4.1", @@ -67,6 +69,6 @@ }, "preferGlobal": true, "commonerConfig": { - "version": 1 + "version": 2 } } diff --git a/src/core/React.js b/src/core/React.js index 3eaddbb8c5545..f2cbcad215a60 100644 --- a/src/core/React.js +++ b/src/core/React.js @@ -67,6 +67,6 @@ var React = { // Version exists only in the open-source version of React, not in Facebook's // internal version. -React.version = '0.6.0-alpha'; +React.version = '0.8.0-alpha'; module.exports = React; diff --git a/vendor/constants.js b/vendor/constants.js index 4d674ceb6ec3b..f7103158e7d8c 100644 --- a/vendor/constants.js +++ b/vendor/constants.js @@ -26,6 +26,20 @@ function propagate(constants, source) { return recast.print(transform(recast.parse(source), constants)); } +var DEV_EXPRESSION = builders.binaryExpression( + '!==', + builders.literal('production'), + builders.memberExpression( + builders.memberExpression( + builders.identifier('process'), + builders.identifier('env'), + false + ), + builders.identifier('NODE_ENV'), + false + ) +); + function transform(ast, constants) { constants = constants || {}; @@ -42,39 +56,31 @@ function transform(ast, constants) { // There could in principle be a constant called "hasOwnProperty", // so be careful always to use Object.prototype.hasOwnProperty. - if (hasOwn.call(constants, node.name)) { + if (node.name === '__DEV__') { + // replace __DEV__ with process.env.NODE_ENV !== 'production' + this.replace(DEV_EXPRESSION); + return false; + } else if (hasOwn.call(constants, node.name)) { this.replace(builders.literal(constants[node.name])); return false; } } else if (namedTypes.CallExpression.check(node)) { - if (!constants.__DEV__) { - if (namedTypes.Identifier.check(node.callee) && - node.callee.name === 'invariant') { - // Truncate the arguments of invariant(condition, ...) - // statements to just the condition. - node.arguments.length = 1; - } - } - - } else if (namedTypes.IfStatement.check(node) && - namedTypes.Literal.check(node.test)) { - if (node.test.value) { - // If the alternate (then) branch is dead code, remove it. - this.get("alternate").replace(); - - // This is what happens when you replace a node with nothing and - // it can't be removed from a list of statements. - assert.strictEqual(node.alternate, null); - - } else if (node.alternate) { - // Replace the whole if-statement with just the alternate clause. - this.replace(node.alternate); - return false; - - } else { - // Remove the entire if-statement. - this.replace(); + if (namedTypes.Identifier.check(node.callee) && + node.callee.name === 'invariant') { + // Truncate the arguments of invariant(condition, ...) + // statements to just the condition based on NODE_ENV + // (dead code removal will remove the extra bytes). + this.replace( + builders.conditionalExpression( + DEV_EXPRESSION, + node, + builders.callExpression( + node.callee, + [node.arguments[0]] + ) + ) + ); return false; } }