Add support for JSON files to `js_file` rule

Reviewed By: cpojer

Differential Revision: D3981152

fbshipit-source-id: 94b5018a1afa27c635ddff5e26c1ff2bed09baf9
This commit is contained in:
David Aurelio 2016-10-13 11:04:59 -07:00 committed by Facebook Github Bot
parent dc0f7875c8
commit 506604c5bb
1 changed files with 111 additions and 47 deletions

View File

@ -10,24 +10,82 @@
// RUNS UNTRANSFORMED IN A WORKER PROCESS. ONLY USE NODE 4 COMPATIBLE FEATURES! // 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 babel = require('babel-core');
const generate = require('babel-generator').default; const babelGenerate = require('babel-generator').default;
const collectDependencies = require('../JSTransformer/worker/collect-dependencies');
const constantFolding = require('../JSTransformer/worker/constant-folding').plugin;
const docblock = require('../node-haste/DependencyGraph/docblock');
const fs = require('fs');
const inline = require('../JSTransformer/worker/inline').plugin;
const minify = require('../JSTransformer/worker/minify');
const mkdirp = require('mkdirp'); const mkdirp = require('mkdirp');
const path = require('path');
const series = require('async/series'); const series = require('async/series');
const sourceMap = require('source-map'); const sourceMap = require('source-map');
const collectDependencies = require('../JSTransformer/worker/collect-dependencies'); const basename = path.basename;
const constantFolding = require('../JSTransformer/worker/constant-folding').plugin; const dirname = path.dirname;
const inline = require('../JSTransformer/worker/inline').plugin; const defaultVariants = {default: {}};
const minify = require('../JSTransformer/worker/minify'); const moduleFactoryParameters = ['require', 'module', 'global', 'exports'];
const docblock = require('../node-haste/DependencyGraph/docblock'); function transformJSON(infile, options, outfile, callback) {
let json, value;
try {
json = fs.readFileSync(infile, 'utf8');
value = JSON.parse(json);
} catch (readError) {
callback(readError);
return;
}
const filename = options.filename || infile;
const code =
`__d(function(${moduleFactoryParameters.join(', ')}) { module.exports = \n${
json
}\n})`;
const moduleData = {
code,
map: null, // no source map for JSON files!
dependencies: [],
};
const transformed = {};
Object
.keys(options.variants || defaultVariants)
.forEach(key => (transformed[key] = moduleData));
const result = {
file: filename,
code: json,
transformed,
hasteID: value.name,
};
if (basename(filename) === 'package.json') {
result.package = {
name: value.name,
browser: value.browser,
'react-native': value['react-native'],
};
}
try {
writeResult(outfile, result);
} catch (writeError) {
callback(writeError);
return;
}
callback(null);
}
function transformModule(infile, options, outfile, callback) { function transformModule(infile, options, outfile, callback) {
const filename = options.filename || infile;
if (filename.endsWith('.json')) {
return transformJSON(infile, options, outfile, callback);
}
let code, transform; let code, transform;
try { try {
transform = require(options.transform); transform = require(options.transform);
@ -37,9 +95,7 @@ function transformModule(infile, options, outfile, callback) {
return; return;
} }
const filename = options.filename || infile; const variants = options.variants || defaultVariants;
const variants = options.variants || {default: {}};
const tasks = {}; const tasks = {};
Object.keys(variants).forEach(name => { Object.keys(variants).forEach(name => {
tasks[name] = cb => transform({ tasks[name] = cb => transform({
@ -111,26 +167,18 @@ function makeResult(ast, filename, sourceCode) {
const dependencies = collectDependencies(ast); const dependencies = collectDependencies(ast);
const file = wrapModule(ast); const file = wrapModule(ast);
const gen = generate(file, { const gen = generate(file, filename, sourceCode);
comments: false,
compact: true,
filename,
sourceMaps: true,
sourceMapTarget: filename,
sourceFileName: filename,
}, sourceCode);
return {code: gen.code, map: gen.map, dependencies}; return {code: gen.code, map: gen.map, dependencies};
} }
function wrapModule(file) { function wrapModule(file) {
const p = file.program; const p = file.program;
const t = babel.types; const t = babel.types;
const factory = t.functionExpression(t.identifier(''), [ const factory = t.functionExpression(
t.identifier('require'), t.identifier(''),
t.identifier('module'), moduleFactoryParameters.map(makeIdentifier),
t.identifier('global'), t.blockStatement(p.body, p.directives),
t.identifier('exports') );
], t.blockStatement(p.body, p.directives));
const def = t.callExpression(t.identifier('__d'), [factory]); const def = t.callExpression(t.identifier('__d'), [factory]);
return t.file(t.program([t.expressionStatement(def)])); return t.file(t.program([t.expressionStatement(def)]));
} }
@ -142,18 +190,42 @@ function optimize(transformed, file, originalCode, options) {
const dependencies = collectDependencies.forOptimization( const dependencies = collectDependencies.forOptimization(
optimized.ast, transformed.dependencies); optimized.ast, transformed.dependencies);
const gen = generate(optimized.ast, { const inputMap = transformed.map;
const gen = generate(optimized.ast, file, originalCode);
const min = minify(
file,
gen.code,
inputMap && mergeSourceMaps(file, inputMap, gen.map),
);
return {code: min.code, map: inputMap && min.map, dependencies};
}
function optimizeCode(code, map, filename, options) {
const inlineOptions = Object.assign({isWrapped: true}, options);
return babel.transform(code, {
plugins: [[constantFolding], [inline, inlineOptions]],
babelrc: false,
code: false,
filename,
});
}
function generate(ast, filename, sourceCode) {
return babelGenerate(ast, {
comments: false, comments: false,
compact: true, compact: true,
filename: file, filename,
sourceFileName: filename,
sourceMaps: true, sourceMaps: true,
sourceMapTarget: file, sourceMapTarget: filename,
sourceFileName: file, }, sourceCode);
}, originalCode); }
function mergeSourceMaps(file, originalMap, secondMap) {
const merged = new sourceMap.SourceMapGenerator(); const merged = new sourceMap.SourceMapGenerator();
const inputMap = new sourceMap.SourceMapConsumer(transformed.map); const inputMap = new sourceMap.SourceMapConsumer(originalMap);
new sourceMap.SourceMapConsumer(gen.map) new sourceMap.SourceMapConsumer(secondMap)
.eachMapping(mapping => { .eachMapping(mapping => {
const original = inputMap.originalPositionFor({ const original = inputMap.originalPositionFor({
line: mapping.originalLine, line: mapping.originalLine,
@ -170,19 +242,7 @@ function optimize(transformed, file, originalCode, options) {
name: original.name || mapping.name, name: original.name || mapping.name,
}); });
}); });
return merged.toJSON();
const min = minify(file, gen.code, merged.toJSON());
return {code: min.code, map: min.map, dependencies};
}
function optimizeCode(code, map, filename, options) {
const inlineOptions = Object.assign({isWrapped: true}, options);
return babel.transform(code, {
plugins: [[constantFolding], [inline, inlineOptions]],
babelrc: false,
code: false,
filename,
});
} }
function writeResult(outfile, result) { function writeResult(outfile, result) {
@ -190,5 +250,9 @@ function writeResult(outfile, result) {
fs.writeFileSync(outfile, JSON.stringify(result), 'utf8'); fs.writeFileSync(outfile, JSON.stringify(result), 'utf8');
} }
function makeIdentifier(name) {
return babel.types.identifier(name);
}
exports.transformModule = transformModule; exports.transformModule = transformModule;
exports.optimizeModule = optimizeModule; exports.optimizeModule = optimizeModule;