Apply inline code + constant fold transforms when dev is false

Reviewed By: davidaurelio

Differential Revision: D5940962

fbshipit-source-id: 699d45416bade8471164d6ddf5ef3ade535376b3
This commit is contained in:
Rafael Oleza 2017-10-19 18:40:08 -07:00 committed by Facebook Github Bot
parent 0fdf36d06d
commit 367a5f5db8
5 changed files with 52 additions and 340 deletions

View File

@ -6,6 +6,7 @@
* 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.
*
* @emails oncall+javascript_foundation
* @format
*/
'use strict';

View File

@ -6,6 +6,7 @@
* 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.
*
* @emails oncall+javascript_foundation
* @format
*/
'use strict';
@ -46,26 +47,56 @@ describe('code transformation worker:', () => {
filename,
localPath,
sourceCode,
{transform: transformOptions},
{dev: true, transform: transformOptions},
() => {},
);
expect(transformer.transform).toBeCalledWith({
filename,
localPath,
options: transformOptions,
plugins: [],
src: sourceCode,
});
});
it('calls the transform with two plugins when not in dev mode', () => {
const filename = 'arbitrary/file.js';
const localPath = `local/${filename}`;
const sourceCode = 'arbitrary(code)';
const options = {dev: false, transform: {arbitrary: 'options'}};
transformCode(
transformer,
filename,
localPath,
sourceCode,
options,
() => {},
);
const plugins = transformer.transform.mock.calls[0][0].plugins;
expect(plugins[0]).toEqual([expect.any(Object), options]);
expect(plugins[1]).toEqual([expect.any(Object), options]);
});
it('prefixes JSON files with an assignment to module.exports to make the code valid', function() {
const filename = 'arbitrary/file.json';
const localPath = `local/${filename}`;
const sourceCode = '{"arbitrary":"property"}';
transformCode(transformer, filename, localPath, sourceCode, {}, () => {});
transformCode(
transformer,
filename,
localPath,
sourceCode,
{dev: true},
() => {},
);
expect(transformer.transform).toBeCalledWith({
filename,
localPath,
options: undefined,
plugins: [],
src: `module.exports=${sourceCode}`,
});
});
@ -232,90 +263,4 @@ describe('code transformation worker:', () => {
);
});
});
describe('Minifications:', () => {
let constantFolding, inline, options;
let transformResult, dependencyData;
const filename = 'arbitrary/file.js';
const foldedCode = 'arbitrary(folded(code));';
const foldedMap = {version: 3, sources: ['fold.js']};
beforeEach(() => {
constantFolding = require('../constant-folding').mockReturnValue({
code: foldedCode,
map: foldedMap,
});
extractDependencies = require('../extract-dependencies');
inline = require('../inline');
options = {minify: true, transform: {generateSourceMaps: true}};
dependencyData = {
dependencies: ['a', 'b', 'c'],
dependencyOffsets: [100, 120, 140],
};
extractDependencies.mockImplementation(
code => (code === foldedCode ? dependencyData : {}),
);
transformer.transform.mockImplementation(
(src, fileName, _) => transformResult,
);
});
it('passes the transform result to `inline` for constant inlining', done => {
transformResult = {map: {version: 3}, code: 'arbitrary(code)'};
transformCode(transformer, filename, filename, 'code', options, () => {
expect(inline).toBeCalledWith(filename, transformResult, options);
done();
});
});
it('passes the result obtained from `inline` on to `constant-folding`', done => {
const inlineResult = {map: {version: 3, sources: []}, ast: {}};
inline.mockReturnValue(inlineResult);
transformCode(transformer, filename, filename, 'code', options, () => {
expect(constantFolding).toBeCalledWith(filename, inlineResult);
done();
});
});
it('Uses the code obtained from `constant-folding` to extract dependencies', done => {
transformCode(transformer, filename, filename, 'code', options, () => {
expect(extractDependencies).toBeCalledWith(foldedCode, filename);
done();
});
});
it('uses the dependencies obtained from the optimized result', done => {
transformCode(
transformer,
filename,
filename,
'code',
options,
(_, data) => {
const result = data.result;
expect(result.dependencies).toEqual(dependencyData.dependencies);
done();
},
);
});
it('uses data produced by `constant-folding` for the result', done => {
transformCode(
transformer,
'filename',
'local/filename',
'code',
options,
(_, data) => {
expect(data.result).toEqual(
objectContaining({code: foldedCode, map: foldedMap}),
);
done();
},
);
});
});
});

