metro-bundler: moar @format

Reviewed By: mjesun

Differential Revision: D5484885

fbshipit-source-id: f9ae126931f0c9f611ee5b5b96243656e86f4ba4
This commit is contained in:
Jean Lauliac 2017-07-24 22:28:32 -07:00 committed by Facebook Github Bot
parent 9f1cce4e89
commit ce0da03a05
15 changed files with 865 additions and 618 deletions

View File

@ -5,6 +5,8 @@
* 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.
*
* @format
*/ */
'use strict'; 'use strict';
@ -26,7 +28,7 @@ describe('AssetServer', () => {
}); });
fs.__setMockFilesystem({ fs.__setMockFilesystem({
'root': { root: {
imgs: { imgs: {
'b.png': 'b image', 'b.png': 'b image',
'b@2x.png': 'b2 image', 'b@2x.png': 'b2 image',
@ -37,11 +39,7 @@ describe('AssetServer', () => {
return Promise.all([ return Promise.all([
server.get('imgs/b.png'), server.get('imgs/b.png'),
server.get('imgs/b@1x.png'), server.get('imgs/b@1x.png'),
]).then(resp => ]).then(resp => resp.forEach(data => expect(data).toBe('b image')));
resp.forEach(data =>
expect(data).toBe('b image')
)
);
}); });
it('should work for the simple case with platform ext', () => { it('should work for the simple case with platform ext', () => {
@ -51,7 +49,7 @@ describe('AssetServer', () => {
}); });
fs.__setMockFilesystem({ fs.__setMockFilesystem({
'root': { root: {
imgs: { imgs: {
'b.ios.png': 'b ios image', 'b.ios.png': 'b ios image',
'b.android.png': 'b android image', 'b.android.png': 'b android image',
@ -62,25 +60,24 @@ describe('AssetServer', () => {
}); });
return Promise.all([ return Promise.all([
server.get('imgs/b.png', 'ios').then( server
data => expect(data).toBe('b ios image') .get('imgs/b.png', 'ios')
), .then(data => expect(data).toBe('b ios image')),
server.get('imgs/b.png', 'android').then( server
data => expect(data).toBe('b android image') .get('imgs/b.png', 'android')
), .then(data => expect(data).toBe('b android image')),
server.get('imgs/c.png', 'android').then( server
data => expect(data).toBe('c android image') .get('imgs/c.png', 'android')
), .then(data => expect(data).toBe('c android image')),
server.get('imgs/c.png', 'ios').then( server
data => expect(data).toBe('c general image') .get('imgs/c.png', 'ios')
), .then(data => expect(data).toBe('c general image')),
server.get('imgs/c.png').then( server
data => expect(data).toBe('c general image') .get('imgs/c.png')
), .then(data => expect(data).toBe('c general image')),
]); ]);
}); });
it('should work for the simple case with jpg', () => { it('should work for the simple case with jpg', () => {
const server = new AssetServer({ const server = new AssetServer({
projectRoots: ['/root'], projectRoots: ['/root'],
@ -88,7 +85,7 @@ describe('AssetServer', () => {
}); });
fs.__setMockFilesystem({ fs.__setMockFilesystem({
'root': { root: {
imgs: { imgs: {
'b.png': 'png image', 'b.png': 'png image',
'b.jpg': 'jpeg image', 'b.jpg': 'jpeg image',
@ -99,12 +96,7 @@ describe('AssetServer', () => {
return Promise.all([ return Promise.all([
server.get('imgs/b.jpg'), server.get('imgs/b.jpg'),
server.get('imgs/b.png'), server.get('imgs/b.png'),
]).then(data => ]).then(data => expect(data).toEqual(['jpeg image', 'png image']));
expect(data).toEqual([
'jpeg image',
'png image',
])
);
}); });
it('should pick the bigger one', () => { it('should pick the bigger one', () => {
@ -114,7 +106,7 @@ describe('AssetServer', () => {
}); });
fs.__setMockFilesystem({ fs.__setMockFilesystem({
'root': { root: {
imgs: { imgs: {
'b@1x.png': 'b1 image', 'b@1x.png': 'b1 image',
'b@2x.png': 'b2 image', 'b@2x.png': 'b2 image',
@ -124,9 +116,9 @@ describe('AssetServer', () => {
}, },
}); });
return server.get('imgs/b@3x.png').then(data => return server
expect(data).toBe('b4 image') .get('imgs/b@3x.png')
); .then(data => expect(data).toBe('b4 image'));
}); });
it('should pick the bigger one with platform ext', () => { it('should pick the bigger one with platform ext', () => {
@ -136,7 +128,7 @@ describe('AssetServer', () => {
}); });
fs.__setMockFilesystem({ fs.__setMockFilesystem({
'root': { root: {
imgs: { imgs: {
'b@1x.png': 'b1 image', 'b@1x.png': 'b1 image',
'b@2x.png': 'b2 image', 'b@2x.png': 'b2 image',
@ -151,12 +143,10 @@ describe('AssetServer', () => {
}); });
return Promise.all([ return Promise.all([
server.get('imgs/b@3x.png').then(data => server.get('imgs/b@3x.png').then(data => expect(data).toBe('b4 image')),
expect(data).toBe('b4 image') server
), .get('imgs/b@3x.png', 'ios')
server.get('imgs/b@3x.png', 'ios').then(data => .then(data => expect(data).toBe('b4 ios image')),
expect(data).toBe('b4 ios image')
),
]); ]);
}); });
@ -167,23 +157,23 @@ describe('AssetServer', () => {
}); });
fs.__setMockFilesystem({ fs.__setMockFilesystem({
'root': { root: {
imgs: { imgs: {
'b.png': 'b image', 'b.png': 'b image',
}, },
}, },
'root2': { root2: {
'newImages': { newImages: {
'imgs': { imgs: {
'b@1x.png': 'b1 image', 'b@1x.png': 'b1 image',
}, },
}, },
}, },
}); });
return server.get('newImages/imgs/b.png').then(data => return server
expect(data).toBe('b1 image') .get('newImages/imgs/b.png')
); .then(data => expect(data).toBe('b1 image'));
}); });
}); });
@ -195,7 +185,7 @@ describe('AssetServer', () => {
}); });
fs.__setMockFilesystem({ fs.__setMockFilesystem({
'root': { root: {
imgs: { imgs: {
'b@1x.png': 'b1 image', 'b@1x.png': 'b1 image',
'b@2x.png': 'b2 image', 'b@2x.png': 'b2 image',
@ -206,17 +196,19 @@ describe('AssetServer', () => {
}); });
return server.getAssetData('imgs/b.png').then(data => { return server.getAssetData('imgs/b.png').then(data => {
expect(data).toEqual(objectContaining({ expect(data).toEqual(
type: 'png', objectContaining({
name: 'b', type: 'png',
scales: [1, 2, 4, 4.5], name: 'b',
files: [ scales: [1, 2, 4, 4.5],
'/root/imgs/b@1x.png', files: [
'/root/imgs/b@2x.png', '/root/imgs/b@1x.png',
'/root/imgs/b@4x.png', '/root/imgs/b@2x.png',
'/root/imgs/b@4.5x.png', '/root/imgs/b@4x.png',
], '/root/imgs/b@4.5x.png',
})); ],
}),
);
}); });
}); });
@ -227,7 +219,7 @@ describe('AssetServer', () => {
}); });
fs.__setMockFilesystem({ fs.__setMockFilesystem({
'root': { root: {
imgs: { imgs: {
'b@1x.jpg': 'b1 image', 'b@1x.jpg': 'b1 image',
'b@2x.jpg': 'b2 image', 'b@2x.jpg': 'b2 image',
@ -238,17 +230,19 @@ describe('AssetServer', () => {
}); });
return server.getAssetData('imgs/b.jpg').then(data => { return server.getAssetData('imgs/b.jpg').then(data => {
expect(data).toEqual(objectContaining({ expect(data).toEqual(
type: 'jpg', objectContaining({
name: 'b', type: 'jpg',
scales: [1, 2, 4, 4.5], name: 'b',
files: [ scales: [1, 2, 4, 4.5],
'/root/imgs/b@1x.jpg', files: [
'/root/imgs/b@2x.jpg', '/root/imgs/b@1x.jpg',
'/root/imgs/b@4x.jpg', '/root/imgs/b@2x.jpg',
'/root/imgs/b@4.5x.jpg', '/root/imgs/b@4x.jpg',
], '/root/imgs/b@4.5x.jpg',
})); ],
}),
);
}); });
}); });
@ -261,7 +255,7 @@ describe('AssetServer', () => {
}); });
mockFS = { mockFS = {
'root': { root: {
imgs: { imgs: {
'b@1x.jpg': 'b1 image', 'b@1x.jpg': 'b1 image',
'b@2x.jpg': 'b2 image', 'b@2x.jpg': 'b2 image',
@ -280,18 +274,20 @@ describe('AssetServer', () => {
hash.update(mockFS.root.imgs[name]); hash.update(mockFS.root.imgs[name]);
} }
return server.getAssetData('imgs/b.jpg').then(data => return server
expect(data).toEqual(objectContaining({hash: hash.digest('hex')})) .getAssetData('imgs/b.jpg')
); .then(data =>
expect(data).toEqual(objectContaining({hash: hash.digest('hex')})),
);
}); });
it('changes the hash when the passed-in file watcher emits an `all` event', () => { it('changes the hash when the passed-in file watcher emits an `all` event', () => {
return server.getAssetData('imgs/b.jpg').then(initialData => { return server.getAssetData('imgs/b.jpg').then(initialData => {
mockFS.root.imgs['b@4x.jpg'] = 'updated data'; mockFS.root.imgs['b@4x.jpg'] = 'updated data';
server.onFileChange('all', '/root/imgs/b@4x.jpg'); server.onFileChange('all', '/root/imgs/b@4x.jpg');
return server.getAssetData('imgs/b.jpg').then(data => return server
expect(data.hash).not.toEqual(initialData.hash) .getAssetData('imgs/b.jpg')
); .then(data => expect(data.hash).not.toEqual(initialData.hash));
}); });
}); });
}); });

View File

@ -7,6 +7,7 @@
* 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 * @flow
* @format
*/ */
'use strict'; 'use strict';
@ -25,7 +26,6 @@ const readDir = denodeify(fs.readdir);
const readFile = denodeify(fs.readFile); const readFile = denodeify(fs.readFile);
class AssetServer { class AssetServer {
_roots: $ReadOnlyArray<string>; _roots: $ReadOnlyArray<string>;
_assetExts: $ReadOnlyArray<string>; _assetExts: $ReadOnlyArray<string>;
_hashes: Map<?string, string>; _hashes: Map<?string, string>;
@ -57,7 +57,10 @@ class AssetServer {
}); });
} }
getAssetData(assetPath: string, platform: ?string = null): Promise<{| getAssetData(
assetPath: string,
platform: ?string = null,
): Promise<{|
files: Array<string>, files: Array<string>,
hash: string, hash: string,
name: string, name: string,
@ -109,22 +112,17 @@ class AssetServer {
* 4. Then try to pick platform-specific asset records * 4. Then try to pick platform-specific asset records
* 5. Then pick the closest resolution (rounding up) to the requested one * 5. Then pick the closest resolution (rounding up) to the requested one
*/ */
_getAssetRecord(assetPath: string, platform: ?string = null): Promise<{| _getAssetRecord(
assetPath: string,
platform: ?string = null,
): Promise<{|
files: Array<string>, files: Array<string>,
scales: Array<number>, scales: Array<number>,
|}> { |}> {
const filename = path.basename(assetPath); const filename = path.basename(assetPath);
return ( return this._findRoot(this._roots, path.dirname(assetPath), assetPath)
this._findRoot( .then(dir => Promise.all([dir, readDir(dir)]))
this._roots,
path.dirname(assetPath),
assetPath,
)
.then(dir => Promise.all([
dir,
readDir(dir),
]))
.then(res => { .then(res => {
const dir = res[0]; const dir = res[0];
const files = res[1]; const files = res[1];
@ -137,8 +135,9 @@ class AssetServer {
let record; let record;
if (platform != null) { if (platform != null) {
record = map.get(getAssetKey(assetData.assetName, platform)) || record =
map.get(assetData.assetName); map.get(getAssetKey(assetData.assetName, platform)) ||
map.get(assetData.assetName);
} else { } else {
record = map.get(assetData.assetName); record = map.get(assetData.assetName);
} }
@ -146,33 +145,39 @@ class AssetServer {
if (!record) { if (!record) {
throw new Error( throw new Error(
/* $FlowFixMe: platform can be null */ /* $FlowFixMe: platform can be null */
`Asset not found: ${assetPath} for platform: ${platform}` `Asset not found: ${assetPath} for platform: ${platform}`,
); );
} }
return record; return record;
}) });
);
} }
_findRoot(roots: $ReadOnlyArray<string>, dir: string, debugInfoFile: string): Promise<string> { _findRoot(
roots: $ReadOnlyArray<string>,
dir: string,
debugInfoFile: string,
): Promise<string> {
return Promise.all( return Promise.all(
roots.map(root => { roots.map(root => {
const absRoot = path.resolve(root); const absRoot = path.resolve(root);
// important: we want to resolve root + dir // important: we want to resolve root + dir
// to ensure the requested path doesn't traverse beyond root // to ensure the requested path doesn't traverse beyond root
const absPath = path.resolve(root, dir); const absPath = path.resolve(root, dir);
return stat(absPath).then(fstat => { return stat(absPath).then(
// keep asset requests from traversing files fstat => {
// up from the root (e.g. ../../../etc/hosts) // keep asset requests from traversing files
if (!absPath.startsWith(absRoot)) { // up from the root (e.g. ../../../etc/hosts)
if (!absPath.startsWith(absRoot)) {
return {path: absPath, isValid: false};
}
return {path: absPath, isValid: fstat.isDirectory()};
},
_ => {
return {path: absPath, isValid: false}; return {path: absPath, isValid: false};
} },
return {path: absPath, isValid: fstat.isDirectory()}; );
}, _ => { }),
return {path: absPath, isValid: false};
});
})
).then(stats => { ).then(stats => {
for (let i = 0; i < stats.length; i++) { for (let i = 0; i < stats.length; i++) {
if (stats[i].isValid) { if (stats[i].isValid) {
@ -183,15 +188,22 @@ class AssetServer {
const rootsString = roots.map(s => `'${s}'`).join(', '); const rootsString = roots.map(s => `'${s}'`).join(', ');
throw new Error( throw new Error(
`'${debugInfoFile}' could not be found, because '${dir}' is not a ` + `'${debugInfoFile}' could not be found, because '${dir}' is not a ` +
`subdirectory of any of the roots (${rootsString})`, `subdirectory of any of the roots (${rootsString})`,
); );
}); });
} }
_buildAssetMap(dir: string, files: $ReadOnlyArray<string>, platform: ?string): Map<string, {| _buildAssetMap(
files: Array<string>, dir: string,
scales: Array<number>, files: $ReadOnlyArray<string>,
|}> { platform: ?string,
): Map<
string,
{|
files: Array<string>,
scales: Array<number>,
|},
> {
const platforms = new Set(platform != null ? [platform] : []); const platforms = new Set(platform != null ? [platform] : []);
const assets = files.map(this._getAssetDataFromName.bind(this, platforms)); const assets = files.map(this._getAssetDataFromName.bind(this, platforms));
const map = new Map(); const map = new Map();
@ -214,7 +226,7 @@ class AssetServer {
const length = record.scales.length; const length = record.scales.length;
for (insertIndex = 0; insertIndex < length; insertIndex++) { for (insertIndex = 0; insertIndex < length; insertIndex++) {
if (asset.resolution < record.scales[insertIndex]) { if (asset.resolution < record.scales[insertIndex]) {
break; break;
} }
} }
@ -244,7 +256,8 @@ function hashFiles(files, hash, callback) {
return; return;
} }
fs.createReadStream(files.shift()) fs
.createReadStream(files.shift())
.on('data', data => hash.update(data)) .on('data', data => hash.update(data))
.once('end', () => hashFiles(files, hash, callback)) .once('end', () => hashFiles(files, hash, callback))
.once('error', error => callback(error)); .once('error', error => callback(error));

View File

@ -7,6 +7,7 @@
* 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 * @flow
* @format
*/ */
'use strict'; 'use strict';
@ -36,10 +37,9 @@ export type Unbundle = {
type SourceMapFormat = 'undetermined' | 'indexed' | 'flattened'; type SourceMapFormat = 'undetermined' | 'indexed' | 'flattened';
const SOURCEMAPPING_URL = '\n\/\/# sourceMappingURL='; const SOURCEMAPPING_URL = '\n//# sourceMappingURL=';
class Bundle extends BundleBase { class Bundle extends BundleBase {
_dev: boolean | void; _dev: boolean | void;
_inlineSourceMap: string | void; _inlineSourceMap: string | void;
_minify: boolean | void; _minify: boolean | void;
@ -51,13 +51,21 @@ class Bundle extends BundleBase {
_sourceMapUrl: ?string; _sourceMapUrl: ?string;
postProcessBundleSourcemap: PostProcessBundleSourcemap; postProcessBundleSourcemap: PostProcessBundleSourcemap;
constructor({sourceMapUrl, dev, minify, ramGroups, postProcessBundleSourcemap}: { constructor(
sourceMapUrl: ?string, {
dev?: boolean, sourceMapUrl,
minify?: boolean, dev,
ramGroups?: Array<string>, minify,
postProcessBundleSourcemap: PostProcessBundleSourcemap, ramGroups,
} = {}) { postProcessBundleSourcemap,
}: {
sourceMapUrl: ?string,
dev?: boolean,
minify?: boolean,
ramGroups?: Array<string>,
postProcessBundleSourcemap: PostProcessBundleSourcemap,
} = {},
) {
super(); super();
this._sourceMap = null; this._sourceMap = null;
this._sourceMapFormat = 'undetermined'; this._sourceMapFormat = 'undetermined';
@ -86,39 +94,44 @@ class Bundle extends BundleBase {
/* $FlowFixMe: erroneous change of signature. */ /* $FlowFixMe: erroneous change of signature. */
): Promise<void> { ): Promise<void> {
const index = super.addModule(moduleTransport); const index = super.addModule(moduleTransport);
return resolver.wrapModule({ return resolver
resolutionResponse, .wrapModule({
module, resolutionResponse,
name: moduleTransport.name, module,
code: moduleTransport.code, name: moduleTransport.name,
map: moduleTransport.map, code: moduleTransport.code,
meta: moduleTransport.meta, map: moduleTransport.map,
minify: this._minify, meta: moduleTransport.meta,
dev: this._dev, minify: this._minify,
}).then(({code, map}) => { dev: this._dev,
// If we get a map from the transformer we'll switch to a mode })
// were we're combining the source maps as opposed to .then(({code, map}) => {
if (map) { // If we get a map from the transformer we'll switch to a mode
const usesRawMappings = isRawMappings(map); // were we're combining the source maps as opposed to
if (map) {
const usesRawMappings = isRawMappings(map);
if (this._sourceMapFormat === 'undetermined') { if (this._sourceMapFormat === 'undetermined') {
this._sourceMapFormat = usesRawMappings ? 'flattened' : 'indexed'; this._sourceMapFormat = usesRawMappings ? 'flattened' : 'indexed';
} else if (usesRawMappings && this._sourceMapFormat === 'indexed') { } else if (usesRawMappings && this._sourceMapFormat === 'indexed') {
throw new Error( throw new Error(
`Got at least one module with a full source map, but ${ `Got at least one module with a full source map, but ${moduleTransport.sourcePath} has raw mappings`,
moduleTransport.sourcePath} has raw mappings` );
); } else if (
} else if (!usesRawMappings && this._sourceMapFormat === 'flattened') { !usesRawMappings &&
throw new Error( this._sourceMapFormat === 'flattened'
`Got at least one module with raw mappings, but ${ ) {
moduleTransport.sourcePath} has a full source map` throw new Error(
); `Got at least one module with raw mappings, but ${moduleTransport.sourcePath} has a full source map`,
);
}
} }
}
this.replaceModuleAt( this.replaceModuleAt(
index, new ModuleTransport({...moduleTransport, code, map})); index,
}); new ModuleTransport({...moduleTransport, code, map}),
);
});
} }
finalize(options: FinalizeOptions) { finalize(options: FinalizeOptions) {
@ -138,15 +151,17 @@ class Bundle extends BundleBase {
_addRequireCall(moduleId: string) { _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(
name, new ModuleTransport({
id: -this._numRequireCalls - 1, name,
code, id: -this._numRequireCalls - 1,
virtual: true, code,
sourceCode: code, virtual: true,
sourcePath: name + '.js', sourceCode: code,
meta: {preloaded: true}, sourcePath: name + '.js',
})); meta: {preloaded: true},
}),
);
this._numRequireCalls += 1; this._numRequireCalls += 1;
} }
@ -191,7 +206,11 @@ class Bundle extends BundleBase {
lazyModules, lazyModules,
get groups() { get groups() {
if (!groups) { if (!groups) {
groups = createRamBundleGroups(ramGroups || [], lazyModules, subtree); groups = createRamBundleGroups(
ramGroups || [],
lazyModules,
subtree,
);
} }
return groups; return groups;
}, },
@ -226,10 +245,10 @@ class Bundle extends BundleBase {
!Array.isArray(module.map), !Array.isArray(module.map),
`Unexpected raw mappings for ${module.sourcePath}`, `Unexpected raw mappings for ${module.sourcePath}`,
); );
let map: SourceMap = module.map == null || module.virtual let map: SourceMap =
? generateSourceMapForVirtualModule(module) module.map == null || module.virtual
: module.map; ? generateSourceMapForVirtualModule(module)
: module.map;
if (options.excludeSource && isMappingsMap(map)) { if (options.excludeSource && isMappingsMap(map)) {
map = {...map, sourcesContent: []}; map = {...map, sourcesContent: []};
@ -287,10 +306,12 @@ class Bundle extends BundleBase {
} }
getJSModulePaths() { getJSModulePaths() {
return this.getModules() return (
// Filter out non-js files. Like images etc. this.getModules()
.filter(module => !module.virtual) // Filter out non-js files. Like images etc.
.map(module => module.sourcePath); .filter(module => !module.virtual)
.map(module => module.sourcePath)
);
} }
getDebugInfo() { getDebugInfo() {
@ -308,11 +329,18 @@ class Bundle extends BundleBase {
'}', '}',
'</style>', '</style>',
'<h3> Module paths and transformed code: </h3>', '<h3> Module paths and transformed code: </h3>',
this.getModules().map(function(m) { this.getModules()
return '<div> <h4> Path: </h4>' + m.sourcePath + '<br/> <h4> Source: </h4>' + .map(function(m) {
'<code><pre class="collapsed" onclick="this.classList.remove(\'collapsed\')">' + return (
_.escape(m.code) + '</pre></code></div>'; '<div> <h4> Path: </h4>' +
}).join('\n'), 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'); ].join('\n');
} }
@ -326,7 +354,7 @@ function generateSourceMapForVirtualModule(module): MappingsMap {
let mappings = 'AAAA;'; let mappings = 'AAAA;';
for (let i = 1; i < module.code.split('\n').length; i++) { for (let i = 1; i < module.code.split('\n').length; i++) {
mappings += 'AACA;'; mappings += 'AACA;';
} }
return { return {
@ -350,7 +378,7 @@ function partition(array, predicate) {
return [included, excluded]; return [included, excluded];
} }
function * subtree( function* subtree(
moduleTransport: ModuleTransport, moduleTransport: ModuleTransport,
moduleTransportsByPath: Map<string, ModuleTransport>, moduleTransportsByPath: Map<string, ModuleTransport>,
seen = new Set(), seen = new Set(),
@ -359,13 +387,14 @@ function * subtree(
const {meta} = moduleTransport; const {meta} = moduleTransport;
invariant( invariant(
meta != null, meta != null,
'Unexpected module transport without meta information: ' + moduleTransport.sourcePath, 'Unexpected module transport without meta information: ' +
moduleTransport.sourcePath,
); );
for (const [, {path}] of meta.dependencyPairs || []) { for (const [, {path}] of meta.dependencyPairs || []) {
const dependency = moduleTransportsByPath.get(path); const dependency = moduleTransportsByPath.get(path);
if (dependency && !seen.has(dependency.id)) { if (dependency && !seen.has(dependency.id)) {
yield dependency.id; yield dependency.id;
yield * subtree(dependency, moduleTransportsByPath, seen); yield* subtree(dependency, moduleTransportsByPath, seen);
} }
} }
} }

View File

@ -7,7 +7,9 @@
* 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 * @flow
* @format
*/ */
'use strict'; 'use strict';
const ModuleTransport = require('../lib/ModuleTransport'); const ModuleTransport = require('../lib/ModuleTransport');
@ -24,7 +26,6 @@ export type GetSourceOptions = {
}; };
class BundleBase { class BundleBase {
_assets: Array<mixed>; _assets: Array<mixed>;
_finalized: boolean; _finalized: boolean;
_mainModuleId: number | void; _mainModuleId: number | void;
@ -104,7 +105,9 @@ class BundleBase {
assertFinalized(message?: string) { assertFinalized(message?: string) {
if (!this._finalized) { if (!this._finalized) {
throw new Error(message || 'Bundle needs to be finalized before getting any source'); throw new Error(
message || 'Bundle needs to be finalized before getting any source',
);
} }
} }

View File

@ -8,6 +8,7 @@
* *
* @format * @format
*/ */
'use strict'; 'use strict';
const Bundle = require('../Bundle'); const Bundle = require('../Bundle');

View File

@ -5,7 +5,10 @@
* 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.
*
* @format
*/ */
'use strict'; 'use strict';
jest jest
@ -23,8 +26,7 @@ jest
.mock('../Bundle') .mock('../Bundle')
.mock('../HMRBundle') .mock('../HMRBundle')
.mock('../../Logger') .mock('../../Logger')
.mock('/path/to/transformer.js', () => ({}), {virtual: true}) .mock('/path/to/transformer.js', () => ({}), {virtual: true});
;
var Bundler = require('../'); var Bundler = require('../');
var Resolver = require('../../Resolver'); var Resolver = require('../../Resolver');
@ -36,7 +38,6 @@ const path = require('path');
const {any, objectContaining} = expect; const {any, objectContaining} = expect;
var commonOptions = { var commonOptions = {
allowBundleUpdates: false, allowBundleUpdates: false,
assetExts: defaults.assetExts, assetExts: defaults.assetExts,
@ -52,7 +53,6 @@ var commonOptions = {
}; };
describe('Bundler', function() { describe('Bundler', function() {
function createModule({ function createModule({
path, path,
id, id,
@ -100,10 +100,12 @@ describe('Bundler', function() {
getModuleSystemDependencies, getModuleSystemDependencies,
}; };
}); });
Resolver.load = jest.fn().mockImplementation(opts => Promise.resolve(new Resolver(opts))); Resolver.load = jest
.fn()
.mockImplementation(opts => Promise.resolve(new Resolver(opts)));
fs.__setMockFilesystem({ fs.__setMockFilesystem({
'path': {'to': {'transformer.js': ''}}, path: {to: {'transformer.js': ''}},
}); });
fs.statSync.mockImplementation(function() { fs.statSync.mockImplementation(function() {
@ -151,7 +153,7 @@ describe('Bundler', function() {
options: transformOptions, options: transformOptions,
getModuleId: () => 123, getModuleId: () => 123,
getResolvedDependencyPairs: () => [], getResolvedDependencyPairs: () => [],
}) }),
); );
getModuleSystemDependencies.mockImplementation(function() { getModuleSystemDependencies.mockImplementation(function() {
@ -188,12 +190,17 @@ describe('Bundler', function() {
}, },
}, },
}, },
]) ]),
); );
}); });
it('allows overriding the platforms array', () => { it('allows overriding the platforms array', () => {
expect(bundler._opts.platforms).toEqual(['ios', 'android', 'windows', 'web']); expect(bundler._opts.platforms).toEqual([
'ios',
'android',
'windows',
'web',
]);
const b = new Bundler({ const b = new Bundler({
...commonOptions, ...commonOptions,
projectRoots, projectRoots,
@ -217,96 +224,115 @@ describe('Bundler', function() {
}; };
beforeEach(() => { beforeEach(() => {
assetServer.getAssetData assetServer.getAssetData.mockImplementation(() =>
.mockImplementation(() => Promise.resolve(mockAsset)); Promise.resolve(mockAsset),
);
}); });
it('creates a bundle', function() { it('creates a bundle', function() {
return bundler.bundle({ return bundler
entryFile: '/root/foo.js', .bundle({
runBeforeMainModule: [], entryFile: '/root/foo.js',
runModule: true,
sourceMapUrl: 'source_map_url',
}).then(bundle => {
const ithAddedModule = i => bundle.addModule.mock.calls[i][2].path;
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: [], runBeforeMainModule: [],
allowUpdates: false, runModule: true,
}]); sourceMapUrl: 'source_map_url',
})
.then(bundle => {
const ithAddedModule = i => bundle.addModule.mock.calls[i][2].path;
expect(bundle.addAsset.mock.calls[0]).toEqual([{ expect(ithAddedModule(0)).toEqual('/root/foo.js');
__packager_asset: true, expect(ithAddedModule(1)).toEqual('/root/bar.js');
fileSystemLocation: '/root/img', expect(ithAddedModule(2)).toEqual('/root/img/new_image.png');
httpServerLocation: '/assets/img', expect(ithAddedModule(3)).toEqual('/root/file.json');
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(bundle.finalize.mock.calls[0]).toEqual([
//expect(ProgressBar.prototype.tick.mock.calls.length).toEqual(modules.length); {
}); 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('loads and runs asset plugins', function() { it('loads and runs asset plugins', function() {
jest.mock('mockPlugin1', () => { jest.mock(
return asset => { 'mockPlugin1',
asset.extraReverseHash = asset.hash.split('').reverse().join(''); () => {
return asset; return asset => {
}; asset.extraReverseHash = asset.hash.split('').reverse().join('');
}, {virtual: true}); return asset;
};
},
{virtual: true},
);
jest.mock('asyncMockPlugin2', () => { jest.mock(
return asset => { 'asyncMockPlugin2',
expect(asset.extraReverseHash).toBeDefined(); () => {
return new Promise(resolve => { return asset => {
asset.extraPixelCount = asset.width * asset.height; expect(asset.extraReverseHash).toBeDefined();
resolve(asset); return new Promise(resolve => {
}); asset.extraPixelCount = asset.width * asset.height;
}; resolve(asset);
}, {virtual: true}); });
};
},
{virtual: true},
);
return bundler.bundle({ return bundler
entryFile: '/root/foo.js', .bundle({
runBeforeMainModule: [], entryFile: '/root/foo.js',
runModule: true, runBeforeMainModule: [],
sourceMapUrl: 'source_map_url', runModule: true,
assetPlugins: ['mockPlugin1', 'asyncMockPlugin2'], sourceMapUrl: 'source_map_url',
}).then(bundle => { assetPlugins: ['mockPlugin1', 'asyncMockPlugin2'],
expect(bundle.addAsset.mock.calls[0]).toEqual([{ })
__packager_asset: true, .then(bundle => {
fileSystemLocation: '/root/img', expect(bundle.addAsset.mock.calls[0]).toEqual([
httpServerLocation: '/assets/img', {
width: 50, __packager_asset: true,
height: 100, fileSystemLocation: '/root/img',
scales: [1, 2, 3], httpServerLocation: '/assets/img',
files: [ width: 50,
'/root/img/img.png', height: 100,
'/root/img/img@2x.png', scales: [1, 2, 3],
'/root/img/img@3x.png', files: [
], '/root/img/img.png',
hash: 'i am a hash', '/root/img/img@2x.png',
name: 'img', '/root/img/img@3x.png',
type: 'png', ],
extraReverseHash: 'hsah a ma i', hash: 'i am a hash',
extraPixelCount: 5000, name: 'img',
}]); type: 'png',
}); extraReverseHash: 'hsah a ma i',
extraPixelCount: 5000,
},
]);
});
}); });
it('calls the module post-processing function', () => { it('calls the module post-processing function', () => {
@ -324,34 +350,39 @@ describe('Bundler', function() {
const platform = 'arbitrary'; const platform = 'arbitrary';
const entryFile = '/root/foo.js'; const entryFile = '/root/foo.js';
return b.bundle({ return b
dev, .bundle({
entryFile, dev,
minify, entryFile,
platform, minify,
runBeforeMainModule: [], platform,
runModule: true, runBeforeMainModule: [],
sourceMapUrl: 'source_map_url', runModule: true,
}).then(() => { sourceMapUrl: 'source_map_url',
expect(postProcessModules) })
.toBeCalledWith( .then(() => {
modules.map(x => objectContaining({ expect(postProcessModules).toBeCalledWith(
name: any(String), modules.map(x =>
id: any(Number), objectContaining({
code: any(String), name: any(String),
sourceCode: any(String), id: any(Number),
sourcePath: x.path, code: any(String),
meta: any(Object), sourceCode: any(String),
polyfill: !!x.isPolyfill(), sourcePath: x.path,
})), meta: any(Object),
polyfill: !!x.isPolyfill(),
}),
),
entryFile, entryFile,
{dev, minify, platform}, {dev, minify, platform},
); );
}); });
}); });
it('respects the order of modules returned by the post-processing function', () => { it('respects the order of modules returned by the post-processing function', () => {
const postProcessModules = jest.fn().mockImplementation((ms, e) => ms.reverse()); const postProcessModules = jest
.fn()
.mockImplementation((ms, e) => ms.reverse());
const b = new Bundler({ const b = new Bundler({
...commonOptions, ...commonOptions,
@ -361,21 +392,23 @@ describe('Bundler', function() {
}); });
const entryFile = '/root/foo.js'; const entryFile = '/root/foo.js';
return b.bundle({ return b
entryFile, .bundle({
runBeforeMainModule: [], entryFile,
runModule: true, runBeforeMainModule: [],
sourceMapUrl: 'source_map_url', runModule: true,
}).then(bundle => { sourceMapUrl: 'source_map_url',
const ithAddedModule = i => bundle.addModule.mock.calls[i][2].path; })
.then(bundle => {
const ithAddedModule = i => bundle.addModule.mock.calls[i][2].path;
[ [
'/root/file.json', '/root/file.json',
'/root/img/new_image.png', '/root/img/new_image.png',
'/root/bar.js', '/root/bar.js',
'/root/foo.js', '/root/foo.js',
].forEach((path, ix) => expect(ithAddedModule(ix)).toEqual(path)); ].forEach((path, ix) => expect(ithAddedModule(ix)).toEqual(path));
}); });
}); });
}); });
@ -423,18 +456,21 @@ describe('Bundler', function() {
}), }),
); );
return bundler.getOrderedDependencyPaths('/root/foo.js', true) return bundler
.then(paths => expect(paths).toEqual([ .getOrderedDependencyPaths('/root/foo.js', true)
'/root/foo.js', .then(paths =>
'/root/bar.js', expect(paths).toEqual([
'/root/img/new_image.png', '/root/foo.js',
'/root/img/new_image@2x.png', '/root/bar.js',
'/root/img/new_image@3x.png', '/root/img/new_image.png',
'/root/file.json', '/root/img/new_image@2x.png',
'/root/img/new_image2.png', '/root/img/new_image@3x.png',
'/root/img/new_image2@2x.png', '/root/file.json',
'/root/img/new_image2@3x.png', '/root/img/new_image2.png',
])); '/root/img/new_image2@2x.png',
'/root/img/new_image2@3x.png',
]),
);
}); });
}); });
}); });

View File

@ -7,6 +7,7 @@
* 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 * @flow
* @format
*/ */
'use strict'; 'use strict';
@ -68,7 +69,7 @@ export type GetTransformOptionsOpts = {|
export type GetTransformOptions = ( export type GetTransformOptions = (
mainModuleName: string, mainModuleName: string,
options: GetTransformOptionsOpts, options: GetTransformOptionsOpts,
getDependenciesOf: string => Promise<Array<string>>, getDependenciesOf: (string) => Promise<Array<string>>,
) => Promise<ExtraTransformOptions>; ) => Promise<ExtraTransformOptions>;
export type AssetDescriptor = { export type AssetDescriptor = {
@ -152,7 +153,6 @@ type Options = {|
const {hasOwnProperty} = Object; const {hasOwnProperty} = Object;
class Bundler { class Bundler {
_opts: Options; _opts: Options;
_getModuleId: (opts: Module) => number; _getModuleId: (opts: Module) => number;
_transformer: Transformer; _transformer: Transformer;
@ -167,14 +167,16 @@ class Bundler {
opts.projectRoots.forEach(verifyRootExists); opts.projectRoots.forEach(verifyRootExists);
const transformModuleStr = fs.readFileSync(opts.transformModulePath); const transformModuleStr = fs.readFileSync(opts.transformModulePath);
const transformModuleHash = const transformModuleHash = crypto
crypto.createHash('sha1').update(transformModuleStr).digest('hex'); .createHash('sha1')
.update(transformModuleStr)
.digest('hex');
const stableProjectRoots = opts.projectRoots.map(p => { const stableProjectRoots = opts.projectRoots.map(p => {
return path.relative(path.join(__dirname, '../../../..'), p); return path.relative(path.join(__dirname, '../../../..'), p);
}); });
const cacheKeyParts = [ const cacheKeyParts = [
'react-packager-cache', 'react-packager-cache',
VERSION, VERSION,
opts.cacheVersion, opts.cacheVersion,
@ -193,17 +195,20 @@ class Bundler {
} }
} }
const transformCacheKey = crypto.createHash('sha1').update( const transformCacheKey = crypto
cacheKeyParts.join('$'), .createHash('sha1')
).digest('hex'); .update(cacheKeyParts.join('$'))
.digest('hex');
debug(`Using transform cache key "${transformCacheKey}"`); debug(`Using transform cache key "${transformCacheKey}"`);
this._transformer = new Transformer( this._transformer = new Transformer(
opts.transformModulePath, opts.transformModulePath,
opts.maxWorkers, opts.maxWorkers,
{ {
stdoutChunk: chunk => opts.reporter.update({type: 'worker_stdout_chunk', chunk}), stdoutChunk: chunk =>
stderrChunk: chunk => opts.reporter.update({type: 'worker_stderr_chunk', chunk}), opts.reporter.update({type: 'worker_stdout_chunk', chunk}),
stderrChunk: chunk =>
opts.reporter.update({type: 'worker_stderr_chunk', chunk}),
}, },
opts.workerPath, opts.workerPath,
); );
@ -232,8 +237,8 @@ class Bundler {
reporter: opts.reporter, reporter: opts.reporter,
resetCache: opts.resetCache, resetCache: opts.resetCache,
sourceExts: opts.sourceExts, sourceExts: opts.sourceExts,
transformCode: transformCode: (module, code, transformCodeOptions) =>
(module, code, transformCodeOptions) => this._transformer.transformFile( this._transformer.transformFile(
module.path, module.path,
module.localPath, module.localPath,
code, code,
@ -251,8 +256,8 @@ class Bundler {
end() { end() {
this._transformer.kill(); this._transformer.kill();
return this._resolverPromise.then( return this._resolverPromise.then(resolver =>
resolver => resolver.getDependencyGraph().getWatcher().end(), resolver.getDependencyGraph().getWatcher().end(),
); );
} }
@ -264,41 +269,40 @@ class Bundler {
}): Promise<Bundle> { }): Promise<Bundle> {
const {dev, minify, unbundle} = options; const {dev, minify, unbundle} = options;
const postProcessBundleSourcemap = this._opts.postProcessBundleSourcemap; const postProcessBundleSourcemap = this._opts.postProcessBundleSourcemap;
return this._resolverPromise.then( return this._resolverPromise
resolver => resolver.getModuleSystemDependencies({dev, unbundle}), .then(resolver => resolver.getModuleSystemDependencies({dev, unbundle}))
).then(moduleSystemDeps => this._bundle({ .then(moduleSystemDeps =>
...options, this._bundle({
bundle: new Bundle({ ...options,
dev, bundle: new Bundle({
minify, dev,
sourceMapUrl: options.sourceMapUrl, minify,
postProcessBundleSourcemap, sourceMapUrl: options.sourceMapUrl,
}), postProcessBundleSourcemap,
moduleSystemDeps, }),
})); moduleSystemDeps,
}),
);
} }
_sourceHMRURL(platform: ?string, hmrpath: string) { _sourceHMRURL(platform: ?string, hmrpath: string) {
return this._hmrURL( return this._hmrURL('', platform, 'bundle', hmrpath);
'',
platform,
'bundle',
hmrpath,
);
} }
_sourceMappingHMRURL(platform: ?string, hmrpath: string) { _sourceMappingHMRURL(platform: ?string, hmrpath: string) {
// Chrome expects `sourceURL` when eval'ing code // Chrome expects `sourceURL` when eval'ing code
return this._hmrURL( return this._hmrURL('//# sourceURL=', platform, 'map', hmrpath);
'\/\/# sourceURL=',
platform,
'map',
hmrpath,
);
} }
_hmrURL(prefix: string, platform: ?string, extensionOverride: string, filePath: string) { _hmrURL(
const matchingRoot = this._projectRoots.find(root => filePath.startsWith(root)); prefix: string,
platform: ?string,
extensionOverride: string,
filePath: string,
) {
const matchingRoot = this._projectRoots.find(root =>
filePath.startsWith(root),
);
if (!matchingRoot) { if (!matchingRoot) {
throw new Error('No matching project root for ' + filePath); throw new Error('No matching project root for ' + filePath);
@ -316,13 +320,22 @@ class Bundler {
); );
return ( return (
prefix + resource + prefix +
'.' + extensionOverride + '?' + resource +
'platform=' + (platform || '') + '&runModule=false&entryModuleOnly=true&hot=true' '.' +
extensionOverride +
'?' +
'platform=' +
(platform || '') +
'&runModule=false&entryModuleOnly=true&hot=true'
); );
} }
hmrBundle(options: {platform: ?string}, host: string, port: number): Promise<HMRBundle> { hmrBundle(
options: {platform: ?string},
host: string,
port: number,
): Promise<HMRBundle> {
return this._bundle({ return this._bundle({
...options, ...options,
bundle: new HMRBundle({ bundle: new HMRBundle({
@ -372,43 +385,54 @@ class Bundler {
runModule?: boolean, runModule?: boolean,
unbundle?: boolean, unbundle?: boolean,
}) { }) {
const onResolutionResponse = (response: ResolutionResponse<Module, BundlingOptions>) => { const onResolutionResponse = (
response: ResolutionResponse<Module, BundlingOptions>,
) => {
/* $FlowFixMe: looks like ResolutionResponse is monkey-patched /* $FlowFixMe: looks like ResolutionResponse is monkey-patched
* with `getModuleId`. */ * with `getModuleId`. */
bundle.setMainModuleId(response.getModuleId(getMainModule(response))); bundle.setMainModuleId(response.getModuleId(getMainModule(response)));
if (entryModuleOnly && entryFile) { if (entryModuleOnly && entryFile) {
response.dependencies = response.dependencies.filter(module => response.dependencies = response.dependencies.filter(module =>
module.path.endsWith(entryFile || '') module.path.endsWith(entryFile || ''),
); );
} else { } else {
response.dependencies = moduleSystemDeps.concat(response.dependencies); response.dependencies = moduleSystemDeps.concat(response.dependencies);
} }
}; };
const finalizeBundle = ({bundle: finalBundle, transformedModules, response, modulesByName}: { const finalizeBundle = ({
bundle: finalBundle,
transformedModules,
response,
modulesByName,
}: {
bundle: Bundle, bundle: Bundle,
transformedModules: Array<{module: Module, transformed: ModuleTransport}>, transformedModules: Array<{module: Module, transformed: ModuleTransport}>,
response: ResolutionResponse<Module, BundlingOptions>, response: ResolutionResponse<Module, BundlingOptions>,
modulesByName: {[name: string]: Module}, modulesByName: {[name: string]: Module},
}) => }) =>
this._resolverPromise.then(resolver => Promise.all( this._resolverPromise
transformedModules.map(({module, transformed}) => .then(resolver =>
finalBundle.addModule(resolver, response, module, transformed) Promise.all(
transformedModules.map(({module, transformed}) =>
finalBundle.addModule(resolver, response, module, transformed),
),
),
) )
)).then(() => { .then(() => {
const runBeforeMainModuleIds = Array.isArray(runBeforeMainModule) const runBeforeMainModuleIds = Array.isArray(runBeforeMainModule)
? runBeforeMainModule ? runBeforeMainModule
.map(name => modulesByName[name]) .map(name => modulesByName[name])
.filter(Boolean) .filter(Boolean)
.map(response.getModuleId) .map(response.getModuleId)
: undefined; : undefined;
finalBundle.finalize({ finalBundle.finalize({
runModule, runModule,
runBeforeMainModule: runBeforeMainModuleIds, runBeforeMainModule: runBeforeMainModuleIds,
allowUpdates: this._opts.allowBundleUpdates, allowUpdates: this._opts.allowBundleUpdates,
});
return finalBundle;
}); });
return finalBundle;
});
return this._buildBundle({ return this._buildBundle({
entryFile, entryFile,
@ -445,12 +469,13 @@ class Bundler {
finalizeBundle = emptyFunction, finalizeBundle = emptyFunction,
onProgress = emptyFunction, onProgress = emptyFunction,
}: *) { }: *) {
const transformingFilesLogEntry = const transformingFilesLogEntry = log(
log(createActionStartEntry({ createActionStartEntry({
action_name: 'Transforming files', action_name: 'Transforming files',
entry_point: entryFile, entry_point: entryFile,
environment: dev ? 'dev' : 'prod', environment: dev ? 'dev' : 'prod',
})); }),
);
const modulesByName = Object.create(null); const modulesByName = Object.create(null);
@ -467,9 +492,10 @@ class Bundler {
}); });
} }
return Promise.all( return Promise.all([
[this._resolverPromise, resolutionResponse], this._resolverPromise,
).then(([resolver, response]) => { resolutionResponse,
]).then(([resolver, response]) => {
bundle.setRamGroups(response.options.ramGroups); bundle.setRamGroups(response.options.ramGroups);
log(createActionEndEntry(transformingFilesLogEntry)); log(createActionEndEntry(transformingFilesLogEntry));
@ -477,12 +503,15 @@ class Bundler {
// get entry file complete path (`entryFile` is a local path, i.e. relative to roots) // get entry file complete path (`entryFile` is a local path, i.e. relative to roots)
let entryFilePath; let entryFilePath;
if (response.dependencies.length > 1) { // skip HMR requests if (response.dependencies.length > 1) {
const numModuleSystemDependencies = // skip HMR requests
resolver.getModuleSystemDependencies({dev, unbundle}).length; const numModuleSystemDependencies = resolver.getModuleSystemDependencies(
{dev, unbundle},
).length;
const dependencyIndex = const dependencyIndex =
(response.numPrependedDependencies || 0) + numModuleSystemDependencies; (response.numPrependedDependencies || 0) +
numModuleSystemDependencies;
if (dependencyIndex in response.dependencies) { if (dependencyIndex in response.dependencies) {
entryFilePath = response.dependencies[dependencyIndex].path; entryFilePath = response.dependencies[dependencyIndex].path;
@ -490,28 +519,27 @@ class Bundler {
} }
const modulesByTransport: Map<ModuleTransport, Module> = new Map(); const modulesByTransport: Map<ModuleTransport, Module> = new Map();
const toModuleTransport: Module => Promise<ModuleTransport> = const toModuleTransport: Module => Promise<ModuleTransport> = module =>
module => this._toModuleTransport({
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);
modulesByName[transformed.name] = module;
onModuleTransformed({
module, module,
response,
bundle, bundle,
entryFilePath, transformed,
assetPlugins,
options: response.options,
/* $FlowFixMe: `getModuleId` is monkey-patched */
getModuleId: (response.getModuleId: () => number),
dependencyPairs: response.getResolvedDependencyPairs(module),
}).then(transformed => {
modulesByTransport.set(transformed, module);
modulesByName[transformed.name] = module;
onModuleTransformed({
module,
response,
bundle,
transformed,
});
return transformed;
}); });
return transformed;
});
const p = this._opts.postProcessModules; const p = this._opts.postProcessModules;
const postProcess = p const postProcess = p
@ -525,8 +553,14 @@ class Bundler {
module: modulesByTransport.get(transformed), module: modulesByTransport.get(transformed),
transformed, transformed,
})); }));
return finalizeBundle({bundle, transformedModules, response, modulesByName}); return finalizeBundle({
}).then(() => bundle); bundle,
transformedModules,
response,
modulesByName,
});
})
.then(() => bundle);
}); });
} }
@ -545,26 +579,25 @@ class Bundler {
hot?: boolean, hot?: boolean,
generateSourceMaps?: boolean, generateSourceMaps?: boolean,
}): Promise<Array<Module>> { }): Promise<Array<Module>> {
return this.getTransformOptions( return this.getTransformOptions(entryFile, {
entryFile, enableBabelRCLookup: this._opts.enableBabelRCLookup,
{ dev,
enableBabelRCLookup: this._opts.enableBabelRCLookup, generateSourceMaps,
dev, hot,
generateSourceMaps, minify,
hot, platform,
minify, projectRoots: this._projectRoots,
platform, }).then(bundlingOptions =>
projectRoots: this._projectRoots,
},
).then(bundlingOptions =>
this._resolverPromise.then(resolver => this._resolverPromise.then(resolver =>
resolver.getShallowDependencies(entryFile, bundlingOptions.transformer), resolver.getShallowDependencies(entryFile, bundlingOptions.transformer),
) ),
); );
} }
getModuleForPath(entryFile: string): Promise<Module> { getModuleForPath(entryFile: string): Promise<Module> {
return this._resolverPromise.then(resolver => resolver.getModuleForPath(entryFile)); return this._resolverPromise.then(resolver =>
resolver.getModuleForPath(entryFile),
);
} }
async getDependencies({ async getDependencies({
@ -612,42 +645,47 @@ class Bundler {
return response; return response;
} }
getOrderedDependencyPaths({entryFile, dev, platform, minify, generateSourceMaps}: { getOrderedDependencyPaths({
entryFile,
dev,
platform,
minify,
generateSourceMaps,
}: {
+entryFile: string, +entryFile: string,
+dev: boolean, +dev: boolean,
+platform: string, +platform: string,
+minify: boolean, +minify: boolean,
+generateSourceMaps: boolean, +generateSourceMaps: boolean,
}) { }) {
return this.getDependencies({entryFile, dev, platform, minify, generateSourceMaps}).then( return this.getDependencies({
({dependencies}) => { entryFile,
const ret = []; dev,
const promises = []; platform,
const placeHolder = {}; minify,
dependencies.forEach(dep => { generateSourceMaps,
if (dep.isAsset()) { }).then(({dependencies}) => {
const localPath = toLocalPath( const ret = [];
this._projectRoots, const promises = [];
dep.path const placeHolder = {};
); dependencies.forEach(dep => {
promises.push( if (dep.isAsset()) {
this._assetServer.getAssetData(localPath, platform) const localPath = toLocalPath(this._projectRoots, dep.path);
); promises.push(this._assetServer.getAssetData(localPath, platform));
ret.push(placeHolder); ret.push(placeHolder);
} else { } else {
ret.push(dep.path); ret.push(dep.path);
} }
}); });
return Promise.all(promises).then(assetsData => { return Promise.all(promises).then(assetsData => {
assetsData.forEach(({files}) => { assetsData.forEach(({files}) => {
const index = ret.indexOf(placeHolder); const index = ret.indexOf(placeHolder);
ret.splice(index, 1, ...files); ret.splice(index, 1, ...files);
});
return ret;
}); });
} return ret;
); });
});
} }
_toModuleTransport({ _toModuleTransport({
@ -673,7 +711,12 @@ class Bundler {
if (module.isAsset()) { if (module.isAsset()) {
moduleTransport = this._generateAssetModule( moduleTransport = this._generateAssetModule(
bundle, module, moduleId, assetPlugins, transformOptions.platform); bundle,
module,
moduleId,
assetPlugins,
transformOptions.platform,
);
} }
if (moduleTransport) { if (moduleTransport) {
@ -683,15 +726,14 @@ class Bundler {
return Promise.all([ return Promise.all([
module.getName(), module.getName(),
module.read(transformOptions), module.read(transformOptions),
]).then(( ]).then(([name, {code, dependencies, dependencyOffsets, map, source}]) => {
[name, {code, dependencies, dependencyOffsets, map, source}]
) => {
const {preloadedModules} = options; const {preloadedModules} = options;
const isPolyfill = module.isPolyfill(); const isPolyfill = module.isPolyfill();
const preloaded = const preloaded =
module.path === entryFilePath || module.path === entryFilePath ||
isPolyfill || isPolyfill ||
preloadedModules && hasOwnProperty.call(preloadedModules, module.path); (preloadedModules &&
hasOwnProperty.call(preloadedModules, module.path));
return new ModuleTransport({ return new ModuleTransport({
name, name,
@ -721,35 +763,45 @@ class Bundler {
const isImage = isAssetTypeAnImage(extname(module.path).slice(1)); const isImage = isAssetTypeAnImage(extname(module.path).slice(1));
return this._assetServer.getAssetData(localPath, platform).then(assetData => { return this._assetServer
return Promise.all([isImage ? sizeOf(assetData.files[0]) : null, assetData]); .getAssetData(localPath, platform)
}).then(res => { .then(assetData => {
const dimensions = res[0]; return Promise.all([
const assetData = res[1]; isImage ? sizeOf(assetData.files[0]) : null,
const scale = assetData.scales[0]; assetData,
const asset = { ]);
__packager_asset: true, })
fileSystemLocation: pathDirname(module.path), .then(res => {
httpServerLocation: assetUrlPath, const dimensions = res[0];
width: dimensions ? dimensions.width / scale : undefined, const assetData = res[1];
height: dimensions ? dimensions.height / scale : undefined, const scale = assetData.scales[0];
scales: assetData.scales, const asset = {
files: assetData.files, __packager_asset: true,
hash: assetData.hash, fileSystemLocation: pathDirname(module.path),
name: assetData.name, httpServerLocation: assetUrlPath,
type: assetData.type, width: dimensions ? dimensions.width / scale : undefined,
}; height: dimensions ? dimensions.height / scale : undefined,
scales: assetData.scales,
files: assetData.files,
hash: assetData.hash,
name: assetData.name,
type: assetData.type,
};
return this._applyAssetPlugins(assetPlugins, asset); return this._applyAssetPlugins(assetPlugins, asset);
}).then(asset => { })
const {code, dependencies, dependencyOffsets} = .then(asset => {
generateAssetTransformResult(this._opts.assetRegistryPath, asset); const {
return { code,
asset, dependencies,
code, dependencyOffsets,
meta: {dependencies, dependencyOffsets, preloaded: null}, } = generateAssetTransformResult(this._opts.assetRegistryPath, asset);
}; return {
}); asset,
code,
meta: {dependencies, dependencyOffsets, preloaded: null},
};
});
} }
_applyAssetPlugins( _applyAssetPlugins(
@ -769,7 +821,7 @@ class Bundler {
// applying the remaining plugins // applying the remaining plugins
if (typeof result.then === 'function') { if (typeof result.then === 'function') {
return result.then(resultAsset => return result.then(resultAsset =>
this._applyAssetPlugins(remainingAssetPlugins, resultAsset) this._applyAssetPlugins(remainingAssetPlugins, resultAsset),
); );
} else { } else {
return this._applyAssetPlugins(remainingAssetPlugins, result); return this._applyAssetPlugins(remainingAssetPlugins, result);
@ -811,14 +863,19 @@ class Bundler {
platform: ?string, platform: ?string,
projectRoots: $ReadOnlyArray<string>, projectRoots: $ReadOnlyArray<string>,
|}, |},
): Promise<BundlingOptions> { ): Promise<BundlingOptions> {
const getDependencies = (entryFile: string) => const getDependencies = (entryFile: string) =>
this.getDependencies({...options, entryFile}) this.getDependencies({...options, entryFile}).then(r =>
.then(r => r.dependencies.map(d => d.path)); r.dependencies.map(d => d.path),
);
const {dev, hot, platform} = options; const {dev, hot, platform} = options;
const extraOptions: ExtraTransformOptions = this._getTransformOptions const extraOptions: ExtraTransformOptions = this._getTransformOptions
? await this._getTransformOptions(mainModuleName, {dev, hot, platform}, getDependencies) ? await this._getTransformOptions(
mainModuleName,
{dev, hot, platform},
getDependencies,
)
: {}; : {};
const {transform = {}} = extraOptions; const {transform = {}} = extraOptions;
@ -846,7 +903,6 @@ class Bundler {
getResolver(): Promise<Resolver> { getResolver(): Promise<Resolver> {
return this._resolverPromise; return this._resolverPromise;
} }
} }
function verifyRootExists(root) { function verifyRootExists(root) {

View File

@ -7,6 +7,7 @@
* 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 * @flow
* @format
*/ */
'use strict'; 'use strict';

View File

@ -7,6 +7,7 @@
* 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 * @flow
* @format
*/ */
'use strict'; 'use strict';
@ -77,13 +78,16 @@ class Generator {
*/ */
addSimpleMapping(generatedLine: number, generatedColumn: number): void { addSimpleMapping(generatedLine: number, generatedColumn: number): void {
const last = this.last; const last = this.last;
if (this.source === -1 || if (
generatedLine === last.generatedLine && this.source === -1 ||
generatedColumn < last.generatedColumn || (generatedLine === last.generatedLine &&
generatedLine < last.generatedLine) { generatedColumn < last.generatedColumn) ||
const msg = this.source === -1 generatedLine < last.generatedLine
? 'Cannot add mapping before starting a file with `addFile()`' ) {
: 'Mapping is for a position preceding an earlier mapping'; const msg =
this.source === -1
? 'Cannot add mapping before starting a file with `addFile()`'
: 'Mapping is for a position preceding an earlier mapping';
throw new Error(msg); throw new Error(msg);
} }
@ -130,7 +134,11 @@ class Generator {
name: string, name: string,
): void { ): void {
this.addSourceMapping( this.addSourceMapping(
generatedLine, generatedColumn, sourceLine, sourceColumn); generatedLine,
generatedColumn,
sourceLine,
sourceColumn,
);
const last = this.last; const last = this.last;
const nameIndex = this.names.indexFor(name); const nameIndex = this.names.indexFor(name);
@ -158,14 +166,16 @@ class Generator {
* This is ~2.5x faster than calling `JSON.stringify(generator.toMap())` * This is ~2.5x faster than calling `JSON.stringify(generator.toMap())`
*/ */
toString(file?: string): string { toString(file?: string): string {
return ('{' + return (
'{' +
'"version":3,' + '"version":3,' +
(file ? `"file":${JSON.stringify(file)},` : '') + (file ? `"file":${JSON.stringify(file)},` : '') +
`"sources":${JSON.stringify(this.sources)},` + `"sources":${JSON.stringify(this.sources)},` +
`"sourcesContent":${JSON.stringify(this.sourcesContent)},` + `"sourcesContent":${JSON.stringify(this.sourcesContent)},` +
`"names":${JSON.stringify(this.names.items())},` + `"names":${JSON.stringify(this.names.items())},` +
`"mappings":"${this.builder.toString()}"` + `"mappings":"${this.builder.toString()}"` +
'}'); '}'
);
} }
} }

View File

@ -5,7 +5,10 @@
* 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.
*
* @format
*/ */
'use strict'; 'use strict';
const B64Builder = require('../B64Builder'); const B64Builder = require('../B64Builder');

View File

@ -5,7 +5,10 @@
* 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.
*
* @format
*/ */
'use strict'; 'use strict';
const Generator = require('../Generator'); const Generator = require('../Generator');
@ -26,11 +29,12 @@ it('adds file name and source code when starting a file', () => {
generator.startFile(file1, source1); generator.startFile(file1, source1);
generator.startFile(file2, source2); generator.startFile(file2, source2);
expect(generator.toMap()) expect(generator.toMap()).toEqual(
.toEqual(objectContaining({ objectContaining({
sources: [file1, file2], sources: [file1, file2],
sourcesContent: [source1, source2], sourcesContent: [source1, source2],
})); }),
);
}); });
it('throws when adding a mapping without starting a file', () => { it('throws when adding a mapping without starting a file', () => {
@ -46,29 +50,32 @@ it('throws when adding a mapping after ending a file', () => {
it('can add a mapping for generated code without corresponding original source', () => { it('can add a mapping for generated code without corresponding original source', () => {
generator.startFile('apples', 'pears'); generator.startFile('apples', 'pears');
generator.addSimpleMapping(12, 87); generator.addSimpleMapping(12, 87);
expect(generator.toMap()) expect(generator.toMap()).toEqual(
.toEqual(objectContaining({ objectContaining({
mappings: ';;;;;;;;;;;uF', mappings: ';;;;;;;;;;;uF',
})); }),
);
}); });
it('can add a mapping with corresponding location in the original source', () => { it('can add a mapping with corresponding location in the original source', () => {
generator.startFile('apples', 'pears'); generator.startFile('apples', 'pears');
generator.addSourceMapping(2, 3, 456, 7); generator.addSourceMapping(2, 3, 456, 7);
expect(generator.toMap()) expect(generator.toMap()).toEqual(
.toEqual(objectContaining({ objectContaining({
mappings: ';GAucO', mappings: ';GAucO',
})); }),
);
}); });
it('can add a mapping with source location and symbol name', () => { it('can add a mapping with source location and symbol name', () => {
generator.startFile('apples', 'pears'); generator.startFile('apples', 'pears');
generator.addNamedSourceMapping(9, 876, 54, 3, 'arbitrary'); generator.addNamedSourceMapping(9, 876, 54, 3, 'arbitrary');
expect(generator.toMap()) expect(generator.toMap()).toEqual(
.toEqual(objectContaining({ objectContaining({
mappings: ';;;;;;;;42BAqDGA', mappings: ';;;;;;;;42BAqDGA',
names: ['arbitrary'], names: ['arbitrary'],
})); }),
);
}); });
describe('full map generation', () => { describe('full map generation', () => {
@ -94,10 +101,11 @@ describe('full map generation', () => {
}); });
it('can add a `file` property to the map', () => { it('can add a `file` property to the map', () => {
expect(generator.toMap('arbitrary')) expect(generator.toMap('arbitrary')).toEqual(
.toEqual(objectContaining({ objectContaining({
file: 'arbitrary', file: 'arbitrary',
})); }),
);
}); });
it('supports direct JSON serialization', () => { it('supports direct JSON serialization', () => {

View File

@ -1,11 +1,14 @@
/** /**
* Copyright (c) 2017-present, Facebook, Inc. * Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved. * All rights reserved.
* *
* 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.
*
* @format
*/ */
'use strict'; 'use strict';
const Generator = require('../Generator'); const Generator = require('../Generator');
@ -13,23 +16,29 @@ const {compactMapping, fromRawMappings} = require('..');
describe('flattening mappings / compacting', () => { describe('flattening mappings / compacting', () => {
it('flattens simple mappings', () => { it('flattens simple mappings', () => {
expect(compactMapping({generated: {line: 12, column: 34}})) expect(compactMapping({generated: {line: 12, column: 34}})).toEqual([
.toEqual([12, 34]); 12,
34,
]);
}); });
it('flattens mappings with a source location', () => { it('flattens mappings with a source location', () => {
expect(compactMapping({ expect(
generated: {column: 34, line: 12}, compactMapping({
original: {column: 78, line: 56}, generated: {column: 34, line: 12},
})).toEqual([12, 34, 56, 78]); original: {column: 78, line: 56},
}),
).toEqual([12, 34, 56, 78]);
}); });
it('flattens mappings with a source location and a symbol name', () => { it('flattens mappings with a source location and a symbol name', () => {
expect(compactMapping({ expect(
generated: {column: 34, line: 12}, compactMapping({
name: 'arbitrary', generated: {column: 34, line: 12},
original: {column: 78, line: 56}, name: 'arbitrary',
})).toEqual([12, 34, 56, 78, 'arbitrary']); original: {column: 78, line: 56},
}),
).toEqual([12, 34, 56, 78, 'arbitrary']);
}); });
}); });
@ -39,44 +48,45 @@ describe('build map from raw mappings', () => {
}); });
it('returns a working source map containing all mappings', () => { it('returns a working source map containing all mappings', () => {
const input = [{ const input = [
code: lines(11), {
map: [ code: lines(11),
[1, 2], map: [
[3, 4, 5, 6, 'apples'], [1, 2],
[7, 8, 9, 10], [3, 4, 5, 6, 'apples'],
[11, 12, 13, 14, 'pears'], [7, 8, 9, 10],
], [11, 12, 13, 14, 'pears'],
sourceCode: 'code1', ],
sourcePath: 'path1', sourceCode: 'code1',
}, { sourcePath: 'path1',
code: lines(3), },
map: [ {
[1, 2], code: lines(3),
[3, 4, 15, 16, 'bananas'], map: [[1, 2], [3, 4, 15, 16, 'bananas']],
], sourceCode: 'code2',
sourceCode: 'code2', sourcePath: 'path2',
sourcePath: 'path2', },
}, { {
code: lines(23), code: lines(23),
map: [ map: [
[11, 12], [11, 12],
[13, 14, 15, 16, 'bananas'], [13, 14, 15, 16, 'bananas'],
[17, 18, 19, 110], [17, 18, 19, 110],
[21, 112, 113, 114, 'pears'], [21, 112, 113, 114, 'pears'],
], ],
sourceCode: 'code3', sourceCode: 'code3',
sourcePath: 'path3', sourcePath: 'path3',
}]; },
];
expect(fromRawMappings(input).toMap()) expect(fromRawMappings(input).toMap()).toEqual({
.toEqual({ mappings:
mappings: 'E;;IAIMA;;;;QAII;;;;YAIIC;E;;ICEEC;;;;;;;;;;;Y;;cCAAA;;;;kBAI8F;;;;gHA8FID', 'E;;IAIMA;;;;QAII;;;;YAIIC;E;;ICEEC;;;;;;;;;;;Y;;cCAAA;;;;kBAI8F;;;;gHA8FID',
names: ['apples', 'pears', 'bananas'], names: ['apples', 'pears', 'bananas'],
sources: ['path1', 'path2', 'path3'], sources: ['path1', 'path2', 'path3'],
sourcesContent: ['code1', 'code2', 'code3'], sourcesContent: ['code1', 'code2', 'code3'],
version: 3, version: 3,
}); });
}); });
}); });

View File

@ -7,6 +7,7 @@
* 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 * @flow
* @format
*/ */
/** /**
@ -53,14 +54,70 @@
// A map of values to characters for the b64 encoding // A map of values to characters for the b64 encoding
const CHAR_MAP = [ const CHAR_MAP = [
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x41,
0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x42,
0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x43,
0x59, 0x5a, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x44,
0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x45,
0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x46,
0x77, 0x78, 0x79, 0x7a, 0x30, 0x31, 0x32, 0x33, 0x47,
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2b, 0x2f, 0x48,
0x49,
0x4a,
0x4b,
0x4c,
0x4d,
0x4e,
0x4f,
0x50,
0x51,
0x52,
0x53,
0x54,
0x55,
0x56,
0x57,
0x58,
0x59,
0x5a,
0x61,
0x62,
0x63,
0x64,
0x65,
0x66,
0x67,
0x68,
0x69,
0x6a,
0x6b,
0x6c,
0x6d,
0x6e,
0x6f,
0x70,
0x71,
0x72,
0x73,
0x74,
0x75,
0x76,
0x77,
0x78,
0x79,
0x7a,
0x30,
0x31,
0x32,
0x33,
0x34,
0x35,
0x36,
0x37,
0x38,
0x39,
0x2b,
0x2f,
]; ];
// A single base 64 digit can contain 6 bits of data. For the base 64 variable // A single base 64 digit can contain 6 bits of data. For the base 64 variable
@ -93,9 +150,7 @@ const VLQ_CONTINUATION_BIT = VLQ_BASE;
* 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
*/ */
function toVLQSigned(value) { function toVLQSigned(value) {
return value < 0 return value < 0 ? (-value << 1) + 1 : (value << 1) + 0;
? ((-value) << 1) + 1
: (value << 1) + 0;
} }
/** /**

View File

@ -7,6 +7,7 @@
* 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 * @flow
* @format
*/ */
'use strict'; 'use strict';
@ -18,10 +19,12 @@ import type {RawMapping as BabelRawMapping} from 'babel-generator';
type GeneratedCodeMapping = [number, number]; type GeneratedCodeMapping = [number, number];
type SourceMapping = [number, number, number, number]; type SourceMapping = [number, number, number, number];
type SourceMappingWithName = [number, number, number, number, string]; type SourceMappingWithName = [number, number, number, number, string];
export type RawMapping = export type RawMapping =
SourceMappingWithName | SourceMapping | GeneratedCodeMapping; | SourceMappingWithName
| SourceMapping
| GeneratedCodeMapping;
/** /**
* Creates a source map from modules with "raw mappings", i.e. an array of * Creates a source map from modules with "raw mappings", i.e. an array of
@ -40,7 +43,7 @@ function fromRawMappings(modules: Array<ModuleTransport>): Generator {
addMappingsForFile(generator, map, module, carryOver); addMappingsForFile(generator, map, module, carryOver);
} else if (map != null) { } else if (map != null) {
throw new Error( throw new Error(
`Unexpected module with full source map found: ${module.sourcePath}` `Unexpected module with full source map found: ${module.sourcePath}`,
); );
} }
@ -74,7 +77,6 @@ function addMappingsForFile(generator, mappings, module, carryOver) {
} }
generator.endFile(); generator.endFile();
} }
function addMapping(generator, mapping, carryOver, columnOffset) { function addMapping(generator, mapping, carryOver, columnOffset) {
@ -89,8 +91,15 @@ function addMapping(generator, mapping, carryOver, columnOffset) {
generator.addSourceMapping(line, column, mapping[2], mapping[3]); generator.addSourceMapping(line, column, mapping[2], mapping[3]);
} else if (n === 5) { } else if (n === 5) {
generator.addNamedSourceMapping( generator.addNamedSourceMapping(
line,
column,
// $FlowIssue #15579526 // $FlowIssue #15579526
line, column, mapping[2], mapping[3], mapping[4]); mapping[2],
// $FlowIssue #15579526
mapping[3],
// $FlowIssue #15579526
mapping[4],
);
} else { } else {
throw new Error(`Invalid mapping: [${mapping.join(', ')}]`); throw new Error(`Invalid mapping: [${mapping.join(', ')}]`);
} }

View File

@ -7,6 +7,7 @@
* 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 * @flow
* @format
*/ */
'use strict'; 'use strict';
@ -23,27 +24,41 @@ type SubTree<T: ModuleTransportLike> = (
moduleTransportsByPath: Map<string, T>, moduleTransportsByPath: Map<string, T>,
) => Generator<number, void, void>; ) => Generator<number, void, void>;
const assetPropertyBlacklist = new Set([ const assetPropertyBlacklist = new Set(['files', 'fileSystemLocation', 'path']);
'files',
'fileSystemLocation',
'path',
]);
function generateAssetCodeFileAst( function generateAssetCodeFileAst(
assetRegistryPath: string, assetRegistryPath: string,
assetDescriptor: AssetDescriptor, assetDescriptor: AssetDescriptor,
): Object { ): Object {
const properDescriptor = filterObject(assetDescriptor, assetPropertyBlacklist); const properDescriptor = filterObject(
const descriptorAst = babylon.parseExpression(JSON.stringify(properDescriptor)); assetDescriptor,
assetPropertyBlacklist,
);
const descriptorAst = babylon.parseExpression(
JSON.stringify(properDescriptor),
);
const t = babel.types; const t = babel.types;
const moduleExports = t.memberExpression(t.identifier('module'), t.identifier('exports')); const moduleExports = t.memberExpression(
const requireCall = t.identifier('module'),
t.callExpression(t.identifier('require'), [t.stringLiteral(assetRegistryPath)]); t.identifier('exports'),
const registerAssetFunction = t.memberExpression(requireCall, t.identifier('registerAsset')); );
const registerAssetCall = t.callExpression(registerAssetFunction, [descriptorAst]); const requireCall = t.callExpression(t.identifier('require'), [
return t.file(t.program([ t.stringLiteral(assetRegistryPath),
t.expressionStatement(t.assignmentExpression('=', moduleExports, registerAssetCall)), ]);
])); const registerAssetFunction = t.memberExpression(
requireCall,
t.identifier('registerAsset'),
);
const registerAssetCall = t.callExpression(registerAssetFunction, [
descriptorAst,
]);
return t.file(
t.program([
t.expressionStatement(
t.assignmentExpression('=', moduleExports, registerAssetCall),
),
]),
);
} }
function generateAssetTransformResult( function generateAssetTransformResult(
@ -66,9 +81,11 @@ function generateAssetTransformResult(
// Test extension against all types supported by image-size module. // Test extension against all types supported by image-size module.
// If it's not one of these, we won't treat it as an image. // If it's not one of these, we won't treat it as an image.
function isAssetTypeAnImage(type: string): boolean { function isAssetTypeAnImage(type: string): boolean {
return [ return (
'png', 'jpg', 'jpeg', 'bmp', 'gif', 'webp', 'psd', 'svg', 'tiff', ['png', 'jpg', 'jpeg', 'bmp', 'gif', 'webp', 'psd', 'svg', 'tiff'].indexOf(
].indexOf(type) !== -1; type,
) !== -1
);
} }
function filterObject(object, blacklist) { function filterObject(object, blacklist) {
@ -95,18 +112,17 @@ function createRamBundleGroups<T: ModuleTransportLike>(
// build a map of group root IDs to an array of module IDs in the group // build a map of group root IDs to an array of module IDs in the group
const result: Map<number, Set<number>> = new Map( const result: Map<number, Set<number>> = new Map(
ramGroups ramGroups.map(modulePath => {
.map(modulePath => { const root = byPath.get(modulePath);
const root = byPath.get(modulePath); if (root == null) {
if (root == null) { throw Error(`Group root ${modulePath} is not part of the bundle`);
throw Error(`Group root ${modulePath} is not part of the bundle`); }
} return [
return [ root.id,
root.id, // `subtree` yields the IDs of all transitive dependencies of a module
// `subtree` yields the IDs of all transitive dependencies of a module new Set(subtree(root, byPath)),
new Set(subtree(root, byPath)), ];
]; }),
})
); );
if (ramGroups.length > 1) { if (ramGroups.length > 1) {
@ -124,9 +140,10 @@ function createRamBundleGroups<T: ModuleTransportLike>(
const parentNames = parents.map(byId.get, byId); const parentNames = parents.map(byId.get, byId);
const lastName = parentNames.pop(); const lastName = parentNames.pop();
throw new Error( throw new Error(
`Module ${byId.get(moduleId) || moduleId} belongs to groups ${ `Module ${byId.get(moduleId) ||
parentNames.join(', ')}, and ${String(lastName) moduleId} belongs to groups ${parentNames.join(', ')}, and ${String(
}. Ensure that each module is only part of one group.` lastName,
)}. Ensure that each module is only part of one group.`,
); );
} }
} }
@ -134,7 +151,7 @@ function createRamBundleGroups<T: ModuleTransportLike>(
return result; return result;
} }
function * filter(iterator, predicate) { function* filter(iterator, predicate) {
for (const value of iterator) { for (const value of iterator) {
if (predicate(value)) { if (predicate(value)) {
yield value; yield value;