packager: JSTransformer/index.js: @flow

Reviewed By: cpojer

Differential Revision: D4167006

fbshipit-source-id: db0deb2262fe2c53735cb46d9de6e6da1b375180
This commit is contained in:
Jean Lauliac 2016-11-15 04:27:20 -08:00 committed by Facebook Github Bot
parent ece4a18d7c
commit 77d1ca470d
2 changed files with 54 additions and 9 deletions

View File

@ -5,7 +5,10 @@
* 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.
*
* @flow
*/ */
'use strict'; 'use strict';
const Logger = require('../Logger'); const Logger = require('../Logger');
@ -17,6 +20,8 @@ const util = require('util');
const workerFarm = require('worker-farm'); const workerFarm = require('worker-farm');
const debug = require('debug')('ReactNativePackager:JStransformer'); const debug = require('debug')('ReactNativePackager:JStransformer');
import type {Data as TransformData, Options as TransformOptions} from './worker/worker';
// Avoid memory leaks caused in workers. This number seems to be a good enough number // Avoid memory leaks caused in workers. This number seems to be a good enough number
// to avoid any memory leak while not slowing down initial builds. // to avoid any memory leak while not slowing down initial builds.
// TODO(amasad): Once we get bundle splitting, we can drive this down a bit more. // TODO(amasad): Once we get bundle splitting, we can drive this down a bit more.
@ -46,6 +51,13 @@ const validateOpts = declareOpts({
}, },
}); });
type Options = {
transformModulePath?: ?string,
transformTimeoutInterval?: ?number,
worker?: ?string,
methods?: ?Array<string>,
};
const maxConcurrentWorkers = ((cores, override) => { const maxConcurrentWorkers = ((cores, override) => {
if (override) { if (override) {
return Math.min(cores, override); return Math.min(cores, override);
@ -61,7 +73,7 @@ const maxConcurrentWorkers = ((cores, override) => {
return Math.floor(3 / 8 * cores + 3); // between cores *.75 and cores / 2 return Math.floor(3 / 8 * cores + 3); // between cores *.75 and cores / 2
} }
return cores / 2; return cores / 2;
})(os.cpus().length, process.env.REACT_NATIVE_MAX_WORKERS); })(os.cpus().length, parseInt(process.env.REACT_NATIVE_MAX_WORKERS, 10));
function makeFarm(worker, methods, timeout) { function makeFarm(worker, methods, timeout) {
return workerFarm( return workerFarm(
@ -79,7 +91,28 @@ function makeFarm(worker, methods, timeout) {
} }
class Transformer { class Transformer {
constructor(options) {
_opts: {
transformModulePath?: ?string,
transformTimeoutInterval: number,
worker: ?string,
methods: Array<string>,
};
_workers: {[name: string]: mixed};
_transformModulePath: ?string;
_transform: (
transform: string,
filename: string,
sourceCode: string,
options: ?TransformOptions,
) => Promise<TransformData>;
minify: (
filename: string,
code: string,
sourceMap: string,
) => Promise<mixed>;
constructor(options: Options) {
const opts = this._opts = validateOpts(options); const opts = this._opts = validateOpts(options);
const {transformModulePath} = opts; const {transformModulePath} = opts;
@ -88,6 +121,8 @@ class Transformer {
this._workers = this._workers =
makeFarm(opts.worker, opts.methods, opts.transformTimeoutInterval); makeFarm(opts.worker, opts.methods, opts.transformTimeoutInterval);
opts.methods.forEach(name => { opts.methods.forEach(name => {
/* $FlowFixMe: assigning the class object fields directly is
* questionable, because it's prone to conflicts. */
this[name] = this._workers[name]; this[name] = this._workers[name];
}); });
} }
@ -108,12 +143,13 @@ class Transformer {
this._workers && workerFarm.end(this._workers); this._workers && workerFarm.end(this._workers);
} }
transformFile(fileName, code, options) { transformFile(fileName: string, code: string, options: TransformOptions) {
if (!this._transform) { if (!this._transform) {
return Promise.reject(new Error('No transform module')); return Promise.reject(new Error('No transform module'));
} }
debug('transforming file', fileName); debug('transforming file', fileName);
return this return this
/* $FlowFixMe: _transformModulePath may be empty, see constructor */
._transform(this._transformModulePath, fileName, code, options) ._transform(this._transformModulePath, fileName, code, options)
.then(data => { .then(data => {
Logger.log(data.transformFileStartLogEntry); Logger.log(data.transformFileStartLogEntry);
@ -128,13 +164,16 @@ class Transformer {
`${this._opts.transformTimeoutInterval / 1000} seconds.\n` + `${this._opts.transformTimeoutInterval / 1000} seconds.\n` +
'You can adjust timeout via the \'transformTimeoutInterval\' option' 'You can adjust timeout via the \'transformTimeoutInterval\' option'
); );
/* $FlowFixMe: monkey-patch Error */
timeoutErr.type = 'TimeoutError'; timeoutErr.type = 'TimeoutError';
throw timeoutErr; throw timeoutErr;
} else if (error.type === 'ProcessTerminatedError') { } else if (error.type === 'ProcessTerminatedError') {
const uncaughtError = new Error( const uncaughtError = new Error(
'Uncaught error in the transformer worker: ' + 'Uncaught error in the transformer worker: ' +
/* $FlowFixMe: _transformModulePath may be empty, see constructor */
this._opts.transformModulePath this._opts.transformModulePath
); );
/* $FlowFixMe: monkey-patch Error */
uncaughtError.type = 'ProcessTerminatedError'; uncaughtError.type = 'ProcessTerminatedError';
throw uncaughtError; throw uncaughtError;
} }
@ -142,6 +181,8 @@ class Transformer {
throw formatError(error, fileName); throw formatError(error, fileName);
}); });
} }
static TransformError;
} }
module.exports = Transformer; module.exports = Transformer;

View File

@ -16,6 +16,8 @@ const extractDependencies = require('./extract-dependencies');
const inline = require('./inline'); const inline = require('./inline');
const minify = require('./minify'); const minify = require('./minify');
import type {LogEntry} from '../../Logger/Types';
function makeTransformParams(filename, sourceCode, options) { function makeTransformParams(filename, sourceCode, options) {
if (filename.endsWith('.json')) { if (filename.endsWith('.json')) {
sourceCode = 'module.exports=' + sourceCode; sourceCode = 'module.exports=' + sourceCode;
@ -36,15 +38,17 @@ type Transform = (params: {
options: ?{}, options: ?{},
}) => mixed; }) => mixed;
type Options = {transform?: {}}; export type Options = {transform?: {}};
export type Data = {
result: TransformedCode,
transformFileStartLogEntry: LogEntry,
transformFileEndLogEntry: LogEntry,
};
type Callback = ( type Callback = (
error: ?Error, error: ?Error,
data: ?{ data: ?Data,
result: TransformedCode,
transformFileStartLogEntry: {},
transformFileEndLogEntry: {},
},
) => mixed; ) => mixed;
function transformCode( function transformCode(