Move the asset JS generation logic to the worker

Summary:
With this, we do all the transformation and wrapping of files inside the workers, which mean faster initial builds (because parallelization and caching), and more legacy code that can be removed (see the following diff).

I've done some tests locally, and while the initial builds are slightly faster, the increase is not super substantial (the big win was in the diff were I moved the wrapping of JS files, in this diff only the assets transformation has speed up).

The most important thing that this diff enables is the possibility of doing the minification of modules also in the worker. This will mean that we can cache minified files and prod builds will get significantly faster - almost as fast as development builds (this will be relevant mainly for the opensource community).

Reviewed By: davidaurelio

Differential Revision: D6439144

fbshipit-source-id: ba2200569a668fcbe68dbfa2b3b60b3db6673326
This commit is contained in:
Rafael Oleza 2017-12-04 16:36:47 -08:00 committed by Facebook Github Bot
parent f3ea76a0b4
commit 8c43848a1e
10 changed files with 105 additions and 44 deletions

View File

@ -29,7 +29,7 @@ const {sep: pathSeparator} = require('path');
const VERSION = require('../../package.json').version;
import type AssetServer from '../AssetServer';
import type Module, {HasteImpl} from '../node-haste/Module';
import type {HasteImpl} from '../node-haste/Module';
import type {MappingsMap, SourceMap} from '../lib/SourceMap';
import type {Options as JSTransformerOptions} from '../JSTransformer/worker';
import type {Reporter} from '../lib/reporting';
@ -215,6 +215,8 @@ class Bundler {
code,
module.isPolyfill(),
transformCodeOptions,
this._opts.assetExts,
this._opts.assetRegistryPath,
),
transformCache: opts.transformCache,
watch: opts.watch,

View File

