Use Babel 7 when building packages

Reviewed By: davidaurelio

Differential Revision: D6834296

fbshipit-source-id: 177c0ff41145961d8debe623eac3fbd2ca0919bf
This commit is contained in:
Peter van der Zee 2018-01-31 08:11:50 -08:00 committed by Facebook Github Bot
parent d986aac32b
commit 45be0395ce
31 changed files with 759 additions and 128 deletions

View File

@ -143,7 +143,7 @@ The JavaScript transformer (`transformModulePath`) is the place where JS code wi
Mandatory method that will transform code. The object received has information about the module being transformed (e.g its path, code...) and the returned object has to contain an `ast` key that is the AST representation of the transformed code. The default shipped transformer does the bare minimum amount of work by just parsing the code to AST:
```js
const babylon = require('babylon');
const babylon = require('babylon7');
module.exports.transform = (file: {filename: string, src: string}) => {
const ast = babylon.parse(code, {sourceType: 'module'});
@ -155,7 +155,7 @@ module.exports.transform = (file: {filename: string, src: string}) => {
If you would like to plug-in babel, you can simply do that by passing the code to it:
```js
const {transform} = require('babel-core');
const {transform} = require('@babel/core');
module.exports.transform = file => {
return transform(file.src, {

View File

@ -125,10 +125,10 @@ declare module 'babel-core' {
declare type BabelSourceMap = _BabelSourceMap;
declare type Ast = {};
declare type TransformOptions = _TransformOptions;
declare function transform(
declare var transform: TransformSync /*(
code: string,
options?: _TransformOptions,
): TransformResult;
): TransformResult*/;
declare function traverse<State>(
ast: Ast,
ast: BabelNode,
@ -138,11 +138,12 @@ declare module 'babel-core' {
parentPath?: ?Object,
): void;
declare var types: BabelTypes;
declare function transformFromAst(
declare var transformFile: TransformFile;
declare var transformFromAst: TransformFromAst /*(
ast: Ast,
code?: ?string,
babelOptions?: _TransformOptions,
): TransformResult;
): TransformResult*/;
}
type _BabelSourceMapSegment = {
@ -199,7 +200,7 @@ type BabelCoreOptions = {|
getModuleId?: ?(string) => string | boolean,
highlightCode?: boolean,
ignore?: ?(string | Array<string> | RegExp | Array<string | RegExp>),
inputSourceMap?: ?BabelSourceMap,
inputSourceMap?: ?_BabelSourceMap,
minified?: boolean,
moduleId?: ?string,
moduleIds?: ?{[string]: string},
@ -237,7 +238,7 @@ type TemplateFunc = (tpl: string, options: TemplateOptions) => Templatable;
// https://github.com/babel/babel/blob/98969b8a7335e831c069164f5c56132111847224/packages/babel-core/src/transform-ast-sync.js#L15
export type Transform7Result = {|
code: string,
map?: BabelSourceMap,
map?: ?_BabelSourceMap,
ast: BabelNode, // note: babel never allows falsy ast value here
|};
@ -291,7 +292,7 @@ declare module '@babel/core' {
filename: string,
options: BabelCoreOptions,
callback: TransformCallback,
): void;
): Transform7Result;
declare var transformFileSync: TransformFileSync;
// Note: DO NOT USE WITHOUT CALLBACK. Use transformFromAstSync instead.
declare function transformFromAst(
@ -299,13 +300,16 @@ declare module '@babel/core' {
code?: string,
options?: BabelCoreOptions,
callback: TransformCallback,
): void;
): Transform7Result;
declare var transformFromAstSync: TransformFromAstSync;
}
// https://github.com/babel/babel/tree/master/packages/babylon
declare module 'babylon7' {
declare function parse(code: string, options?: Babylon7Options): BabelNode;
declare function parse(
code: string,
options?: Babylon7Options,
): BabelNode & {program: {body: BabelNode}};
declare function parseExpression(
code: string,
options?: Babylon7Options,

View File

@ -32,7 +32,7 @@ import type {DynamicRequiresBehavior} from '../ModuleGraph/worker/collectDepende
import type {GlobalTransformCache} from '../lib/GlobalTransformCache';
import type {TransformCache} from '../lib/TransformCaching';
import type {Reporter} from '../lib/reporting';
import type {BabelSourceMap} from 'babel-core';
import type {BabelSourceMap} from '@babel/core';
import type {
MetroSourceMapSegmentTuple,
MetroSourceMap,

View File

@ -12,8 +12,8 @@
'use strict';
const {babelCore: babel} = require('../babel-bridge');
const {babylon} = require('../babel-bridge');
const {babelTypes} = require('../babel-bridge');
import type {AssetDataWithoutFiles} from '../Assets';
import type {ModuleTransportLike} from '../shared/types.flow';
@ -54,7 +54,7 @@ function generateAssetCodeFileAst(
const descriptorAst = babylon.parseExpression(
JSON.stringify(properDescriptor),
);
const t = babel.types;
const t = babelTypes;
// module.exports
const moduleExports = t.memberExpression(
@ -98,7 +98,7 @@ function generateRemoteAssetCodeFileAst(
remoteServer: string,
remoteFileMap: RemoteFileMap,
): ?Ast {
const t = babel.types;
const t = babelTypes;
const file = remoteFileMap[assetDescriptor.fileSystemLocation];
const descriptor = file && file[assetDescriptor.name];

View File

@ -28,7 +28,7 @@ import type DeltaTransformer, {
DeltaEntry,
DeltaTransformResponse,
} from './DeltaTransformer';
import type {BabelSourceMap} from 'babel-core';
import type {BabelSourceMap} from '@babel/core';
export type Options = BundleOptions & {
deltaBundleId: ?string,

View File

@ -17,7 +17,7 @@ const {Logger} = require('metro-core');
const debug = require('debug')('Metro:JStransformer');
const Worker = require('jest-worker').default;
import type {BabelSourceMap} from 'babel-core';
import type {BabelSourceMap} from '@babel/core';
import type {Options, TransformedCode} from './worker';
import type {LocalPath} from '../node-haste/lib/toLocalPath';
import type {ResultWithMap} from './worker/minify';

View File

@ -14,10 +14,10 @@
const constantFolding = require('../constant-folding');
const {babelCore: babel} = require('../../../babel-bridge');
const {transform} = require('../../../babel-bridge');
function parse(code) {
return babel.transform(code, {code: false, babelrc: false, compact: true});
return transform(code, {code: false, babelrc: false, compact: true});
}
const babelOptions = {
@ -30,7 +30,7 @@ function normalize({code}) {
if (code === undefined || code === null) {
return 'FAIL';
}
return babel.transform(code, babelOptions).code;
return transform(code, babelOptions).code;
}
describe('constant expressions', () => {

View File

@ -13,9 +13,8 @@
const inline = require('../inline');
const {
babelCore: {transform, transformFromAst},
} = require('../../../babel-bridge');
const {transform} = require('../../../babel-bridge');
const {transformFromAst} = require('../../../babel-bridge');
const babelOptions = {
babelrc: false,

View File

@ -19,6 +19,7 @@ jest
const path = require('path');
const transformCode = require('..').transform;
const {InvalidRequireCallError} = require('..');
const {version: BABEL_VERSION} = require('../../../babel-bridge');
describe('code transformation worker:', () => {
it('transforms a simple script', async () => {
@ -77,46 +78,92 @@ describe('code transformation worker:', () => {
expect(result.dependencies).toEqual([]);
});
it('transforms a module with dependencies', async () => {
const {result} = await transformCode(
'arbitrary/file.js',
`local/file.js`,
[
'require("./a");',
'arbitrary(code);',
'const b = require("b");',
'import c from "./c";',
].join('\n'),
path.join(__dirname, '../../../transformer.js'),
false,
{
dev: true,
transform: {},
},
[],
'',
'asyncRequire',
'reject',
);
if (BABEL_VERSION === 7) {
it(`transforms a module with dependencies (v${BABEL_VERSION})`, async () => {
const {result} = await transformCode(
'arbitrary/file.js',
`local/file.js`,
[
"'use strict';",
'require("./a");',
'arbitrary(code);',
'const b = require("b");',
'import c from "./c";',
].join('\n'),
path.join(__dirname, '../../../transformer.js'),
false,
{
dev: true,
transform: {},
},
[],
'',
'asyncRequire',
'reject',
);
expect(result.code).toBe(
[
'__d(function (global, require, module, exports, _dependencyMap) {',
' var _c = require(_dependencyMap[0], "./c");',
expect(BABEL_VERSION).toBe(7);
expect(result.code).toBe(
[
'__d(function (global, require, module, exports, _dependencyMap) {',
" 'use strict';",
'',
' var _c = babelHelpers.interopRequireDefault(require(_dependencyMap[0], "./c"));',
'',
' require(_dependencyMap[1], "./a");',
'',
' arbitrary(code);',
'',
' var b = require(_dependencyMap[2], "b");',
'});',
].join('\n'),
);
expect(result.map).toHaveLength(14);
expect(result.dependencies).toEqual(['./c', './a', 'b']);
});
} else {
it(`transforms a module with dependencies (v${BABEL_VERSION})`, async () => {
const {result} = await transformCode(
'arbitrary/file.js',
`local/file.js`,
[
'require("./a");',
'arbitrary(code);',
'const b = require("b");',
'import c from "./c";',
].join('\n'),
path.join(__dirname, '../../../transformer.js'),
false,
{
dev: true,
transform: {},
},
[],
'',
' var _c2 = babelHelpers.interopRequireDefault(_c);',
'',
' require(_dependencyMap[1], "./a");',
'',
' arbitrary(code);',
'',
' var b = require(_dependencyMap[2], "b");',
'});',
].join('\n'),
);
expect(result.map).toHaveLength(13);
expect(result.dependencies).toEqual(['./c', './a', 'b']);
});
'asyncRequire',
'reject',
);
expect(BABEL_VERSION).toBe(6);
expect(result.code).toBe(
[
'__d(function (global, require, module, exports, _dependencyMap) {',
' var _c = require(_dependencyMap[0], "./c");',
'',
' var _c2 = babelHelpers.interopRequireDefault(_c);',
'',
' require(_dependencyMap[1], "./a");',
'',
' arbitrary(code);',
'',
' var b = require(_dependencyMap[2], "b");',
'});',
].join('\n'),
);
expect(result.map).toHaveLength(13);
expect(result.dependencies).toEqual(['./c', './a', 'b']);
});
}
it('reports filename when encountering unsupported dynamic dependency', async () => {
try {
@ -158,6 +205,7 @@ describe('code transformation worker:', () => {
{
dev: true,
transform: {},
enableBabelRCLookup: false,
},
[],
'',

View File

@ -12,11 +12,10 @@
'use strict';
const {babelCore: babel} = require('../../babel-bridge');
const {babelTypes: t} = require('../../babel-bridge');
const {transformFromAst} = require('../../babel-bridge');
import type {Ast, BabelSourceMap} from 'babel-core';
const t = babel.types;
import type {Ast, BabelSourceMap} from '@babel/core';
const Conditional = {
exit(path) {
@ -76,14 +75,14 @@ function constantFolding(
filename: string,
transformResult: {
ast: Ast,
code?: ?string,
code: string,
map: ?BabelSourceMap,
},
) {
return babel.transformFromAst(transformResult.ast, transformResult.code, {
return transformFromAst(transformResult.ast, transformResult.code, {
filename,
plugins: [plugin],
inputSourceMap: transformResult.map,
inputSourceMap: transformResult.map || undefined, // may not be null
sourceMaps: true,
sourceFileName: filename,
babelrc: false,

View File

@ -29,8 +29,9 @@ const {toSegmentTuple} = require('metro-source-map');
import type {DynamicRequiresBehavior} from '../../ModuleGraph/worker/collectDependencies';
import type {LocalPath} from '../../node-haste/lib/toLocalPath';
import type {ResultWithMap} from './minify';
import type {Ast, Plugins as BabelPlugins} from 'babel-core';
import type {BabelSourceMap} from 'babel-core';
import type {Ast} from '@babel/core';
import type {BabelSourceMap} from '@babel/core';
import type {Plugins as BabelPlugins} from 'babel-core';
import type {LogEntry} from 'metro-core/src/Logger';
import type {MetroSourceMapSegmentTuple} from 'metro-source-map';

View File

@ -12,9 +12,8 @@
'use strict';
const {babelCore: babel} = require('../../babel-bridge');
const {babelTypes: t} = require('../../babel-bridge');
const t = babel.types;
const importMap = new Map([['ReactNative', 'react-native']]);
const isPlatformNode = (

View File

@ -15,11 +15,12 @@
const inlinePlatform = require('./inline-platform');
const invariant = require('fbjs/lib/invariant');
const {babelCore: babel} = require('../../babel-bridge');
const {transform} = require('../../babel-bridge');
const {transformFromAst} = require('../../babel-bridge');
const {babelTypes: t} = require('../../babel-bridge');
import type {Ast} from 'babel-core';
import type {BabelSourceMap} from 'babel-core';
const t = babel.types;
import type {Ast} from '@babel/core';
import type {BabelSourceMap} from '@babel/core';
const env = {name: 'env'};
const nodeEnv = {name: 'NODE_ENV'};
@ -127,8 +128,8 @@ function inline(
};
const result = transformResult.ast
? babel.transformFromAst(transformResult.ast, code, babelOptions)
: babel.transform(code, babelOptions);
? transformFromAst(transformResult.ast, code, babelOptions)
: transform(code, babelOptions);
const {ast} = result;
invariant(ast != null, 'Missing AST in babel transform results.');
return {ast, code: result.code, map: result.map};

View File

@ -14,7 +14,7 @@
const uglify = require('uglify-es');
import type {BabelSourceMap} from 'babel-core';
import type {BabelSourceMap} from '@babel/core';
export type ResultWithMap = {
code: string,

View File

@ -11,8 +11,8 @@
*/
'use strict';
import type {Ast} from 'babel-core';
import type {BabelSourceMap} from 'babel-core';
import type {Ast} from '@babel/core';
import type {BabelSourceMap} from '@babel/core';
import type {Console} from 'console';
import type {FBSourceMap, MetroSourceMap} from 'metro-source-map';

View File

@ -14,13 +14,13 @@
/* eslint-disable no-unclear-flowtypes */
const {babelCore: babel} = require('../../babel-bridge');
const {babelTypes} = require('../../babel-bridge');
const MODULE_FACTORY_PARAMETERS = ['global', 'require', 'module', 'exports'];
const POLYFILL_FACTORY_PARAMETERS = ['global'];
function wrapModule(fileAst: Object, dependencyMapName: string): Object {
const t = babel.types;
const t = babelTypes;
const params = MODULE_FACTORY_PARAMETERS.concat(dependencyMapName);
const factory = functionFromProgram(fileAst.program, params);
const def = t.callExpression(t.identifier('__d'), [factory]);
@ -28,7 +28,7 @@ function wrapModule(fileAst: Object, dependencyMapName: string): Object {
}
function wrapPolyfill(fileAst: Object): Object {
const t = babel.types;
const t = babelTypes;
const factory = functionFromProgram(
fileAst.program,
POLYFILL_FACTORY_PARAMETERS,
@ -41,7 +41,7 @@ function functionFromProgram(
program: Object,
parameters: Array<string>,
): Object {
const t = babel.types;
const t = babelTypes;
return t.functionExpression(
t.identifier(''),
parameters.map(makeIdentifier),
@ -50,7 +50,7 @@ function functionFromProgram(
}
function makeIdentifier(name: string): Object {
return babel.types.identifier(name);
return babelTypes.identifier(name);
}
module.exports = {

View File

@ -17,8 +17,8 @@ const invariant = require('fbjs/lib/invariant');
const nullthrows = require('fbjs/lib/nullthrows');
const transformModule = require('../transform-module');
const {babelTypes: types} = require('../../../babel-bridge');
const {babylon: {parse}} = require('../../../babel-bridge');
const {babelTypes: types} = require('../../../babel-bridge');
const {babelGenerate: generate} = require('../../../babel-bridge');
const {babelTraverse: traverse} = require('../../../babel-bridge');
const {fn} = require('../../test-helpers');

View File

@ -20,7 +20,7 @@ const {babelTypes: types} = require('../../babel-bridge');
const {babelGenerate: generate} = require('../../babel-bridge');
import type {TransformResultDependency} from '../types.flow';
import type {Ast} from 'babel-core';
import type {Ast} from '@babel/core';
export type DynamicRequiresBehavior = 'throwAtRuntime' | 'reject';
type Options = {|

View File

@ -12,7 +12,6 @@
'use strict';
const {babelCore: babel} = require('../../babel-bridge');
const constantFolding = require('../../JSTransformer/worker/constant-folding')
.plugin;
const generate = require('./generate');
@ -22,8 +21,10 @@ const minify = require('../../JSTransformer/worker/minify');
const optimizeDependencies = require('./optimizeDependencies');
const sourceMap = require('source-map');
const {transform} = require('../../babel-bridge');
import type {TransformedSourceFile, TransformResult} from '../types.flow';
import type {BabelSourceMap} from 'babel-core';
import type {BabelSourceMap} from '@babel/core';
import type {MetroSourceMap} from 'metro-source-map';
import type {PostMinifyProcess} from '../../Bundler/index.js';
@ -98,7 +99,7 @@ function optimize(transformed: TransformResult, file, options) {
}
function optimizeCode(code, map, filename, inliningOptions) {
return babel.transform(code, {
return transform(code, {
plugins: [
[constantFolding],
[inline, {...inliningOptions, isWrapped: true}],

View File

@ -16,7 +16,7 @@ const {babelTraverse: traverse} = require('../../babel-bridge');
const {babelGenerate} = require('../../babel-bridge');
import type {TransformResultDependency} from '../types.flow';
import type {Ast} from 'babel-core';
import type {Ast} from '@babel/core';
type Context = {
oldToNewIndex: Map<number, number>,

View File

@ -36,7 +36,7 @@ import type {
TransformResult,
TransformVariants,
} from '../types.flow';
import type {Ast} from 'babel-core';
import type {Ast} from '@babel/core';
export type TransformOptions<ExtraOptions> = {|
+asyncRequireModulePath: string,

View File

@ -15,7 +15,7 @@ const {getAssetData} = require('./Assets');
const {generateAssetCodeFileAst} = require('./Bundler/util');
import type {TransformOptions} from './JSTransformer/worker';
import type {Ast} from 'babel-core';
import type {Ast} from '@babel/core';
type Params = {
localPath: string,

View File

@ -15,6 +15,10 @@
// This is a temporary migration bridge to switch between babel 6 and 7
const IS_BABEL7 = process.env.BABEL_VERSION === '7';
// ## Babel 6 stuff
const babelCore6 = require('babel-core');
const babelGenerate6 = require('babel-generator').default;
const babelTemplate6 = require('babel-template');
@ -26,20 +30,376 @@ const externalHelpersPlugin6 = require('babel-plugin-external-helpers');
const inlineRequiresPlugin6 = require('babel-preset-fbjs/plugins/inline-requires');
const makeHMRConfig6 = require('babel-preset-react-native/configs/hmr');
const resolvePlugins6 = require('babel-preset-react-native/lib/resolvePlugins');
// register has side effects so don't include by default (only used in a test)
const getBabelRegisterConfig6 = () => require('./babelRegisterOnly').config;
// load given preset as a babel6 preset
const getPreset6 = (preset: string) =>
// $FlowFixMe: dynamic require can't be avoided
require('babel-preset-' + preset);
// ## Babel 7 stuff
const babelCore7 = require('@babel/core');
const babelGenerate7 = require('@babel/generator').default;
const babelTemplate7 = require('@babel/template').default;
const babelTraverse7 = require('@babel/traverse').default;
const babelTypes7 = require('@babel/types');
const babylon7 = require('babylon7');
const externalHelpersPlugin7 = require('babel-plugin-external-helpers');
const inlineRequiresPlugin7 = require('babel-preset-fbjs/plugins/inline-requires');
const makeHMRConfig7 = makeMakeHMRConfig7();
function resolvePlugins7(plugins: Array<any>) {
/**
* from: babel-preset-react-native/lib/resolvePlugins
* "Ported" to Babel 7
*
* 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.
*/
type ModuleES6 = {__esModule?: boolean, default?: {}};
return plugins.map(plugin => {
// Normalise plugin to an array.
plugin = Array.isArray(plugin) ? plugin : [plugin];
// Only resolve the plugin if it's a string reference.
if (typeof plugin[0] === 'string') {
// $FlowFixMe these plugins need to be included here
const required: ModuleES6 | {} = require('@babel/plugin-' + plugin[0]);
// es6 import default?
// $FlowFixMe should properly type this plugin structure
plugin[0] = required.__esModule ? required.default : required;
}
return plugin;
});
}
module.exports = {
// TODO: `babelGenerate: process.env.BABEL_VER === 7 ? babelGenerate7 : babelGenerate6,` etc
version: process.env.BABEL_VER === '7' ? 7 : 6,
version: IS_BABEL7 ? 7 : 6,
babelCore: babelCore6,
babelGenerate: babelGenerate6,
babelTemplate: babelTemplate6,
babelTraverse: babelTraverse6,
babelTypes: babelTypes6,
babylon: babylon6,
// need to abstract the transform* funcs here since their name changed
transform: IS_BABEL7 ? babelCore7.transformSync : babelCore6.transform,
transformFile: IS_BABEL7
? babelCore7.transformFileSync
: babelCore6.transformFile,
transformFromAst: IS_BABEL7
? babelCore7.transformFromAstSync
: babelCore6.transformFromAst,
externalHelpersPlugin: externalHelpersPlugin6,
inlineRequiresPlugin: inlineRequiresPlugin6,
makeHMRConfig: makeHMRConfig6,
resolvePlugins: resolvePlugins6,
babelGenerate: IS_BABEL7 ? babelGenerate7 : babelGenerate6,
babelTemplate: IS_BABEL7 ? babelTemplate7 : babelTemplate6,
babelTraverse: IS_BABEL7 ? babelTraverse7 : babelTraverse6,
babelTypes: IS_BABEL7 ? babelTypes7 : babelTypes6,
getBabelRegisterConfig: IS_BABEL7
? getBabelRegisterConfig7
: getBabelRegisterConfig6,
babylon: IS_BABEL7 ? babylon7 : babylon6,
externalHelpersPlugin: IS_BABEL7
? externalHelpersPlugin7
: externalHelpersPlugin6,
inlineRequiresPlugin: IS_BABEL7
? inlineRequiresPlugin7
: inlineRequiresPlugin6,
makeHMRConfig: IS_BABEL7 ? makeHMRConfig7 : makeHMRConfig6,
resolvePlugins: IS_BABEL7 ? resolvePlugins7 : resolvePlugins6,
getPreset: IS_BABEL7 ? getPreset7 : getPreset6,
};
function makeMakeHMRConfig7() {
// from: babel-preset-react-native/configs/hmr
/**
* 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';
var path = require('path');
var hmrTransform = 'react-transform-hmr/lib/index.js';
var transformPath = require.resolve(hmrTransform);
return function(options: mixed, filename?: string) {
var transform = filename
? './' + path.relative(path.dirname(filename), transformPath) // packager can't handle absolute paths
: hmrTransform;
// Fix the module path to use '/' on Windows.
if (path.sep === '\\') {
transform = transform.replace(/\\/g, '/');
}
return {
plugins: resolvePlugins7([
[
'react-transform',
{
transforms: [
{
transform,
imports: ['react'],
locals: ['module'],
},
],
},
],
]),
};
};
}
function getPreset7() {
// from: fbsource/xplat/js/node_modules/babel-preset-react-native/configs/main.js
/**
* 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';
function addPluginsWhenSourceNeedsIt(src, plugins) {
// not sure what happened to this plugin. obsoleted?
// if (src.indexOf('async') !== -1 || src.indexOf('await') !== -1) {
// plugins.push('syntax-async-functions');
// }
if (src.indexOf('class') !== -1) {
plugins.push('transform-classes');
if (src.indexOf('...') !== -1) {
plugins.push('transform-spread');
plugins.push('proposal-object-rest-spread');
}
}
if (src.indexOf('=>') !== -1) {
plugins.push('transform-arrow-functions');
}
if (src.indexOf('const') !== -1) {
plugins.push('check-constants');
}
if (src.indexOf('`') !== -1) {
plugins.push('transform-template-literals');
}
if (src.indexOf('Object.assign') !== -1) {
plugins.push('transform-object-assign');
}
if (src.indexOf('for') !== -1 && src.indexOf('of') !== -1) {
plugins.push(['transform-for-of', {loose: true}]);
if (src.indexOf('Symbol') !== -1) {
plugins.push(transformSymbolMember());
}
}
if (
src.indexOf('React.createClass') !== -1 ||
src.indexOf('createReactClass') !== -1
) {
plugins.push('transform-react-display-name');
}
if (src.indexOf('import(')) {
plugins.push(transformDynamicImport());
}
}
const getPreset = (src, options) => {
const plugins = [];
plugins.push(
// 'syntax-class-properties',
// 'syntax-trailing-function-commas',
'proposal-class-properties',
'transform-block-scoping',
'transform-computed-properties',
'transform-destructuring',
'transform-function-name',
'transform-literals',
'transform-parameters',
'transform-shorthand-properties',
'transform-flow-strip-types',
'transform-react-jsx',
'transform-regenerator',
['transform-modules-commonjs', {strict: false, allowTopLevelThis: true}],
);
if (src !== null && src !== undefined) {
addPluginsWhenSourceNeedsIt(src, plugins);
}
if (options && options.dev) {
plugins.push('transform-react-jsx-source');
}
return {
comments: false,
compact: true,
plugins: resolvePlugins7(plugins),
};
};
let base;
let devTools;
// TODO: options probably has more properties...
return (options: {withDevTools?: boolean}) => {
if (options.withDevTools == null) {
const env = process.env.BABEL_ENV || process.env.NODE_ENV;
if (!env || env === 'development') {
return devTools || (devTools = getPreset(null, {dev: true}));
}
}
return base || (base = getPreset(null));
};
}
function transformSymbolMember() {
// from: fbsource/xplat/js/node_modules/babel-preset-react-native/transforms/transform-symbol-member.js
/**
* Copyright 2004-present Facebook. All Rights Reserved.
*/
'use strict';
/*eslint consistent-return: 0*/
/**
* Transforms function properties of the `Symbol` into
* the presence check, and fallback string "@@<name>".
*
* Example:
*
* Symbol.iterator;
*
* Transformed to:
*
* typeof Symbol.iterator === 'function' ? Symbol.iterator : '@@iterator';
*/
return function symbolMember() {
const t = babelTypes7;
return {
visitor: {
MemberExpression(path) {
if (!isAppropriateMember(path)) {
return;
}
const node = path.node;
path.replaceWith(
t.conditionalExpression(
t.binaryExpression(
'===',
t.unaryExpression('typeof', t.identifier('Symbol'), true),
t.stringLiteral('function'),
),
node,
t.stringLiteral(`@@${node.property.name}`),
),
);
// We should stop to avoid infinite recursion, since Babel
// traverses replaced path, and again would hit our transform.
path.stop();
},
},
};
};
function isAppropriateMember(path) {
const node = path.node;
return (
path.parentPath.type !== 'AssignmentExpression' &&
node.object.type === 'Identifier' &&
node.object.name === 'Symbol' &&
node.property.type === 'Identifier'
);
}
}
function transformDynamicImport() {
// from: fbsource/xplat/js/node_modules/babel-preset-react-native/transforms/transform-dynamic-import.js
/**
* Copyright (c) 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.
*/
'use strict';
const buildImport = babelTemplate7(
'Promise.resolve().then(() => require(ARGS))',
);
const TYPE_IMPORT = 'Import';
const plugin = {
inherits: require('@babel/plugin-syntax-dynamic-import').default,
visitor: {
CallExpression(path) {
if (path.node.callee.type !== TYPE_IMPORT) {
return;
}
const newImport = buildImport({ARGS: path.node.arguments});
path.replaceWith(newImport);
},
},
};
return plugin;
}
function getBabelRegisterConfig7() {
// from: fbsource/xplat/js/metro/packages/metro/src/babelRegisterOnly.js
// (dont use babel-register anymore, it obsoleted with babel 7)
/**
* 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.
*
* @format
*/
'use strict';
require('./setupNodePolyfills');
var _only = [];
const PLUGINS = [
'transform-flow-strip-types',
'proposal-object-rest-spread',
'proposal-class-properties',
];
function config(onlyList: Array<string>) {
_only = _only.concat(onlyList);
return {
presets: [],
plugins: PLUGINS.map(pluginName =>
// $FlowFixMe must require with dynamic string
require(`@babel/plugin-${pluginName}`),
),
only: _only,
retainLines: true,
sourceMaps: 'inline',
babelrc: false,
};
}
return config;
}

View File

@ -11,7 +11,7 @@
*/
'use strict';
const {babelCore: babel} = require('./babel-bridge');
const {transform} = require('./babel-bridge');
import type {TransformOptions} from './JSTransformer/worker';
import type {Plugins as BabelPlugins} from 'babel-core';
@ -28,7 +28,7 @@ module.exports.transform = ({filename, options, plugins, src}: Params) => {
process.env.BABEL_ENV = options.dev ? 'development' : 'production';
try {
const {ast} = babel.transform(src, {filename, code: false, plugins});
const {ast} = transform(src, {filename, code: false, plugins});
return {ast};
} finally {

View File

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`basic_bundle bundles package with polyfills 1`] = `
exports[`basic_bundle bundles package with polyfills (babel v6) 1`] = `
"var __DEV__=false,__BUNDLE_START_TIME__=this.nativePerformanceNow?nativePerformanceNow():Date.now(),process=this.process||{};process.env=process.env||{};process.env.NODE_ENV='production';
(function (global) {
'use strict';
@ -188,12 +188,12 @@ __d(function (global, require, module, exports, _dependencyMap) {
require(4);"
`;
exports[`basic_bundle bundles package without polyfills 1`] = `
exports[`basic_bundle bundles package with polyfills (babel v7) 1`] = `
"var __DEV__=false,__BUNDLE_START_TIME__=this.nativePerformanceNow?nativePerformanceNow():Date.now(),process=this.process||{};process.env=process.env||{};process.env.NODE_ENV='production';
(function (global) {
'use strict';
global.require = _require;
global.require = require;
global.__d = define;
var modules = Object.create(null);
@ -211,7 +211,7 @@ exports[`basic_bundle bundles package without polyfills 1`] = `
};
}
function _require(moduleId) {
function require(moduleId) {
var moduleIdReallyIsNumber = moduleId;
var module = modules[moduleIdReallyIsNumber];
return module && module.isInitialized ? module.exports : guardedLoadModule(moduleIdReallyIsNumber, module);
@ -222,7 +222,7 @@ exports[`basic_bundle bundles package without polyfills 1`] = `
function guardedLoadModule(moduleId, module) {
if (!inGuard && global.ErrorUtils) {
inGuard = true;
var returnValue = void 0;
var returnValue;
try {
returnValue = loadModuleImplementation(moduleId, module);
@ -249,13 +249,13 @@ exports[`basic_bundle bundles package without polyfills 1`] = `
};
}
_require.unpackModuleId = unpackModuleId;
require.unpackModuleId = unpackModuleId;
function packModuleId(value) {
return value.segmentId << ID_MASK_SHIFT + value.localId;
}
_require.packModuleId = packModuleId;
require.packModuleId = packModuleId;
function loadModuleImplementation(moduleId, module) {
var nativeRequire = global.nativeRequire;
@ -287,7 +287,195 @@ exports[`basic_bundle bundles package without polyfills 1`] = `
var _moduleObject = {
exports: exports
};
factory(global, _require, _moduleObject, exports, dependencyMap);
factory(global, require, _moduleObject, exports, dependencyMap);
{
module.factory = undefined;
module.dependencyMap = undefined;
}
return module.exports = _moduleObject.exports;
} catch (e) {
module.hasError = true;
module.error = e;
module.isInitialized = false;
module.exports = undefined;
throw e;
}
}
function unknownModuleError(id) {
var message = 'Requiring unknown module \\"' + id + '\\".';
return Error(message);
}
function moduleThrewError(id, error) {
var displayName = id;
return Error('Requiring module \\"' + displayName + '\\", which threw an exception: ' + error);
}
})(this);
(function (global) {
'use strict';
if (!Object.keys) {
Object.keys = function () {};
}
})(this);
(function (global) {
'use strict';
if (!String.prototype.repeat) {
String.prototype.repeat = function () {};
}
})(this);
__d(function (global, require, module, exports, _dependencyMap) {
'use strict';
var Bar = require(_dependencyMap[0]);
var Foo = require(_dependencyMap[1]);
module.exports = {
Foo: Foo,
Bar: Bar
};
},4,[5,6]);
__d(function (global, require, module, exports, _dependencyMap) {
'use strict';
var Foo = require(_dependencyMap[0]);
module.exports = {
type: 'bar',
foo: Foo.type
};
},5,[6]);
__d(function (global, require, module, exports, _dependencyMap) {
'use strict';
var asset = require(_dependencyMap[0]);
module.exports = {
type: 'foo',
asset: asset
};
},6,[7]);
__d(function (global, require, module, exports, _dependencyMap) {
module.exports = require(_dependencyMap[0]).registerAsset({
\\"__packager_asset\\": true,
\\"httpServerLocation\\": \\"/assets\\",
\\"width\\": 8,
\\"height\\": 8,
\\"scales\\": [1],
\\"hash\\": \\"77d45c1f7fa73c0f6c444a830dc42f67\\",
\\"name\\": \\"test\\",
\\"type\\": \\"png\\"
});
},7,[8]);
__d(function (global, require, module, exports, _dependencyMap) {
'use strict';
},8,[]);
require(4);"
`;
exports[`basic_bundle bundles package without polyfills 1`] = `
"var __DEV__=false,__BUNDLE_START_TIME__=this.nativePerformanceNow?nativePerformanceNow():Date.now(),process=this.process||{};process.env=process.env||{};process.env.NODE_ENV='production';
(function (global) {
'use strict';
global.require = require;
global.__d = define;
var modules = Object.create(null);
function define(factory, moduleId, dependencyMap) {
if (moduleId in modules) {
return;
}
modules[moduleId] = {
dependencyMap: dependencyMap,
exports: undefined,
factory: factory,
hasError: false,
isInitialized: false
};
}
function require(moduleId) {
var moduleIdReallyIsNumber = moduleId;
var module = modules[moduleIdReallyIsNumber];
return module && module.isInitialized ? module.exports : guardedLoadModule(moduleIdReallyIsNumber, module);
}
var inGuard = false;
function guardedLoadModule(moduleId, module) {
if (!inGuard && global.ErrorUtils) {
inGuard = true;
var returnValue;
try {
returnValue = loadModuleImplementation(moduleId, module);
} catch (e) {
global.ErrorUtils.reportFatalError(e);
}
inGuard = false;
return returnValue;
} else {
return loadModuleImplementation(moduleId, module);
}
}
var ID_MASK_SHIFT = 16;
var LOCAL_ID_MASK = ~0 >>> ID_MASK_SHIFT;
function unpackModuleId(moduleId) {
var segmentId = moduleId >>> ID_MASK_SHIFT;
var localId = moduleId & LOCAL_ID_MASK;
return {
segmentId: segmentId,
localId: localId
};
}
require.unpackModuleId = unpackModuleId;
function packModuleId(value) {
return value.segmentId << ID_MASK_SHIFT + value.localId;
}
require.packModuleId = packModuleId;
function loadModuleImplementation(moduleId, module) {
var nativeRequire = global.nativeRequire;
if (!module && nativeRequire) {
var _unpackModuleId = unpackModuleId(moduleId),
_segmentId = _unpackModuleId.segmentId,
_localId = _unpackModuleId.localId;
nativeRequire(_localId, _segmentId);
module = modules[moduleId];
}
if (!module) {
throw unknownModuleError(moduleId);
}
if (module.hasError) {
throw moduleThrewError(moduleId, module.error);
}
module.isInitialized = true;
var exports = module.exports = {};
var _module = module,
factory = _module.factory,
dependencyMap = _module.dependencyMap;
try {
var _moduleObject = {
exports: exports
};
factory(global, require, _moduleObject, exports, dependencyMap);
{
module.factory = undefined;
module.dependencyMap = undefined;

View File

@ -89,7 +89,7 @@ describe('basic_bundle', () => {
).toMatchSnapshot();
}
it('bundles package with polyfills', async () => {
it('bundles package with polyfills (babel v6)', async () => {
const bundleWithPolyfills = await Metro.build(
{
assetRegistryPath: ASSET_REGISTRY_PATH,
@ -100,6 +100,31 @@ describe('basic_bundle', () => {
transformCache: Metro.TransformCaching.none(),
transformModulePath: require.resolve('../../transformer'),
nonPersistent: true,
enableBabelRCLookup: false, // dont use metro's own babelrc!
targetBabelVersion: 6,
},
{
dev: false,
entryFile: path.join(INPUT_PATH, 'TestBundle.js'),
platform: 'ios',
},
);
verifyResultCode(bundleWithPolyfills.code);
});
it('bundles package with polyfills (babel v7)', async () => {
const bundleWithPolyfills = await Metro.build(
{
assetRegistryPath: ASSET_REGISTRY_PATH,
dynamicDepsInPackages: 'reject',
getModulesRunBeforeMainModule: () => ['InitializeCore'],
getPolyfills: () => [polyfill1, polyfill2],
projectRoots: [INPUT_PATH, POLYFILLS_PATH],
transformCache: Metro.TransformCaching.none(),
transformModulePath: require.resolve('../../transformer'),
nonPersistent: true,
enableBabelRCLookup: false, // dont use metro's own babelrc!
targetBabelVersion: 7,
},
{
dev: false,
@ -121,6 +146,7 @@ describe('basic_bundle', () => {
transformCache: Metro.TransformCaching.none(),
transformModulePath: require.resolve('../../transformer'),
nonPersistent: true,
enableBabelRCLookup: false, // dont use metro's own babelrc!
},
{
dev: false,

View File

@ -38,6 +38,7 @@ type Options = {|
nonPersistent?: ?boolean,
transformCache?: ?TransformCache,
verbose?: ?boolean,
targetBabelVersion?: number,
|};
type PublicBundleOptions = {
@ -98,6 +99,9 @@ exports.build = async function(
options: Options,
bundleOptions: PublicBundleOptions,
): Promise<{code: string, map: string}> {
if (options.targetBabelVersion !== undefined) {
process.env.BABEL_VERSION = String(options.targetBabelVersion);
}
var server = createNonPersistentServer(options);
const ServerClass = require('./Server');

View File

@ -1,3 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`getTransformCacheKeyFn Should return always the same key for the same params 1`] = `"dbf93a9e80558a6a2a7d3deb2f79c523dca3c7ce"`;
exports[`getTransformCacheKeyFn Should return always the same key for the same params 1`] = `"c6e850717870148d747719a226e7402fce329933"`;

View File

@ -14,16 +14,19 @@
'use strict';
const {babelCore: babel} = require('../../../babel-bridge');
const babelConfig = require('../../../babelRegisterOnly').config;
const fs = require('fs');
const {getBabelRegisterConfig} = require('../../../babel-bridge');
const {transform} = require('../../../babel-bridge');
const babelConfig = getBabelRegisterConfig();
describe('require', () => {
const moduleSystemCode = (() => {
const {only, ...config} = babelConfig([]);
only;
const rawCode = fs.readFileSync(require.resolve('../require'), 'utf8');
return babel.transform(rawCode, config).code;
return transform(rawCode, config).code;
})();
// eslint-disable-next-line no-new-func

View File

@ -15,7 +15,7 @@ const invariant = require('fbjs/lib/invariant');
import type {RamModule} from '../../../DeltaBundler/Serializers';
import type {ModuleGroups, ModuleTransportLike} from '../../types.flow';
import type {BabelSourceMap} from 'babel-core';
import type {BabelSourceMap} from '@babel/core';
import type {FBIndexMap, IndexMap, MetroSourceMap} from 'metro-source-map';
const newline = /\r\n?|\n|\u2028|\u2029/g;

View File

@ -18,11 +18,12 @@ const fs = require('fs');
const json5 = require('json5');
const path = require('path');
const {babelCore: babel} = require('./babel-bridge');
const {externalHelpersPlugin} = require('./babel-bridge');
const {getPreset} = require('./babel-bridge');
const {inlineRequiresPlugin} = require('./babel-bridge');
const {makeHMRConfig} = require('./babel-bridge');
const {resolvePlugins} = require('./babel-bridge');
const {transform: babelTransform} = require('./babel-bridge');
import type {Transformer, TransformOptions} from './JSTransformer/worker';
import type {Plugins as BabelPlugins} from 'babel-core';
@ -65,10 +66,7 @@ const getBabelRC = (function() {
);
// Require the babel-preset's listed in the default babel config
babelRC.presets = babelRC.presets.map(preset =>
// $FlowFixMe: dynamic require can't be avoided
require('babel-preset-' + preset),
);
babelRC.presets = babelRC.presets.map(getPreset);
babelRC.plugins = resolvePlugins(babelRC.plugins);
} else {
// if we find a .babelrc file we tell babel to use it
@ -135,7 +133,7 @@ function transform({filename, options, src, plugins}: Params) {
try {
const babelConfig = buildBabelConfig(filename, options, plugins);
const {ast} = babel.transform(src, babelConfig);
const {ast} = babelTransform(src, babelConfig);
return {ast};
} finally {