From 6961fd23ba9b071acfdb286577240e01f280f575 Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Sat, 21 May 2016 06:53:32 -0700 Subject: [PATCH] Use a separate babel config for the local-cli and the packager Summary: This separates the babel config of the local-cli and the packager from the one used by the transforms of the packager since it doesn't run in the same environment and the local-cli/packager doesn't require react specific transforms and runs in node 4 so we can also avoid some es2015 transforms that node already supports. I had to move the code in cli.js so it can still run in node 0.12 that doesn't support `const` since it is no longer transformed. **Test plan** Run the local-cli on node 0.12 and there should be a message saying that it requires at least node 4. Run the local-cli on node 4 and 5 and everything should work the same as before. I was also hoping for some perf gains but there was nothing noticeable. I did benchmark the babel-register call and it stayed pretty much the same. As for runtime performance it can help if there are optimisations for es2015 features in node. Closes https://github.com/facebook/react-native/pull/6155 Reviewed By: bestander Differential Revision: D3301008 Pulled By: davidaurelio fbshipit-source-id: 504180d158a1e50bc03e28fb0d1e53d0731ce32f --- local-cli/cli.js | 158 ++------------------------ local-cli/cliEntry.js | 161 +++++++++++++++++++++++++++ local-cli/server/checkNodeVersion.js | 10 +- local-cli/server/server.js | 3 - package.json | 4 + packager/babelRegisterOnly.js | 23 ++-- packager/package.json | 2 +- 7 files changed, 189 insertions(+), 172 deletions(-) create mode 100644 local-cli/cliEntry.js diff --git a/local-cli/cli.js b/local-cli/cli.js index a1418cd25..c8837ee7c 100644 --- a/local-cli/cli.js +++ b/local-cli/cli.js @@ -8,164 +8,20 @@ */ 'use strict'; +// This file must be able to run in node 0.12 without babel so we can show that +// it is not supported. This is why the rest of the cli code is in `cliEntry.js`. +require('./server/checkNodeVersion')(); + require('../packager/babelRegisterOnly')([ /private-cli\/src/, /local-cli/, /react-packager\/src/ ]); -var bundle = require('./bundle/bundle'); -var childProcess = require('child_process'); -var Config = require('./util/Config'); -var defaultConfig = require('./default.config'); -var dependencies = require('./dependencies/dependencies'); -var generate = require('./generate/generate'); -var library = require('./library/library'); -var path = require('path'); -var Promise = require('promise'); -var runAndroid = require('./runAndroid/runAndroid'); -var runIOS = require('./runIOS/runIOS'); -var server = require('./server/server'); -var TerminalAdapter = require('yeoman-environment/lib/adapter.js'); -var yeoman = require('yeoman-environment'); -var unbundle = require('./bundle/unbundle'); -var upgrade = require('./upgrade/upgrade'); -var version = require('./version/version'); - -var fs = require('fs'); -var gracefulFs = require('graceful-fs'); - -// graceful-fs helps on getting an error when we run out of file -// descriptors. When that happens it will enqueue the operation and retry it. -gracefulFs.gracefulify(fs); - -var documentedCommands = { - 'start': [server, 'starts the webserver'], - 'bundle': [bundle, 'builds the javascript bundle for offline use'], - 'unbundle': [unbundle, 'builds javascript as "unbundle" for offline use'], - 'new-library': [library, 'generates a native library bridge'], - 'android': [generateWrapper, 'generates an Android project for your app'], - 'run-android': [runAndroid, 'builds your app and starts it on a connected Android emulator or device'], - 'run-ios': [runIOS, 'builds your app and starts it on iOS simulator'], - 'upgrade': [upgrade, 'upgrade your app\'s template files to the latest version; run this after ' + - 'updating the react-native version in your package.json and running npm install'] -}; - -var exportedCommands = {dependencies: dependencies}; -Object.keys(documentedCommands).forEach(function(command) { - exportedCommands[command] = documentedCommands[command][0]; -}); - -var undocumentedCommands = { - '--version': [version, ''], - 'init': [printInitWarning, ''], -}; - -var commands = Object.assign({}, documentedCommands, undocumentedCommands); - -/** - * Parses the command line and runs a command of the CLI. - */ -function run() { - var args = process.argv.slice(2); - if (args.length === 0) { - printUsage(); - } - - var setupEnvScript = /^win/.test(process.platform) - ? 'setup_env.bat' - : 'setup_env.sh'; - childProcess.execFileSync(path.join(__dirname, setupEnvScript)); - - var command = commands[args[0]]; - if (!command) { - console.error('Command `%s` unrecognized', args[0]); - printUsage(); - return; - } - - command[0](args, Config.get(__dirname, defaultConfig)).done(); -} - -function generateWrapper(args, config) { - return generate([ - '--platform', 'android', - '--project-path', process.cwd(), - '--project-name', JSON.parse( - fs.readFileSync('package.json', 'utf8') - ).name - ], config); -} - -function printUsage() { - console.log([ - 'Usage: react-native ', - '', - 'Commands:' - ].concat(Object.keys(documentedCommands).map(function(name) { - return ' - ' + name + ': ' + documentedCommands[name][1]; - })).join('\n')); - process.exit(1); -} - -// The user should never get here because projects are inited by -// using `react-native-cli` from outside a project directory. -function printInitWarning() { - return Promise.resolve().then(function() { - console.log([ - 'Looks like React Native project already exists in the current', - 'folder. Run this command from a different folder or remove node_modules/react-native' - ].join('\n')); - process.exit(1); - }); -} - -class CreateSuppressingTerminalAdapter extends TerminalAdapter { - constructor() { - super(); - // suppress 'create' output generated by yeoman - this.log.create = function() {}; - } -} - -/** - * Creates the template for a React Native project given the provided - * parameters: - * - projectDir: templates will be copied here. - * - argsOrName: project name or full list of custom arguments to pass to the - * generator. - */ -function init(projectDir, argsOrName) { - console.log('Setting up new React Native app in ' + projectDir); - var env = yeoman.createEnv( - undefined, - undefined, - new CreateSuppressingTerminalAdapter() - ); - - env.register( - require.resolve(path.join(__dirname, 'generator')), - 'react:app' - ); - - // argv is for instance - // ['node', 'react-native', 'init', 'AwesomeApp', '--verbose'] - // args should be ['AwesomeApp', '--verbose'] - var args = Array.isArray(argsOrName) - ? argsOrName - : [argsOrName].concat(process.argv.slice(4)); - - var generator = env.create('react:app', {args: args}); - generator.destinationRoot(projectDir); - generator.run(); -} +var cliEntry = require('./cliEntry'); if (require.main === module) { - run(); + cliEntry.run(); } -module.exports = { - run: run, - init: init, - commands: exportedCommands -}; +module.exports = cliEntry; diff --git a/local-cli/cliEntry.js b/local-cli/cliEntry.js new file mode 100644 index 000000000..6ec0f794f --- /dev/null +++ b/local-cli/cliEntry.js @@ -0,0 +1,161 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +'use strict'; + +const bundle = require('./bundle/bundle'); +const childProcess = require('child_process'); +const Config = require('./util/Config'); +const defaultConfig = require('./default.config'); +const dependencies = require('./dependencies/dependencies'); +const generate = require('./generate/generate'); +const library = require('./library/library'); +const path = require('path'); +const Promise = require('promise'); +const runAndroid = require('./runAndroid/runAndroid'); +const runIOS = require('./runIOS/runIOS'); +const server = require('./server/server'); +const TerminalAdapter = require('yeoman-environment/lib/adapter.js'); +const yeoman = require('yeoman-environment'); +const unbundle = require('./bundle/unbundle'); +const upgrade = require('./upgrade/upgrade'); +const version = require('./version/version'); + +const fs = require('fs'); +const gracefulFs = require('graceful-fs'); + +// graceful-fs helps on getting an error when we run out of file +// descriptors. When that happens it will enqueue the operation and retry it. +gracefulFs.gracefulify(fs); + +const documentedCommands = { + 'start': [server, 'starts the webserver'], + 'bundle': [bundle, 'builds the javascript bundle for offline use'], + 'unbundle': [unbundle, 'builds javascript as "unbundle" for offline use'], + 'new-library': [library, 'generates a native library bridge'], + 'android': [generateWrapper, 'generates an Android project for your app'], + 'run-android': [runAndroid, 'builds your app and starts it on a connected Android emulator or device'], + 'run-ios': [runIOS, 'builds your app and starts it on iOS simulator'], + 'upgrade': [upgrade, 'upgrade your app\'s template files to the latest version; run this after ' + + 'updating the react-native version in your package.json and running npm install'] +}; + +const exportedCommands = {dependencies: dependencies}; +Object.keys(documentedCommands).forEach(function(command) { + exportedCommands[command] = documentedCommands[command][0]; +}); + +const undocumentedCommands = { + '--version': [version, ''], + 'init': [printInitWarning, ''], +}; + +const commands = Object.assign({}, documentedCommands, undocumentedCommands); + +/** + * Parses the command line and runs a command of the CLI. + */ +function run() { + const args = process.argv.slice(2); + if (args.length === 0) { + printUsage(); + } + + const setupEnvScript = /^win/.test(process.platform) + ? 'setup_env.bat' + : 'setup_env.sh'; + childProcess.execFileSync(path.join(__dirname, setupEnvScript)); + + const command = commands[args[0]]; + if (!command) { + console.error('Command `%s` unrecognized', args[0]); + printUsage(); + return; + } + + command[0](args, Config.get(__dirname, defaultConfig)).done(); +} + +function generateWrapper(args, config) { + return generate([ + '--platform', 'android', + '--project-path', process.cwd(), + '--project-name', JSON.parse( + fs.readFileSync('package.json', 'utf8') + ).name + ], config); +} + +function printUsage() { + console.log([ + 'Usage: react-native ', + '', + 'Commands:' + ].concat(Object.keys(documentedCommands).map(function(name) { + return ' - ' + name + ': ' + documentedCommands[name][1]; + })).join('\n')); + process.exit(1); +} + +// The user should never get here because projects are inited by +// using `react-native-cli` from outside a project directory. +function printInitWarning() { + return Promise.resolve().then(function() { + console.log([ + 'Looks like React Native project already exists in the current', + 'folder. Run this command from a different folder or remove node_modules/react-native' + ].join('\n')); + process.exit(1); + }); +} + +class CreateSuppressingTerminalAdapter extends TerminalAdapter { + constructor() { + super(); + // suppress 'create' output generated by yeoman + this.log.create = function() {}; + } +} + +/** + * Creates the template for a React Native project given the provided + * parameters: + * - projectDir: templates will be copied here. + * - argsOrName: project name or full list of custom arguments to pass to the + * generator. + */ +function init(projectDir, argsOrName) { + console.log('Setting up new React Native app in ' + projectDir); + const env = yeoman.createEnv( + undefined, + undefined, + new CreateSuppressingTerminalAdapter() + ); + + env.register( + require.resolve(path.join(__dirname, 'generator')), + 'react:app' + ); + + // argv is for instance + // ['node', 'react-native', 'init', 'AwesomeApp', '--verbose'] + // args should be ['AwesomeApp', '--verbose'] + const args = Array.isArray(argsOrName) + ? argsOrName + : [argsOrName].concat(process.argv.slice(4)); + + const generator = env.create('react:app', {args: args}); + generator.destinationRoot(projectDir); + generator.run(); +} + +module.exports = { + run: run, + init: init, + commands: exportedCommands +}; diff --git a/local-cli/server/checkNodeVersion.js b/local-cli/server/checkNodeVersion.js index 406c08cf8..013913d53 100644 --- a/local-cli/server/checkNodeVersion.js +++ b/local-cli/server/checkNodeVersion.js @@ -8,17 +8,17 @@ */ 'use strict'; -const chalk = require('chalk'); -const formatBanner = require('./formatBanner'); -const semver = require('semver'); +var chalk = require('chalk'); +var formatBanner = require('./formatBanner'); +var semver = require('semver'); module.exports = function() { if (!semver.satisfies(process.version, '>=4')) { - const engine = semver.satisfies(process.version, '<1 >=4') + var engine = semver.satisfies(process.version, '<1') ? 'Node' : 'io.js'; - const message = 'You are currently running ' + engine + ' ' + + var message = 'You are currently running ' + engine + ' ' + process.version + '.\n' + '\n' + 'React Native runs on Node 4.0 or newer. There are several ways to ' + diff --git a/local-cli/server/server.js b/local-cli/server/server.js index 21d1d8e2c..2b7efe803 100644 --- a/local-cli/server/server.js +++ b/local-cli/server/server.js @@ -9,7 +9,6 @@ 'use strict'; const chalk = require('chalk'); -const checkNodeVersion = require('./checkNodeVersion'); const formatBanner = require('./formatBanner'); const parseCommandLine = require('../util/parseCommandLine'); const path = require('path'); @@ -88,8 +87,6 @@ function _server(argv, config, resolve, reject) { ) : config.getAssetRoots(); - checkNodeVersion(); - console.log(formatBanner( 'Running packager on port ' + args.port + '.\n\n' + 'Keep this packager running while developing on any JS projects. ' + diff --git a/package.json b/package.json index 8dd766fcd..ddf4d240e 100644 --- a/package.json +++ b/package.json @@ -131,7 +131,11 @@ "art": "^0.10.0", "babel-core": "^6.6.4", "babel-plugin-external-helpers": "^6.5.0", + "babel-plugin-syntax-trailing-function-commas": "^6.5.0", + "babel-plugin-transform-flow-strip-types": "^6.6.5", + "babel-plugin-transform-object-rest-spread": "^6.6.5", "babel-polyfill": "^6.6.1", + "babel-preset-es2015-node": "^4.0.2", "babel-preset-react-native": "^1.8.0", "babel-register": "^6.6.0", "babel-types": "^6.6.4", diff --git a/packager/babelRegisterOnly.js b/packager/babelRegisterOnly.js index bb973ba4e..703ba3385 100644 --- a/packager/babelRegisterOnly.js +++ b/packager/babelRegisterOnly.js @@ -12,20 +12,19 @@ Array.prototype.values || require('core-js/fn/array/values'); Object.entries || require('core-js/fn/object/entries'); Object.values || require('core-js/fn/object/values'); -var fs = require('fs'); -var path = require('path'); - var _only = []; -function readBabelRC() { - var rcpath = path.join(__dirname, 'react-packager', 'rn-babelrc.json'); - var source = fs.readFileSync(rcpath).toString(); - return JSON.parse(source); -} - module.exports = function(onlyList) { _only = _only.concat(onlyList); - var config = readBabelRC(); - config.only = _only; - require('babel-register')(config); + + require('babel-register')({ + presets: ['es2015-node'], + plugins: [ + 'transform-flow-strip-types', + 'syntax-trailing-function-commas', + 'transform-object-rest-spread', + ], + only: _only, + sourceMaps: 'inline', + }); }; diff --git a/packager/package.json b/packager/package.json index 36f258d6c..f18fc259b 100644 --- a/packager/package.json +++ b/packager/package.json @@ -1,5 +1,5 @@ { - "version": "0.3.2", + "version": "0.4.0", "name": "react-native-packager", "description": "Build native apps with React!", "repository": {