Use "babel-preset-react-native"

Summary:
Rather than specifying Babel plugins in the `.babelrc` packaged with react-native, leverage a Babel preset to define the plugins (https://github.com/exponentjs/babel-preset-react-native).

This allows for a much better user experience for those who want (or need) to override options in their project's `.babelrc`.

Prior to this PR, if a user wanted to use a custom babel-plugin (or a custom set of babel plugins), they'd have either 1) manually override the `.babelrc` in the react-packager directory (or fork RN), or 2) specify a custom transformer to use when running the packager that loaded their own `.babelrc`. Note - the custom transformer was necessary because without it, RN's `.babelrc` options would supersede the options defined in the project's `.babelrc`...potentially causing issues with plugin ordering.

This PR makes the transformer check for the existence of a project-level `.babelrc`, and if it it's there, it _doesn't_ use the react-native `.babelrc`. This prevents any oddities with Babel plug
Closes https://github.com/facebook/react-native/pull/5214

Reviewed By: davidaurelio

Differential Revision: D2881814

Pulled By: martinbigio

fb-gh-sync-id: 4168144b7a365fae62bbeed094d8a03a48b4798c
This commit is contained in:
Adam Miskiewicz 2016-02-03 08:14:38 -08:00 committed by facebook-github-bot-3
parent 6d8ccc318b
commit 38cea2edeb
10 changed files with 88 additions and 118 deletions

View File

@ -16,7 +16,7 @@ var path = require('path');
var _only = [];
function readBabelRC() {
var rcpath = path.join(__dirname, 'react-packager', '.babelrc');
var rcpath = path.join(__dirname, 'react-packager', 'rn-babelrc.json');
var source = fs.readFileSync(rcpath).toString();
return JSON.parse(source);
}
@ -25,5 +25,5 @@ module.exports = function(onlyList) {
_only = _only.concat(onlyList);
var config = readBabelRC();
config.only = _only;
require('babel-core/register')(config);
require('babel-register')(config);
};

View File

@ -1,30 +0,0 @@
{
"retainLines": true,
"compact": true,
"comments": false,
"plugins": [
"syntax-async-functions",
"syntax-class-properties",
"syntax-trailing-function-commas",
"transform-class-properties",
"transform-es2015-arrow-functions",
"transform-es2015-block-scoping",
"transform-es2015-classes",
"transform-es2015-computed-properties",
"transform-es2015-constants",
"transform-es2015-destructuring",
["transform-es2015-modules-commonjs", {"strict": false, "allowTopLevelThis": true}],
"transform-es2015-parameters",
"transform-es2015-shorthand-properties",
"transform-es2015-spread",
"transform-es2015-template-literals",
"transform-flow-strip-types",
"transform-object-assign",
"transform-object-rest-spread",
"transform-react-display-name",
"transform-react-jsx",
"transform-regenerator",
"transform-es2015-for-of"
],
"sourceMaps": false
}

View File

@ -15,7 +15,6 @@ useGracefulFs();
var debug = require('debug');
var omit = require('underscore').omit;
var Activity = require('./src/Activity');
var Transforms = require('./src/transforms');
exports.createServer = createServer;
exports.middleware = function(options) {
@ -24,7 +23,6 @@ exports.middleware = function(options) {
};
exports.Activity = Activity;
exports.getTransforms = Transforms.getAll;
// Renamed "package" to "bundle". But maintain backwards
// compat.

View File

@ -0,0 +1,4 @@
{
"presets": [ "react-native" ],
"plugins": []
}

View File

@ -66,6 +66,7 @@ class Transformer {
this._cache = opts.cache;
this._transformModulePath = opts.transformModulePath;
this._projectRoots = opts.projectRoots;
if (opts.transformModulePath != null) {
let transformer;
@ -129,6 +130,7 @@ class Transformer {
filename: filePath,
options: {
...options,
projectRoots: this._projectRoots,
externalTransformModulePath: this._transformModulePath,
},
}).then(res => {

View File

@ -1,32 +0,0 @@
/**
* 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';
/**
* Manually resolve all default Babel plugins.
* `babel.transform` will attempt to resolve all base plugins relative to
* the file it's compiling. This makes sure that we're using the plugins
* installed in the react-native package.
*/
function resolvePlugins(plugins) {
return plugins.map(function(plugin) {
// Normalise plugin to an array.
if (!Array.isArray(plugin)) {
plugin = [plugin];
}
// Only resolve the plugin if it's a string reference.
if (typeof plugin[0] === 'string') {
plugin[0] = require('babel-plugin-' + plugin[0]);
plugin[0] = plugin[0].__esModule ? plugin[0].default : plugin[0];
}
return plugin;
});
}
module.exports = resolvePlugins;

View File

@ -9,30 +9,25 @@
'use strict';
var babel = require('babel-core');
var resolvePlugins = require('./resolvePlugins');
var Transforms = require('../transforms');
var makeInternalConfig = require('babel-preset-react-native/configs/internal');
// Runs internal transforms on the given sourceCode. Note that internal
// transforms should be run after the external ones to ensure that they run on
// Javascript code
function internalTransforms(sourceCode, filename, options) {
var plugins = resolvePlugins(Transforms.getAll(options));
if (plugins.length === 0) {
var internalBabelConfig = makeInternalConfig(options);
if (!internalBabelConfig) {
return {
code: sourceCode,
filename: filename,
};
}
var result = babel.transform(sourceCode, {
retainLines: true,
compact: true,
comments: false,
var result = babel.transform(sourceCode, Object.assign({
filename: filename,
sourceFileName: filename,
sourceMaps: false,
plugins: plugins,
});
}, internalBabelConfig));
return {
code: result.code,

View File

@ -93,7 +93,6 @@ class Resolver {
// should work after this release and we can
// remove it from here.
'parse',
'react-transform-hmr',
],
platforms: ['ios', 'android'],
preferNativePlatform: true,

View File

@ -1,14 +0,0 @@
/**
* 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';
exports.getAll = function(options) {
return [];
};

View File

@ -11,47 +11,95 @@
'use strict';
const babel = require('babel-core');
const externalHelpersPlugin = require('babel-plugin-external-helpers');
const fs = require('fs');
const inlineRequires = require('fbjs-scripts/babel-6/inline-requires');
const makeHMRConfig = require('babel-preset-react-native/configs/hmr');
const resolvePlugins = require('babel-preset-react-native/lib/resolvePlugins');
const inlineRequiresPlugin = require('fbjs-scripts/babel-6/inline-requires');
const json5 = require('json5');
const path = require('path');
const ReactPackager = require('./react-packager');
const resolvePlugins = require('./react-packager/src/JSTransformer/resolvePlugins');
const babelRC =
json5.parse(
fs.readFileSync(
path.resolve(__dirname, 'react-packager', '.babelrc')));
/**
* Return a memoized function that checks for the existence of a
* project level .babelrc file, and if it doesn't exist, reads the
* default RN babelrc file and uses that.
*/
const getBabelRC = (function() {
let babelRC = null;
function transform(src, filename, options) {
options = options || {};
return function _getBabelRC(projectRoots) {
if (babelRC !== null) {
return babelRC;
}
babelRC = { plugins: [] }; // empty babelrc
// Let's look for the .babelrc in the first project root.
// In the future let's look into adding a command line option to specify
// this location.
//
// NOTE: we're not reading the project's .babelrc here. We leave it up to
// Babel to do that automatically and apply the transforms accordingly
// (which works because we pass in `filename` and `sourceFilename` to
// Babel when we transform).
let projectBabelRCPath;
if (projectRoots && projectRoots.length > 0) {
projectBabelRCPath = path.resolve(projectRoots[0], '.babelrc');
}
// If a .babelrc file doesn't exist in the project,
// use the Babel config provided with react-native.
if (!projectBabelRCPath || !fs.existsSync(projectBabelRCPath)) {
babelRC = json5.parse(
fs.readFileSync(
path.resolve(__dirname, 'react-packager', 'rn-babelrc.json'))
);
// Require the babel-preset's listed in the default babel config
babelRC.presets = babelRC.presets.map((preset) => require('babel-preset-' + preset));
babelRC.plugins = resolvePlugins(babelRC.plugins);
}
return babelRC;
}
})();
/**
* Given a filename and options, build a Babel
* config object with the appropriate plugins.
*/
function buildBabelConfig(filename, options) {
const babelRC = getBabelRC(options.projectRoots);
const extraPlugins = ['external-helpers-2'];
const extraConfig = {
filename,
sourceFileName: filename,
};
const config = Object.assign({}, babelRC, extraConfig);
if (options.hot) {
extraPlugins.push([
'react-transform',
{
transforms: [{
transform: 'react-transform-hmr/lib/index.js',
imports: ['React'],
locals: ['module'],
}]
},
]);
}
let config = Object.assign({}, babelRC, extraConfig);
// Add extra plugins
const extraPlugins = [externalHelpersPlugin];
if (options.inlineRequires) {
extraPlugins.push(inlineRequires);
extraPlugins.push(inlineRequiresPlugin);
}
config.plugins = resolvePlugins(extraPlugins.concat(config.plugins));
const result = babel.transform(src, Object.assign({}, babelRC, config));
config.plugins = extraPlugins.concat(config.plugins);
if (options.hot) {
const hmrConfig = makeHMRConfig(options);
config = Object.assign({}, config, hmrConfig);
}
return Object.assign({}, babelRC, config);
}
function transform(src, filename, options) {
options = options || {};
const babelConfig = buildBabelConfig(filename, options);
const result = babel.transform(src, babelConfig);
return {
code: result.code,