Add `js_file` rule

Reviewed By: matryoshcow

Differential Revision: D3900455

fbshipit-source-id: 61384ade035db978ef3ca258e4c0109bcb450692
This commit is contained in:
David Aurelio 2016-10-05 09:15:41 -07:00 committed by Facebook Github Bot
parent 8622998335
commit aef3d8128f
4 changed files with 191 additions and 14 deletions

View File

@ -138,6 +138,7 @@
"art": "^0.10.0",
"async": "^2.0.1",
"babel-core": "^6.10.4",
"babel-generator": "^6.14.0",
"babel-plugin-external-helpers": "^6.8.0",
"babel-plugin-syntax-trailing-function-commas": "^6.5.0",
"babel-plugin-transform-flow-strip-types": "^6.6.5",

View File

@ -36,6 +36,13 @@ const validateOpts = declareOpts({
type: 'number',
default: DEFAULT_MAX_CALL_TIME,
},
worker: {
type: 'string',
},
methods: {
type: 'array',
default: [],
},
});
const maxConcurrentWorkers = ((cores, override) => {
@ -55,28 +62,42 @@ const maxConcurrentWorkers = ((cores, override) => {
return cores / 2;
})(os.cpus().length, process.env.REACT_NATIVE_MAX_WORKERS);
function makeFarm(worker, methods, timeout) {
return workerFarm(
{
autoStart: true,
maxConcurrentCallsPerWorker: 1,
maxConcurrentWorkers: maxConcurrentWorkers,
maxCallsPerWorker: MAX_CALLS_PER_WORKER,
maxCallTime: timeout,
maxRetries: MAX_RETRIES,
},
worker,
methods,
);
}
class Transformer {
constructor(options) {
const opts = this._opts = validateOpts(options);
const {transformModulePath} = opts;
if (transformModulePath) {
if (opts.worker) {
this._workers =
makeFarm(opts.worker, opts.methods, opts.transformTimeoutInterval);
opts.methods.forEach(name => {
this[name] = this._workers[name];
});
}
else if (transformModulePath) {
this._transformModulePath = require.resolve(transformModulePath);
this._workers = workerFarm(
{
autoStart: true,
maxConcurrentCallsPerWorker: 1,
maxConcurrentWorkers: maxConcurrentWorkers,
maxCallsPerWorker: MAX_CALLS_PER_WORKER,
maxCallTime: opts.transformTimeoutInterval,
maxRetries: MAX_RETRIES,
},
this._workers = makeFarm(
require.resolve('./worker'),
['minify', 'transformAndExtractDependencies']
['minify', 'transformAndExtractDependencies'],
opts.transformTimeoutInterval,
);
this._transform = Promise.denodeify(this._workers.transformAndExtractDependencies);
this.minify = Promise.denodeify(this._workers.minify);
}
@ -111,7 +132,7 @@ class Transformer {
const timeoutErr = new Error(
`TimeoutError: transforming ${fileName} took longer than ` +
`${this._opts.transformTimeoutInterval / 1000} seconds.\n` +
`You can adjust timeout via the 'transformTimeoutInterval' option`
'You can adjust timeout via the \'transformTimeoutInterval\' option'
);
timeoutErr.type = 'TimeoutError';
throw timeoutErr;

View File

@ -0,0 +1,51 @@
/**
* 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';
// RUNS UNTRANSFORMED IN A WORKER PROCESS. ONLY USE NODE 4 COMPATIBLE FEATURES!
const {traverse, types} = require('babel-core');
const isRequireCall = (callee, firstArg) =>
callee.type !== 'Identifier' ||
callee.name !== 'require' ||
!firstArg ||
firstArg.type !== 'StringLiteral';
function collectDependencies(ast, code) {
let nextIndex = 0;
const dependencyIndexes = new Map();
function getIndex(depencyId) {
let index = dependencyIndexes.get(depencyId);
if (index !== undefined) {
return index;
}
index = nextIndex++;
dependencyIndexes.set(depencyId, index);
return index;
}
traverse(ast, {
CallExpression(path) {
const node = path.node;
const arg = node.arguments[0];
if (isRequireCall(node.callee, arg)) {
return;
}
node.arguments[0] = types.numericLiteral(getIndex(arg.value));
}
});
return Array.from(dependencyIndexes.keys());
}
module.exports = collectDependencies;

View File

@ -0,0 +1,104 @@
/**
* 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';
// RUNS UNTRANSFORMED IN A WORKER PROCESS. ONLY USE NODE 4 COMPATIBLE FEATURES!
const fs = require('fs');
const dirname = require('path').dirname;
const babel = require('babel-core');
const generate = require('babel-generator').default;
const series = require('async/series');
const mkdirp = require('mkdirp');
const collectDependencies = require('../JSTransformer/worker/collect-dependencies');
const docblock = require('../node-haste/DependencyGraph/docblock');
function transformModule(infile, options, outfile, callback) {
let code, transform;
try {
transform = require(options.transform);
code = fs.readFileSync(infile, 'utf8');
} catch (readError) {
callback(readError);
return;
}
const filename = options.filename || infile;
const variants = options.variants || {default: {}};
const tasks = {};
Object.keys(variants).forEach(name => {
tasks[name] = cb => transform({
filename,
sourceCode: code,
options: variants[name],
}, cb);
});
series(tasks, (error, transformed) => {
if (error) {
callback(error);
return;
}
Object.keys(transformed).forEach(key => {
transformed[key] = makeResult(transformed[key].ast, filename, code);
});
const annotations = docblock.parseAsObject(docblock.extract(code));
const result = {
file: filename,
code,
transformed,
hasteID: annotations.providesModule || annotations.provide || null,
};
try {
mkdirp.sync(dirname(outfile));
fs.writeFileSync(outfile, JSON.stringify(result), 'utf8');
} catch (writeError) {
callback(writeError);
return;
}
callback(null);
});
}
function makeResult(ast, filename, sourceCode) {
const dependencies = collectDependencies(ast);
const file = wrapModule(ast);
const gen = generate(file, {
comments: false,
compact: true,
filename,
sourceMaps: true,
sourceMapTarget: filename,
sourceFileName: filename,
}, sourceCode);
return {code: gen.code, map: gen.map, dependencies};
}
function wrapModule(file) {
const p = file.program;
const t = babel.types;
const factory = t.functionExpression(t.identifier(''), [
t.identifier('require'),
t.identifier('module'),
t.identifier('global'),
t.identifier('exports')
], t.blockStatement(p.body, p.directives));
const def = t.callExpression(t.identifier('__d'), [factory]);
return t.file(t.program([t.expressionStatement(def)]));
}
exports.transformModule = transformModule;