View File

@ -111,10 +111,15 @@ const transformCode: TransformCode = asyncify(
start_timestamp: process.hrtime(),
};
const plugins = options.dev
? []
: [[inline.plugin, options], [constantFolding.plugin, options]];
const transformed = transformer.transform({
filename,
localPath,
options: options.transform,
plugins,
src: sourceCode,
});
@ -123,16 +128,7 @@ const transformCode: TransformCode = asyncify(
'Missing transform results despite having no error.',
);
var code, map;
if (options.minify) {
({code, map} = constantFolding(
filename,
inline(filename, transformed, options),
));
invariant(code != null, 'Missing code from constant-folding transform.');
} else {
({code, map} = transformed);
}
let {code, map} = transformed;
if (isJson) {
code = code.replace(/^\w+\.exports=/, '');

View File

@ -15,9 +15,7 @@ global.require = _require;
global.__d = define;
var modules = Object.create(null);
if (__DEV__) {
var verboseNamesToModuleIds = Object.create(null);
}
function define(factory, moduleId, dependencyMap) {
if (moduleId in modules) {
@ -30,28 +28,9 @@ function define(factory, moduleId, dependencyMap) {
hasError: false,
isInitialized: false
};
if (__DEV__) {
modules[moduleId].hot = createHotReloadingObject();
var _verboseName = arguments[3];
if (_verboseName) {
modules[moduleId].verboseName = _verboseName;
verboseNamesToModuleIds[_verboseName] = moduleId;
}
}
}
function _require(moduleId) {
if (__DEV__ && typeof moduleId === 'string') {
var _verboseName2 = moduleId;
moduleId = verboseNamesToModuleIds[_verboseName2];
if (moduleId == null) {
throw new Error('Unknown named module: \\\\'' + _verboseName2 + '\\\\'');
} else {
console.warn('Requiring module \\\\'' + _verboseName2 + '\\\\' by name is only supported for ' + 'debugging purposes and will BREAK IN PRODUCTION!');
}
}
var moduleIdReallyIsNumber = moduleId;
var module = modules[moduleIdReallyIsNumber];
return module && module.isInitialized ? module.exports : guardedLoadModule(moduleIdReallyIsNumber, module);
@ -100,10 +79,6 @@ function loadModuleImplementation(moduleId, module) {
throw moduleThrewError(moduleId, module.error);
}
if (__DEV__) {
var Systrace = _require.Systrace;
}
module.isInitialized = true;
var exports = module.exports = {};
var _module = module,
@ -111,25 +86,16 @@ function loadModuleImplementation(moduleId, module) {
dependencyMap = _module.dependencyMap;
try {
if (__DEV__) {
Systrace.beginEvent('JS_require_' + (module.verboseName || moduleId));
}
var _moduleObject = { exports: exports };
if (__DEV__ && module.hot) {
_moduleObject.hot = module.hot;
}
factory(global, _require, _moduleObject, exports, dependencyMap);
if (!__DEV__) {
{
module.factory = undefined;
module.dependencyMap = undefined;
}
if (__DEV__) {
Systrace.endEvent();
}
return module.exports = _moduleObject.exports;
} catch (e) {
module.hasError = true;
@ -142,95 +108,14 @@ function loadModuleImplementation(moduleId, module) {
function unknownModuleError(id) {
var message = 'Requiring unknown module \\"' + id + '\\".';
if (__DEV__) {
message += 'If you are sure the module is there, try restarting Metro Bundler. ' + 'You may also want to run \`yarn\`, or \`npm install\` (depending on your environment).';
}
return Error(message);
}
function moduleThrewError(id, error) {
var displayName = __DEV__ && modules[id] && modules[id].verboseName || id;
var displayName = id;
return Error('Requiring module \\"' + displayName + '\\", which threw an exception: ' + error);
}
if (__DEV__) {
var createHotReloadingObject;
(function () {
_require.Systrace = { beginEvent: function beginEvent() {}, endEvent: function endEvent() {} };
_require.getModules = function () {
return modules;
};
createHotReloadingObject = function createHotReloadingObject() {
var hot = {
acceptCallback: null,
accept: function accept(callback) {
hot.acceptCallback = callback;
}
};
return hot;
};
var acceptAll = function acceptAll(dependentModules, inverseDependencies) {
if (!dependentModules || dependentModules.length === 0) {
return true;
}
var notAccepted = dependentModules.filter(function (module) {
return !accept(module, undefined, inverseDependencies);
});
var parents = [];
for (var i = 0; i < notAccepted.length; i++) {
if (inverseDependencies[notAccepted[i]].length === 0) {
return false;
}
parents.push.apply(parents, babelHelpers.toConsumableArray(inverseDependencies[notAccepted[i]]));
}
return acceptAll(parents, inverseDependencies);
};
var accept = function accept(id, factory, inverseDependencies) {
var mod = modules[id];
if (!mod && factory) {
define(factory, id);
return true;
}
var hot = mod.hot;
if (!hot) {
console.warn('Cannot accept module because Hot Module Replacement ' + 'API was not installed.');
return false;
}
if (factory) {
mod.factory = factory;
}
mod.hasError = false;
mod.isInitialized = false;
_require(id);
if (hot.acceptCallback) {
hot.acceptCallback();
return true;
} else {
if (!inverseDependencies) {
throw new Error('Undefined \`inverseDependencies\`');
}
return acceptAll(inverseDependencies[id], inverseDependencies);
}
};
global.__accept = accept;
})();
}
})(typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this);
(function(global) {
@ -297,9 +182,7 @@ global.require = _require;
global.__d = define;
var modules = Object.create(null);
if (__DEV__) {
var verboseNamesToModuleIds = Object.create(null);
}
function define(factory, moduleId, dependencyMap) {
if (moduleId in modules) {
@ -312,28 +195,9 @@ function define(factory, moduleId, dependencyMap) {
hasError: false,
isInitialized: false
};
if (__DEV__) {
modules[moduleId].hot = createHotReloadingObject();
var _verboseName = arguments[3];
if (_verboseName) {
modules[moduleId].verboseName = _verboseName;
verboseNamesToModuleIds[_verboseName] = moduleId;
}
}
}
function _require(moduleId) {
if (__DEV__ && typeof moduleId === 'string') {
var _verboseName2 = moduleId;
moduleId = verboseNamesToModuleIds[_verboseName2];
if (moduleId == null) {
throw new Error('Unknown named module: \\\\'' + _verboseName2 + '\\\\'');
} else {
console.warn('Requiring module \\\\'' + _verboseName2 + '\\\\' by name is only supported for ' + 'debugging purposes and will BREAK IN PRODUCTION!');
}
}
var moduleIdReallyIsNumber = moduleId;
var module = modules[moduleIdReallyIsNumber];
return module && module.isInitialized ? module.exports : guardedLoadModule(moduleIdReallyIsNumber, module);
@ -382,10 +246,6 @@ function loadModuleImplementation(moduleId, module) {
throw moduleThrewError(moduleId, module.error);
}
if (__DEV__) {
var Systrace = _require.Systrace;
}
module.isInitialized = true;
var exports = module.exports = {};
var _module = module,
@ -393,25 +253,16 @@ function loadModuleImplementation(moduleId, module) {
dependencyMap = _module.dependencyMap;
try {
if (__DEV__) {
Systrace.beginEvent('JS_require_' + (module.verboseName || moduleId));
}
var _moduleObject = { exports: exports };
if (__DEV__ && module.hot) {
_moduleObject.hot = module.hot;
}
factory(global, _require, _moduleObject, exports, dependencyMap);
if (!__DEV__) {
{
module.factory = undefined;
module.dependencyMap = undefined;
}
if (__DEV__) {
Systrace.endEvent();
}
return module.exports = _moduleObject.exports;
} catch (e) {
module.hasError = true;
@ -424,95 +275,14 @@ function loadModuleImplementation(moduleId, module) {
function unknownModuleError(id) {
var message = 'Requiring unknown module \\"' + id + '\\".';
if (__DEV__) {
message += 'If you are sure the module is there, try restarting Metro Bundler. ' + 'You may also want to run \`yarn\`, or \`npm install\` (depending on your environment).';
}
return Error(message);
}
function moduleThrewError(id, error) {
var displayName = __DEV__ && modules[id] && modules[id].verboseName || id;
var displayName = id;
return Error('Requiring module \\"' + displayName + '\\", which threw an exception: ' + error);
}
if (__DEV__) {
var createHotReloadingObject;
(function () {
_require.Systrace = { beginEvent: function beginEvent() {}, endEvent: function endEvent() {} };
_require.getModules = function () {
return modules;
};
createHotReloadingObject = function createHotReloadingObject() {
var hot = {
acceptCallback: null,
accept: function accept(callback) {
hot.acceptCallback = callback;
}
};
return hot;
};
var acceptAll = function acceptAll(dependentModules, inverseDependencies) {
if (!dependentModules || dependentModules.length === 0) {
return true;
}
var notAccepted = dependentModules.filter(function (module) {
return !accept(module, undefined, inverseDependencies);
});
var parents = [];
for (var i = 0; i < notAccepted.length; i++) {
if (inverseDependencies[notAccepted[i]].length === 0) {
return false;
}
parents.push.apply(parents, babelHelpers.toConsumableArray(inverseDependencies[notAccepted[i]]));
}
return acceptAll(parents, inverseDependencies);
};
var accept = function accept(id, factory, inverseDependencies) {
var mod = modules[id];
if (!mod && factory) {
define(factory, id);
return true;
}
var hot = mod.hot;
if (!hot) {
console.warn('Cannot accept module because Hot Module Replacement ' + 'API was not installed.');
return false;
}
if (factory) {
mod.factory = factory;
}
mod.hasError = false;
mod.isInitialized = false;
_require(id);
if (hot.acceptCallback) {
hot.acceptCallback();
return true;
} else {
if (!inverseDependencies) {
throw new Error('Undefined \`inverseDependencies\`');
}
return acceptAll(inverseDependencies[id], inverseDependencies);
}
};
global.__accept = accept;
})();
}
})(typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this);
__d(/* /TestBundle.js */function(global, require, module, exports) {

View File

@ -85,7 +85,7 @@ const getBabelRC = (function() {
* Given a filename and options, build a Babel
* config object with the appropriate plugins.
*/
function buildBabelConfig(filename, options) {
function buildBabelConfig(filename, options, plugins?: BabelPlugins = []) {
const babelRC = getBabelRC(options.projectRoot);
const extraConfig = {
@ -109,7 +109,7 @@ function buildBabelConfig(filename, options) {
extraPlugins.push(inlineRequiresPlugin);
}
config.plugins = extraPlugins.concat(config.plugins);
config.plugins = extraPlugins.concat(config.plugins, plugins);
if (options.dev && options.hot) {
const hmrConfig = makeHMRConfig(options, filename);
@ -126,14 +126,14 @@ type Params = {
src: string,
};
function transform({filename, options, src}: Params) {
function transform({filename, options, src, plugins}: Params) {
options = options || {platform: '', projectRoot: '', inlineRequires: false};
const OLD_BABEL_ENV = process.env.BABEL_ENV;
process.env.BABEL_ENV = options.dev ? 'development' : 'production';
try {
const babelConfig = buildBabelConfig(filename, options);
const babelConfig = buildBabelConfig(filename, options, plugins);
const {ast, ignored} = babel.transform(src, babelConfig);
if (ignored) {