mirror of
https://github.com/status-im/metro.git
synced 2025-02-10 18:17:12 +00:00
Remove old Bundler/* logic
Reviewed By: mjesun Differential Revision: D6259048 fbshipit-source-id: b39e4c602a0af9d2ad91b72a699b1369526ac9f2
This commit is contained in:
parent
2eefccc72a
commit
7b93876541
@ -1,456 +0,0 @@
|
||||
/**
|
||||
* 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 BundleBase = require('./BundleBase');
|
||||
const ModuleTransport = require('../lib/ModuleTransport');
|
||||
|
||||
const _ = require('lodash');
|
||||
const crypto = require('crypto');
|
||||
const debug = require('debug')('Metro:Bundle');
|
||||
const invariant = require('fbjs/lib/invariant');
|
||||
|
||||
const {createRamBundleGroups} = require('./util');
|
||||
const {fromRawMappings} = require('./source-map');
|
||||
const {isMappingsMap} = require('../lib/SourceMap');
|
||||
|
||||
import type {IndexMap, MappingsMap, SourceMap} from '../lib/SourceMap';
|
||||
import type {GetSourceOptions, FinalizeOptions} from './BundleBase';
|
||||
|
||||
import type {PostProcessBundleSourcemap} from './index.js';
|
||||
|
||||
export type Unbundle = {
|
||||
startupModules: Array<*>,
|
||||
lazyModules: Array<*>,
|
||||
groups: Map<number, Set<number>>,
|
||||
};
|
||||
|
||||
type SourceMapFormat = 'undetermined' | 'indexed' | 'flattened';
|
||||
|
||||
const SOURCEMAPPING_URL = '\n//# sourceMappingURL=';
|
||||
|
||||
class Bundle extends BundleBase {
|
||||
_dev: boolean | void;
|
||||
_inlineSourceMap: string | void;
|
||||
_minify: boolean | void;
|
||||
_numRequireCalls: number;
|
||||
_ramBundle: Unbundle | null;
|
||||
_ramGroups: ?Array<string>;
|
||||
_sourceMap: string | null;
|
||||
_sourceMapFormat: SourceMapFormat;
|
||||
_sourceMapUrl: ?string;
|
||||
postProcessBundleSourcemap: PostProcessBundleSourcemap;
|
||||
|
||||
constructor(
|
||||
{
|
||||
sourceMapUrl,
|
||||
dev,
|
||||
minify,
|
||||
ramGroups,
|
||||
postProcessBundleSourcemap,
|
||||
}: {
|
||||
sourceMapUrl: ?string,
|
||||
dev?: boolean,
|
||||
minify?: boolean,
|
||||
ramGroups?: Array<string>,
|
||||
postProcessBundleSourcemap: PostProcessBundleSourcemap,
|
||||
} = {},
|
||||
) {
|
||||
super();
|
||||
this._sourceMap = null;
|
||||
this._sourceMapFormat = 'undetermined';
|
||||
this._sourceMapUrl = sourceMapUrl;
|
||||
this._numRequireCalls = 0;
|
||||
this._dev = dev;
|
||||
this._minify = minify;
|
||||
|
||||
this._ramGroups = ramGroups;
|
||||
this._ramBundle = null; // cached RAM Bundle
|
||||
this.postProcessBundleSourcemap = postProcessBundleSourcemap;
|
||||
}
|
||||
|
||||
addModule(
|
||||
/**
|
||||
* $FlowFixMe: this code is inherently incorrect, because it modifies the
|
||||
* signature of the base class function "addModule". That means callsites
|
||||
* using an instance typed as the base class would be broken. This must be
|
||||
* refactored.
|
||||
*/
|
||||
resolver: {
|
||||
wrapModule: (options: any) => {code: any, map: any},
|
||||
minifyModule: (
|
||||
code: any,
|
||||
map: any,
|
||||
path: any,
|
||||
) => Promise<{
|
||||
code: any,
|
||||
map: any,
|
||||
}>,
|
||||
},
|
||||
resolutionResponse: any,
|
||||
module: any,
|
||||
/* $FlowFixMe: erroneous change of signature. */
|
||||
moduleTransport: ModuleTransport,
|
||||
/* $FlowFixMe: erroneous change of signature. */
|
||||
): Promise<void> {
|
||||
const index = super.addModule(moduleTransport);
|
||||
|
||||
const dependencyPairs = resolutionResponse.getResolvedDependencyPairs(
|
||||
module,
|
||||
);
|
||||
|
||||
const dependencyPairsMap = new Map();
|
||||
for (const [relativePath, dependencyModule] of dependencyPairs) {
|
||||
dependencyPairsMap.set(relativePath, dependencyModule.path);
|
||||
}
|
||||
|
||||
return Promise.resolve(
|
||||
resolver.wrapModule({
|
||||
module,
|
||||
getModuleId: resolutionResponse.getModuleId,
|
||||
dependencyPairs: dependencyPairsMap,
|
||||
name: moduleTransport.name,
|
||||
code: moduleTransport.code,
|
||||
map: moduleTransport.map,
|
||||
dependencyOffsets: moduleTransport.meta
|
||||
? moduleTransport.meta.dependencyOffsets
|
||||
: undefined,
|
||||
dev: this._dev,
|
||||
}),
|
||||
)
|
||||
.then(({code, map}) => {
|
||||
return this._minify
|
||||
? resolver.minifyModule({code, map, path: module.path})
|
||||
: {code, map};
|
||||
})
|
||||
.then(({code, map}) => {
|
||||
// If we get a map from the transformer we'll switch to a mode
|
||||
// were we're combining the source maps as opposed to
|
||||
if (map) {
|
||||
let usesRawMappings = isRawMappings(map);
|
||||
|
||||
// Transform the raw mappings into standard source maps so the RAM
|
||||
// bundler for production can build the source maps correctly.
|
||||
if (usesRawMappings) {
|
||||
map = fromRawMappings(module).toMap(undefined, {});
|
||||
usesRawMappings = false;
|
||||
}
|
||||
|
||||
if (this._sourceMapFormat === 'undetermined') {
|
||||
this._sourceMapFormat = usesRawMappings ? 'flattened' : 'indexed';
|
||||
} else if (usesRawMappings && this._sourceMapFormat === 'indexed') {
|
||||
throw new Error(
|
||||
`Got at least one module with a full source map, but ${moduleTransport.sourcePath} has raw mappings`,
|
||||
);
|
||||
} else if (
|
||||
!usesRawMappings &&
|
||||
this._sourceMapFormat === 'flattened'
|
||||
) {
|
||||
throw new Error(
|
||||
`Got at least one module with raw mappings, but ${moduleTransport.sourcePath} has a full source map`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this.replaceModuleAt(
|
||||
index,
|
||||
new ModuleTransport({...moduleTransport, code, map}),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
finalize(options: FinalizeOptions) {
|
||||
options = options || {};
|
||||
if (options.runModule) {
|
||||
/* $FlowFixMe: this is unsound, as nothing enforces runBeforeMainModule
|
||||
* to be available if `runModule` is true. Refactor. */
|
||||
options.runBeforeMainModule.forEach(this._addRequireCall, this);
|
||||
/* $FlowFixMe: this is unsound, as nothing enforces the module ID to have
|
||||
* been set beforehand. */
|
||||
this._addRequireCall(this.getMainModuleId());
|
||||
}
|
||||
|
||||
super.finalize(options);
|
||||
}
|
||||
|
||||
_addRequireCall(moduleId: string) {
|
||||
const code = `;require(${JSON.stringify(moduleId)});`;
|
||||
const name = 'require-' + moduleId;
|
||||
super.addModule(
|
||||
new ModuleTransport({
|
||||
name,
|
||||
id: -this._numRequireCalls - 1,
|
||||
code,
|
||||
virtual: true,
|
||||
sourceCode: code,
|
||||
sourcePath: name + '.js',
|
||||
meta: {preloaded: true},
|
||||
}),
|
||||
);
|
||||
this._numRequireCalls += 1;
|
||||
}
|
||||
|
||||
_getInlineSourceMap(dev: ?boolean) {
|
||||
if (this._inlineSourceMap == null) {
|
||||
const sourceMap = this.getSourceMapString({excludeSource: true, dev});
|
||||
/*eslint-env node*/
|
||||
const encoded = new Buffer(sourceMap).toString('base64');
|
||||
this._inlineSourceMap = 'data:application/json;base64,' + encoded;
|
||||
}
|
||||
return this._inlineSourceMap;
|
||||
}
|
||||
|
||||
getSource(options: GetSourceOptions) {
|
||||
this.assertFinalized();
|
||||
|
||||
options = options || {};
|
||||
|
||||
let source = super.getSource(options);
|
||||
|
||||
if (options.inlineSourceMap) {
|
||||
source += SOURCEMAPPING_URL + this._getInlineSourceMap(options.dev);
|
||||
} else if (this._sourceMapUrl) {
|
||||
source += SOURCEMAPPING_URL + this._sourceMapUrl;
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
getUnbundle(): Unbundle {
|
||||
this.assertFinalized();
|
||||
if (!this._ramBundle) {
|
||||
const modules = this.getModules().slice();
|
||||
|
||||
// separate modules we need to preload from the ones we don't
|
||||
const [startupModules, lazyModules] = partition(modules, shouldPreload);
|
||||
|
||||
const ramGroups = this._ramGroups;
|
||||
let groups;
|
||||
this._ramBundle = {
|
||||
startupModules,
|
||||
lazyModules,
|
||||
get groups() {
|
||||
if (!groups) {
|
||||
groups = createRamBundleGroups(
|
||||
ramGroups || [],
|
||||
lazyModules,
|
||||
subtree,
|
||||
);
|
||||
}
|
||||
return groups;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return this._ramBundle;
|
||||
}
|
||||
|
||||
invalidateSource() {
|
||||
debug('invalidating bundle');
|
||||
super.invalidateSource();
|
||||
this._sourceMap = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine each of the sourcemaps multiple modules have into a single big
|
||||
* one. This works well thanks to a neat trick defined on the sourcemap spec
|
||||
* that makes use of of the `sections` field to combine sourcemaps by adding
|
||||
* an offset. This is supported only by Chrome for now.
|
||||
*/
|
||||
_getCombinedSourceMaps(options: {excludeSource?: boolean}): IndexMap {
|
||||
const result = {
|
||||
version: 3,
|
||||
file: this._getSourceMapFile(),
|
||||
sections: [],
|
||||
};
|
||||
|
||||
let line = 0;
|
||||
this.getModules().forEach(module => {
|
||||
invariant(
|
||||
!Array.isArray(module.map),
|
||||
`Unexpected raw mappings for ${module.sourcePath}`,
|
||||
);
|
||||
let map: SourceMap =
|
||||
module.map == null || module.virtual
|
||||
? generateSourceMapForVirtualModule(module)
|
||||
: module.map;
|
||||
|
||||
if (options.excludeSource && isMappingsMap(map)) {
|
||||
map = {...map, sourcesContent: []};
|
||||
}
|
||||
|
||||
result.sections.push({
|
||||
offset: {line, column: 0},
|
||||
map,
|
||||
});
|
||||
line += module.code.split('\n').length;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
getSourceMap(options: {excludeSource?: boolean}): SourceMap {
|
||||
this.assertFinalized();
|
||||
|
||||
return this._sourceMapFormat === 'indexed'
|
||||
? this._getCombinedSourceMaps(options)
|
||||
: this._fromRawMappings().toMap(undefined, options);
|
||||
}
|
||||
|
||||
getSourceMapString(options: {excludeSource?: boolean}): string {
|
||||
if (this._sourceMapFormat === 'indexed') {
|
||||
return JSON.stringify(this.getSourceMap(options));
|
||||
}
|
||||
|
||||
// The following code is an optimization specific to the development server:
|
||||
// 1. generator.toSource() is faster than JSON.stringify(generator.toMap()).
|
||||
// 2. caching the source map unless there are changes saves time in
|
||||
// development settings.
|
||||
let map = this._sourceMap;
|
||||
if (map == null) {
|
||||
debug('Start building flat source map');
|
||||
map = this._sourceMap = this._fromRawMappings().toString(
|
||||
undefined,
|
||||
options,
|
||||
);
|
||||
debug('End building flat source map');
|
||||
} else {
|
||||
debug('Returning cached source map');
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
getEtag() {
|
||||
var eTag = crypto
|
||||
.createHash('md5')
|
||||
/* $FlowFixMe: we must pass options, or rename the
|
||||
* base `getSource` function, as it does not actually need options. */
|
||||
.update(this.getSource())
|
||||
.digest('hex');
|
||||
return eTag;
|
||||
}
|
||||
|
||||
_getSourceMapFile() {
|
||||
return this._sourceMapUrl
|
||||
? this._sourceMapUrl.replace('.map', '.bundle')
|
||||
: 'bundle.js';
|
||||
}
|
||||
|
||||
getJSModulePaths() {
|
||||
return (
|
||||
this.getModules()
|
||||
// Filter out non-js files. Like images etc.
|
||||
.filter(module => !module.virtual)
|
||||
.map(module => module.sourcePath)
|
||||
);
|
||||
}
|
||||
|
||||
getDebugInfo() {
|
||||
return [
|
||||
/* $FlowFixMe: this is unsound as the module ID could be unset. */
|
||||
'<div><h3>Main Module:</h3> ' + this.getMainModuleId() + '</div>',
|
||||
'<style>',
|
||||
'pre.collapsed {',
|
||||
' height: 10px;',
|
||||
' width: 100px;',
|
||||
' display: block;',
|
||||
' text-overflow: ellipsis;',
|
||||
' overflow: hidden;',
|
||||
' cursor: pointer;',
|
||||
'}',
|
||||
'</style>',
|
||||
'<h3> Module paths and transformed code: </h3>',
|
||||
this.getModules()
|
||||
.map(function(m) {
|
||||
return (
|
||||
'<div> <h4> Path: </h4>' +
|
||||
m.sourcePath +
|
||||
'<br/> <h4> Source: </h4>' +
|
||||
'<code><pre class="collapsed" onclick="this.classList.remove(\'collapsed\')">' +
|
||||
_.escape(m.code) +
|
||||
'</pre></code></div>'
|
||||
);
|
||||
})
|
||||
.join('\n'),
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
setRamGroups(ramGroups: ?Array<string>) {
|
||||
this._ramGroups = ramGroups;
|
||||
}
|
||||
|
||||
_fromRawMappings() {
|
||||
return fromRawMappings(
|
||||
this.getModules().map(module => ({
|
||||
map: Array.isArray(module.map) ? module.map : undefined,
|
||||
path: module.sourcePath,
|
||||
source: module.sourceCode,
|
||||
code: module.code,
|
||||
})),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function generateSourceMapForVirtualModule(module): MappingsMap {
|
||||
// All lines map 1-to-1
|
||||
let mappings = 'AAAA;';
|
||||
|
||||
for (let i = 1; i < module.code.split('\n').length; i++) {
|
||||
mappings += 'AACA;';
|
||||
}
|
||||
|
||||
return {
|
||||
version: 3,
|
||||
sources: [module.sourcePath],
|
||||
names: [],
|
||||
mappings,
|
||||
file: module.sourcePath,
|
||||
sourcesContent: [module.sourceCode],
|
||||
};
|
||||
}
|
||||
|
||||
function shouldPreload({meta}) {
|
||||
return meta && meta.preloaded;
|
||||
}
|
||||
|
||||
function partition(array, predicate) {
|
||||
const included = [];
|
||||
const excluded = [];
|
||||
array.forEach(item => (predicate(item) ? included : excluded).push(item));
|
||||
return [included, excluded];
|
||||
}
|
||||
|
||||
function* subtree(
|
||||
moduleTransport: ModuleTransport,
|
||||
moduleTransportsByPath: Map<string, ModuleTransport>,
|
||||
seen = new Set(),
|
||||
) {
|
||||
seen.add(moduleTransport.id);
|
||||
const {meta} = moduleTransport;
|
||||
invariant(
|
||||
meta != null,
|
||||
'Unexpected module transport without meta information: ' +
|
||||
moduleTransport.sourcePath,
|
||||
);
|
||||
for (const [, {path}] of meta.dependencyPairs || []) {
|
||||
const dependency = moduleTransportsByPath.get(path);
|
||||
if (dependency && !seen.has(dependency.id)) {
|
||||
yield dependency.id;
|
||||
yield* subtree(dependency, moduleTransportsByPath, seen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const isRawMappings = Array.isArray;
|
||||
|
||||
module.exports = Bundle;
|
@ -1,117 +0,0 @@
|
||||
/**
|
||||
* 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 ModuleTransport = require('../lib/ModuleTransport');
|
||||
|
||||
export type FinalizeOptions = {
|
||||
allowUpdates?: boolean,
|
||||
runBeforeMainModule?: Array<string>,
|
||||
runModule?: boolean,
|
||||
};
|
||||
|
||||
export type GetSourceOptions = {
|
||||
inlineSourceMap?: boolean,
|
||||
dev: boolean,
|
||||
};
|
||||
|
||||
class BundleBase {
|
||||
_assets: Array<mixed>;
|
||||
_finalized: boolean;
|
||||
_mainModuleId: number | void;
|
||||
_source: ?string;
|
||||
__modules: Array<ModuleTransport>;
|
||||
|
||||
constructor() {
|
||||
this._finalized = false;
|
||||
this.__modules = [];
|
||||
this._assets = [];
|
||||
this._mainModuleId = undefined;
|
||||
}
|
||||
|
||||
isEmpty() {
|
||||
return this.__modules.length === 0 && this._assets.length === 0;
|
||||
}
|
||||
|
||||
getMainModuleId(): number | void {
|
||||
return this._mainModuleId;
|
||||
}
|
||||
|
||||
setMainModuleId(moduleId: number) {
|
||||
this._mainModuleId = moduleId;
|
||||
}
|
||||
|
||||
addModule(module: ModuleTransport): number {
|
||||
if (!(module instanceof ModuleTransport)) {
|
||||
throw new Error('Expected a ModuleTransport object');
|
||||
}
|
||||
|
||||
return this.__modules.push(module) - 1;
|
||||
}
|
||||
|
||||
replaceModuleAt(index: number, module: ModuleTransport) {
|
||||
if (!(module instanceof ModuleTransport)) {
|
||||
throw new Error('Expeceted a ModuleTransport object');
|
||||
}
|
||||
|
||||
this.__modules[index] = module;
|
||||
}
|
||||
|
||||
getModules() {
|
||||
return this.__modules;
|
||||
}
|
||||
|
||||
getAssets() {
|
||||
return this._assets;
|
||||
}
|
||||
|
||||
addAsset(asset: mixed) {
|
||||
this._assets.push(asset);
|
||||
}
|
||||
|
||||
finalize(options: FinalizeOptions) {
|
||||
if (!options.allowUpdates) {
|
||||
Object.freeze(this.__modules);
|
||||
Object.freeze(this._assets);
|
||||
}
|
||||
|
||||
this._finalized = true;
|
||||
}
|
||||
|
||||
getSource(options: GetSourceOptions) {
|
||||
this.assertFinalized();
|
||||
|
||||
if (this._source) {
|
||||
return this._source;
|
||||
}
|
||||
|
||||
this._source = this.__modules.map(module => module.code).join('\n');
|
||||
return this._source;
|
||||
}
|
||||
|
||||
invalidateSource() {
|
||||
this._source = null;
|
||||
}
|
||||
|
||||
assertFinalized(message?: string) {
|
||||
if (!this._finalized) {
|
||||
throw new Error(
|
||||
message || 'Bundle needs to be finalized before getting any source',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
setRamGroups(ramGroups: Array<string>) {}
|
||||
}
|
||||
|
||||
module.exports = BundleBase;
|
@ -1,96 +0,0 @@
|
||||
/**
|
||||
* 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 BundleBase = require('./BundleBase');
|
||||
const ModuleTransport = require('../lib/ModuleTransport');
|
||||
|
||||
import type Resolver from '../Resolver';
|
||||
import type ResolutionResponse from '../node-haste/DependencyGraph/ResolutionResponse';
|
||||
import type Module from '../node-haste/Module';
|
||||
|
||||
class HMRBundle extends BundleBase {
|
||||
_sourceMappingURLFn: (hmrpath: string) => mixed;
|
||||
_sourceMappingURLs: Array<mixed>;
|
||||
_sourceURLFn: (hmrpath: string) => mixed;
|
||||
_sourceURLs: Array<mixed>;
|
||||
|
||||
constructor({
|
||||
sourceURLFn,
|
||||
sourceMappingURLFn,
|
||||
}: {
|
||||
sourceURLFn: (hmrpath: string) => mixed,
|
||||
sourceMappingURLFn: (hmrpath: string) => mixed,
|
||||
}) {
|
||||
super();
|
||||
this._sourceURLFn = sourceURLFn;
|
||||
this._sourceMappingURLFn = sourceMappingURLFn;
|
||||
this._sourceURLs = [];
|
||||
this._sourceMappingURLs = [];
|
||||
}
|
||||
|
||||
addModule(
|
||||
/* $FlowFixMe: broken OOP design: function signature should be the same */
|
||||
resolver: Resolver,
|
||||
/* $FlowFixMe: broken OOP design: function signature should be the same */
|
||||
response: ResolutionResponse<Module, {}>,
|
||||
/* $FlowFixMe: broken OOP design: function signature should be the same */
|
||||
module: Module,
|
||||
/* $FlowFixMe: broken OOP design: function signature should be the same */
|
||||
moduleTransport: ModuleTransport,
|
||||
) {
|
||||
const dependencyPairs = response.getResolvedDependencyPairs(module);
|
||||
|
||||
const dependencyPairsMap = new Map();
|
||||
for (const [relativePath, module] of dependencyPairs) {
|
||||
dependencyPairsMap.set(relativePath, module.path);
|
||||
}
|
||||
|
||||
const code = resolver.resolveRequires(
|
||||
module,
|
||||
/* $FlowFixMe: `getModuleId` is monkey-patched so may not exist */
|
||||
response.getModuleId,
|
||||
moduleTransport.code,
|
||||
dependencyPairsMap,
|
||||
/* $FlowFixMe: may not exist */
|
||||
moduleTransport.meta.dependencyOffsets,
|
||||
);
|
||||
|
||||
super.addModule(new ModuleTransport({...moduleTransport, code}));
|
||||
this._sourceMappingURLs.push(
|
||||
this._sourceMappingURLFn(moduleTransport.sourcePath),
|
||||
);
|
||||
this._sourceURLs.push(this._sourceURLFn(moduleTransport.sourcePath));
|
||||
// inconsistent with parent class return type
|
||||
return (Promise.resolve(): any);
|
||||
}
|
||||
|
||||
getModulesIdsAndCode(): Array<{id: string, code: string}> {
|
||||
return this.__modules.map(module => {
|
||||
return {
|
||||
id: JSON.stringify(module.id),
|
||||
code: module.code,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
getSourceURLs() {
|
||||
return this._sourceURLs;
|
||||
}
|
||||
|
||||
getSourceMappingURLs() {
|
||||
return this._sourceMappingURLs;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HMRBundle;
|
@ -1,510 +0,0 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @emails oncall+javascript_foundation
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const Bundle = require('../Bundle');
|
||||
const ModuleTransport = require('../../lib/ModuleTransport');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const resolutionResponse = {
|
||||
getModuleId() {},
|
||||
getResolvedDependencyPairs() {
|
||||
return [];
|
||||
},
|
||||
};
|
||||
|
||||
describe('Bundle', () => {
|
||||
var bundle;
|
||||
|
||||
beforeEach(() => {
|
||||
bundle = new Bundle({sourceMapUrl: 'test_url'});
|
||||
bundle.getSourceMap = jest.fn(() => {
|
||||
return 'test-source-map';
|
||||
});
|
||||
});
|
||||
|
||||
describe('source bundle', () => {
|
||||
it('should create a bundle and get the source', () => {
|
||||
return Promise.resolve()
|
||||
.then(() => {
|
||||
return addModule({
|
||||
bundle,
|
||||
code: 'transformed foo;',
|
||||
sourceCode: 'source foo',
|
||||
sourcePath: 'foo path',
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
return addModule({
|
||||
bundle,
|
||||
code: 'transformed bar;',
|
||||
sourceCode: 'source bar',
|
||||
sourcePath: 'bar path',
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
bundle.finalize({});
|
||||
expect(bundle.getSource({dev: true})).toBe(
|
||||
[
|
||||
'transformed foo;',
|
||||
'transformed bar;',
|
||||
'//# sourceMappingURL=test_url',
|
||||
].join('\n'),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should be ok to leave out the source map url', () => {
|
||||
const otherBundle = new Bundle();
|
||||
return Promise.resolve()
|
||||
.then(() => {
|
||||
return addModule({
|
||||
bundle: otherBundle,
|
||||
code: 'transformed foo;',
|
||||
sourceCode: 'source foo',
|
||||
sourcePath: 'foo path',
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
return addModule({
|
||||
bundle: otherBundle,
|
||||
code: 'transformed bar;',
|
||||
sourceCode: 'source bar',
|
||||
sourcePath: 'bar path',
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
otherBundle.finalize({});
|
||||
expect(otherBundle.getSource({dev: true})).toBe(
|
||||
['transformed foo;', 'transformed bar;'].join('\n'),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should create a bundle and add run module code', () => {
|
||||
return Promise.resolve()
|
||||
.then(() => {
|
||||
return addModule({
|
||||
bundle,
|
||||
code: 'transformed foo;',
|
||||
sourceCode: 'source foo',
|
||||
sourcePath: 'foo path',
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
return addModule({
|
||||
bundle,
|
||||
code: 'transformed bar;',
|
||||
sourceCode: 'source bar',
|
||||
sourcePath: 'bar path',
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
bundle.setMainModuleId('foo');
|
||||
bundle.finalize({
|
||||
runBeforeMainModule: ['bar'],
|
||||
runModule: true,
|
||||
});
|
||||
expect(bundle.getSource({dev: true})).toBe(
|
||||
[
|
||||
'transformed foo;',
|
||||
'transformed bar;',
|
||||
';require("bar");',
|
||||
';require("foo");',
|
||||
'//# sourceMappingURL=test_url',
|
||||
].join('\n'),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('inserts modules in a deterministic order, independent of timing of the wrapper process', () => {
|
||||
const moduleTransports = [
|
||||
createModuleTransport({name: 'module1'}),
|
||||
createModuleTransport({name: 'module2'}),
|
||||
createModuleTransport({name: 'module3'}),
|
||||
];
|
||||
|
||||
const resolves = {};
|
||||
const resolver = {
|
||||
wrapModule({name}) {
|
||||
return new Promise(resolve => {
|
||||
resolves[name] = resolve;
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
const promise = Promise.all(
|
||||
moduleTransports.map(m =>
|
||||
bundle.addModule(
|
||||
resolver,
|
||||
resolutionResponse,
|
||||
{isPolyfill: () => false},
|
||||
m,
|
||||
),
|
||||
),
|
||||
).then(() => {
|
||||
expect(bundle.getModules()).toEqual(moduleTransports);
|
||||
});
|
||||
|
||||
resolves.module2({code: ''});
|
||||
resolves.module3({code: ''});
|
||||
resolves.module1({code: ''});
|
||||
|
||||
return promise;
|
||||
});
|
||||
});
|
||||
|
||||
describe('sourcemap bundle', () => {
|
||||
it('should create sourcemap', () => {
|
||||
//TODO: #15357872 add a meaningful test here
|
||||
});
|
||||
|
||||
it('should combine sourcemaps', () => {
|
||||
const otherBundle = new Bundle({sourceMapUrl: 'test_url'});
|
||||
|
||||
return Promise.resolve()
|
||||
.then(() => {
|
||||
return addModule({
|
||||
bundle: otherBundle,
|
||||
code: 'transformed foo;\n',
|
||||
sourceCode: 'source foo',
|
||||
map: {name: 'sourcemap foo'},
|
||||
sourcePath: 'foo path',
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
return addModule({
|
||||
bundle: otherBundle,
|
||||
code: 'transformed bar;\n',
|
||||
sourceCode: 'source bar',
|
||||
map: {name: 'sourcemap bar'},
|
||||
sourcePath: 'bar path',
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
return addModule({
|
||||
bundle: otherBundle,
|
||||
code: 'image module;\nimage module;',
|
||||
virtual: true,
|
||||
sourceCode: 'image module;\nimage module;',
|
||||
sourcePath: 'image.png',
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
otherBundle.setMainModuleId('foo');
|
||||
otherBundle.finalize({
|
||||
runBeforeMainModule: ['InitializeCore'],
|
||||
runModule: true,
|
||||
});
|
||||
|
||||
const sourceMap = otherBundle.getSourceMap({dev: true});
|
||||
expect(sourceMap).toEqual({
|
||||
file: 'test_url',
|
||||
version: 3,
|
||||
sections: [
|
||||
{offset: {line: 0, column: 0}, map: {name: 'sourcemap foo'}},
|
||||
{offset: {line: 2, column: 0}, map: {name: 'sourcemap bar'}},
|
||||
{
|
||||
offset: {
|
||||
column: 0,
|
||||
line: 4,
|
||||
},
|
||||
map: {
|
||||
file: 'image.png',
|
||||
mappings: 'AAAA;AACA;',
|
||||
names: [],
|
||||
sources: ['image.png'],
|
||||
sourcesContent: ['image module;\nimage module;'],
|
||||
version: 3,
|
||||
},
|
||||
},
|
||||
{
|
||||
offset: {
|
||||
column: 0,
|
||||
line: 6,
|
||||
},
|
||||
map: {
|
||||
file: 'require-InitializeCore.js',
|
||||
mappings: 'AAAA;',
|
||||
names: [],
|
||||
sources: ['require-InitializeCore.js'],
|
||||
sourcesContent: [';require("InitializeCore");'],
|
||||
version: 3,
|
||||
},
|
||||
},
|
||||
{
|
||||
offset: {
|
||||
column: 0,
|
||||
line: 7,
|
||||
},
|
||||
map: {
|
||||
file: 'require-foo.js',
|
||||
mappings: 'AAAA;',
|
||||
names: [],
|
||||
sources: ['require-foo.js'],
|
||||
sourcesContent: [';require("foo");'],
|
||||
version: 3,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAssets()', () => {
|
||||
it('should save and return asset objects', () => {
|
||||
var p = new Bundle({sourceMapUrl: 'test_url'});
|
||||
var asset1 = {};
|
||||
var asset2 = {};
|
||||
p.addAsset(asset1);
|
||||
p.addAsset(asset2);
|
||||
p.finalize();
|
||||
expect(p.getAssets()).toEqual([asset1, asset2]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getJSModulePaths()', () => {
|
||||
it('should return module paths', () => {
|
||||
var otherBundle = new Bundle({sourceMapUrl: 'test_url'});
|
||||
return Promise.resolve()
|
||||
.then(() => {
|
||||
return addModule({
|
||||
bundle: otherBundle,
|
||||
code: 'transformed foo;\n',
|
||||
sourceCode: 'source foo',
|
||||
sourcePath: 'foo path',
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
return addModule({
|
||||
bundle: otherBundle,
|
||||
code: 'image module;\nimage module;',
|
||||
virtual: true,
|
||||
sourceCode: 'image module;\nimage module;',
|
||||
sourcePath: 'image.png',
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
expect(otherBundle.getJSModulePaths()).toEqual(['foo path']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getEtag()', function() {
|
||||
it('should return an etag', function() {
|
||||
bundle.finalize({});
|
||||
var eTag = crypto
|
||||
.createHash('md5')
|
||||
.update(bundle.getSource())
|
||||
.digest('hex');
|
||||
expect(bundle.getEtag()).toEqual(eTag);
|
||||
});
|
||||
});
|
||||
|
||||
describe('main module id:', function() {
|
||||
it('can save a main module ID', function() {
|
||||
const id = 'arbitrary module ID';
|
||||
bundle.setMainModuleId(id);
|
||||
expect(bundle.getMainModuleId()).toEqual(id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('random access bundle groups:', () => {
|
||||
let moduleTransports;
|
||||
beforeEach(() => {
|
||||
moduleTransports = [
|
||||
transport('Product1', ['React', 'Relay']),
|
||||
transport('React', ['ReactFoo', 'ReactBar']),
|
||||
transport('ReactFoo', ['invariant']),
|
||||
transport('invariant', []),
|
||||
transport('ReactBar', ['cx']),
|
||||
transport('cx', []),
|
||||
transport('OtherFramework', ['OtherFrameworkFoo', 'OtherFrameworkBar']),
|
||||
transport('OtherFrameworkFoo', ['invariant']),
|
||||
transport('OtherFrameworkBar', ['crc32']),
|
||||
transport('crc32', ['OtherFrameworkBar']),
|
||||
];
|
||||
});
|
||||
|
||||
it('can create a single group', () => {
|
||||
bundle = createBundle([fsLocation('React')]);
|
||||
const {groups} = bundle.getUnbundle();
|
||||
expect(groups).toEqual(
|
||||
new Map([
|
||||
[
|
||||
idFor('React'),
|
||||
new Set(['ReactFoo', 'invariant', 'ReactBar', 'cx'].map(idFor)),
|
||||
],
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it('can create two groups', () => {
|
||||
bundle = createBundle([fsLocation('ReactFoo'), fsLocation('ReactBar')]);
|
||||
const {groups} = bundle.getUnbundle();
|
||||
expect(groups).toEqual(
|
||||
new Map([
|
||||
[idFor('ReactFoo'), new Set([idFor('invariant')])],
|
||||
[idFor('ReactBar'), new Set([idFor('cx')])],
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it('can handle circular dependencies', () => {
|
||||
bundle = createBundle([fsLocation('OtherFramework')]);
|
||||
const {groups} = bundle.getUnbundle();
|
||||
expect(groups).toEqual(
|
||||
new Map([
|
||||
[
|
||||
idFor('OtherFramework'),
|
||||
new Set(
|
||||
[
|
||||
'OtherFrameworkFoo',
|
||||
'invariant',
|
||||
'OtherFrameworkBar',
|
||||
'crc32',
|
||||
].map(idFor),
|
||||
),
|
||||
],
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it('omits modules that are contained by more than one group', () => {
|
||||
bundle = createBundle([
|
||||
fsLocation('React'),
|
||||
fsLocation('OtherFramework'),
|
||||
]);
|
||||
expect(() => {
|
||||
const {groups} = bundle.getUnbundle(); //eslint-disable-line no-unused-vars
|
||||
}).toThrow(
|
||||
new Error(
|
||||
`Module ${fsLocation('invariant')} belongs to groups ${fsLocation(
|
||||
'React',
|
||||
)}` +
|
||||
`, and ${fsLocation(
|
||||
'OtherFramework',
|
||||
)}. Ensure that each module is only part of one group.`,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it('ignores missing dependencies', () => {
|
||||
bundle = createBundle([fsLocation('Product1')]);
|
||||
const {groups} = bundle.getUnbundle();
|
||||
expect(groups).toEqual(
|
||||
new Map([
|
||||
[
|
||||
idFor('Product1'),
|
||||
new Set(
|
||||
['React', 'ReactFoo', 'invariant', 'ReactBar', 'cx'].map(idFor),
|
||||
),
|
||||
],
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it('throws for group roots that do not exist', () => {
|
||||
bundle = createBundle([fsLocation('DoesNotExist')]);
|
||||
expect(() => {
|
||||
const {groups} = bundle.getUnbundle(); //eslint-disable-line no-unused-vars
|
||||
}).toThrow(
|
||||
new Error(
|
||||
`Group root ${fsLocation('DoesNotExist')} is not part of the bundle`,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
function idFor(name) {
|
||||
const {map} = idFor;
|
||||
if (!map) {
|
||||
idFor.map = new Map([[name, 0]]);
|
||||
idFor.next = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (map.has(name)) {
|
||||
return map.get(name);
|
||||
}
|
||||
|
||||
const id = idFor.next++;
|
||||
map.set(name, id);
|
||||
return id;
|
||||
}
|
||||
function createBundle(ramGroups, options = {}) {
|
||||
const b = new Bundle(Object.assign(options, {ramGroups}));
|
||||
moduleTransports.forEach(t => addModule({bundle: b, ...t}));
|
||||
b.finalize();
|
||||
return b;
|
||||
}
|
||||
function fsLocation(name) {
|
||||
return `/fs/${name}.js`;
|
||||
}
|
||||
function module(name) {
|
||||
return {path: fsLocation(name)};
|
||||
}
|
||||
function transport(name, deps) {
|
||||
return createModuleTransport({
|
||||
name,
|
||||
id: idFor(name),
|
||||
sourcePath: fsLocation(name),
|
||||
meta: {dependencyPairs: deps.map(d => [d, module(d)])},
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function resolverFor(code, map) {
|
||||
return {
|
||||
wrapModule: () => Promise.resolve({code, map}),
|
||||
};
|
||||
}
|
||||
|
||||
function addModule({
|
||||
bundle,
|
||||
code,
|
||||
sourceCode,
|
||||
sourcePath,
|
||||
map,
|
||||
virtual,
|
||||
polyfill,
|
||||
meta,
|
||||
id = '',
|
||||
}) {
|
||||
return bundle.addModule(
|
||||
resolverFor(code, map),
|
||||
resolutionResponse,
|
||||
{isPolyfill: () => polyfill},
|
||||
createModuleTransport({
|
||||
code,
|
||||
sourceCode,
|
||||
sourcePath,
|
||||
id,
|
||||
map,
|
||||
meta,
|
||||
virtual,
|
||||
polyfill,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
function createModuleTransport(data) {
|
||||
return new ModuleTransport({
|
||||
code: '',
|
||||
sourceCode: '',
|
||||
sourcePath: '',
|
||||
id: 'id' in data ? data.id : '',
|
||||
...data,
|
||||
});
|
||||
}
|
@ -23,8 +23,6 @@ jest
|
||||
.mock('../../node-haste/DependencyGraph')
|
||||
.mock('../../JSTransformer')
|
||||
.mock('../../Resolver')
|
||||
.mock('../Bundle')
|
||||
.mock('../HMRBundle')
|
||||
.mock('../../Logger')
|
||||
.mock('/path/to/transformer.js', () => ({}), {virtual: true});
|
||||
|
||||
@ -36,8 +34,6 @@ var fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
const {any, objectContaining} = expect;
|
||||
|
||||
var commonOptions = {
|
||||
allowBundleUpdates: false,
|
||||
assetExts: defaults.assetExts,
|
||||
@ -53,36 +49,9 @@ var commonOptions = {
|
||||
};
|
||||
|
||||
describe('Bundler', function() {
|
||||
function createModule({
|
||||
path,
|
||||
id,
|
||||
dependencies,
|
||||
isAsset,
|
||||
isJSON,
|
||||
isPolyfill,
|
||||
resolution,
|
||||
}) {
|
||||
return {
|
||||
path,
|
||||
resolution,
|
||||
getDependencies: () => Promise.resolve(dependencies),
|
||||
getName: () => id,
|
||||
isJSON: () => isJSON,
|
||||
isAsset: () => isAsset,
|
||||
isPolyfill: () => isPolyfill,
|
||||
read: async () => ({
|
||||
code: 'arbitrary',
|
||||
source: 'arbitrary',
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
var getDependencies;
|
||||
var getModuleSystemDependencies;
|
||||
var bundler;
|
||||
var assetServer;
|
||||
var modules;
|
||||
var projectRoots;
|
||||
let bundler;
|
||||
let assetServer;
|
||||
let projectRoots;
|
||||
|
||||
beforeEach(function() {
|
||||
os.cpus.mockReturnValue({length: 1});
|
||||
@ -90,19 +59,8 @@ describe('Bundler', function() {
|
||||
// anything to the disk during a unit test!
|
||||
os.tmpDir.mockReturnValue(path.join(__dirname));
|
||||
|
||||
getDependencies = jest.fn();
|
||||
getModuleSystemDependencies = jest.fn();
|
||||
projectRoots = ['/root'];
|
||||
|
||||
Resolver.mockImplementation(function() {
|
||||
return {
|
||||
getDependencies,
|
||||
getModuleSystemDependencies,
|
||||
getModuleForPath(path) {
|
||||
return {path};
|
||||
},
|
||||
};
|
||||
});
|
||||
Resolver.load = jest
|
||||
.fn()
|
||||
.mockImplementation(opts => Promise.resolve(new Resolver(opts)));
|
||||
@ -117,10 +75,6 @@ describe('Bundler', function() {
|
||||
};
|
||||
});
|
||||
|
||||
fs.readFile.mockImplementation(function(file, callback) {
|
||||
callback(null, '{"json":true}');
|
||||
});
|
||||
|
||||
assetServer = {
|
||||
getAssetData: jest.fn(),
|
||||
};
|
||||
@ -131,84 +85,11 @@ describe('Bundler', function() {
|
||||
assetServer,
|
||||
});
|
||||
|
||||
modules = [
|
||||
createModule({id: 'foo', path: '/root/foo.js', dependencies: []}),
|
||||
createModule({id: 'bar', path: '/root/bar.js', dependencies: []}),
|
||||
createModule({
|
||||
id: 'new_image.png',
|
||||
path: '/root/img/new_image.png',
|
||||
isAsset: true,
|
||||
resolution: 2,
|
||||
dependencies: [],
|
||||
}),
|
||||
createModule({
|
||||
id: 'package/file.json',
|
||||
path: '/root/file.json',
|
||||
isJSON: true,
|
||||
dependencies: [],
|
||||
}),
|
||||
];
|
||||
|
||||
getDependencies.mockImplementation((main, options, transformOptions) =>
|
||||
Promise.resolve({
|
||||
mainModuleId: 'foo',
|
||||
dependencies: modules,
|
||||
options: transformOptions,
|
||||
getModuleId: ({path}) => path,
|
||||
getResolvedDependencyPairs: () => [],
|
||||
}),
|
||||
);
|
||||
|
||||
getModuleSystemDependencies.mockImplementation(function() {
|
||||
return [];
|
||||
});
|
||||
|
||||
sizeOf.mockImplementation(function(path, cb) {
|
||||
cb(null, {width: 50, height: 100});
|
||||
});
|
||||
});
|
||||
|
||||
it('gets the list of dependencies from the resolver', function() {
|
||||
const entryFile = '/root/foo.js';
|
||||
return bundler
|
||||
.getDependencies({
|
||||
entryFile,
|
||||
rootEntryFile: entryFile,
|
||||
recursive: true,
|
||||
prependPolyfills: true,
|
||||
})
|
||||
.then(() =>
|
||||
// jest calledWith does not support jasmine.any
|
||||
expect(getDependencies.mock.calls[0].slice(0, -2)).toEqual([
|
||||
'/root/foo.js',
|
||||
{
|
||||
dev: true,
|
||||
platform: undefined,
|
||||
recursive: true,
|
||||
prependPolyfills: true,
|
||||
},
|
||||
{
|
||||
preloadedModules: undefined,
|
||||
ramGroups: undefined,
|
||||
transformer: {
|
||||
dev: true,
|
||||
minify: false,
|
||||
platform: undefined,
|
||||
transform: {
|
||||
enableBabelRCLookup: true,
|
||||
dev: true,
|
||||
generateSourceMaps: false,
|
||||
hot: false,
|
||||
inlineRequires: false,
|
||||
platform: undefined,
|
||||
projectRoot: projectRoots[0],
|
||||
},
|
||||
},
|
||||
},
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it('allows overriding the platforms array', () => {
|
||||
expect(bundler._opts.platforms).toEqual([
|
||||
'ios',
|
||||
@ -225,7 +106,7 @@ describe('Bundler', function() {
|
||||
expect(b._opts.platforms).toEqual(['android', 'vr']);
|
||||
});
|
||||
|
||||
describe('.bundle', () => {
|
||||
it('.generateAssetObjAndCode', async () => {
|
||||
const mockAsset = {
|
||||
__packager_asset: true,
|
||||
fileSystemLocation: '/root/img',
|
||||
@ -243,295 +124,44 @@ describe('Bundler', function() {
|
||||
width: 50,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
assetServer.getAssetData.mockImplementation(() =>
|
||||
Promise.resolve(mockAsset),
|
||||
);
|
||||
});
|
||||
assetServer.getAssetData.mockImplementation(() =>
|
||||
Promise.resolve(mockAsset),
|
||||
);
|
||||
|
||||
it('creates a bundle', function() {
|
||||
return bundler
|
||||
.bundle({
|
||||
entryFile: '/root/foo.js',
|
||||
runBeforeMainModule: [],
|
||||
runModule: true,
|
||||
sourceMapUrl: 'source_map_url',
|
||||
})
|
||||
.then(bundle => {
|
||||
const ithAddedModule = i => bundle.addModule.mock.calls[i][2].path;
|
||||
jest.mock(
|
||||
'mockPlugin1',
|
||||
() => {
|
||||
return asset => {
|
||||
asset.extraReverseHash = asset.hash
|
||||
.split('')
|
||||
.reverse()
|
||||
.join('');
|
||||
return asset;
|
||||
};
|
||||
},
|
||||
{virtual: true},
|
||||
);
|
||||
|
||||
expect(ithAddedModule(0)).toEqual('/root/foo.js');
|
||||
expect(ithAddedModule(1)).toEqual('/root/bar.js');
|
||||
expect(ithAddedModule(2)).toEqual('/root/img/new_image.png');
|
||||
expect(ithAddedModule(3)).toEqual('/root/file.json');
|
||||
|
||||
expect(bundle.finalize.mock.calls[0]).toEqual([
|
||||
{
|
||||
runModule: true,
|
||||
runBeforeMainModule: [],
|
||||
allowUpdates: false,
|
||||
},
|
||||
]);
|
||||
|
||||
expect(bundle.addAsset.mock.calls[0]).toEqual([
|
||||
{
|
||||
__packager_asset: true,
|
||||
fileSystemLocation: '/root/img',
|
||||
httpServerLocation: '/assets/img',
|
||||
width: 50,
|
||||
height: 100,
|
||||
scales: [1, 2, 3],
|
||||
files: [
|
||||
'/root/img/img.png',
|
||||
'/root/img/img@2x.png',
|
||||
'/root/img/img@3x.png',
|
||||
],
|
||||
hash: 'i am a hash',
|
||||
name: 'img',
|
||||
type: 'png',
|
||||
},
|
||||
]);
|
||||
|
||||
// TODO(amasad) This fails with 0 != 5 in OSS
|
||||
//expect(ProgressBar.prototype.tick.mock.calls.length).toEqual(modules.length);
|
||||
});
|
||||
});
|
||||
|
||||
it('passes runBeforeMainModule correctly to finalize', function() {
|
||||
return bundler
|
||||
.bundle({
|
||||
entryFile: '/root/foo.js',
|
||||
runBeforeMainModule: ['/root/bar.js'],
|
||||
runModule: true,
|
||||
sourceMapUrl: 'source_map_url',
|
||||
})
|
||||
.then(bundle => {
|
||||
expect(bundle.finalize.mock.calls[0]).toEqual([
|
||||
{
|
||||
runModule: true,
|
||||
runBeforeMainModule: ['/root/bar.js'],
|
||||
allowUpdates: false,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('ignores runBeforeMainModule when it is not part of the bundle', function() {
|
||||
return bundler
|
||||
.bundle({
|
||||
entryFile: '/root/foo.js',
|
||||
runBeforeMainModule: ['/root/not-valid.js'],
|
||||
runModule: true,
|
||||
sourceMapUrl: 'source_map_url',
|
||||
})
|
||||
.then(bundle => {
|
||||
expect(bundle.finalize.mock.calls[0]).toEqual([
|
||||
{
|
||||
runModule: true,
|
||||
runBeforeMainModule: [],
|
||||
allowUpdates: false,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('loads and runs asset plugins', function() {
|
||||
jest.mock(
|
||||
'mockPlugin1',
|
||||
() => {
|
||||
return asset => {
|
||||
asset.extraReverseHash = asset.hash
|
||||
.split('')
|
||||
.reverse()
|
||||
.join('');
|
||||
return asset;
|
||||
};
|
||||
},
|
||||
{virtual: true},
|
||||
);
|
||||
|
||||
jest.mock(
|
||||
'asyncMockPlugin2',
|
||||
() => {
|
||||
return asset => {
|
||||
expect(asset.extraReverseHash).toBeDefined();
|
||||
return new Promise(resolve => {
|
||||
asset.extraPixelCount = asset.width * asset.height;
|
||||
resolve(asset);
|
||||
});
|
||||
};
|
||||
},
|
||||
{virtual: true},
|
||||
);
|
||||
|
||||
return bundler
|
||||
.bundle({
|
||||
entryFile: '/root/foo.js',
|
||||
runBeforeMainModule: [],
|
||||
runModule: true,
|
||||
sourceMapUrl: 'source_map_url',
|
||||
assetPlugins: ['mockPlugin1', 'asyncMockPlugin2'],
|
||||
})
|
||||
.then(bundle => {
|
||||
expect(bundle.addAsset.mock.calls[0]).toEqual([
|
||||
{
|
||||
__packager_asset: true,
|
||||
fileSystemLocation: '/root/img',
|
||||
httpServerLocation: '/assets/img',
|
||||
width: 50,
|
||||
height: 100,
|
||||
scales: [1, 2, 3],
|
||||
files: [
|
||||
'/root/img/img.png',
|
||||
'/root/img/img@2x.png',
|
||||
'/root/img/img@3x.png',
|
||||
],
|
||||
hash: 'i am a hash',
|
||||
name: 'img',
|
||||
type: 'png',
|
||||
extraReverseHash: 'hsah a ma i',
|
||||
extraPixelCount: 5000,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('calls the module post-processing function', () => {
|
||||
const postProcessModules = jest.fn().mockImplementation((ms, e) => ms);
|
||||
|
||||
const b = new Bundler({
|
||||
...commonOptions,
|
||||
postProcessModules,
|
||||
projectRoots,
|
||||
assetServer,
|
||||
});
|
||||
|
||||
const dev = false;
|
||||
const minify = true;
|
||||
const platform = 'arbitrary';
|
||||
|
||||
const entryFile = '/root/foo.js';
|
||||
return b
|
||||
.bundle({
|
||||
dev,
|
||||
entryFile,
|
||||
minify,
|
||||
platform,
|
||||
runBeforeMainModule: [],
|
||||
runModule: true,
|
||||
sourceMapUrl: 'source_map_url',
|
||||
})
|
||||
.then(() => {
|
||||
expect(postProcessModules).toBeCalledWith(
|
||||
modules.map(x =>
|
||||
objectContaining({
|
||||
name: any(String),
|
||||
id: any(String), // Our mock of getModuleId returns a string
|
||||
code: any(String),
|
||||
sourceCode: any(String),
|
||||
sourcePath: x.path,
|
||||
meta: any(Object),
|
||||
polyfill: !!x.isPolyfill(),
|
||||
}),
|
||||
),
|
||||
entryFile,
|
||||
{dev, minify, platform},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('respects the order of modules returned by the post-processing function', () => {
|
||||
const postProcessModules = jest
|
||||
.fn()
|
||||
.mockImplementation((ms, e) => ms.reverse());
|
||||
|
||||
const b = new Bundler({
|
||||
...commonOptions,
|
||||
postProcessModules,
|
||||
projectRoots,
|
||||
assetServer,
|
||||
});
|
||||
|
||||
const entryFile = '/root/foo.js';
|
||||
return b
|
||||
.bundle({
|
||||
entryFile,
|
||||
runBeforeMainModule: [],
|
||||
runModule: true,
|
||||
sourceMapUrl: 'source_map_url',
|
||||
})
|
||||
.then(bundle => {
|
||||
const ithAddedModule = i => bundle.addModule.mock.calls[i][2].path;
|
||||
|
||||
[
|
||||
'/root/file.json',
|
||||
'/root/img/new_image.png',
|
||||
'/root/bar.js',
|
||||
'/root/foo.js',
|
||||
].forEach((path, ix) => expect(ithAddedModule(ix)).toEqual(path));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('.getOrderedDependencyPaths', () => {
|
||||
beforeEach(() => {
|
||||
assetServer.getAssetData.mockImplementation(function(relPath) {
|
||||
if (relPath === 'img/new_image.png') {
|
||||
return Promise.resolve({
|
||||
scales: [1, 2, 3],
|
||||
files: [
|
||||
'/root/img/new_image.png',
|
||||
'/root/img/new_image@2x.png',
|
||||
'/root/img/new_image@3x.png',
|
||||
],
|
||||
hash: 'i am a hash',
|
||||
name: 'img',
|
||||
type: 'png',
|
||||
jest.mock(
|
||||
'asyncMockPlugin2',
|
||||
() => {
|
||||
return asset => {
|
||||
expect(asset.extraReverseHash).toBeDefined();
|
||||
return new Promise(resolve => {
|
||||
asset.extraPixelCount = asset.width * asset.height;
|
||||
resolve(asset);
|
||||
});
|
||||
} else if (relPath === 'img/new_image2.png') {
|
||||
return Promise.resolve({
|
||||
scales: [1, 2, 3],
|
||||
files: [
|
||||
'/root/img/new_image2.png',
|
||||
'/root/img/new_image2@2x.png',
|
||||
'/root/img/new_image2@3x.png',
|
||||
],
|
||||
hash: 'i am a hash',
|
||||
name: 'img',
|
||||
type: 'png',
|
||||
});
|
||||
}
|
||||
};
|
||||
},
|
||||
{virtual: true},
|
||||
);
|
||||
|
||||
throw new Error('unknown image ' + relPath);
|
||||
});
|
||||
});
|
||||
|
||||
it('should get the concrete list of all dependency files', () => {
|
||||
modules.push(
|
||||
createModule({
|
||||
id: 'new_image2.png',
|
||||
path: '/root/img/new_image2.png',
|
||||
isAsset: true,
|
||||
resolution: 2,
|
||||
dependencies: [],
|
||||
}),
|
||||
);
|
||||
|
||||
return bundler
|
||||
.getOrderedDependencyPaths('/root/foo.js', true)
|
||||
.then(paths =>
|
||||
expect(paths).toEqual([
|
||||
'/root/foo.js',
|
||||
'/root/bar.js',
|
||||
'/root/img/new_image.png',
|
||||
'/root/img/new_image@2x.png',
|
||||
'/root/img/new_image@3x.png',
|
||||
'/root/file.json',
|
||||
'/root/img/new_image2.png',
|
||||
'/root/img/new_image2@2x.png',
|
||||
'/root/img/new_image2@3x.png',
|
||||
]),
|
||||
);
|
||||
});
|
||||
expect(
|
||||
await bundler.generateAssetObjAndCode(
|
||||
{},
|
||||
['mockPlugin1', 'asyncMockPlugin2'],
|
||||
'ios',
|
||||
),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
@ -0,0 +1,38 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Bundler .generateAssetObjAndCode 1`] = `
|
||||
Object {
|
||||
"asset": Object {
|
||||
"__packager_asset": true,
|
||||
"extraPixelCount": 5000,
|
||||
"extraReverseHash": "hsah a ma i",
|
||||
"fileSystemLocation": "/root/img",
|
||||
"files": Array [
|
||||
"/root/img/img.png",
|
||||
"/root/img/img@2x.png",
|
||||
"/root/img/img@3x.png",
|
||||
],
|
||||
"hash": "i am a hash",
|
||||
"height": 100,
|
||||
"httpServerLocation": "/assets/img",
|
||||
"name": "img",
|
||||
"scales": Array [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
],
|
||||
"type": "png",
|
||||
"width": 50,
|
||||
},
|
||||
"code": "module.exports=require(\\"/AssetRegistry.js\\").registerAsset({\\"__packager_asset\\":true,\\"scales\\":[1,2,3],\\"hash\\":\\"i am a hash\\",\\"height\\":100,\\"httpServerLocation\\":\\"/assets/img\\",\\"name\\":\\"img\\",\\"type\\":\\"png\\",\\"width\\":50,\\"extraReverseHash\\":\\"hsah a ma i\\",\\"extraPixelCount\\":5000});",
|
||||
"meta": Object {
|
||||
"dependencies": Array [
|
||||
"/AssetRegistry.js",
|
||||
],
|
||||
"dependencyOffsets": Array [
|
||||
23,
|
||||
],
|
||||
"preloaded": null,
|
||||
},
|
||||
}
|
||||
`;
|
@ -15,16 +15,12 @@
|
||||
const assert = require('assert');
|
||||
const crypto = require('crypto');
|
||||
const debug = require('debug')('Metro:Bundler');
|
||||
const emptyFunction = require('fbjs/lib/emptyFunction');
|
||||
const fs = require('fs');
|
||||
const Transformer = require('../JSTransformer');
|
||||
const Resolver = require('../Resolver');
|
||||
const Bundle = require('./Bundle');
|
||||
const HMRBundle = require('./HMRBundle');
|
||||
const ModuleTransport = require('../lib/ModuleTransport');
|
||||
const path = require('path');
|
||||
const defaults = require('../defaults');
|
||||
const toLocalPath = require('../node-haste/lib/toLocalPath');
|
||||
const createModuleIdFactory = require('../lib/createModuleIdFactory');
|
||||
|
||||
const {generateAssetTransformResult} = require('./util');
|
||||
@ -35,7 +31,6 @@ const VERSION = require('../../package.json').version;
|
||||
|
||||
import type AssetServer from '../AssetServer';
|
||||
import type Module, {HasteImpl} from '../node-haste/Module';
|
||||
import type ResolutionResponse from '../node-haste/DependencyGraph/ResolutionResponse';
|
||||
import type {MappingsMap, SourceMap} from '../lib/SourceMap';
|
||||
import type {Options as JSTransformerOptions} from '../JSTransformer/worker';
|
||||
import type {Reporter} from '../lib/reporting';
|
||||
@ -85,12 +80,6 @@ export type ExtendedAssetDescriptor = AssetDescriptor & {
|
||||
+fileSystemLocation: string,
|
||||
};
|
||||
|
||||
const {
|
||||
createActionStartEntry,
|
||||
createActionEndEntry,
|
||||
log,
|
||||
} = require('../Logger');
|
||||
|
||||
export type PostProcessModulesOptions = {|
|
||||
dev: boolean,
|
||||
minify: boolean,
|
||||
@ -115,7 +104,6 @@ export type PostProcessBundleSourcemap = ({
|
||||
}) => {code: Buffer | string, map: SourceMap | string};
|
||||
|
||||
type Options = {|
|
||||
+allowBundleUpdates: boolean,
|
||||
+assetExts: Array<string>,
|
||||
+assetRegistryPath: string,
|
||||
+assetServer: AssetServer,
|
||||
@ -144,8 +132,6 @@ type Options = {|
|
||||
+workerPath: ?string,
|
||||
|};
|
||||
|
||||
const {hasOwnProperty} = Object;
|
||||
|
||||
class Bundler {
|
||||
_opts: Options;
|
||||
_getModuleId: ({path: string}) => number;
|
||||
@ -265,542 +251,34 @@ class Bundler {
|
||||
);
|
||||
}
|
||||
|
||||
bundle(options: {
|
||||
dev: boolean,
|
||||
minify: boolean,
|
||||
unbundle: boolean,
|
||||
sourceMapUrl: ?string,
|
||||
}): Promise<Bundle> {
|
||||
const {dev, minify, unbundle} = options;
|
||||
const postProcessBundleSourcemap = this._opts.postProcessBundleSourcemap;
|
||||
return this._resolverPromise
|
||||
.then(resolver => resolver.getModuleSystemDependencies({dev, unbundle}))
|
||||
.then(moduleSystemDeps =>
|
||||
this._bundle({
|
||||
...options,
|
||||
bundle: new Bundle({
|
||||
dev,
|
||||
minify,
|
||||
sourceMapUrl: options.sourceMapUrl,
|
||||
postProcessBundleSourcemap,
|
||||
}),
|
||||
moduleSystemDeps,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
_sourceHMRURL(platform: ?string, hmrpath: string) {
|
||||
return this._hmrURL('', platform, 'bundle', hmrpath);
|
||||
}
|
||||
|
||||
_sourceMappingHMRURL(platform: ?string, hmrpath: string) {
|
||||
// Chrome expects `sourceURL` when eval'ing code
|
||||
return this._hmrURL('//# sourceURL=', platform, 'map', hmrpath);
|
||||
}
|
||||
|
||||
_hmrURL(
|
||||
prefix: string,
|
||||
platform: ?string,
|
||||
extensionOverride: string,
|
||||
filePath: string,
|
||||
) {
|
||||
const matchingRoot = this._projectRoots.find(root =>
|
||||
filePath.startsWith(root),
|
||||
);
|
||||
|
||||
if (!matchingRoot) {
|
||||
throw new Error('No matching project root for ' + filePath);
|
||||
}
|
||||
|
||||
// Replaces '\' with '/' for Windows paths.
|
||||
if (pathSeparator === '\\') {
|
||||
filePath = filePath.replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
const extensionStart = filePath.lastIndexOf('.');
|
||||
const resource = filePath.substring(
|
||||
matchingRoot.length,
|
||||
extensionStart !== -1 ? extensionStart : undefined,
|
||||
);
|
||||
|
||||
return (
|
||||
prefix +
|
||||
resource +
|
||||
'.' +
|
||||
extensionOverride +
|
||||
'?' +
|
||||
'platform=' +
|
||||
(platform || '') +
|
||||
'&runModule=false&entryModuleOnly=true'
|
||||
);
|
||||
}
|
||||
|
||||
hmrBundle(
|
||||
options: {platform: ?string},
|
||||
host: string,
|
||||
port: number,
|
||||
): Promise<HMRBundle> {
|
||||
return this._bundle({
|
||||
...options,
|
||||
bundle: new HMRBundle({
|
||||
sourceURLFn: this._sourceHMRURL.bind(this, options.platform),
|
||||
sourceMappingURLFn: this._sourceMappingHMRURL.bind(
|
||||
this,
|
||||
options.platform,
|
||||
),
|
||||
}),
|
||||
hot: true,
|
||||
dev: true,
|
||||
});
|
||||
}
|
||||
|
||||
_bundle<T: Bundle | HMRBundle>({
|
||||
assetPlugins,
|
||||
bundle,
|
||||
dev,
|
||||
entryFile,
|
||||
entryModuleOnly,
|
||||
generateSourceMaps,
|
||||
hot,
|
||||
isolateModuleIDs,
|
||||
minify,
|
||||
moduleSystemDeps = [],
|
||||
onProgress,
|
||||
platform,
|
||||
resolutionResponse,
|
||||
runBeforeMainModule,
|
||||
runModule,
|
||||
unbundle,
|
||||
}: {
|
||||
assetPlugins?: Array<string>,
|
||||
bundle: T,
|
||||
dev: boolean,
|
||||
entryFile?: string,
|
||||
entryModuleOnly?: boolean,
|
||||
generateSourceMaps?: boolean,
|
||||
hot?: boolean,
|
||||
isolateModuleIDs?: boolean,
|
||||
minify?: boolean,
|
||||
moduleSystemDeps?: Array<Module>,
|
||||
onProgress?: () => void,
|
||||
platform?: ?string,
|
||||
resolutionResponse?: ResolutionResponse<Module, BundlingOptions>,
|
||||
runBeforeMainModule?: Array<string>,
|
||||
runModule?: boolean,
|
||||
unbundle?: boolean,
|
||||
}): Promise<T> {
|
||||
const onResolutionResponse = (
|
||||
response: ResolutionResponse<Module, BundlingOptions>,
|
||||
) => {
|
||||
/* $FlowFixMe: looks like ResolutionResponse is monkey-patched
|
||||
* with `getModuleId`. */
|
||||
bundle.setMainModuleId(response.getModuleId(getMainModule(response)));
|
||||
if (entryModuleOnly && entryFile) {
|
||||
response.dependencies = response.dependencies.filter(module =>
|
||||
module.path.endsWith(entryFile || ''),
|
||||
);
|
||||
} else {
|
||||
response.dependencies = moduleSystemDeps.concat(response.dependencies);
|
||||
}
|
||||
};
|
||||
const finalizeBundle = ({
|
||||
bundle: finalBundle,
|
||||
transformedModules,
|
||||
response,
|
||||
modulesByPath,
|
||||
}: {
|
||||
bundle: Bundle,
|
||||
transformedModules: Array<{module: Module, transformed: ModuleTransport}>,
|
||||
response: ResolutionResponse<Module, BundlingOptions>,
|
||||
modulesByPath: {[path: string]: Module},
|
||||
}) =>
|
||||
this._resolverPromise
|
||||
.then(resolver =>
|
||||
Promise.all(
|
||||
transformedModules.map(({module, transformed}) =>
|
||||
finalBundle.addModule(resolver, response, module, transformed),
|
||||
),
|
||||
),
|
||||
)
|
||||
.then(() => {
|
||||
return Promise.all(
|
||||
runBeforeMainModule
|
||||
? runBeforeMainModule.map(path => this.getModuleForPath(path))
|
||||
: [],
|
||||
);
|
||||
})
|
||||
.then(runBeforeMainModules => {
|
||||
runBeforeMainModules = runBeforeMainModules
|
||||
.map(module => modulesByPath[module.path])
|
||||
.filter(Boolean);
|
||||
|
||||
finalBundle.finalize({
|
||||
runModule,
|
||||
runBeforeMainModule: runBeforeMainModules.map(module =>
|
||||
/* $FlowFixMe: looks like ResolutionResponse is monkey-patched
|
||||
* with `getModuleId`. */
|
||||
response.getModuleId(module),
|
||||
),
|
||||
allowUpdates: this._opts.allowBundleUpdates,
|
||||
});
|
||||
return finalBundle;
|
||||
});
|
||||
|
||||
return this._buildBundle({
|
||||
entryFile,
|
||||
dev,
|
||||
minify,
|
||||
platform,
|
||||
bundle,
|
||||
hot,
|
||||
unbundle,
|
||||
resolutionResponse,
|
||||
onResolutionResponse,
|
||||
finalizeBundle,
|
||||
isolateModuleIDs,
|
||||
generateSourceMaps,
|
||||
assetPlugins,
|
||||
onProgress,
|
||||
});
|
||||
}
|
||||
|
||||
_buildBundle<T: Bundle | HMRBundle>({
|
||||
entryFile,
|
||||
dev,
|
||||
minify,
|
||||
platform,
|
||||
bundle,
|
||||
hot,
|
||||
unbundle,
|
||||
resolutionResponse,
|
||||
isolateModuleIDs,
|
||||
generateSourceMaps,
|
||||
assetPlugins,
|
||||
onResolutionResponse = emptyFunction,
|
||||
onModuleTransformed = emptyFunction,
|
||||
finalizeBundle = emptyFunction,
|
||||
onProgress = emptyFunction,
|
||||
}: *): Promise<T> {
|
||||
const transformingFilesLogEntry = log(
|
||||
createActionStartEntry({
|
||||
action_name: 'Transforming files',
|
||||
entry_point: entryFile,
|
||||
environment: dev ? 'dev' : 'prod',
|
||||
}),
|
||||
);
|
||||
|
||||
const modulesByPath = Object.create(null);
|
||||
|
||||
if (!resolutionResponse) {
|
||||
resolutionResponse = this.getDependencies({
|
||||
entryFile,
|
||||
rootEntryFile: entryFile,
|
||||
dev,
|
||||
platform,
|
||||
hot,
|
||||
onProgress,
|
||||
minify,
|
||||
isolateModuleIDs,
|
||||
generateSourceMaps: unbundle || minify || generateSourceMaps,
|
||||
prependPolyfills: true,
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.all([
|
||||
this._resolverPromise,
|
||||
resolutionResponse,
|
||||
]).then(([resolver, response]) => {
|
||||
bundle.setRamGroups(response.options.ramGroups);
|
||||
|
||||
log(createActionEndEntry(transformingFilesLogEntry));
|
||||
onResolutionResponse(response);
|
||||
|
||||
// get entry file complete path (`entryFile` is a local path, i.e. relative to roots)
|
||||
let entryFilePath;
|
||||
if (response.dependencies.length > 1) {
|
||||
// skip HMR requests
|
||||
const numModuleSystemDependencies = resolver.getModuleSystemDependencies(
|
||||
{dev, unbundle},
|
||||
).length;
|
||||
|
||||
const dependencyIndex =
|
||||
(response.numPrependedDependencies || 0) +
|
||||
numModuleSystemDependencies;
|
||||
|
||||
if (dependencyIndex in response.dependencies) {
|
||||
entryFilePath = response.dependencies[dependencyIndex].path;
|
||||
}
|
||||
}
|
||||
|
||||
const modulesByTransport: Map<ModuleTransport, Module> = new Map();
|
||||
const toModuleTransport: Module => Promise<ModuleTransport> = module =>
|
||||
this._toModuleTransport({
|
||||
module,
|
||||
bundle,
|
||||
entryFilePath,
|
||||
assetPlugins,
|
||||
options: response.options,
|
||||
/* $FlowFixMe: `getModuleId` is monkey-patched */
|
||||
getModuleId: (response.getModuleId: () => number),
|
||||
dependencyPairs: response.getResolvedDependencyPairs(module),
|
||||
}).then(transformed => {
|
||||
modulesByTransport.set(transformed, module);
|
||||
modulesByPath[module.path] = module;
|
||||
onModuleTransformed({
|
||||
module,
|
||||
response,
|
||||
bundle,
|
||||
transformed,
|
||||
});
|
||||
return transformed;
|
||||
});
|
||||
|
||||
const p = this._opts.postProcessModules;
|
||||
const postProcess = p
|
||||
? modules => p(modules, entryFile, {dev, minify, platform})
|
||||
: null;
|
||||
|
||||
return Promise.all(response.dependencies.map(toModuleTransport))
|
||||
.then(postProcess)
|
||||
.then(moduleTransports => {
|
||||
const transformedModules = moduleTransports.map(transformed => ({
|
||||
module: modulesByTransport.get(transformed),
|
||||
transformed,
|
||||
}));
|
||||
return finalizeBundle({
|
||||
bundle,
|
||||
transformedModules,
|
||||
response,
|
||||
modulesByPath,
|
||||
});
|
||||
})
|
||||
.then(() => bundle);
|
||||
});
|
||||
}
|
||||
|
||||
async getShallowDependencies({
|
||||
entryFile,
|
||||
rootEntryFile,
|
||||
platform,
|
||||
dev = true,
|
||||
minify = !dev,
|
||||
hot = false,
|
||||
generateSourceMaps = false,
|
||||
transformerOptions,
|
||||
}: {
|
||||
entryFile: string,
|
||||
+rootEntryFile: string,
|
||||
platform: ?string,
|
||||
dev?: boolean,
|
||||
minify?: boolean,
|
||||
hot?: boolean,
|
||||
generateSourceMaps?: boolean,
|
||||
transformerOptions?: JSTransformerOptions,
|
||||
}): Promise<Array<string>> {
|
||||
if (!transformerOptions) {
|
||||
transformerOptions = (await this._getLegacyTransformOptions_Do_Not_Use(
|
||||
rootEntryFile,
|
||||
{
|
||||
dev,
|
||||
generateSourceMaps,
|
||||
hot,
|
||||
minify,
|
||||
platform,
|
||||
prependPolyfills: false,
|
||||
},
|
||||
)).transformer;
|
||||
}
|
||||
|
||||
const notNullOptions = transformerOptions;
|
||||
|
||||
return this._resolverPromise.then(resolver =>
|
||||
resolver.getShallowDependencies(entryFile, notNullOptions),
|
||||
);
|
||||
}
|
||||
|
||||
getModuleForPath(entryFile: string): Promise<Module> {
|
||||
return this._resolverPromise.then(resolver =>
|
||||
resolver.getModuleForPath(entryFile),
|
||||
);
|
||||
}
|
||||
|
||||
async getDependencies({
|
||||
entryFile,
|
||||
platform,
|
||||
dev = true,
|
||||
minify = !dev,
|
||||
hot = false,
|
||||
recursive = true,
|
||||
generateSourceMaps = false,
|
||||
isolateModuleIDs = false,
|
||||
rootEntryFile,
|
||||
prependPolyfills,
|
||||
onProgress,
|
||||
}: {
|
||||
entryFile: string,
|
||||
platform: ?string,
|
||||
dev?: boolean,
|
||||
minify?: boolean,
|
||||
hot?: boolean,
|
||||
recursive?: boolean,
|
||||
generateSourceMaps?: boolean,
|
||||
isolateModuleIDs?: boolean,
|
||||
+rootEntryFile: string,
|
||||
+prependPolyfills: boolean,
|
||||
onProgress?: ?(finishedModules: number, totalModules: number) => mixed,
|
||||
}): Promise<ResolutionResponse<Module, BundlingOptions>> {
|
||||
const bundlingOptions: BundlingOptions = await this._getLegacyTransformOptions_Do_Not_Use(
|
||||
rootEntryFile,
|
||||
{
|
||||
dev,
|
||||
platform,
|
||||
hot,
|
||||
generateSourceMaps,
|
||||
minify,
|
||||
prependPolyfills,
|
||||
},
|
||||
);
|
||||
|
||||
const resolver = await this._resolverPromise;
|
||||
const response = await resolver.getDependencies(
|
||||
entryFile,
|
||||
{dev, platform, recursive, prependPolyfills},
|
||||
bundlingOptions,
|
||||
onProgress,
|
||||
isolateModuleIDs ? createModuleIdFactory() : this._getModuleId,
|
||||
);
|
||||
return response;
|
||||
}
|
||||
|
||||
getOrderedDependencyPaths({
|
||||
entryFile,
|
||||
dev,
|
||||
platform,
|
||||
minify,
|
||||
generateSourceMaps,
|
||||
}: {
|
||||
+entryFile: string,
|
||||
+dev: boolean,
|
||||
+platform: string,
|
||||
+minify: boolean,
|
||||
+generateSourceMaps: boolean,
|
||||
}): Promise<Array<string>> {
|
||||
return this.getDependencies({
|
||||
entryFile,
|
||||
rootEntryFile: entryFile,
|
||||
dev,
|
||||
platform,
|
||||
minify,
|
||||
generateSourceMaps,
|
||||
prependPolyfills: true,
|
||||
}).then(({dependencies}) => {
|
||||
const ret = [];
|
||||
const promises = [];
|
||||
/* $FlowFixMe: these are always removed */
|
||||
const placeHolder: string = {};
|
||||
dependencies.forEach(dep => {
|
||||
if (dep.isAsset()) {
|
||||
const localPath = toLocalPath(this._projectRoots, dep.path);
|
||||
promises.push(this._assetServer.getAssetData(localPath, platform));
|
||||
ret.push(placeHolder);
|
||||
} else {
|
||||
ret.push(dep.path);
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all(promises).then(assetsData => {
|
||||
assetsData.forEach(({files}) => {
|
||||
const index = ret.indexOf(placeHolder);
|
||||
ret.splice(index, 1, ...files);
|
||||
});
|
||||
return ret;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_toModuleTransport({
|
||||
module,
|
||||
bundle,
|
||||
entryFilePath,
|
||||
options,
|
||||
getModuleId,
|
||||
dependencyPairs,
|
||||
assetPlugins,
|
||||
}: {
|
||||
module: Module,
|
||||
bundle: Bundle,
|
||||
entryFilePath: string,
|
||||
options: BundlingOptions,
|
||||
getModuleId: (module: Module) => number,
|
||||
dependencyPairs: Array<[string, Module]>,
|
||||
assetPlugins: Array<string>,
|
||||
}): Promise<ModuleTransport> {
|
||||
let moduleTransport;
|
||||
const moduleId = getModuleId(module);
|
||||
const transformOptions = options.transformer;
|
||||
|
||||
if (module.isAsset()) {
|
||||
moduleTransport = this._generateAssetModule(
|
||||
bundle,
|
||||
module,
|
||||
moduleId,
|
||||
assetPlugins,
|
||||
transformOptions.platform,
|
||||
);
|
||||
}
|
||||
|
||||
if (moduleTransport) {
|
||||
return Promise.resolve(moduleTransport);
|
||||
}
|
||||
|
||||
return module
|
||||
.read(transformOptions)
|
||||
.then(({code, dependencies, dependencyOffsets, map, source}) => {
|
||||
const name = module.getName();
|
||||
|
||||
const {preloadedModules} = options;
|
||||
const isPolyfill = module.isPolyfill();
|
||||
const preloaded =
|
||||
module.path === entryFilePath ||
|
||||
isPolyfill ||
|
||||
(preloadedModules &&
|
||||
hasOwnProperty.call(preloadedModules, module.path));
|
||||
|
||||
return new ModuleTransport({
|
||||
name,
|
||||
id: moduleId,
|
||||
code,
|
||||
map,
|
||||
meta: {dependencies, dependencyOffsets, preloaded, dependencyPairs},
|
||||
polyfill: isPolyfill,
|
||||
sourceCode: source,
|
||||
sourcePath: module.path,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
generateAssetObjAndCode(
|
||||
async generateAssetObjAndCode(
|
||||
module: Module,
|
||||
assetPlugins: Array<string>,
|
||||
platform: ?string = null,
|
||||
) {
|
||||
return this._assetServer
|
||||
.getAssetData(module.path, platform)
|
||||
.then(asset => {
|
||||
return this._applyAssetPlugins(assetPlugins, asset);
|
||||
})
|
||||
.then(asset => {
|
||||
const {
|
||||
code,
|
||||
dependencies,
|
||||
dependencyOffsets,
|
||||
} = generateAssetTransformResult(this._opts.assetRegistryPath, asset);
|
||||
return {
|
||||
asset,
|
||||
code,
|
||||
meta: {dependencies, dependencyOffsets, preloaded: null},
|
||||
};
|
||||
});
|
||||
const assetData = await this._assetServer.getAssetData(
|
||||
module.path,
|
||||
platform,
|
||||
);
|
||||
const asset = await this._applyAssetPlugins(assetPlugins, assetData);
|
||||
|
||||
const {
|
||||
code,
|
||||
dependencies,
|
||||
dependencyOffsets,
|
||||
} = generateAssetTransformResult(this._opts.assetRegistryPath, asset);
|
||||
|
||||
return {
|
||||
asset,
|
||||
code,
|
||||
meta: {dependencies, dependencyOffsets, preloaded: null},
|
||||
};
|
||||
}
|
||||
|
||||
_applyAssetPlugins(
|
||||
@ -827,31 +305,6 @@ class Bundler {
|
||||
}
|
||||
}
|
||||
|
||||
_generateAssetModule(
|
||||
bundle: Bundle,
|
||||
module: Module,
|
||||
moduleId: number,
|
||||
assetPlugins: Array<string> = [],
|
||||
platform: ?string = null,
|
||||
) {
|
||||
return this.generateAssetObjAndCode(
|
||||
module,
|
||||
assetPlugins,
|
||||
platform,
|
||||
).then(({asset, code, meta}) => {
|
||||
bundle.addAsset(asset);
|
||||
return new ModuleTransport({
|
||||
name: module.getName(),
|
||||
id: moduleId,
|
||||
code,
|
||||
meta,
|
||||
sourceCode: code,
|
||||
sourcePath: module.path,
|
||||
virtual: true,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the transform options related to a specific entry file, by calling
|
||||
* the config parameter getTransformOptions().
|
||||
@ -920,64 +373,6 @@ class Bundler {
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Old logic to get the transform options, which automatically calculates
|
||||
* the dependencies of the inlineRequires and preloadedModules params.
|
||||
*
|
||||
* TODO: Remove this.
|
||||
*/
|
||||
async _getLegacyTransformOptions_Do_Not_Use(
|
||||
mainModuleName: string,
|
||||
options: {|
|
||||
dev: boolean,
|
||||
generateSourceMaps: boolean,
|
||||
hot: boolean,
|
||||
minify: boolean,
|
||||
platform: ?string,
|
||||
+prependPolyfills: boolean,
|
||||
|},
|
||||
): Promise<BundlingOptions> {
|
||||
const getDependencies = (entryFile: string) =>
|
||||
this.getDependencies({
|
||||
...options,
|
||||
enableBabelRCLookup: this._opts.enableBabelRCLookup,
|
||||
entryFile,
|
||||
projectRoots: this._projectRoots,
|
||||
rootEntryFile: entryFile,
|
||||
prependPolyfills: false,
|
||||
}).then(r => r.dependencies.map(d => d.path));
|
||||
|
||||
const {dev, hot, platform} = options;
|
||||
const extraOptions: ExtraTransformOptions = this._getTransformOptions
|
||||
? await this._getTransformOptions(
|
||||
[mainModuleName],
|
||||
{dev, hot, platform},
|
||||
getDependencies,
|
||||
)
|
||||
: {};
|
||||
|
||||
const {transform = {}} = extraOptions;
|
||||
|
||||
return {
|
||||
transformer: {
|
||||
dev,
|
||||
minify: options.minify,
|
||||
platform,
|
||||
transform: {
|
||||
enableBabelRCLookup: this._opts.enableBabelRCLookup,
|
||||
dev,
|
||||
generateSourceMaps: options.generateSourceMaps,
|
||||
hot,
|
||||
inlineRequires: transform.inlineRequires || false,
|
||||
platform,
|
||||
projectRoot: this._projectRoots[0],
|
||||
},
|
||||
},
|
||||
preloadedModules: extraOptions.preloadedModules,
|
||||
ramGroups: extraOptions.ramGroups,
|
||||
};
|
||||
}
|
||||
|
||||
getResolver(): Promise<Resolver> {
|
||||
return this._resolverPromise;
|
||||
}
|
||||
@ -988,8 +383,4 @@ function verifyRootExists(root) {
|
||||
assert(fs.statSync(root).isDirectory(), 'Root has to be a valid directory');
|
||||
}
|
||||
|
||||
function getMainModule({dependencies, numPrependedDependencies = 0}) {
|
||||
return dependencies[numPrependedDependencies];
|
||||
}
|
||||
|
||||
module.exports = Bundler;
|
||||
|
@ -217,7 +217,6 @@ class Server {
|
||||
|
||||
const bundlerOpts = Object.create(this._opts);
|
||||
bundlerOpts.assetServer = this._assetServer;
|
||||
bundlerOpts.allowBundleUpdates = this._opts.watch;
|
||||
bundlerOpts.globalTransformCache = options.globalTransformCache;
|
||||
bundlerOpts.watch = this._opts.watch;
|
||||
bundlerOpts.reporter = reporter;
|
||||
|
Loading…
x
Reference in New Issue
Block a user