diff --git a/flow/babel.js.flow b/flow/babel.js.flow index 372b4ea0755..ad97546cc12 100644 --- a/flow/babel.js.flow +++ b/flow/babel.js.flow @@ -9,11 +9,11 @@ type _SourceMap = { version: number, - file: string, + file?: string, sources: Array, names: Array, mappings: string, - sourcesContent: Array, + sourcesContent?: Array, }; // based on babylon v6.13.1 diff --git a/local-cli/bundle/buildBundle.js b/local-cli/bundle/buildBundle.js index 0a4d0282816..a8b3c44eacc 100644 --- a/local-cli/bundle/buildBundle.js +++ b/local-cli/bundle/buildBundle.js @@ -16,6 +16,8 @@ const path = require('path'); const saveAssets = require('./saveAssets'); const defaultAssetExts = require('../../packager/defaults').assetExts; +import type RequestOptions from './types.flow'; + function saveBundle(output, bundle, args) { return Promise.resolve( output.save(bundle, args, log) @@ -27,7 +29,7 @@ function buildBundle(args, config, output = outputBundle, packagerInstance) { // have other choice than defining it as an env variable here. process.env.NODE_ENV = args.dev ? 'development' : 'production'; - const requestOpts = { + const requestOpts: RequestOptions = { entryFile: args.entryFile, sourceMapUrl: args.sourcemapOutput && path.basename(args.sourcemapOutput), dev: args.dev, diff --git a/local-cli/bundle/output/bundle.js b/local-cli/bundle/output/bundle.js index a54ebe9de85..06217ba3061 100644 --- a/local-cli/bundle/output/bundle.js +++ b/local-cli/bundle/output/bundle.js @@ -5,27 +5,37 @@ * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. + * + * @flow */ 'use strict'; const meta = require('./meta'); const writeFile = require('./writeFile'); -function buildBundle(packagerClient, requestOptions) { +import type Bundle from '../../../packager/react-packager/src/Bundler/Bundle'; +import type Server from '../../../packager/react-packager/src/Server'; +import type {OutputOptions, RequestOptions} from '../types.flow'; + +function buildBundle(packagerClient: Server, requestOptions: RequestOptions) { return packagerClient.buildBundle({ ...requestOptions, isolateModuleIDs: true, }); } -function createCodeWithMap(bundle, dev) { +function createCodeWithMap(bundle: Bundle, dev: boolean): * { return { code: bundle.getSource({dev}), map: JSON.stringify(bundle.getSourceMap({dev})), }; } -function saveBundleAndMap(bundle, options, log) { +function saveBundleAndMap( + bundle: Bundle, + options: OutputOptions, + log: (x: string) => {}, +): Promise<> { const { bundleOutput, bundleEncoding: encoding, @@ -34,7 +44,7 @@ function saveBundleAndMap(bundle, options, log) { } = options; log('start'); - const codeWithMap = createCodeWithMap(bundle, dev); + const codeWithMap = createCodeWithMap(bundle, !!dev); log('finish'); log('Writing bundle output to:', bundleOutput); diff --git a/local-cli/bundle/output/unbundle/as-assets.js b/local-cli/bundle/output/unbundle/as-assets.js index 82258502cb5..ae2d8c1d813 100644 --- a/local-cli/bundle/output/unbundle/as-assets.js +++ b/local-cli/bundle/output/unbundle/as-assets.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. + * + * @flow */ 'use strict'; @@ -18,6 +20,9 @@ const writeSourceMap = require('./write-sourcemap'); const {joinModules} = require('./util'); +import type Bundle from '../../../../packager/react-packager/src/Bundler/Bundle'; +import type {OutputOptions} from '../../types.flow'; + // must not start with a dot, as that won't go into the apk const MAGIC_UNBUNDLE_FILENAME = 'UNBUNDLE'; const MODULES_DIR = 'js-modules'; @@ -29,7 +34,11 @@ const MODULES_DIR = 'js-modules'; * All other modules go into a 'js-modules' folder that in the same parent * directory as the startup file. */ -function saveAsAssets(bundle, options, log) { +function saveAsAssets( + bundle: Bundle, + options: OutputOptions, + log: (x: string) => void, +): Promise { const { bundleOutput, bundleEncoding: encoding, @@ -54,11 +63,14 @@ function saveAsAssets(bundle, options, log) { writeUnbundle.then(() => log('Done writing unbundle output')); const sourceMap = - buildSourceMapWithMetaData({startupModules, lazyModules}); + buildSourceMapWithMetaData({ + startupModules: startupModules.concat(), + lazyModules: lazyModules.concat(), + }); return Promise.all([ writeUnbundle, - writeSourceMap(sourcemapOutput, JSON.stringify(sourceMap), log) + sourcemapOutput && writeSourceMap(sourcemapOutput, JSON.stringify(sourceMap), log) ]); } @@ -80,8 +92,8 @@ function writeModules(modules, modulesDir, encoding) { function writeMagicFlagFile(outputDir) { /* global Buffer: true */ - const buffer = Buffer(4); - buffer.writeUInt32LE(MAGIC_UNBUNDLE_NUMBER); + const buffer = new Buffer(4); + buffer.writeUInt32LE(MAGIC_UNBUNDLE_NUMBER, 0); return writeFile(path.join(outputDir, MAGIC_UNBUNDLE_FILENAME), buffer); } diff --git a/local-cli/bundle/output/unbundle/as-indexed-file.js b/local-cli/bundle/output/unbundle/as-indexed-file.js index 573a0df5a43..05978ec8f8a 100644 --- a/local-cli/bundle/output/unbundle/as-indexed-file.js +++ b/local-cli/bundle/output/unbundle/as-indexed-file.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. + * + * @flow */ 'use strict'; @@ -15,6 +17,10 @@ const fs = require('fs'); const writeSourceMap = require('./write-sourcemap'); const {joinModules} = require('./util'); + +import type ModuleTransport from '../../../../packager/react-packager/src/lib/ModuleTransport'; +import type {Bundle, ModuleGroups, OutputOptions} from '../../types.flow'; + const SIZEOF_UINT32 = 4; /** @@ -24,18 +30,22 @@ const SIZEOF_UINT32 = 4; * The module id for the startup code (prelude, polyfills etc.) is the * empty string. */ -function saveAsIndexedFile(bundle, options, log) { +function saveAsIndexedFile( + bundle: Bundle, + options: OutputOptions, + log: (x: string) => void, +): Promise<> { const { bundleOutput, bundleEncoding: encoding, - sourcemapOutput + sourcemapOutput, } = options; log('start'); const {startupModules, lazyModules, groups} = bundle.getUnbundle(); log('finish'); - const moduleGroups = ModuleGroups(groups, lazyModules); + const moduleGroups = createModuleGroups(groups, lazyModules); const startupCode = joinModules(startupModules); log('Writing unbundle output to:', bundleOutput); @@ -45,21 +55,26 @@ function saveAsIndexedFile(bundle, options, log) { ).then(() => log('Done writing unbundle output')); const sourceMap = - buildSourceMapWithMetaData({startupModules, lazyModules, moduleGroups}); + buildSourceMapWithMetaData({ + startupModules: startupModules.concat(), + lazyModules: lazyModules.concat(), + moduleGroups, + }); return Promise.all([ writeUnbundle, - writeSourceMap(sourcemapOutput, JSON.stringify(sourceMap), log), + sourcemapOutput && writeSourceMap(sourcemapOutput, JSON.stringify(sourceMap), log), ]); } /* global Buffer: true */ -const fileHeader = Buffer(4); -fileHeader.writeUInt32LE(MAGIC_UNBUNDLE_FILE_HEADER); -const nullByteBuffer = Buffer(1).fill(0); +const fileHeader = new Buffer(4); +fileHeader.writeUInt32LE(MAGIC_UNBUNDLE_FILE_HEADER, 0); +//$FlowIssue #14640206 +const nullByteBuffer: Buffer = new Buffer(1).fill(0); -function writeBuffers(stream, buffers) { +function writeBuffers(stream, buffers: Array) { buffers.forEach(buffer => stream.write(buffer)); return new Promise((resolve, reject) => { stream.on('error', reject); @@ -69,7 +84,7 @@ function writeBuffers(stream, buffers) { } function nullTerminatedBuffer(contents, encoding) { - return Buffer.concat([Buffer(contents, encoding), nullByteBuffer]); + return Buffer.concat([new Buffer(contents, encoding), nullByteBuffer]); } function moduleToBuffer(id, code, encoding) { @@ -98,7 +113,8 @@ function buildModuleTable(startupCode, buffers, moduleGroups) { const moduleIds = Array.from(moduleGroups.modulesById.keys()); const maxId = moduleIds.reduce((max, id) => Math.max(max, id)); const numEntries = maxId + 1; - const table = new Buffer(entryOffset(numEntries)).fill(0); + //$FlowIssue #14640206 + const table: Buffer = new Buffer(entryOffset(numEntries)).fill(0); // num_entries table.writeUInt32LE(numEntries, 0); @@ -109,9 +125,8 @@ function buildModuleTable(startupCode, buffers, moduleGroups) { // entries let codeOffset = startupCode.length; buffers.forEach(({id, buffer}) => { - const idsInGroup = moduleGroups.groups.has(id) - ? [id].concat(Array.from(moduleGroups.groups.get(id))) - : [id]; + const group = moduleGroups.groups.get(id); + const idsInGroup = group ? [id].concat(Array.from(group)) : [id]; idsInGroup.forEach(moduleId => { const offset = entryOffset(moduleId); @@ -132,7 +147,7 @@ function groupCode(rootCode, moduleGroup, modulesById) { } const code = [rootCode]; for (const id of moduleGroup) { - code.push(modulesById.get(id).code); + code.push((modulesById.get(id) || {}).code); } return code.join('\n'); @@ -170,7 +185,10 @@ function buildTableAndContents(startupCode, modules, moduleGroups, encoding) { ].concat(moduleBuffers.map(({buffer}) => buffer)); } -function ModuleGroups(groups, modules) { +function createModuleGroups( + groups: Map>, + modules: Array, +): ModuleGroups { return { groups, modulesById: new Map(modules.map(m => [m.id, m])), diff --git a/local-cli/bundle/output/unbundle/build-unbundle-sourcemap-with-metadata.js b/local-cli/bundle/output/unbundle/build-unbundle-sourcemap-with-metadata.js index 0d69657c8a6..74c2348fb1b 100644 --- a/local-cli/bundle/output/unbundle/build-unbundle-sourcemap-with-metadata.js +++ b/local-cli/bundle/output/unbundle/build-unbundle-sourcemap-with-metadata.js @@ -5,14 +5,25 @@ * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. + * + * @flow */ 'use strict'; const {combineSourceMaps, joinModules} = require('./util'); -module.exports = ({startupModules, lazyModules, moduleGroups}) => { - const startupModule = { +import type {ModuleGroups, ModuleTransportLike} from '../../types.flow'; + +type Params = { + lazyModules: Array, + moduleGroups?: ModuleGroups, + startupModules: Array, +}; + +module.exports = ({startupModules, lazyModules, moduleGroups}: Params) => { + const startupModule: ModuleTransportLike = { code: joinModules(startupModules), + id: Number.MIN_SAFE_INTEGER, map: combineSourceMaps({modules: startupModules}), }; return combineSourceMaps({ diff --git a/local-cli/bundle/output/unbundle/index.js b/local-cli/bundle/output/unbundle/index.js index 8b9e9cda17d..aaa9c2de66c 100644 --- a/local-cli/bundle/output/unbundle/index.js +++ b/local-cli/bundle/output/unbundle/index.js @@ -5,13 +5,19 @@ * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. + * + * @flow */ 'use strict'; const asAssets = require('./as-assets'); const asIndexedFile = require('./as-indexed-file'); -function buildBundle(packagerClient, requestOptions) { +import type Bundle from '../../../../packager/react-packager/src/Bundler/Bundle'; +import type Server from '../../../../packager/react-packager/src/Server'; +import type {OutputOptions, RequestOptions} from '../../types.flow'; + +function buildBundle(packagerClient: Server, requestOptions: RequestOptions) { return packagerClient.buildBundle({ ...requestOptions, unbundle: true, @@ -19,7 +25,11 @@ function buildBundle(packagerClient, requestOptions) { }); } -function saveUnbundle(bundle, options, log) { +function saveUnbundle( + bundle: Bundle, + options: OutputOptions, + log: (x: string) => void, +): Promise { // we fork here depending on the platform: // while android is pretty good at loading individual assets, ios has a large // overhead when reading hundreds pf assets from disk diff --git a/local-cli/bundle/output/unbundle/magic-number.js b/local-cli/bundle/output/unbundle/magic-number.js index 6e6a9d3759d..64293f8762a 100644 --- a/local-cli/bundle/output/unbundle/magic-number.js +++ b/local-cli/bundle/output/unbundle/magic-number.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. + * + * @flow */ 'use strict'; diff --git a/local-cli/bundle/output/unbundle/util.js b/local-cli/bundle/output/unbundle/util.js index 16458bae10e..ab48c31a075 100644 --- a/local-cli/bundle/output/unbundle/util.js +++ b/local-cli/bundle/output/unbundle/util.js @@ -5,14 +5,19 @@ * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. + * + * @flow */ 'use strict'; -const newline = /\r\n?|\n|\u2028|\u2029/g; -const countLines = - string => (string.match(newline) || []).length + 1; // fastest implementation +import type {ModuleGroups, ModuleTransportLike, SourceMap} from '../../types.flow'; -function lineToLineSourceMap(source, filename) { +const newline = /\r\n?|\n|\u2028|\u2029/g; +// fastest implementation +const countLines = (string: string) => (string.match(newline) || []).length + 1; + + +function lineToLineSourceMap(source: string, filename: string = ''): SourceMap { // The first line mapping in our package is the base64vlq code for zeros (A). const firstLine = 'AAAA;'; @@ -21,34 +26,52 @@ function lineToLineSourceMap(source, filename) { const line = 'AACA;'; return { - version: 3, - sources: [filename], + file: filename, mappings: firstLine + Array(countLines(source)).join(line), + sources: [filename], + names: [], + version: 3, }; } const wrapperEnd = wrappedCode => wrappedCode.indexOf('{') + 1; -const Section = (line, column, map) => ({map, offset: {line, column}}); +const Section = + (line: number, column: number, map: SourceMap) => + ({map, offset: {line, column}}); -function combineSourceMaps({modules, withCustomOffsets, moduleGroups}) { +type CombineSourceMapsOptions = { + moduleGroups?: ModuleGroups, + modules: Array, + withCustomOffsets?: boolean, +}; + +function combineSourceMaps({ + moduleGroups, + modules, + withCustomOffsets, +}: CombineSourceMapsOptions): SourceMap { let offsets; const sections = []; - const sourceMap = { - version: 3, + const sourceMap: Object = { + file: '', sections, + version: 3, }; + if (withCustomOffsets) { offsets = sourceMap.x_facebook_offsets = []; } let line = 0; - modules.forEach(({code, id, map, name}) => { + modules.forEach(moduleTransport => { + const {code, id, name} = moduleTransport; let column = 0; let hasOffset = false; let group; let groupLines = 0; + let {map} = moduleTransport; if (withCustomOffsets) { if (moduleGroups && moduleGroups.modulesInGroups.has(id)) { @@ -56,15 +79,19 @@ function combineSourceMaps({modules, withCustomOffsets, moduleGroups}) { return; } - if (moduleGroups && moduleGroups.groups.has(id)) { - group = moduleGroups.groups.get(id); - const otherModules = Array.from(group).map( - moduleId => moduleGroups.modulesById.get(moduleId)); + + group = moduleGroups && moduleGroups.groups.get(id); + if (group && moduleGroups) { + const {modulesById} = moduleGroups; + const otherModules: Array = + Array.from(group || []) + .map(moduleId => modulesById.get(moduleId)) + .filter(Boolean); // needed to appease flow otherModules.forEach(m => { groupLines += countLines(m.code); }); map = combineSourceMaps({ - modules: [{code, id, map, name}].concat(otherModules), + modules: [moduleTransport].concat(otherModules), }); } @@ -85,7 +112,9 @@ function combineSourceMaps({modules, withCustomOffsets, moduleGroups}) { return sourceMap; } -const joinModules = modules => modules.map(m => m.code).join('\n'); +const joinModules = + (modules: Array<*>): string => + modules.map(m => m.code).join('\n'); module.exports = { countLines, diff --git a/local-cli/bundle/output/unbundle/write-sourcemap.js b/local-cli/bundle/output/unbundle/write-sourcemap.js index f10e8aa2dc2..35af1b77b67 100644 --- a/local-cli/bundle/output/unbundle/write-sourcemap.js +++ b/local-cli/bundle/output/unbundle/write-sourcemap.js @@ -5,12 +5,18 @@ * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. + * + * @flow */ 'use strict'; const writeFile = require('../writeFile'); -function writeSourcemap(fileName, contents, log) { +function writeSourcemap( + fileName: string, + contents: string, + log: (x: string) => void, +): Promise<> { if (!fileName) { return Promise.resolve(); } diff --git a/local-cli/bundle/output/writeFile.js b/local-cli/bundle/output/writeFile.js index 794b39020d1..445ccef2259 100644 --- a/local-cli/bundle/output/writeFile.js +++ b/local-cli/bundle/output/writeFile.js @@ -5,20 +5,16 @@ * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. + * + * @flow */ 'use strict'; +const denodeify = require('denodeify'); const fs = require('fs'); -function writeFile(file, data, encoding) { - return new Promise((resolve, reject) => { - fs.writeFile( - file, - data, - encoding, - error => error ? reject(error) : resolve() - ); - }); -} +type WriteFn = + (file: string, data: string | Buffer, encoding?: ?string) => Promise; +const writeFile: WriteFn = denodeify(fs.writeFile); module.exports = writeFile; diff --git a/local-cli/bundle/types.flow.js b/local-cli/bundle/types.flow.js new file mode 100644 index 00000000000..d0768fef854 --- /dev/null +++ b/local-cli/bundle/types.flow.js @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @flow + */ +'use strict'; + +import type Bundle from '../../packager/react-packager/src/Bundler/Bundle'; +import type {Unbundle} from '../../packager/react-packager/src/Bundler/Bundle'; +import type ModuleTransport from '../../packager/react-packager/src/lib/ModuleTransport'; +import type {MixedSourceMap} from '../../packager/react-packager/src/lib/SourceMap'; + +export type {Bundle, ModuleTransport, MixedSourceMap as SourceMap, Unbundle}; + +export type ModuleGroups = {| + groups: Map>, + modulesById: Map, + modulesInGroups: Set, +|}; + +export type ModuleTransportLike = { + code: string, + id: number, + map?: ?MixedSourceMap, + +name?: string, +}; + +export type OutputOptions = { + bundleOutput: string, + bundleEncoding?: 'utf8' | 'utf16le' | 'ascii', + dev?: boolean, + platform: string, + sourcemapOutput?: string, +}; + +export type RequestOptions = {| + entryFile: string, + sourceMapUrl?: string, + dev?: boolean, + minify: boolean, + platform: string, +|}; diff --git a/packager/react-packager/src/Bundler/Bundle.js b/packager/react-packager/src/Bundler/Bundle.js index 37b6f5e677d..bf4066960f5 100644 --- a/packager/react-packager/src/Bundler/Bundle.js +++ b/packager/react-packager/src/Bundler/Bundle.js @@ -21,6 +21,12 @@ const crypto = require('crypto'); import type {SourceMap, CombinedSourceMap, MixedSourceMap} from '../lib/SourceMap'; import type {GetSourceOptions, FinalizeOptions} from './BundleBase'; +export type Unbundle = { + startupModules: Array<*>, + lazyModules: Array<*>, + groups: Map>, +}; + const SOURCEMAPPING_URL = '\n\/\/# sourceMappingURL='; class Bundle extends BundleBase { @@ -29,7 +35,7 @@ class Bundle extends BundleBase { _inlineSourceMap: string | void; _minify: boolean | void; _numRequireCalls: number; - _ramBundle: mixed | void; + _ramBundle: Unbundle | null; _ramGroups: Array | void; _shouldCombineSourceMaps: boolean; _sourceMap: boolean; @@ -144,7 +150,7 @@ class Bundle extends BundleBase { return source; } - getUnbundle() { + getUnbundle(): Unbundle { this.assertFinalized(); if (!this._ramBundle) { const modules = this.getModules().slice(); @@ -419,7 +425,7 @@ function createGroups(ramGroups: Array, lazyModules) { }); // build a map of group root IDs to an array of module IDs in the group - const result = new Map( + const result: Map> = new Map( ramGroups .map(modulePath => { const root = byPath.get(modulePath); diff --git a/packager/react-packager/src/Bundler/BundleBase.js b/packager/react-packager/src/Bundler/BundleBase.js index 35c8a56dbbd..a39a14f6670 100644 --- a/packager/react-packager/src/Bundler/BundleBase.js +++ b/packager/react-packager/src/Bundler/BundleBase.js @@ -19,7 +19,7 @@ export type FinalizeOptions = { }; export type GetSourceOptions = { - inlineSourceMap: boolean, + inlineSourceMap?: boolean, dev: boolean, }; diff --git a/packager/react-packager/src/lib/ModuleTransport.js b/packager/react-packager/src/lib/ModuleTransport.js index 51a7780299a..1222ac9399c 100644 --- a/packager/react-packager/src/lib/ModuleTransport.js +++ b/packager/react-packager/src/lib/ModuleTransport.js @@ -11,7 +11,7 @@ 'use strict'; -import type {SourceMap} from './SourceMap'; +import type {MixedSourceMap} from './SourceMap'; type Metadata = { dependencyPairs?: Array<[mixed, {path: string}]>, @@ -21,25 +21,25 @@ type Metadata = { class ModuleTransport { name: string; - id: string | number; + id: number; code: string; sourceCode: string; sourcePath: string; virtual: ?boolean; meta: ?Metadata; polyfill: ?boolean; - map: ?SourceMap; + map: ?MixedSourceMap; constructor(data: { name: string, - id: string | number, + id: number, code: string, sourceCode: string, sourcePath: string, virtual?: ?boolean, meta?: ?Metadata, polyfill?: ?boolean, - map?: ?SourceMap, + map?: ?MixedSourceMap, }) { this.name = data.name; diff --git a/packager/react-packager/src/lib/SourceMap.js b/packager/react-packager/src/lib/SourceMap.js index a10ab619aff..5ffa42f0bb2 100644 --- a/packager/react-packager/src/lib/SourceMap.js +++ b/packager/react-packager/src/lib/SourceMap.js @@ -20,8 +20,14 @@ export type CombinedSourceMap = { file: string, sections: Array<{ offset: {line: number, column: number}, - map: SourceMap, + map: MixedSourceMap, }>, }; -export type MixedSourceMap = SourceMap | CombinedSourceMap; +type FBExtensions = {x_facebook_offsets?: Array}; + +export type MixedSourceMap + = SourceMap + | CombinedSourceMap + | (SourceMap & FBExtensions) + | (CombinedSourceMap & FBExtensions);