From ff7ec2e6bfcb631b1103c4e3807551bafa276d49 Mon Sep 17 00:00:00 2001 From: Rafael Oleza Date: Tue, 31 Oct 2017 10:17:35 -0700 Subject: [PATCH] Prepare metro to build production bundles using the Delta Bundler Reviewed By: davidaurelio Differential Revision: D6186012 fbshipit-source-id: a9c2757211345881e3feba331bdbe66cdc0f6b97 --- packages/metro-bundler/src/Bundler/index.js | 4 + .../src/DeltaBundler/Serializers.js | 67 +++- .../__tests__/Serializers-test.js | 72 ++++- .../__snapshots__/Serializers-test.js.snap | 290 ++++++++++++++++++ .../metro-bundler/src/DeltaBundler/index.js | 4 + packages/metro-bundler/src/Server/index.js | 27 ++ 6 files changed, 460 insertions(+), 4 deletions(-) diff --git a/packages/metro-bundler/src/Bundler/index.js b/packages/metro-bundler/src/Bundler/index.js index 3d098f37..9367c01d 100644 --- a/packages/metro-bundler/src/Bundler/index.js +++ b/packages/metro-bundler/src/Bundler/index.js @@ -248,6 +248,10 @@ class Bundler { this._getTransformOptions = opts.getTransformOptions; } + getAssetServer(): AssetServer { + return this._assetServer; + } + end() { this._transformer.kill(); /* $FlowFixMe(>=0.54.0 site=react_native_fb) This comment suppresses an diff --git a/packages/metro-bundler/src/DeltaBundler/Serializers.js b/packages/metro-bundler/src/DeltaBundler/Serializers.js index 156d5fd7..dca38b4c 100644 --- a/packages/metro-bundler/src/DeltaBundler/Serializers.js +++ b/packages/metro-bundler/src/DeltaBundler/Serializers.js @@ -16,8 +16,10 @@ const DeltaPatcher = require('./DeltaPatcher'); const {fromRawMappings} = require('../Bundler/source-map'); +import type {AssetData} from '../AssetServer'; import type {BundleOptions} from '../Server'; import type {MappingsMap} from '../lib/SourceMap'; +import type {ModuleTransportLike} from '../shared/types.flow'; import type DeltaBundler, {Options as BuildOptions} from './'; import type {DeltaEntry, DeltaTransformResponse} from './DeltaTransformer'; @@ -25,6 +27,14 @@ export type Options = BundleOptions & { deltaBundleId: ?string, }; +export type RamModule = ModuleTransportLike; + +export type RamBundleInfo = { + startupModules: $ReadOnlyArray, + lazyModules: $ReadOnlyArray, + groups: Map>, +}; + /** * This module contains many serializers for the Delta Bundler. Each serializer * returns a string representation for any specific type of bundle, which can @@ -115,7 +125,7 @@ async function fullBundle( async function getAllModules( deltaBundler: DeltaBundler, options: Options, -): Promise> { +): Promise<$ReadOnlyArray> { const {id, delta} = await _build(deltaBundler, { ...options, wrapModules: true, @@ -126,6 +136,59 @@ async function getAllModules( .getAllModules(); } +async function getRamBundleInfo( + deltaBundler: DeltaBundler, + options: Options, +): Promise { + let modules = await getAllModules(deltaBundler, options); + + modules = modules.map(module => { + const map = fromRawMappings([module]).toMap(module.path, { + excludeSource: options.excludeSource, + }); + + return { + id: module.id, + code: module.code, + map, + name: module.name, + sourcePath: module.path, + source: module.source, + type: module.type, + }; + }); + + const startupModules = modules.filter(module => { + return module.type === 'script' || module.type === 'require'; + }); + const lazyModules = modules.filter(module => { + return module.type === 'asset' || module.type === 'module'; + }); + + // TODO: Implement RAM groups functionality in Delta Bundler. + return {startupModules, lazyModules, groups: new Map()}; +} + +async function getAssets( + deltaBundler: DeltaBundler, + options: Options, +): Promise<$ReadOnlyArray> { + const modules = await getAllModules(deltaBundler, options); + + const assets = await Promise.all( + modules.map(async module => { + if (module.type === 'asset') { + return await deltaBundler + .getAssetServer() + .getAssetData(module.path, options.platform); + } + return null; + }), + ); + + return assets.filter(Boolean); +} + async function _build( deltaBundler: DeltaBundler, options: BuildOptions, @@ -146,4 +209,6 @@ module.exports = { fullSourceMap, fullSourceMapObject, getAllModules, + getAssets, + getRamBundleInfo, }; diff --git a/packages/metro-bundler/src/DeltaBundler/__tests__/Serializers-test.js b/packages/metro-bundler/src/DeltaBundler/__tests__/Serializers-test.js index ee2c7bd7..3c7df543 100644 --- a/packages/metro-bundler/src/DeltaBundler/__tests__/Serializers-test.js +++ b/packages/metro-bundler/src/DeltaBundler/__tests__/Serializers-test.js @@ -22,9 +22,12 @@ describe('Serializers', () => { let deltaBundler; const deltaResponse = { - pre: new Map([[1, {code: 'pre;'}]]), - post: new Map([[2, {code: 'post;'}]]), - delta: new Map([[3, {code: 'module3;'}], [4, {code: 'another;'}]]), + pre: new Map([[1, {type: 'script', code: 'pre;', id: 1, path: '/pre.js'}]]), + post: new Map([[2, {type: 'require', code: 'post;', id: 2, path: '/p'}]]), + delta: new Map([ + [3, {type: 'module', code: 'module3;', id: 3, path: '/3.js'}], + [4, {type: 'module', code: 'another;', id: 4, path: '/4.js'}], + ]), inverseDependencies: [], reset: true, }; @@ -45,6 +48,13 @@ describe('Serializers', () => { }, }; }, + getAssetServer() { + return { + async getAssetData(path, platform) { + return {path, platform, assetData: true}; + }, + }; + }, }; setCurrentTime(CURRENT_TIME); @@ -133,4 +143,60 @@ describe('Serializers', () => { await Serializers.getAllModules(deltaBundler, {deltaBundleId: 10}), ).toMatchSnapshot(); }); + + it('should return the RAM bundle info', async () => { + expect( + await Serializers.getRamBundleInfo(deltaBundler, {deltaBundleId: 10}), + ).toMatchSnapshot(); + + getDelta.mockReturnValueOnce( + Promise.resolve({ + delta: new Map([ + [3, {type: 'module', code: 'modified;', id: 3, path: '/foo/3.js'}], + [7, {type: 'asset', code: 'code', id: 7, path: '/foo/path.png'}], + [8, {type: 'module', code: 'code', id: 8, path: '/foo/8.js'}], + [9, {type: 'asset', code: 'code', id: 9, path: '/foo/path3.png'}], + ]), + pre: new Map([ + [5, {type: 'script', code: 'more pre;', id: 5, path: '/foo/5.js'}], + ]), + post: new Map([ + [6, {type: 'require', code: 'bananas;', id: 6, path: '/foo/6.js'}], + [10, {type: 'comment', code: 'bananas;', id: 10, path: '/foo/10.js'}], + ]), + inverseDependencies: [], + }), + ); + + expect( + await Serializers.getRamBundleInfo(deltaBundler, {deltaBundleId: 10}), + ).toMatchSnapshot(); + }); + + it('should return the bundle assets', async () => { + expect( + await Serializers.getAllModules(deltaBundler, {deltaBundleId: 10}), + ).toMatchSnapshot(); + + getDelta.mockReturnValueOnce( + Promise.resolve({ + delta: new Map([ + [3, {code: 'modified module;'}], + [7, {code: 'code', type: 'asset', path: '/foo/path.png'}], + [8, {code: 'code', type: 'module', path: '/foo/path2.png'}], + [9, {code: 'code', type: 'asset', path: '/foo/path3.png'}], + ]), + pre: new Map([[5, {code: 'more pre;'}]]), + post: new Map([[6, {code: 'bananas;'}]]), + inverseDependencies: [], + }), + ); + + expect( + await Serializers.getAssets(deltaBundler, { + deltaBundleId: 10, + platform: 'ios', + }), + ).toMatchSnapshot(); + }); }); diff --git a/packages/metro-bundler/src/DeltaBundler/__tests__/__snapshots__/Serializers-test.js.snap b/packages/metro-bundler/src/DeltaBundler/__tests__/__snapshots__/Serializers-test.js.snap index c8b7fb01..e27b9c67 100644 --- a/packages/metro-bundler/src/DeltaBundler/__tests__/__snapshots__/Serializers-test.js.snap +++ b/packages/metro-bundler/src/DeltaBundler/__tests__/__snapshots__/Serializers-test.js.snap @@ -32,15 +32,27 @@ exports[`Serializers should return all the bundle modules 1`] = ` Array [ Object { "code": "pre;", + "id": 1, + "path": "/pre.js", + "type": "script", }, Object { "code": "module3;", + "id": 3, + "path": "/3.js", + "type": "module", }, Object { "code": "another;", + "id": 4, + "path": "/4.js", + "type": "module", }, Object { "code": "post;", + "id": 2, + "path": "/p", + "type": "require", }, ] `; @@ -49,6 +61,9 @@ exports[`Serializers should return all the bundle modules 2`] = ` Array [ Object { "code": "pre;", + "id": 1, + "path": "/pre.js", + "type": "script", }, Object { "code": "more pre;", @@ -58,6 +73,9 @@ Array [ }, Object { "code": "post;", + "id": 2, + "path": "/p", + "type": "require", }, Object { "code": "bananas;", @@ -68,6 +86,278 @@ Array [ ] `; +exports[`Serializers should return the RAM bundle info 1`] = ` +Object { + "groups": Map {}, + "lazyModules": Array [ + Object { + "code": "module3;", + "id": 3, + "map": Object { + "file": "/3.js", + "mappings": "", + "names": Array [], + "sources": Array [], + "sourcesContent": Array [], + "version": 3, + }, + "name": undefined, + "source": undefined, + "sourcePath": "/3.js", + "type": "module", + }, + Object { + "code": "another;", + "id": 4, + "map": Object { + "file": "/4.js", + "mappings": "", + "names": Array [], + "sources": Array [], + "sourcesContent": Array [], + "version": 3, + }, + "name": undefined, + "source": undefined, + "sourcePath": "/4.js", + "type": "module", + }, + ], + "startupModules": Array [ + Object { + "code": "pre;", + "id": 1, + "map": Object { + "file": "/pre.js", + "mappings": "", + "names": Array [], + "sources": Array [], + "sourcesContent": Array [], + "version": 3, + }, + "name": undefined, + "source": undefined, + "sourcePath": "/pre.js", + "type": "script", + }, + Object { + "code": "post;", + "id": 2, + "map": Object { + "file": "/p", + "mappings": "", + "names": Array [], + "sources": Array [], + "sourcesContent": Array [], + "version": 3, + }, + "name": undefined, + "source": undefined, + "sourcePath": "/p", + "type": "require", + }, + ], +} +`; + +exports[`Serializers should return the RAM bundle info 2`] = ` +Object { + "groups": Map {}, + "lazyModules": Array [ + Object { + "code": "modified;", + "id": 3, + "map": Object { + "file": "/foo/3.js", + "mappings": "", + "names": Array [], + "sources": Array [], + "sourcesContent": Array [], + "version": 3, + }, + "name": undefined, + "source": undefined, + "sourcePath": "/foo/3.js", + "type": "module", + }, + Object { + "code": "another;", + "id": 4, + "map": Object { + "file": "/4.js", + "mappings": "", + "names": Array [], + "sources": Array [], + "sourcesContent": Array [], + "version": 3, + }, + "name": undefined, + "source": undefined, + "sourcePath": "/4.js", + "type": "module", + }, + Object { + "code": "code", + "id": 7, + "map": Object { + "file": "/foo/path.png", + "mappings": "", + "names": Array [], + "sources": Array [], + "sourcesContent": Array [], + "version": 3, + }, + "name": undefined, + "source": undefined, + "sourcePath": "/foo/path.png", + "type": "asset", + }, + Object { + "code": "code", + "id": 8, + "map": Object { + "file": "/foo/8.js", + "mappings": "", + "names": Array [], + "sources": Array [], + "sourcesContent": Array [], + "version": 3, + }, + "name": undefined, + "source": undefined, + "sourcePath": "/foo/8.js", + "type": "module", + }, + Object { + "code": "code", + "id": 9, + "map": Object { + "file": "/foo/path3.png", + "mappings": "", + "names": Array [], + "sources": Array [], + "sourcesContent": Array [], + "version": 3, + }, + "name": undefined, + "source": undefined, + "sourcePath": "/foo/path3.png", + "type": "asset", + }, + ], + "startupModules": Array [ + Object { + "code": "pre;", + "id": 1, + "map": Object { + "file": "/pre.js", + "mappings": "", + "names": Array [], + "sources": Array [], + "sourcesContent": Array [], + "version": 3, + }, + "name": undefined, + "source": undefined, + "sourcePath": "/pre.js", + "type": "script", + }, + Object { + "code": "more pre;", + "id": 5, + "map": Object { + "file": "/foo/5.js", + "mappings": "", + "names": Array [], + "sources": Array [], + "sourcesContent": Array [], + "version": 3, + }, + "name": undefined, + "source": undefined, + "sourcePath": "/foo/5.js", + "type": "script", + }, + Object { + "code": "post;", + "id": 2, + "map": Object { + "file": "/p", + "mappings": "", + "names": Array [], + "sources": Array [], + "sourcesContent": Array [], + "version": 3, + }, + "name": undefined, + "source": undefined, + "sourcePath": "/p", + "type": "require", + }, + Object { + "code": "bananas;", + "id": 6, + "map": Object { + "file": "/foo/6.js", + "mappings": "", + "names": Array [], + "sources": Array [], + "sourcesContent": Array [], + "version": 3, + }, + "name": undefined, + "source": undefined, + "sourcePath": "/foo/6.js", + "type": "require", + }, + ], +} +`; + +exports[`Serializers should return the bundle assets 1`] = ` +Array [ + Object { + "code": "pre;", + "id": 1, + "path": "/pre.js", + "type": "script", + }, + Object { + "code": "module3;", + "id": 3, + "path": "/3.js", + "type": "module", + }, + Object { + "code": "another;", + "id": 4, + "path": "/4.js", + "type": "module", + }, + Object { + "code": "post;", + "id": 2, + "path": "/p", + "type": "require", + }, +] +`; + +exports[`Serializers should return the bundle assets 2`] = ` +Array [ + Object { + "assetData": true, + "path": "/foo/path.png", + "platform": "ios", + }, + Object { + "assetData": true, + "path": "/foo/path3.png", + "platform": "ios", + }, +] +`; + exports[`Serializers should return the stringified delta bundle 1`] = ` Object { "bundle": "{\\"id\\":\\"1234\\",\\"pre\\":[[1,\\"pre;\\"]],\\"post\\":[[2,\\"post;\\"]],\\"delta\\":[[3,\\"module3;\\"],[4,\\"another;\\"]],\\"reset\\":true}", diff --git a/packages/metro-bundler/src/DeltaBundler/index.js b/packages/metro-bundler/src/DeltaBundler/index.js index 1707bbf9..19547471 100644 --- a/packages/metro-bundler/src/DeltaBundler/index.js +++ b/packages/metro-bundler/src/DeltaBundler/index.js @@ -50,6 +50,10 @@ class DeltaBundler { this._deltaTransformers = new Map(); } + getAssetServer() { + return this._bundler.getAssetServer(); + } + async getDeltaTransformer( options: Options, ): Promise<{deltaTransformer: DeltaTransformer, id: string}> { diff --git a/packages/metro-bundler/src/Server/index.js b/packages/metro-bundler/src/Server/index.js index 89ce1225..1ce07047 100644 --- a/packages/metro-bundler/src/Server/index.js +++ b/packages/metro-bundler/src/Server/index.js @@ -47,6 +47,8 @@ import type { import type {TransformCache} from '../lib/TransformCaching'; import type {GlobalTransformCache} from '../lib/GlobalTransformCache'; import type {SourceMap, Symbolicate} from './symbolicate'; +import type {AssetData} from '../AssetServer'; +import type {RamBundleInfo} from '../DeltaBundler/Serializers'; const { createActionStartEntry, @@ -348,6 +350,31 @@ class Server { return bundle; } + async build(options: BundleOptions): Promise<{code: string, map: string}> { + options = { + ...options, + deltaBundleId: null, + }; + + return { + code: (await Serializers.fullBundle(this._deltaBundler, options)).bundle, + map: await Serializers.fullSourceMap(this._deltaBundler, options), + }; + } + + async getRamBundleInfo(options: BundleOptions): Promise { + options = {...options, deltaBundleId: null}; + + return await Serializers.getRamBundleInfo(this._deltaBundler, options); + } + + async getAssets(options: BundleOptions): Promise<$ReadOnlyArray> { + return await Serializers.getAssets(this._deltaBundler, { + ...options, + deltaBundleId: null, + }); + } + buildBundleFromUrl(reqUrl: string): Promise { const options = this._getOptionsFromUrl(reqUrl); return this.buildBundle(options);