diff --git a/packages/metro-bundler/src/AssetServer/__tests__/AssetServer-test.js b/packages/metro-bundler/src/AssetServer/__tests__/AssetServer-test.js index 9e869403..6d141dcf 100644 --- a/packages/metro-bundler/src/AssetServer/__tests__/AssetServer-test.js +++ b/packages/metro-bundler/src/AssetServer/__tests__/AssetServer-test.js @@ -5,6 +5,8 @@ * 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. + * + * @format */ 'use strict'; @@ -26,7 +28,7 @@ describe('AssetServer', () => { }); fs.__setMockFilesystem({ - 'root': { + root: { imgs: { 'b.png': 'b image', 'b@2x.png': 'b2 image', @@ -37,11 +39,7 @@ describe('AssetServer', () => { return Promise.all([ server.get('imgs/b.png'), server.get('imgs/b@1x.png'), - ]).then(resp => - resp.forEach(data => - expect(data).toBe('b image') - ) - ); + ]).then(resp => resp.forEach(data => expect(data).toBe('b image'))); }); it('should work for the simple case with platform ext', () => { @@ -51,7 +49,7 @@ describe('AssetServer', () => { }); fs.__setMockFilesystem({ - 'root': { + root: { imgs: { 'b.ios.png': 'b ios image', 'b.android.png': 'b android image', @@ -62,25 +60,24 @@ describe('AssetServer', () => { }); return Promise.all([ - server.get('imgs/b.png', 'ios').then( - data => expect(data).toBe('b ios image') - ), - server.get('imgs/b.png', 'android').then( - data => expect(data).toBe('b android image') - ), - server.get('imgs/c.png', 'android').then( - data => expect(data).toBe('c android image') - ), - server.get('imgs/c.png', 'ios').then( - data => expect(data).toBe('c general image') - ), - server.get('imgs/c.png').then( - data => expect(data).toBe('c general image') - ), + server + .get('imgs/b.png', 'ios') + .then(data => expect(data).toBe('b ios image')), + server + .get('imgs/b.png', 'android') + .then(data => expect(data).toBe('b android image')), + server + .get('imgs/c.png', 'android') + .then(data => expect(data).toBe('c android image')), + server + .get('imgs/c.png', 'ios') + .then(data => expect(data).toBe('c general image')), + server + .get('imgs/c.png') + .then(data => expect(data).toBe('c general image')), ]); }); - it('should work for the simple case with jpg', () => { const server = new AssetServer({ projectRoots: ['/root'], @@ -88,7 +85,7 @@ describe('AssetServer', () => { }); fs.__setMockFilesystem({ - 'root': { + root: { imgs: { 'b.png': 'png image', 'b.jpg': 'jpeg image', @@ -99,12 +96,7 @@ describe('AssetServer', () => { return Promise.all([ server.get('imgs/b.jpg'), server.get('imgs/b.png'), - ]).then(data => - expect(data).toEqual([ - 'jpeg image', - 'png image', - ]) - ); + ]).then(data => expect(data).toEqual(['jpeg image', 'png image'])); }); it('should pick the bigger one', () => { @@ -114,7 +106,7 @@ describe('AssetServer', () => { }); fs.__setMockFilesystem({ - 'root': { + root: { imgs: { 'b@1x.png': 'b1 image', 'b@2x.png': 'b2 image', @@ -124,9 +116,9 @@ describe('AssetServer', () => { }, }); - return server.get('imgs/b@3x.png').then(data => - expect(data).toBe('b4 image') - ); + return server + .get('imgs/b@3x.png') + .then(data => expect(data).toBe('b4 image')); }); it('should pick the bigger one with platform ext', () => { @@ -136,7 +128,7 @@ describe('AssetServer', () => { }); fs.__setMockFilesystem({ - 'root': { + root: { imgs: { 'b@1x.png': 'b1 image', 'b@2x.png': 'b2 image', @@ -151,12 +143,10 @@ describe('AssetServer', () => { }); return Promise.all([ - server.get('imgs/b@3x.png').then(data => - expect(data).toBe('b4 image') - ), - server.get('imgs/b@3x.png', 'ios').then(data => - expect(data).toBe('b4 ios image') - ), + server.get('imgs/b@3x.png').then(data => expect(data).toBe('b4 image')), + server + .get('imgs/b@3x.png', 'ios') + .then(data => expect(data).toBe('b4 ios image')), ]); }); @@ -167,23 +157,23 @@ describe('AssetServer', () => { }); fs.__setMockFilesystem({ - 'root': { + root: { imgs: { 'b.png': 'b image', }, }, - 'root2': { - 'newImages': { - 'imgs': { + root2: { + newImages: { + imgs: { 'b@1x.png': 'b1 image', }, }, }, }); - return server.get('newImages/imgs/b.png').then(data => - expect(data).toBe('b1 image') - ); + return server + .get('newImages/imgs/b.png') + .then(data => expect(data).toBe('b1 image')); }); }); @@ -195,7 +185,7 @@ describe('AssetServer', () => { }); fs.__setMockFilesystem({ - 'root': { + root: { imgs: { 'b@1x.png': 'b1 image', 'b@2x.png': 'b2 image', @@ -206,17 +196,19 @@ describe('AssetServer', () => { }); return server.getAssetData('imgs/b.png').then(data => { - expect(data).toEqual(objectContaining({ - type: 'png', - name: 'b', - scales: [1, 2, 4, 4.5], - files: [ - '/root/imgs/b@1x.png', - '/root/imgs/b@2x.png', - '/root/imgs/b@4x.png', - '/root/imgs/b@4.5x.png', - ], - })); + expect(data).toEqual( + objectContaining({ + type: 'png', + name: 'b', + scales: [1, 2, 4, 4.5], + files: [ + '/root/imgs/b@1x.png', + '/root/imgs/b@2x.png', + '/root/imgs/b@4x.png', + '/root/imgs/b@4.5x.png', + ], + }), + ); }); }); @@ -227,7 +219,7 @@ describe('AssetServer', () => { }); fs.__setMockFilesystem({ - 'root': { + root: { imgs: { 'b@1x.jpg': 'b1 image', 'b@2x.jpg': 'b2 image', @@ -238,17 +230,19 @@ describe('AssetServer', () => { }); return server.getAssetData('imgs/b.jpg').then(data => { - expect(data).toEqual(objectContaining({ - type: 'jpg', - name: 'b', - scales: [1, 2, 4, 4.5], - files: [ - '/root/imgs/b@1x.jpg', - '/root/imgs/b@2x.jpg', - '/root/imgs/b@4x.jpg', - '/root/imgs/b@4.5x.jpg', - ], - })); + expect(data).toEqual( + objectContaining({ + type: 'jpg', + name: 'b', + scales: [1, 2, 4, 4.5], + files: [ + '/root/imgs/b@1x.jpg', + '/root/imgs/b@2x.jpg', + '/root/imgs/b@4x.jpg', + '/root/imgs/b@4.5x.jpg', + ], + }), + ); }); }); @@ -261,7 +255,7 @@ describe('AssetServer', () => { }); mockFS = { - 'root': { + root: { imgs: { 'b@1x.jpg': 'b1 image', 'b@2x.jpg': 'b2 image', @@ -280,18 +274,20 @@ describe('AssetServer', () => { hash.update(mockFS.root.imgs[name]); } - return server.getAssetData('imgs/b.jpg').then(data => - expect(data).toEqual(objectContaining({hash: hash.digest('hex')})) - ); + return server + .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', () => { return server.getAssetData('imgs/b.jpg').then(initialData => { mockFS.root.imgs['b@4x.jpg'] = 'updated data'; server.onFileChange('all', '/root/imgs/b@4x.jpg'); - return server.getAssetData('imgs/b.jpg').then(data => - expect(data.hash).not.toEqual(initialData.hash) - ); + return server + .getAssetData('imgs/b.jpg') + .then(data => expect(data.hash).not.toEqual(initialData.hash)); }); }); }); diff --git a/packages/metro-bundler/src/AssetServer/index.js b/packages/metro-bundler/src/AssetServer/index.js index 0d5a62e8..77bf77dc 100644 --- a/packages/metro-bundler/src/AssetServer/index.js +++ b/packages/metro-bundler/src/AssetServer/index.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @flow + * @format */ 'use strict'; @@ -25,7 +26,6 @@ const readDir = denodeify(fs.readdir); const readFile = denodeify(fs.readFile); class AssetServer { - _roots: $ReadOnlyArray; _assetExts: $ReadOnlyArray; _hashes: Map; @@ -57,7 +57,10 @@ class AssetServer { }); } - getAssetData(assetPath: string, platform: ?string = null): Promise<{| + getAssetData( + assetPath: string, + platform: ?string = null, + ): Promise<{| files: Array, hash: string, name: string, @@ -109,22 +112,17 @@ class AssetServer { * 4. Then try to pick platform-specific asset records * 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, scales: Array, |}> { const filename = path.basename(assetPath); - return ( - this._findRoot( - this._roots, - path.dirname(assetPath), - assetPath, - ) - .then(dir => Promise.all([ - dir, - readDir(dir), - ])) + return this._findRoot(this._roots, path.dirname(assetPath), assetPath) + .then(dir => Promise.all([dir, readDir(dir)])) .then(res => { const dir = res[0]; const files = res[1]; @@ -137,8 +135,9 @@ class AssetServer { let record; if (platform != null) { - record = map.get(getAssetKey(assetData.assetName, platform)) || - map.get(assetData.assetName); + record = + map.get(getAssetKey(assetData.assetName, platform)) || + map.get(assetData.assetName); } else { record = map.get(assetData.assetName); } @@ -146,33 +145,39 @@ class AssetServer { if (!record) { throw new Error( /* $FlowFixMe: platform can be null */ - `Asset not found: ${assetPath} for platform: ${platform}` + `Asset not found: ${assetPath} for platform: ${platform}`, ); } return record; - }) - ); + }); } - _findRoot(roots: $ReadOnlyArray, dir: string, debugInfoFile: string): Promise { + _findRoot( + roots: $ReadOnlyArray, + dir: string, + debugInfoFile: string, + ): Promise { return Promise.all( roots.map(root => { const absRoot = path.resolve(root); // important: we want to resolve root + dir // to ensure the requested path doesn't traverse beyond root const absPath = path.resolve(root, dir); - return stat(absPath).then(fstat => { - // keep asset requests from traversing files - // up from the root (e.g. ../../../etc/hosts) - if (!absPath.startsWith(absRoot)) { + return stat(absPath).then( + fstat => { + // keep asset requests from traversing files + // 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: fstat.isDirectory()}; - }, _ => { - return {path: absPath, isValid: false}; - }); - }) + }, + ); + }), ).then(stats => { for (let i = 0; i < stats.length; i++) { if (stats[i].isValid) { @@ -183,15 +188,22 @@ class AssetServer { const rootsString = roots.map(s => `'${s}'`).join(', '); throw new Error( `'${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, platform: ?string): Map, - scales: Array, - |}> { + _buildAssetMap( + dir: string, + files: $ReadOnlyArray, + platform: ?string, + ): Map< + string, + {| + files: Array, + scales: Array, + |}, + > { const platforms = new Set(platform != null ? [platform] : []); const assets = files.map(this._getAssetDataFromName.bind(this, platforms)); const map = new Map(); @@ -214,7 +226,7 @@ class AssetServer { const length = record.scales.length; for (insertIndex = 0; insertIndex < length; insertIndex++) { - if (asset.resolution < record.scales[insertIndex]) { + if (asset.resolution < record.scales[insertIndex]) { break; } } @@ -244,7 +256,8 @@ function hashFiles(files, hash, callback) { return; } - fs.createReadStream(files.shift()) + fs + .createReadStream(files.shift()) .on('data', data => hash.update(data)) .once('end', () => hashFiles(files, hash, callback)) .once('error', error => callback(error)); diff --git a/packages/metro-bundler/src/Bundler/Bundle.js b/packages/metro-bundler/src/Bundler/Bundle.js index a53d2b18..a687c4f6 100644 --- a/packages/metro-bundler/src/Bundler/Bundle.js +++ b/packages/metro-bundler/src/Bundler/Bundle.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @flow + * @format */ 'use strict'; @@ -36,10 +37,9 @@ export type Unbundle = { type SourceMapFormat = 'undetermined' | 'indexed' | 'flattened'; -const SOURCEMAPPING_URL = '\n\/\/# sourceMappingURL='; +const SOURCEMAPPING_URL = '\n//# sourceMappingURL='; class Bundle extends BundleBase { - _dev: boolean | void; _inlineSourceMap: string | void; _minify: boolean | void; @@ -51,13 +51,21 @@ class Bundle extends BundleBase { _sourceMapUrl: ?string; postProcessBundleSourcemap: PostProcessBundleSourcemap; - constructor({sourceMapUrl, dev, minify, ramGroups, postProcessBundleSourcemap}: { - sourceMapUrl: ?string, - dev?: boolean, - minify?: boolean, - ramGroups?: Array, - postProcessBundleSourcemap: PostProcessBundleSourcemap, - } = {}) { + constructor( + { + sourceMapUrl, + dev, + minify, + ramGroups, + postProcessBundleSourcemap, + }: { + sourceMapUrl: ?string, + dev?: boolean, + minify?: boolean, + ramGroups?: Array, + postProcessBundleSourcemap: PostProcessBundleSourcemap, + } = {}, + ) { super(); this._sourceMap = null; this._sourceMapFormat = 'undetermined'; @@ -86,39 +94,44 @@ class Bundle extends BundleBase { /* $FlowFixMe: erroneous change of signature. */ ): Promise { const index = super.addModule(moduleTransport); - return resolver.wrapModule({ - resolutionResponse, - module, - name: moduleTransport.name, - code: moduleTransport.code, - map: moduleTransport.map, - meta: moduleTransport.meta, - minify: this._minify, - dev: this._dev, - }).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) { - const usesRawMappings = isRawMappings(map); + return resolver + .wrapModule({ + resolutionResponse, + module, + name: moduleTransport.name, + code: moduleTransport.code, + map: moduleTransport.map, + meta: moduleTransport.meta, + minify: this._minify, + dev: this._dev, + }) + .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) { + const usesRawMappings = isRawMappings(map); - 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` - ); + 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})); - }); + this.replaceModuleAt( + index, + new ModuleTransport({...moduleTransport, code, map}), + ); + }); } finalize(options: FinalizeOptions) { @@ -138,15 +151,17 @@ class Bundle extends BundleBase { _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}, - })); + super.addModule( + new ModuleTransport({ + name, + id: -this._numRequireCalls - 1, + code, + virtual: true, + sourceCode: code, + sourcePath: name + '.js', + meta: {preloaded: true}, + }), + ); this._numRequireCalls += 1; } @@ -191,7 +206,11 @@ class Bundle extends BundleBase { lazyModules, get groups() { if (!groups) { - groups = createRamBundleGroups(ramGroups || [], lazyModules, subtree); + groups = createRamBundleGroups( + ramGroups || [], + lazyModules, + subtree, + ); } return groups; }, @@ -226,10 +245,10 @@ class Bundle extends BundleBase { !Array.isArray(module.map), `Unexpected raw mappings for ${module.sourcePath}`, ); - let map: SourceMap = module.map == null || module.virtual - ? generateSourceMapForVirtualModule(module) - : module.map; - + let map: SourceMap = + module.map == null || module.virtual + ? generateSourceMapForVirtualModule(module) + : module.map; if (options.excludeSource && isMappingsMap(map)) { map = {...map, sourcesContent: []}; @@ -287,10 +306,12 @@ class Bundle extends BundleBase { } getJSModulePaths() { - return this.getModules() - // Filter out non-js files. Like images etc. - .filter(module => !module.virtual) - .map(module => module.sourcePath); + return ( + this.getModules() + // Filter out non-js files. Like images etc. + .filter(module => !module.virtual) + .map(module => module.sourcePath) + ); } getDebugInfo() { @@ -308,11 +329,18 @@ class Bundle extends BundleBase { '}', '', '

Module paths and transformed code:

', - this.getModules().map(function(m) { - return '

Path:

' + m.sourcePath + '

Source:

' + - '
'; - }).join('\n'), + this.getModules() + .map(function(m) { + return ( + '

Path:

' + + m.sourcePath + + '

Source:

' + + '
' + ); + }) + .join('\n'), ].join('\n'); } @@ -326,7 +354,7 @@ function generateSourceMapForVirtualModule(module): MappingsMap { let mappings = 'AAAA;'; for (let i = 1; i < module.code.split('\n').length; i++) { - mappings += 'AACA;'; + mappings += 'AACA;'; } return { @@ -350,7 +378,7 @@ function partition(array, predicate) { return [included, excluded]; } -function * subtree( +function* subtree( moduleTransport: ModuleTransport, moduleTransportsByPath: Map, seen = new Set(), @@ -359,13 +387,14 @@ function * subtree( const {meta} = moduleTransport; invariant( meta != null, - 'Unexpected module transport without meta information: ' + moduleTransport.sourcePath, + '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); + yield* subtree(dependency, moduleTransportsByPath, seen); } } } diff --git a/packages/metro-bundler/src/Bundler/BundleBase.js b/packages/metro-bundler/src/Bundler/BundleBase.js index 549c6656..77e45210 100644 --- a/packages/metro-bundler/src/Bundler/BundleBase.js +++ b/packages/metro-bundler/src/Bundler/BundleBase.js @@ -7,7 +7,9 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @flow + * @format */ + 'use strict'; const ModuleTransport = require('../lib/ModuleTransport'); @@ -24,7 +26,6 @@ export type GetSourceOptions = { }; class BundleBase { - _assets: Array; _finalized: boolean; _mainModuleId: number | void; @@ -104,7 +105,9 @@ class BundleBase { assertFinalized(message?: string) { 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', + ); } } diff --git a/packages/metro-bundler/src/Bundler/__tests__/Bundle-test.js b/packages/metro-bundler/src/Bundler/__tests__/Bundle-test.js index 85b7f746..d2da4d2b 100644 --- a/packages/metro-bundler/src/Bundler/__tests__/Bundle-test.js +++ b/packages/metro-bundler/src/Bundler/__tests__/Bundle-test.js @@ -8,6 +8,7 @@ * * @format */ + 'use strict'; const Bundle = require('../Bundle'); diff --git a/packages/metro-bundler/src/Bundler/__tests__/Bundler-test.js b/packages/metro-bundler/src/Bundler/__tests__/Bundler-test.js index bdfd3809..a56d7769 100644 --- a/packages/metro-bundler/src/Bundler/__tests__/Bundler-test.js +++ b/packages/metro-bundler/src/Bundler/__tests__/Bundler-test.js @@ -5,7 +5,10 @@ * 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. + * + * @format */ + 'use strict'; jest @@ -23,8 +26,7 @@ jest .mock('../Bundle') .mock('../HMRBundle') .mock('../../Logger') - .mock('/path/to/transformer.js', () => ({}), {virtual: true}) - ; + .mock('/path/to/transformer.js', () => ({}), {virtual: true}); var Bundler = require('../'); var Resolver = require('../../Resolver'); @@ -36,7 +38,6 @@ const path = require('path'); const {any, objectContaining} = expect; - var commonOptions = { allowBundleUpdates: false, assetExts: defaults.assetExts, @@ -52,7 +53,6 @@ var commonOptions = { }; describe('Bundler', function() { - function createModule({ path, id, @@ -100,10 +100,12 @@ describe('Bundler', function() { 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({ - 'path': {'to': {'transformer.js': ''}}, + path: {to: {'transformer.js': ''}}, }); fs.statSync.mockImplementation(function() { @@ -151,7 +153,7 @@ describe('Bundler', function() { options: transformOptions, getModuleId: () => 123, getResolvedDependencyPairs: () => [], - }) + }), ); getModuleSystemDependencies.mockImplementation(function() { @@ -188,12 +190,17 @@ describe('Bundler', function() { }, }, }, - ]) + ]), ); }); 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({ ...commonOptions, projectRoots, @@ -217,96 +224,115 @@ describe('Bundler', function() { }; 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; - - 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, + return bundler + .bundle({ + entryFile: '/root/foo.js', 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([{ - __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', - }]); + 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'); - // TODO(amasad) This fails with 0 != 5 in OSS - //expect(ProgressBar.prototype.tick.mock.calls.length).toEqual(modules.length); - }); + 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('loads and runs asset plugins', function() { - jest.mock('mockPlugin1', () => { - return asset => { - asset.extraReverseHash = asset.hash.split('').reverse().join(''); - return asset; - }; - }, {virtual: true}); + 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}); + 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, - }]); - }); + 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', () => { @@ -324,34 +350,39 @@ describe('Bundler', function() { 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(Number), - code: any(String), - sourceCode: any(String), - sourcePath: x.path, - meta: any(Object), - polyfill: !!x.isPolyfill(), - })), + 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(Number), + 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 postProcessModules = jest + .fn() + .mockImplementation((ms, e) => ms.reverse()); const b = new Bundler({ ...commonOptions, @@ -361,21 +392,23 @@ describe('Bundler', function() { }); 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; + 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)); - }); + [ + '/root/file.json', + '/root/img/new_image.png', + '/root/bar.js', + '/root/foo.js', + ].forEach((path, ix) => expect(ithAddedModule(ix)).toEqual(path)); + }); }); }); @@ -423,18 +456,21 @@ describe('Bundler', function() { }), ); - 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', - ])); + 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', + ]), + ); }); }); }); diff --git a/packages/metro-bundler/src/Bundler/index.js b/packages/metro-bundler/src/Bundler/index.js index 3a5db474..290d1291 100644 --- a/packages/metro-bundler/src/Bundler/index.js +++ b/packages/metro-bundler/src/Bundler/index.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @flow + * @format */ 'use strict'; @@ -68,7 +69,7 @@ export type GetTransformOptionsOpts = {| export type GetTransformOptions = ( mainModuleName: string, options: GetTransformOptionsOpts, - getDependenciesOf: string => Promise>, + getDependenciesOf: (string) => Promise>, ) => Promise; export type AssetDescriptor = { @@ -152,7 +153,6 @@ type Options = {| const {hasOwnProperty} = Object; class Bundler { - _opts: Options; _getModuleId: (opts: Module) => number; _transformer: Transformer; @@ -167,14 +167,16 @@ class Bundler { opts.projectRoots.forEach(verifyRootExists); const transformModuleStr = fs.readFileSync(opts.transformModulePath); - const transformModuleHash = - crypto.createHash('sha1').update(transformModuleStr).digest('hex'); + const transformModuleHash = crypto + .createHash('sha1') + .update(transformModuleStr) + .digest('hex'); const stableProjectRoots = opts.projectRoots.map(p => { return path.relative(path.join(__dirname, '../../../..'), p); }); - const cacheKeyParts = [ + const cacheKeyParts = [ 'react-packager-cache', VERSION, opts.cacheVersion, @@ -193,17 +195,20 @@ class Bundler { } } - const transformCacheKey = crypto.createHash('sha1').update( - cacheKeyParts.join('$'), - ).digest('hex'); + const transformCacheKey = crypto + .createHash('sha1') + .update(cacheKeyParts.join('$')) + .digest('hex'); debug(`Using transform cache key "${transformCacheKey}"`); this._transformer = new Transformer( opts.transformModulePath, opts.maxWorkers, { - stdoutChunk: chunk => opts.reporter.update({type: 'worker_stdout_chunk', chunk}), - stderrChunk: chunk => opts.reporter.update({type: 'worker_stderr_chunk', chunk}), + stdoutChunk: chunk => + opts.reporter.update({type: 'worker_stdout_chunk', chunk}), + stderrChunk: chunk => + opts.reporter.update({type: 'worker_stderr_chunk', chunk}), }, opts.workerPath, ); @@ -232,8 +237,8 @@ class Bundler { reporter: opts.reporter, resetCache: opts.resetCache, sourceExts: opts.sourceExts, - transformCode: - (module, code, transformCodeOptions) => this._transformer.transformFile( + transformCode: (module, code, transformCodeOptions) => + this._transformer.transformFile( module.path, module.localPath, code, @@ -251,8 +256,8 @@ class Bundler { end() { this._transformer.kill(); - return this._resolverPromise.then( - resolver => resolver.getDependencyGraph().getWatcher().end(), + return this._resolverPromise.then(resolver => + resolver.getDependencyGraph().getWatcher().end(), ); } @@ -264,41 +269,40 @@ class Bundler { }): Promise { 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, - })); + 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, - ); + 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, - ); + 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)); + _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); @@ -316,13 +320,22 @@ class Bundler { ); return ( - prefix + resource + - '.' + extensionOverride + '?' + - 'platform=' + (platform || '') + '&runModule=false&entryModuleOnly=true&hot=true' + prefix + + resource + + '.' + + extensionOverride + + '?' + + 'platform=' + + (platform || '') + + '&runModule=false&entryModuleOnly=true&hot=true' ); } - hmrBundle(options: {platform: ?string}, host: string, port: number): Promise { + hmrBundle( + options: {platform: ?string}, + host: string, + port: number, + ): Promise { return this._bundle({ ...options, bundle: new HMRBundle({ @@ -372,43 +385,54 @@ class Bundler { runModule?: boolean, unbundle?: boolean, }) { - const onResolutionResponse = (response: ResolutionResponse) => { + const onResolutionResponse = ( + response: ResolutionResponse, + ) => { /* $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 || '') + module.path.endsWith(entryFile || ''), ); } else { response.dependencies = moduleSystemDeps.concat(response.dependencies); } }; - const finalizeBundle = ({bundle: finalBundle, transformedModules, response, modulesByName}: { + const finalizeBundle = ({ + bundle: finalBundle, + transformedModules, + response, + modulesByName, + }: { bundle: Bundle, transformedModules: Array<{module: Module, transformed: ModuleTransport}>, response: ResolutionResponse, modulesByName: {[name: string]: Module}, }) => - this._resolverPromise.then(resolver => Promise.all( - transformedModules.map(({module, transformed}) => - finalBundle.addModule(resolver, response, module, transformed) + this._resolverPromise + .then(resolver => + Promise.all( + transformedModules.map(({module, transformed}) => + finalBundle.addModule(resolver, response, module, transformed), + ), + ), ) - )).then(() => { - const runBeforeMainModuleIds = Array.isArray(runBeforeMainModule) - ? runBeforeMainModule - .map(name => modulesByName[name]) - .filter(Boolean) - .map(response.getModuleId) - : undefined; + .then(() => { + const runBeforeMainModuleIds = Array.isArray(runBeforeMainModule) + ? runBeforeMainModule + .map(name => modulesByName[name]) + .filter(Boolean) + .map(response.getModuleId) + : undefined; - finalBundle.finalize({ - runModule, - runBeforeMainModule: runBeforeMainModuleIds, - allowUpdates: this._opts.allowBundleUpdates, + finalBundle.finalize({ + runModule, + runBeforeMainModule: runBeforeMainModuleIds, + allowUpdates: this._opts.allowBundleUpdates, + }); + return finalBundle; }); - return finalBundle; - }); return this._buildBundle({ entryFile, @@ -445,12 +469,13 @@ class Bundler { finalizeBundle = emptyFunction, onProgress = emptyFunction, }: *) { - const transformingFilesLogEntry = - log(createActionStartEntry({ + const transformingFilesLogEntry = log( + createActionStartEntry({ action_name: 'Transforming files', entry_point: entryFile, environment: dev ? 'dev' : 'prod', - })); + }), + ); const modulesByName = Object.create(null); @@ -467,9 +492,10 @@ class Bundler { }); } - return Promise.all( - [this._resolverPromise, resolutionResponse], - ).then(([resolver, response]) => { + return Promise.all([ + this._resolverPromise, + resolutionResponse, + ]).then(([resolver, response]) => { bundle.setRamGroups(response.options.ramGroups); log(createActionEndEntry(transformingFilesLogEntry)); @@ -477,12 +503,15 @@ class Bundler { // 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; + if (response.dependencies.length > 1) { + // skip HMR requests + const numModuleSystemDependencies = resolver.getModuleSystemDependencies( + {dev, unbundle}, + ).length; const dependencyIndex = - (response.numPrependedDependencies || 0) + numModuleSystemDependencies; + (response.numPrependedDependencies || 0) + + numModuleSystemDependencies; if (dependencyIndex in response.dependencies) { entryFilePath = response.dependencies[dependencyIndex].path; @@ -490,28 +519,27 @@ class Bundler { } const modulesByTransport: Map = new Map(); - const toModuleTransport: Module => Promise = - module => - this._toModuleTransport({ + const toModuleTransport: Module => Promise = 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); + modulesByName[transformed.name] = module; + onModuleTransformed({ module, + response, 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, - response, - bundle, - transformed, - }); - return transformed; + transformed, }); + return transformed; + }); const p = this._opts.postProcessModules; const postProcess = p @@ -525,8 +553,14 @@ class Bundler { module: modulesByTransport.get(transformed), transformed, })); - return finalizeBundle({bundle, transformedModules, response, modulesByName}); - }).then(() => bundle); + return finalizeBundle({ + bundle, + transformedModules, + response, + modulesByName, + }); + }) + .then(() => bundle); }); } @@ -545,26 +579,25 @@ class Bundler { hot?: boolean, generateSourceMaps?: boolean, }): Promise> { - return this.getTransformOptions( - entryFile, - { - enableBabelRCLookup: this._opts.enableBabelRCLookup, - dev, - generateSourceMaps, - hot, - minify, - platform, - projectRoots: this._projectRoots, - }, - ).then(bundlingOptions => + return this.getTransformOptions(entryFile, { + enableBabelRCLookup: this._opts.enableBabelRCLookup, + dev, + generateSourceMaps, + hot, + minify, + platform, + projectRoots: this._projectRoots, + }).then(bundlingOptions => this._resolverPromise.then(resolver => resolver.getShallowDependencies(entryFile, bundlingOptions.transformer), - ) + ), ); } getModuleForPath(entryFile: string): Promise { - return this._resolverPromise.then(resolver => resolver.getModuleForPath(entryFile)); + return this._resolverPromise.then(resolver => + resolver.getModuleForPath(entryFile), + ); } async getDependencies({ @@ -612,42 +645,47 @@ class Bundler { return response; } - getOrderedDependencyPaths({entryFile, dev, platform, minify, generateSourceMaps}: { + getOrderedDependencyPaths({ + entryFile, + dev, + platform, + minify, + generateSourceMaps, + }: { +entryFile: string, +dev: boolean, +platform: string, +minify: boolean, +generateSourceMaps: boolean, }) { - return this.getDependencies({entryFile, dev, platform, minify, generateSourceMaps}).then( - ({dependencies}) => { - const ret = []; - const promises = []; - const placeHolder = {}; - 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 this.getDependencies({ + entryFile, + dev, + platform, + minify, + generateSourceMaps, + }).then(({dependencies}) => { + const ret = []; + const promises = []; + const placeHolder = {}; + 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; + return Promise.all(promises).then(assetsData => { + assetsData.forEach(({files}) => { + const index = ret.indexOf(placeHolder); + ret.splice(index, 1, ...files); }); - } - ); + return ret; + }); + }); } _toModuleTransport({ @@ -673,7 +711,12 @@ class Bundler { if (module.isAsset()) { moduleTransport = this._generateAssetModule( - bundle, module, moduleId, assetPlugins, transformOptions.platform); + bundle, + module, + moduleId, + assetPlugins, + transformOptions.platform, + ); } if (moduleTransport) { @@ -683,15 +726,14 @@ class Bundler { return Promise.all([ module.getName(), module.read(transformOptions), - ]).then(( - [name, {code, dependencies, dependencyOffsets, map, source}] - ) => { + ]).then(([name, {code, dependencies, dependencyOffsets, map, source}]) => { const {preloadedModules} = options; const isPolyfill = module.isPolyfill(); const preloaded = module.path === entryFilePath || isPolyfill || - preloadedModules && hasOwnProperty.call(preloadedModules, module.path); + (preloadedModules && + hasOwnProperty.call(preloadedModules, module.path)); return new ModuleTransport({ name, @@ -721,35 +763,45 @@ class Bundler { const isImage = isAssetTypeAnImage(extname(module.path).slice(1)); - return this._assetServer.getAssetData(localPath, platform).then(assetData => { - return Promise.all([isImage ? sizeOf(assetData.files[0]) : null, assetData]); - }).then(res => { - const dimensions = res[0]; - const assetData = res[1]; - const scale = assetData.scales[0]; - const asset = { - __packager_asset: true, - fileSystemLocation: pathDirname(module.path), - httpServerLocation: assetUrlPath, - 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._assetServer + .getAssetData(localPath, platform) + .then(assetData => { + return Promise.all([ + isImage ? sizeOf(assetData.files[0]) : null, + assetData, + ]); + }) + .then(res => { + const dimensions = res[0]; + const assetData = res[1]; + const scale = assetData.scales[0]; + const asset = { + __packager_asset: true, + fileSystemLocation: pathDirname(module.path), + httpServerLocation: assetUrlPath, + 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); - }).then(asset => { - const {code, dependencies, dependencyOffsets} = - generateAssetTransformResult(this._opts.assetRegistryPath, asset); - return { - asset, - code, - meta: {dependencies, dependencyOffsets, preloaded: null}, - }; - }); + return this._applyAssetPlugins(assetPlugins, asset); + }) + .then(asset => { + const { + code, + dependencies, + dependencyOffsets, + } = generateAssetTransformResult(this._opts.assetRegistryPath, asset); + return { + asset, + code, + meta: {dependencies, dependencyOffsets, preloaded: null}, + }; + }); } _applyAssetPlugins( @@ -769,7 +821,7 @@ class Bundler { // applying the remaining plugins if (typeof result.then === 'function') { return result.then(resultAsset => - this._applyAssetPlugins(remainingAssetPlugins, resultAsset) + this._applyAssetPlugins(remainingAssetPlugins, resultAsset), ); } else { return this._applyAssetPlugins(remainingAssetPlugins, result); @@ -811,14 +863,19 @@ class Bundler { platform: ?string, projectRoots: $ReadOnlyArray, |}, - ): Promise { + ): Promise { const getDependencies = (entryFile: string) => - this.getDependencies({...options, entryFile}) - .then(r => r.dependencies.map(d => d.path)); + this.getDependencies({...options, entryFile}).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) + ? await this._getTransformOptions( + mainModuleName, + {dev, hot, platform}, + getDependencies, + ) : {}; const {transform = {}} = extraOptions; @@ -846,7 +903,6 @@ class Bundler { getResolver(): Promise { return this._resolverPromise; } - } function verifyRootExists(root) { diff --git a/packages/metro-bundler/src/Bundler/source-map/B64Builder.js b/packages/metro-bundler/src/Bundler/source-map/B64Builder.js index a4d31f9d..7405902d 100644 --- a/packages/metro-bundler/src/Bundler/source-map/B64Builder.js +++ b/packages/metro-bundler/src/Bundler/source-map/B64Builder.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @flow + * @format */ 'use strict'; diff --git a/packages/metro-bundler/src/Bundler/source-map/Generator.js b/packages/metro-bundler/src/Bundler/source-map/Generator.js index cce6a1d2..6dce07c7 100644 --- a/packages/metro-bundler/src/Bundler/source-map/Generator.js +++ b/packages/metro-bundler/src/Bundler/source-map/Generator.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @flow + * @format */ 'use strict'; @@ -77,13 +78,16 @@ class Generator { */ addSimpleMapping(generatedLine: number, generatedColumn: number): void { const last = this.last; - if (this.source === -1 || - generatedLine === last.generatedLine && - generatedColumn < last.generatedColumn || - generatedLine < last.generatedLine) { - const msg = this.source === -1 - ? 'Cannot add mapping before starting a file with `addFile()`' - : 'Mapping is for a position preceding an earlier mapping'; + if ( + this.source === -1 || + (generatedLine === last.generatedLine && + generatedColumn < last.generatedColumn) || + generatedLine < last.generatedLine + ) { + 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); } @@ -130,7 +134,11 @@ class Generator { name: string, ): void { this.addSourceMapping( - generatedLine, generatedColumn, sourceLine, sourceColumn); + generatedLine, + generatedColumn, + sourceLine, + sourceColumn, + ); const last = this.last; const nameIndex = this.names.indexFor(name); @@ -158,14 +166,16 @@ class Generator { * This is ~2.5x faster than calling `JSON.stringify(generator.toMap())` */ toString(file?: string): string { - return ('{' + + return ( + '{' + '"version":3,' + (file ? `"file":${JSON.stringify(file)},` : '') + `"sources":${JSON.stringify(this.sources)},` + `"sourcesContent":${JSON.stringify(this.sourcesContent)},` + `"names":${JSON.stringify(this.names.items())},` + `"mappings":"${this.builder.toString()}"` + - '}'); + '}' + ); } } diff --git a/packages/metro-bundler/src/Bundler/source-map/__tests__/B64Builder-test.js b/packages/metro-bundler/src/Bundler/source-map/__tests__/B64Builder-test.js index affceb3b..4cfb13c5 100644 --- a/packages/metro-bundler/src/Bundler/source-map/__tests__/B64Builder-test.js +++ b/packages/metro-bundler/src/Bundler/source-map/__tests__/B64Builder-test.js @@ -5,7 +5,10 @@ * 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. + * + * @format */ + 'use strict'; const B64Builder = require('../B64Builder'); diff --git a/packages/metro-bundler/src/Bundler/source-map/__tests__/Generator-test.js b/packages/metro-bundler/src/Bundler/source-map/__tests__/Generator-test.js index 2447dc21..54ff54f7 100644 --- a/packages/metro-bundler/src/Bundler/source-map/__tests__/Generator-test.js +++ b/packages/metro-bundler/src/Bundler/source-map/__tests__/Generator-test.js @@ -5,7 +5,10 @@ * 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. + * + * @format */ + 'use strict'; 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(file2, source2); - expect(generator.toMap()) - .toEqual(objectContaining({ + expect(generator.toMap()).toEqual( + objectContaining({ sources: [file1, file2], sourcesContent: [source1, source2], - })); + }), + ); }); 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', () => { generator.startFile('apples', 'pears'); generator.addSimpleMapping(12, 87); - expect(generator.toMap()) - .toEqual(objectContaining({ + expect(generator.toMap()).toEqual( + objectContaining({ mappings: ';;;;;;;;;;;uF', - })); + }), + ); }); it('can add a mapping with corresponding location in the original source', () => { generator.startFile('apples', 'pears'); generator.addSourceMapping(2, 3, 456, 7); - expect(generator.toMap()) - .toEqual(objectContaining({ + expect(generator.toMap()).toEqual( + objectContaining({ mappings: ';GAucO', - })); + }), + ); }); it('can add a mapping with source location and symbol name', () => { generator.startFile('apples', 'pears'); generator.addNamedSourceMapping(9, 876, 54, 3, 'arbitrary'); - expect(generator.toMap()) - .toEqual(objectContaining({ + expect(generator.toMap()).toEqual( + objectContaining({ mappings: ';;;;;;;;42BAqDGA', names: ['arbitrary'], - })); + }), + ); }); describe('full map generation', () => { @@ -94,10 +101,11 @@ describe('full map generation', () => { }); it('can add a `file` property to the map', () => { - expect(generator.toMap('arbitrary')) - .toEqual(objectContaining({ + expect(generator.toMap('arbitrary')).toEqual( + objectContaining({ file: 'arbitrary', - })); + }), + ); }); it('supports direct JSON serialization', () => { diff --git a/packages/metro-bundler/src/Bundler/source-map/__tests__/source-map-test.js b/packages/metro-bundler/src/Bundler/source-map/__tests__/source-map-test.js index 9637991b..7c1c037a 100644 --- a/packages/metro-bundler/src/Bundler/source-map/__tests__/source-map-test.js +++ b/packages/metro-bundler/src/Bundler/source-map/__tests__/source-map-test.js @@ -1,11 +1,14 @@ - /** +/** * Copyright (c) 2017-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. + * + * @format */ + 'use strict'; const Generator = require('../Generator'); @@ -13,23 +16,29 @@ const {compactMapping, fromRawMappings} = require('..'); describe('flattening mappings / compacting', () => { it('flattens simple mappings', () => { - expect(compactMapping({generated: {line: 12, column: 34}})) - .toEqual([12, 34]); + expect(compactMapping({generated: {line: 12, column: 34}})).toEqual([ + 12, + 34, + ]); }); it('flattens mappings with a source location', () => { - expect(compactMapping({ - generated: {column: 34, line: 12}, - original: {column: 78, line: 56}, - })).toEqual([12, 34, 56, 78]); + expect( + compactMapping({ + generated: {column: 34, line: 12}, + original: {column: 78, line: 56}, + }), + ).toEqual([12, 34, 56, 78]); }); it('flattens mappings with a source location and a symbol name', () => { - expect(compactMapping({ - generated: {column: 34, line: 12}, - name: 'arbitrary', - original: {column: 78, line: 56}, - })).toEqual([12, 34, 56, 78, 'arbitrary']); + expect( + compactMapping({ + generated: {column: 34, line: 12}, + name: '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', () => { - const input = [{ - code: lines(11), - map: [ - [1, 2], - [3, 4, 5, 6, 'apples'], - [7, 8, 9, 10], - [11, 12, 13, 14, 'pears'], - ], - sourceCode: 'code1', - sourcePath: 'path1', - }, { - code: lines(3), - map: [ - [1, 2], - [3, 4, 15, 16, 'bananas'], - ], - sourceCode: 'code2', - sourcePath: 'path2', - }, { - code: lines(23), - map: [ - [11, 12], - [13, 14, 15, 16, 'bananas'], - [17, 18, 19, 110], - [21, 112, 113, 114, 'pears'], - ], - sourceCode: 'code3', - sourcePath: 'path3', - }]; + const input = [ + { + code: lines(11), + map: [ + [1, 2], + [3, 4, 5, 6, 'apples'], + [7, 8, 9, 10], + [11, 12, 13, 14, 'pears'], + ], + sourceCode: 'code1', + sourcePath: 'path1', + }, + { + code: lines(3), + map: [[1, 2], [3, 4, 15, 16, 'bananas']], + sourceCode: 'code2', + sourcePath: 'path2', + }, + { + code: lines(23), + map: [ + [11, 12], + [13, 14, 15, 16, 'bananas'], + [17, 18, 19, 110], + [21, 112, 113, 114, 'pears'], + ], + sourceCode: 'code3', + sourcePath: 'path3', + }, + ]; - expect(fromRawMappings(input).toMap()) - .toEqual({ - mappings: 'E;;IAIMA;;;;QAII;;;;YAIIC;E;;ICEEC;;;;;;;;;;;Y;;cCAAA;;;;kBAI8F;;;;gHA8FID', - names: ['apples', 'pears', 'bananas'], - sources: ['path1', 'path2', 'path3'], - sourcesContent: ['code1', 'code2', 'code3'], - version: 3, - }); + expect(fromRawMappings(input).toMap()).toEqual({ + mappings: + 'E;;IAIMA;;;;QAII;;;;YAIIC;E;;ICEEC;;;;;;;;;;;Y;;cCAAA;;;;kBAI8F;;;;gHA8FID', + names: ['apples', 'pears', 'bananas'], + sources: ['path1', 'path2', 'path3'], + sourcesContent: ['code1', 'code2', 'code3'], + version: 3, + }); }); }); diff --git a/packages/metro-bundler/src/Bundler/source-map/encode.js b/packages/metro-bundler/src/Bundler/source-map/encode.js index cc05fa10..12585935 100644 --- a/packages/metro-bundler/src/Bundler/source-map/encode.js +++ b/packages/metro-bundler/src/Bundler/source-map/encode.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @flow + * @format */ /** @@ -53,14 +54,70 @@ // A map of values to characters for the b64 encoding const CHAR_MAP = [ - 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 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, + 0x41, + 0x42, + 0x43, + 0x44, + 0x45, + 0x46, + 0x47, + 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 @@ -93,9 +150,7 @@ const VLQ_CONTINUATION_BIT = VLQ_BASE; * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) */ function toVLQSigned(value) { - return value < 0 - ? ((-value) << 1) + 1 - : (value << 1) + 0; + return value < 0 ? (-value << 1) + 1 : (value << 1) + 0; } /** diff --git a/packages/metro-bundler/src/Bundler/source-map/source-map.js b/packages/metro-bundler/src/Bundler/source-map/source-map.js index fcc2e9a3..d2a01df2 100644 --- a/packages/metro-bundler/src/Bundler/source-map/source-map.js +++ b/packages/metro-bundler/src/Bundler/source-map/source-map.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @flow + * @format */ 'use strict'; @@ -18,10 +19,12 @@ import type {RawMapping as BabelRawMapping} from 'babel-generator'; type GeneratedCodeMapping = [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 = - SourceMappingWithName | SourceMapping | GeneratedCodeMapping; + | SourceMappingWithName + | SourceMapping + | GeneratedCodeMapping; /** * Creates a source map from modules with "raw mappings", i.e. an array of @@ -40,7 +43,7 @@ function fromRawMappings(modules: Array): Generator { addMappingsForFile(generator, map, module, carryOver); } else if (map != null) { 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(); - } function addMapping(generator, mapping, carryOver, columnOffset) { @@ -89,8 +91,15 @@ function addMapping(generator, mapping, carryOver, columnOffset) { generator.addSourceMapping(line, column, mapping[2], mapping[3]); } else if (n === 5) { generator.addNamedSourceMapping( + line, + column, // $FlowIssue #15579526 - line, column, mapping[2], mapping[3], mapping[4]); + mapping[2], + // $FlowIssue #15579526 + mapping[3], + // $FlowIssue #15579526 + mapping[4], + ); } else { throw new Error(`Invalid mapping: [${mapping.join(', ')}]`); } diff --git a/packages/metro-bundler/src/Bundler/util.js b/packages/metro-bundler/src/Bundler/util.js index c6c71ec6..00428baf 100644 --- a/packages/metro-bundler/src/Bundler/util.js +++ b/packages/metro-bundler/src/Bundler/util.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @flow + * @format */ 'use strict'; @@ -23,27 +24,41 @@ type SubTree = ( moduleTransportsByPath: Map, ) => Generator; -const assetPropertyBlacklist = new Set([ - 'files', - 'fileSystemLocation', - 'path', -]); +const assetPropertyBlacklist = new Set(['files', 'fileSystemLocation', 'path']); function generateAssetCodeFileAst( assetRegistryPath: string, assetDescriptor: AssetDescriptor, ): Object { - const properDescriptor = filterObject(assetDescriptor, assetPropertyBlacklist); - const descriptorAst = babylon.parseExpression(JSON.stringify(properDescriptor)); + const properDescriptor = filterObject( + assetDescriptor, + assetPropertyBlacklist, + ); + const descriptorAst = babylon.parseExpression( + JSON.stringify(properDescriptor), + ); const t = babel.types; - const moduleExports = t.memberExpression(t.identifier('module'), t.identifier('exports')); - const requireCall = - t.callExpression(t.identifier('require'), [t.stringLiteral(assetRegistryPath)]); - 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)), - ])); + const moduleExports = t.memberExpression( + t.identifier('module'), + t.identifier('exports'), + ); + const requireCall = t.callExpression(t.identifier('require'), [ + t.stringLiteral(assetRegistryPath), + ]); + 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( @@ -66,9 +81,11 @@ function generateAssetTransformResult( // 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. function isAssetTypeAnImage(type: string): boolean { - return [ - 'png', 'jpg', 'jpeg', 'bmp', 'gif', 'webp', 'psd', 'svg', 'tiff', - ].indexOf(type) !== -1; + return ( + ['png', 'jpg', 'jpeg', 'bmp', 'gif', 'webp', 'psd', 'svg', 'tiff'].indexOf( + type, + ) !== -1 + ); } function filterObject(object, blacklist) { @@ -95,18 +112,17 @@ function createRamBundleGroups( // build a map of group root IDs to an array of module IDs in the group const result: Map> = new Map( - ramGroups - .map(modulePath => { - const root = byPath.get(modulePath); - if (root == null) { - throw Error(`Group root ${modulePath} is not part of the bundle`); - } - return [ - root.id, - // `subtree` yields the IDs of all transitive dependencies of a module - new Set(subtree(root, byPath)), - ]; - }) + ramGroups.map(modulePath => { + const root = byPath.get(modulePath); + if (root == null) { + throw Error(`Group root ${modulePath} is not part of the bundle`); + } + return [ + root.id, + // `subtree` yields the IDs of all transitive dependencies of a module + new Set(subtree(root, byPath)), + ]; + }), ); if (ramGroups.length > 1) { @@ -124,9 +140,10 @@ function createRamBundleGroups( const parentNames = parents.map(byId.get, byId); const lastName = parentNames.pop(); throw new Error( - `Module ${byId.get(moduleId) || moduleId} belongs to groups ${ - parentNames.join(', ')}, and ${String(lastName) - }. Ensure that each module is only part of one group.` + `Module ${byId.get(moduleId) || + moduleId} belongs to groups ${parentNames.join(', ')}, and ${String( + lastName, + )}. Ensure that each module is only part of one group.`, ); } } @@ -134,7 +151,7 @@ function createRamBundleGroups( return result; } -function * filter(iterator, predicate) { +function* filter(iterator, predicate) { for (const value of iterator) { if (predicate(value)) { yield value;