@ -441,14 +441,7 @@ class DeltaTransformer extends EventEmitter {
nullthrows(dependencyPairs.get(dependency)),
);
if (module.isAsset()) {
wrappedCode = await this._wrapAsset({
code: metadata.code,
dependencyPairs,
name,
path: module.path,
});
} else if (!module.isPolyfill()) {
if (!module.isPolyfill()) {
wrappedCode = this._addDependencyMap({
code: metadata.code,
dependencies,

View File

@ -61,6 +61,8 @@ describe('Transformer', function() {
code,
false,
transformOptions,
[],
'',
);
expect(api.transform).toBeCalledWith(
@ -70,6 +72,8 @@ describe('Transformer', function() {
code,
false,
transformOptions,
[],
'',
);
});

View File

@ -85,6 +85,8 @@ module.exports = class Transformer {
code: string,
isScript: boolean,
options: Options,
assetExts: $ReadOnlyArray<string>,
assetRegistryPath: string,
): Promise<TransformedCode> {
try {
debug('Started ransforming file', filename);
@ -96,6 +98,8 @@ module.exports = class Transformer {
code,
isScript,
options,
assetExts,
assetRegistryPath,
);
debug('Done transforming file', filename);

View File

@ -31,6 +31,8 @@ describe('code transformation worker:', () => {
dev: true,
transform: {},
},
[],
'',
);
expect(result.code).toBe(
@ -55,6 +57,8 @@ describe('code transformation worker:', () => {
dev: true,
transform: {},
},
[],
'',
);
expect(result.code).toBe(
@ -84,6 +88,8 @@ describe('code transformation worker:', () => {
dev: true,
transform: {},
},
[],
'',
);
expect(result.code).toBe(

View File

@ -14,12 +14,15 @@
const JsFileWrapping = require('../../ModuleGraph/worker/JsFileWrapping');
const assetTransformer = require('../../assetTransformer');
const babylon = require('babylon');
const collectDependencies = require('../../ModuleGraph/worker/collect-dependencies');
const constantFolding = require('./constant-folding');
const generate = require('babel-generator').default;
const inline = require('./inline');
const minify = require('./minify');
const path = require('path');
const {compactMapping} = require('../../Bundler/source-map');
@ -166,6 +169,8 @@ function transformCode(
sourceCode: string,
isScript: boolean,
options: Options,
assetExts: $ReadOnlyArray<string>,
assetRegistryPath: string,
): Data | Promise<Data> {
const isJson = filename.endsWith('.json');
@ -187,13 +192,18 @@ function transformCode(
// $FlowFixMe: impossible to type a dynamic require.
const transformer: Transformer<*> = require(transformerPath);
const transformResult = transformer.transform({
const transformerArgs = {
filename,
localPath,
options,
plugins,
src: sourceCode,
});
};
const transformResult = isAsset(filename, assetExts)
? assetTransformer.transform(transformerArgs, assetRegistryPath)
: transformer.transform(transformerArgs);
const postTransformArgs = [
filename,
@ -204,7 +214,7 @@ function transformCode(
transformFileStartLogEntry,
];
return typeof transformResult.then === 'function'
return transformResult instanceof Promise
? transformResult.then(({ast}) => postTransform(...postTransformArgs, ast))
: postTransform(...postTransformArgs, transformResult.ast);
}
@ -227,6 +237,10 @@ function minifyCode(
}
}
function isAsset(filePath: string, assetExts: $ReadOnlyArray<string>): boolean {
return assetExts.indexOf(path.extname(filePath).slice(1)) !== -1;
}
module.exports = {
transform: transformCode,
minify: minifyCode,

View File

@ -0,0 +1,46 @@
/**
* 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.
*
* @flow
* @format
*/
'use strict';
const {getAssetData} = require('./AssetServer/util');
const {generateAssetCodeFileAst} = require('./Bundler/util');
import type {TransformOptions} from './JSTransformer/worker';
type Params = {
localPath: string,
filename: string,
options: TransformOptions,
src: string,
};
async function transform(
{filename, localPath, options, src}: Params,
assetRegistryPath: string,
): Promise<{ast: Ast}> {
options = options || {
platform: '',
projectRoot: '',
inlineRequires: false,
minify: false,
};
const data = await getAssetData(filename, localPath, options.platform);
return {
ast: generateAssetCodeFileAst(assetRegistryPath, data),
};
}
module.exports = {
transform,
};

View File

@ -167,8 +167,18 @@ __d(function (global, require, module, exports, _dependencyMap) {
asset: asset
};
},6,[7]);
__d(/* test.png */function(global, require, module, exports) {module.exports=require(8).registerAsset({\\"__packager_asset\\":true,\\"httpServerLocation\\":\\"/assets\\",\\"width\\":8,\\"height\\":8,\\"scales\\":[1],\\"hash\\":\\"77d45c1f7fa73c0f6c444a830dc42f67\\",\\"name\\":\\"test\\",\\"type\\":\\"png\\"}); // 8 = /AssetRegistry
}, 7);
__d(function (global, require, module, exports, _dependencyMap) {
module.exports = require(_dependencyMap[0]).registerAsset({
\\"__packager_asset\\": true,
\\"httpServerLocation\\": \\"/assets\\",
\\"width\\": 8,
\\"height\\": 8,
\\"scales\\": [1],
\\"hash\\": \\"77d45c1f7fa73c0f6c444a830dc42f67\\",
\\"name\\": \\"test\\",
\\"type\\": \\"png\\"
});
},7,[8]);
__d(function (global, require, module, exports, _dependencyMap) {
'use strict';
},8,[]);
@ -328,8 +338,18 @@ __d(function (global, require, module, exports, _dependencyMap) {
asset: asset
};
},4,[5]);
__d(/* test.png */function(global, require, module, exports) {module.exports=require(6).registerAsset({\\"__packager_asset\\":true,\\"httpServerLocation\\":\\"/assets\\",\\"width\\":8,\\"height\\":8,\\"scales\\":[1],\\"hash\\":\\"77d45c1f7fa73c0f6c444a830dc42f67\\",\\"name\\":\\"test\\",\\"type\\":\\"png\\"}); // 6 = /AssetRegistry
}, 5);
__d(function (global, require, module, exports, _dependencyMap) {
module.exports = require(_dependencyMap[0]).registerAsset({
\\"__packager_asset\\": true,
\\"httpServerLocation\\": \\"/assets\\",
\\"width\\": 8,
\\"height\\": 8,
\\"scales\\": [1],
\\"hash\\": \\"77d45c1f7fa73c0f6c444a830dc42f67\\",
\\"name\\": \\"test\\",
\\"type\\": \\"png\\"
});
},5,[6]);
__d(function (global, require, module, exports, _dependencyMap) {
'use strict';
},6,[]);

View File

@ -14,16 +14,7 @@
const Module = require('./Module');
import type {CachedReadResult, ConstructorArgs, ReadResult} from './Module';
class AssetModule extends Module {
_dependencies: Array<string>;
constructor(args: ConstructorArgs & {dependencies: Array<string>}) {
super(args);
this._dependencies = args.dependencies || [];
}
getPackage() {
return null;
}
@ -32,24 +23,6 @@ class AssetModule extends Module {
return false;
}
readCached(): CachedReadResult {
return {
/** $FlowFixMe: improper OOP design. AssetModule, being different from a
* normal Module, shouldn't inherit it in the first place. */
result: {dependencies: this._dependencies},
outdatedDependencies: [],
};
}
/** $FlowFixMe: improper OOP design. */
readFresh(): Promise<ReadResult> {
return Promise.resolve({dependencies: this._dependencies});
}
hash() {
return `AssetModule : ${this.path}`;
}
isAsset() {
return true;
}

View File

@ -112,7 +112,6 @@ class ModuleCache {
*/
this._moduleCache[filePath] = new AssetModule({
depGraphHelpers: this._depGraphHelpers,
dependencies: this._assetDependencies,
file: filePath,
getTransformCacheKey: this._getTransformCacheKey,
globalTransformCache: this._globalTransformCache,