Bundler.js: @flow

Reviewed By: davidaurelio

Differential Revision: D4036964

fbshipit-source-id: dceb81299d076493d12b2ade13354e927c10ea7c
This commit is contained in:
Jean Lauliac 2016-10-19 04:42:26 -07:00 committed by Facebook Github Bot
parent c803b69ce1
commit 977e7209ac
2 changed files with 72 additions and 21 deletions

View File

@ -5,19 +5,41 @@
* 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 BundleBase = require('./BundleBase');
const ModuleTransport = require('../lib/ModuleTransport');
const _ = require('lodash'); const _ = require('lodash');
const base64VLQ = require('./base64-vlq'); const base64VLQ = require('./base64-vlq');
const BundleBase = require('./BundleBase');
const ModuleTransport = require('../lib/ModuleTransport');
const crypto = require('crypto'); const crypto = require('crypto');
import type {GetSourceOptions, FinalizeOptions} from './BundleBase';
const SOURCEMAPPING_URL = '\n\/\/# sourceMappingURL='; const SOURCEMAPPING_URL = '\n\/\/# sourceMappingURL=';
class Bundle extends BundleBase { class Bundle extends BundleBase {
constructor({sourceMapUrl, dev, minify, ramGroups} = {}) {
_dev: boolean | void;
_inlineSourceMap: string | void;
_minify: boolean | void;
_numRequireCalls: number;
_ramBundle: mixed | void;
_ramGroups: Array<string> | void;
_shouldCombineSourceMaps: boolean;
_sourceMap: boolean;
_sourceMapUrl: string | void;
constructor({sourceMapUrl, dev, minify, ramGroups}: {
sourceMapUrl?: string,
dev?: boolean,
minify?: boolean,
ramGroups?: Array<string>,
} = {}) {
super(); super();
this._sourceMap = false; this._sourceMap = false;
this._sourceMapUrl = sourceMapUrl; this._sourceMapUrl = sourceMapUrl;
@ -30,7 +52,16 @@ class Bundle extends BundleBase {
this._ramBundle = null; // cached RAM Bundle this._ramBundle = null; // cached RAM Bundle
} }
addModule(resolver, resolutionResponse, module, moduleTransport) { addModule(
resolver: {wrapModule: (options: any) => Promise<{code: any, map: any}>},
resolutionResponse: mixed,
module: mixed,
moduleTransport: ModuleTransport,
/* $FlowFixMe: this code is inherently incorrect, because it modifies the
* signature of the base class function "addModule", originally returning
* a number. That means callsites using an instance typed as the base class
* would be broken. This must be refactored. */
): Promise<void> {
const index = super.addModule(moduleTransport); const index = super.addModule(moduleTransport);
return resolver.wrapModule({ return resolver.wrapModule({
resolutionResponse, resolutionResponse,
@ -53,17 +84,21 @@ class Bundle extends BundleBase {
}); });
} }
finalize(options) { finalize(options: FinalizeOptions) {
options = options || {}; options = options || {};
if (options.runMainModule) { if (options.runMainModule) {
/* $FlowFixMe: this is unsound, as nothing enforces runBeforeMainModule
* to be available if `runMainModule` is true. Refactor. */
options.runBeforeMainModule.forEach(this._addRequireCall, this); options.runBeforeMainModule.forEach(this._addRequireCall, this);
/* $FlowFixMe: this is unsound, as nothing enforces the module ID to have
* been set beforehand. */
this._addRequireCall(super.getMainModuleId()); this._addRequireCall(super.getMainModuleId());
} }
super.finalize(options); super.finalize(options);
} }
_addRequireCall(moduleId) { _addRequireCall(moduleId: string) {
const code = `;require(${JSON.stringify(moduleId)});`; const code = `;require(${JSON.stringify(moduleId)});`;
const name = 'require-' + moduleId; const name = 'require-' + moduleId;
super.addModule(new ModuleTransport({ super.addModule(new ModuleTransport({
@ -88,12 +123,12 @@ class Bundle extends BundleBase {
return this._inlineSourceMap; return this._inlineSourceMap;
} }
getSource(options) { getSource(options: GetSourceOptions) {
super.assertFinalized(); super.assertFinalized();
options = options || {}; options = options || {};
let source = super.getSource(); let source = super.getSource(options);
if (options.inlineSourceMap) { if (options.inlineSourceMap) {
source += SOURCEMAPPING_URL + this._getInlineSourceMap(options.dev); source += SOURCEMAPPING_URL + this._getInlineSourceMap(options.dev);
@ -104,7 +139,7 @@ class Bundle extends BundleBase {
return source; return source;
} }
getUnbundle(type) { getUnbundle() {
this.assertFinalized(); this.assertFinalized();
if (!this._ramBundle) { if (!this._ramBundle) {
const modules = this.getModules().slice(); const modules = this.getModules().slice();
@ -122,7 +157,7 @@ class Bundle extends BundleBase {
groups = createGroups(ramGroups || [], lazyModules); groups = createGroups(ramGroups || [], lazyModules);
} }
return groups; return groups;
} },
}; };
} }
@ -166,7 +201,7 @@ class Bundle extends BundleBase {
return result; return result;
} }
getSourceMap(options) { getSourceMap(options: {excludeSource?: boolean}) {
super.assertFinalized(); super.assertFinalized();
if (this._shouldCombineSourceMaps) { if (this._shouldCombineSourceMaps) {
@ -182,12 +217,14 @@ class Bundle extends BundleBase {
names: [], names: [],
mappings: mappings, mappings: mappings,
sourcesContent: options.excludeSource sourcesContent: options.excludeSource
? [] : modules.map(module => module.sourceCode) ? [] : modules.map(module => module.sourceCode),
}; };
return map; return map;
} }
getEtag() { getEtag() {
/* $FlowFixMe: we must pass options, or rename the
* base `getSource` function, as it does not actually need options. */
var eTag = crypto.createHash('md5').update(this.getSource()).digest('hex'); var eTag = crypto.createHash('md5').update(this.getSource()).digest('hex');
return eTag; return eTag;
} }
@ -254,6 +291,7 @@ class Bundle extends BundleBase {
getDebugInfo() { getDebugInfo() {
return [ return [
/* $FlowFixMe: this is unsound as the module ID could be unset. */
'<div><h3>Main Module:</h3> ' + super.getMainModuleId() + '</div>', '<div><h3>Main Module:</h3> ' + super.getMainModuleId() + '</div>',
'<style>', '<style>',
'pre.collapsed {', 'pre.collapsed {',
@ -274,7 +312,7 @@ class Bundle extends BundleBase {
].join('\n'); ].join('\n');
} }
setRamGroups(ramGroups) { setRamGroups(ramGroups: Array<string>) {
this._ramGroups = ramGroups; this._ramGroups = ramGroups;
} }
@ -339,7 +377,7 @@ function * filter(iterator, predicate) {
} }
} }
function * subtree(moduleTransport, moduleTransportsByPath, seen = new Set()) { function * subtree(moduleTransport: ModuleTransport, moduleTransportsByPath, seen = new Set()) {
seen.add(moduleTransport.id); seen.add(moduleTransport.id);
for (const [, {path}] of moduleTransport.meta.dependencyPairs || []) { for (const [, {path}] of moduleTransport.meta.dependencyPairs || []) {
const dependency = moduleTransportsByPath.get(path); const dependency = moduleTransportsByPath.get(path);
@ -361,7 +399,7 @@ class ArrayMap extends Map {
} }
} }
function createGroups(ramGroups, lazyModules) { function createGroups(ramGroups: Array<string>, lazyModules) {
// build two maps that allow to lookup module data // build two maps that allow to lookup module data
// by path or (numeric) module id; // by path or (numeric) module id;
const byPath = new Map(); const byPath = new Map();
@ -400,12 +438,14 @@ function createGroups(ramGroups, lazyModules) {
const doubles = filter(all, ([, parents]) => parents.length > 1); const doubles = filter(all, ([, parents]) => parents.length > 1);
for (const [moduleId, parents] of doubles) { for (const [moduleId, parents] of doubles) {
// remove them from their groups // remove them from their groups
/* $FlowFixMe: this assumes the element exists. */
parents.forEach(p => result.get(p).delete(moduleId)); parents.forEach(p => result.get(p).delete(moduleId));
// print a warning for each removed module // print a warning for each removed module
const parentNames = parents.map(byId.get, byId); const parentNames = parents.map(byId.get, byId);
const lastName = parentNames.pop(); const lastName = parentNames.pop();
console.warn( console.warn(
/* $FlowFixMe: this assumes the element exists. */
`Module ${byId.get(moduleId)} belongs to groups ${ `Module ${byId.get(moduleId)} belongs to groups ${
parentNames.join(', ')}, and ${lastName parentNames.join(', ')}, and ${lastName
}. Removing it from all groups.` }. Removing it from all groups.`

View File

@ -12,11 +12,22 @@
const ModuleTransport = require('../lib/ModuleTransport'); const ModuleTransport = require('../lib/ModuleTransport');
export type FinalizeOptions = {
allowUpdates?: boolean,
runBeforeMainModule?: Array<mixed>,
runMainModule?: boolean,
};
export type GetSourceOptions = {
inlineSourceMap: boolean,
dev: boolean,
};
class BundleBase { class BundleBase {
_assets: Array<mixed>; _assets: Array<mixed>;
_finalized: boolean; _finalized: boolean;
_mainModuleId: mixed | void; _mainModuleId: string | void;
_modules: Array<ModuleTransport>; _modules: Array<ModuleTransport>;
_source: ?string; _source: ?string;
@ -35,7 +46,7 @@ class BundleBase {
return this._mainModuleId; return this._mainModuleId;
} }
setMainModuleId(moduleId: mixed) { setMainModuleId(moduleId: string) {
this._mainModuleId = moduleId; this._mainModuleId = moduleId;
} }
@ -67,7 +78,7 @@ class BundleBase {
this._assets.push(asset); this._assets.push(asset);
} }
finalize(options: {allowUpdates?: boolean}) { finalize(options: FinalizeOptions) {
if (!options.allowUpdates) { if (!options.allowUpdates) {
Object.freeze(this._modules); Object.freeze(this._modules);
Object.freeze(this._assets); Object.freeze(this._assets);
@ -76,7 +87,7 @@ class BundleBase {
this._finalized = true; this._finalized = true;
} }
getSource() { getSource(options: GetSourceOptions) {
this.assertFinalized(); this.assertFinalized();
if (this._source) { if (this._source) {
@ -97,9 +108,9 @@ class BundleBase {
} }
} }
setRamGroups() {} setRamGroups(ramGroups: Array<string>) {}
toJSON() { toJSON(): mixed {
return { return {
modules: this._modules, modules: this._modules,
assets: this._assets, assets: this._assets,