mirror of https://github.com/status-im/metro.git
Introduce transforms pipeline
Summary: public This diff introduces an internal transforms pipeline that integrates with the external one. This has been a feature we've been looking to implement for a long time to use babel instead of `replace` with regexps on many parts of the packager. Also, to split the bundle we'll need to run one transform. Internally for Facebook we can run the system-import transform altogether withe the other ones. For OSS we offer `transformer.js` which people can use out of the box if they're writing ES6 code. For those people, `transformer.js` will also run the internal transforms`. However they might want to tune the transforms, or even write the code on another language that compiles to Javascript and use a complete different transformer. On those cases we'll need to run the external transforms first and pipe the output through the internal transforms. Note that the order it's important as the internal transforms assume the code is written in JS, though the original code could be on other scripting languages (CoffeeScript, TypeScript, etc). Reviewed By: davidaurelio Differential Revision: D2725109 fb-gh-sync-id: d764e209c78743419c4cb97068495c771372ab90
This commit is contained in:
parent
b174b2fcd9
commit
e94c9d042b
|
@ -15,6 +15,7 @@ useGracefulFs();
|
||||||
var debug = require('debug');
|
var debug = require('debug');
|
||||||
var omit = require('underscore').omit;
|
var omit = require('underscore').omit;
|
||||||
var Activity = require('./src/Activity');
|
var Activity = require('./src/Activity');
|
||||||
|
var Transforms = require('./src/transforms');
|
||||||
|
|
||||||
exports.createServer = createServer;
|
exports.createServer = createServer;
|
||||||
exports.middleware = function(options) {
|
exports.middleware = function(options) {
|
||||||
|
@ -23,6 +24,7 @@ exports.middleware = function(options) {
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.Activity = Activity;
|
exports.Activity = Activity;
|
||||||
|
exports.getTransforms = Transforms.getAll;
|
||||||
|
|
||||||
// Renamed "package" to "bundle". But maintain backwards
|
// Renamed "package" to "bundle". But maintain backwards
|
||||||
// compat.
|
// compat.
|
||||||
|
|
|
@ -56,6 +56,10 @@ const validateOpts = declareOpts({
|
||||||
type:'string',
|
type:'string',
|
||||||
required: false,
|
required: false,
|
||||||
},
|
},
|
||||||
|
enableInternalTransforms: {
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
nonPersistent: {
|
nonPersistent: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
default: false,
|
default: false,
|
||||||
|
@ -131,6 +135,7 @@ class Bundler {
|
||||||
blacklistRE: opts.blacklistRE,
|
blacklistRE: opts.blacklistRE,
|
||||||
cache: this._cache,
|
cache: this._cache,
|
||||||
transformModulePath: opts.transformModulePath,
|
transformModulePath: opts.transformModulePath,
|
||||||
|
enableInternalTransforms: opts.enableInternalTransforms,
|
||||||
});
|
});
|
||||||
|
|
||||||
this._projectRoots = opts.projectRoots;
|
this._projectRoots = opts.projectRoots;
|
||||||
|
|
|
@ -52,7 +52,11 @@ const validateOpts = declareOpts({
|
||||||
transformTimeoutInterval: {
|
transformTimeoutInterval: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
default: DEFAULT_MAX_CALL_TIME,
|
default: DEFAULT_MAX_CALL_TIME,
|
||||||
}
|
},
|
||||||
|
enableInternalTransforms: {
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
class Transformer {
|
class Transformer {
|
||||||
|
@ -60,6 +64,8 @@ class Transformer {
|
||||||
const opts = this._opts = validateOpts(options);
|
const opts = this._opts = validateOpts(options);
|
||||||
|
|
||||||
this._cache = opts.cache;
|
this._cache = opts.cache;
|
||||||
|
this._transformModulePath = opts.transformModulePath;
|
||||||
|
this._enableInternalTransforms = opts.enableInternalTransforms;
|
||||||
|
|
||||||
if (opts.transformModulePath != null) {
|
if (opts.transformModulePath != null) {
|
||||||
this._workers = workerFarm({
|
this._workers = workerFarm({
|
||||||
|
@ -68,7 +74,7 @@ class Transformer {
|
||||||
maxCallsPerWorker: MAX_CALLS_PER_WORKER,
|
maxCallsPerWorker: MAX_CALLS_PER_WORKER,
|
||||||
maxCallTime: opts.transformTimeoutInterval,
|
maxCallTime: opts.transformTimeoutInterval,
|
||||||
maxRetries: MAX_RETRIES,
|
maxRetries: MAX_RETRIES,
|
||||||
}, opts.transformModulePath);
|
}, require.resolve('./worker'));
|
||||||
|
|
||||||
this._transform = Promise.denodeify(this._workers);
|
this._transform = Promise.denodeify(this._workers);
|
||||||
}
|
}
|
||||||
|
@ -102,7 +108,11 @@ class Transformer {
|
||||||
return this._transform({
|
return this._transform({
|
||||||
sourceCode,
|
sourceCode,
|
||||||
filename: filePath,
|
filename: filePath,
|
||||||
options,
|
options: {
|
||||||
|
...options,
|
||||||
|
externalTransformModulePath: this._transformModulePath,
|
||||||
|
enableInternalTransforms: this._enableInternalTransforms,
|
||||||
|
},
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
console.warn(
|
console.warn(
|
||||||
|
|
|
@ -8,27 +8,66 @@
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var transformer = require('./transformer');
|
var babel = require('babel-core');
|
||||||
|
var Transforms = require('../transforms');
|
||||||
|
|
||||||
module.exports = function (data, callback) {
|
// 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 result = babel.transform(sourceCode, {
|
||||||
|
retainLines: true,
|
||||||
|
compact: true,
|
||||||
|
comments: false,
|
||||||
|
filename: filename,
|
||||||
|
sourceFileName: filename,
|
||||||
|
sourceMaps: false,
|
||||||
|
plugins: Transforms.getAll()
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
code: result.code,
|
||||||
|
filename: filename,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function onExternalTransformDone(data, callback, error, externalOutput) {
|
||||||
var result;
|
var result;
|
||||||
try {
|
if (data.options.enableInternalTransforms) {
|
||||||
result = transformer.transform(
|
result = internalTransforms(
|
||||||
data.transformSets,
|
externalOutput.code,
|
||||||
data.sourceCode,
|
externalOutput.filename,
|
||||||
data.options
|
data.options
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} else {
|
||||||
return callback(null, {
|
result = externalOutput;
|
||||||
error: {
|
|
||||||
lineNumber: e.lineNumber,
|
|
||||||
column: e.column,
|
|
||||||
message: e.message,
|
|
||||||
stack: e.stack,
|
|
||||||
description: e.description
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(null, result);
|
callback(null, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = function(data, callback) {
|
||||||
|
try {
|
||||||
|
if (data.options.externalTransformModulePath) {
|
||||||
|
var externalTransformModule = require(
|
||||||
|
data.options.externalTransformModulePath
|
||||||
|
);
|
||||||
|
externalTransformModule(
|
||||||
|
data,
|
||||||
|
onExternalTransformDone.bind(null, data, callback)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
onExternalTransformDone(
|
||||||
|
data,
|
||||||
|
callback,
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
code: data.sourceCode,
|
||||||
|
filename: data.filename
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
callback(e);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -52,6 +52,10 @@ const validateOpts = declareOpts({
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
enableInternalTransforms: {
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
assetRoots: {
|
assetRoots: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
required: false,
|
required: false,
|
||||||
|
|
|
@ -30,7 +30,9 @@ const SocketInterface = {
|
||||||
const value = options[key];
|
const value = options[key];
|
||||||
if (value) {
|
if (value) {
|
||||||
hash.update(
|
hash.update(
|
||||||
typeof value === 'string' ? value : JSON.stringify(value)
|
options[key] != null && typeof value === 'string'
|
||||||
|
? value
|
||||||
|
: JSON.stringify(value)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,63 +1,63 @@
|
||||||
/**
|
/**
|
||||||
* Copyright (c) 2015-present, Facebook, Inc.
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* This source code is licensed under the BSD-style license found in the
|
* 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
|
* 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.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
/*jslint node: true */
|
/*jslint node: true */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const t = require('babel-types');
|
const t = require('babel-types');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms asynchronous module importing into a function call
|
* Transforms asynchronous module importing into a function call
|
||||||
* that includes which bundle needs to be loaded
|
* that includes which bundle needs to be loaded
|
||||||
*
|
*
|
||||||
* Transforms:
|
* Transforms:
|
||||||
*
|
*
|
||||||
* System.import('moduleA')
|
* System.import('moduleA')
|
||||||
*
|
*
|
||||||
* to:
|
* to:
|
||||||
*
|
*
|
||||||
* loadBundles('bundleA')
|
* loadBundles('bundleA')
|
||||||
*/
|
*/
|
||||||
module.exports = function() {
|
module.exports = function() {
|
||||||
return {
|
return {
|
||||||
visitor: {
|
visitor: {
|
||||||
CallExpression: function (path, state) {
|
CallExpression: function (path, state) {
|
||||||
if (!isAppropriateSystemImportCall(path.node)) {
|
if (!isAppropriateSystemImportCall(path.node)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var bundlesLayout = state.opts.bundlesLayout;
|
var bundlesLayout = state.opts.bundlesLayout;
|
||||||
var bundleID = bundlesLayout.getBundleIDForModule(
|
var bundleID = bundlesLayout.getBundleIDForModule(
|
||||||
path.node.arguments[0].value
|
path.node.arguments[0].value
|
||||||
);
|
);
|
||||||
|
|
||||||
var bundles = bundleID.split('.');
|
var bundles = bundleID.split('.');
|
||||||
bundles.splice(0, 1);
|
bundles.splice(0, 1);
|
||||||
bundles = bundles.map(function(id) {
|
bundles = bundles.map(function(id) {
|
||||||
return t.stringLiteral('bundle.' + id);
|
return t.stringLiteral('bundle.' + id);
|
||||||
});
|
});
|
||||||
|
|
||||||
path.replaceWith(t.callExpression(
|
path.replaceWith(t.callExpression(
|
||||||
t.identifier('loadBundles'),
|
t.identifier('loadBundles'),
|
||||||
[t.arrayExpression(bundles)]
|
[t.arrayExpression(bundles)]
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
function isAppropriateSystemImportCall(node) {
|
function isAppropriateSystemImportCall(node) {
|
||||||
return (
|
return (
|
||||||
node.callee.type === 'MemberExpression' &&
|
node.callee.type === 'MemberExpression' &&
|
||||||
node.callee.object.name === 'System' &&
|
node.callee.object.name === 'System' &&
|
||||||
node.callee.property.name === 'import' &&
|
node.callee.property.name === 'import' &&
|
||||||
node.arguments.length === 1 &&
|
node.arguments.length === 1 &&
|
||||||
node.arguments[0].type === 'StringLiteral'
|
node.arguments[0].type === 'StringLiteral'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
/**
|
||||||
|
* 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() {
|
||||||
|
return [
|
||||||
|
require('./babel-plugin-system-import'),
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
|
@ -15,6 +15,7 @@ const fs = require('fs');
|
||||||
const inlineRequires = require('fbjs-scripts/babel-6/inline-requires');
|
const inlineRequires = require('fbjs-scripts/babel-6/inline-requires');
|
||||||
const json5 = require('json5');
|
const json5 = require('json5');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const ReactPackager = require('./react-packager');
|
||||||
|
|
||||||
const babelRC =
|
const babelRC =
|
||||||
json5.parse(
|
json5.parse(
|
||||||
|
@ -53,10 +54,13 @@ function transform(src, filename, options) {
|
||||||
return plugin;
|
return plugin;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
config.plugins = config.plugins.concat(ReactPackager.getTransforms());
|
||||||
|
|
||||||
const result = babel.transform(src, Object.assign({}, babelRC, config));
|
const result = babel.transform(src, Object.assign({}, babelRC, config));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
code: result.code
|
code: result.code,
|
||||||
|
filename: filename,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue