diff --git a/local-cli/bundle/output/unbundle/as-assets.js b/local-cli/bundle/output/unbundle/as-assets.js index 7a4378c8a7118e..da9e30af50b572 100644 --- a/local-cli/bundle/output/unbundle/as-assets.js +++ b/local-cli/bundle/output/unbundle/as-assets.js @@ -12,6 +12,7 @@ const mkdirp = require('mkdirp'); const path = require('path'); const Promise = require('promise'); +const buildSourceMapWithMetaData = require('./build-unbundle-sourcemap-with-metadata'); const writeFile = require('../writeFile'); const writeSourceMap = require('./write-sourcemap'); const MAGIC_UNBUNDLE_NUMBER = require('./magic-number'); @@ -29,12 +30,11 @@ function saveAsAssets(bundle, options, log) { const { 'bundle-output': bundleOutput, 'bundle-encoding': encoding, - dev, 'sourcemap-output': sourcemapOutput, } = options; log('start'); - const {startupCode, modules} = bundle.getUnbundle({minify: !dev}); + const {startupCode, startupModules, modules} = bundle.getUnbundle(); log('finish'); log('Writing bundle output to:', bundleOutput); @@ -49,7 +49,13 @@ function saveAsAssets(bundle, options, log) { ); writeUnbundle.then(() => log('Done writing unbundle output')); - return Promise.all([writeUnbundle, writeSourceMap(sourcemapOutput, '', log)]); + const sourceMap = + buildSourceMapWithMetaData({startupModules, modules}); + + return Promise.all([ + writeUnbundle, + writeSourceMap(sourcemapOutput, JSON.stringify(sourceMap), log) + ]); } function createDir(dirName) { 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 new file mode 100644 index 00000000000000..9c18c1ef963ea2 --- /dev/null +++ b/local-cli/bundle/output/unbundle/build-unbundle-sourcemap-with-metadata.js @@ -0,0 +1,22 @@ +/** + * 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. + */ +'use strict'; + +const {combineSourceMaps} = require('./util'); + +module.exports = ({startupModules, modules}) => { + const startupModule = { + code: startupModules.map(m => m.code).join('\n'), + map: combineSourceMaps({modules: startupModules}), + }; + return combineSourceMaps({ + modules: [startupModule].concat(modules), + withCustomOffsets: true, + }); +}; diff --git a/local-cli/bundle/output/unbundle/util.js b/local-cli/bundle/output/unbundle/util.js new file mode 100644 index 00000000000000..9c2a14c267fc0c --- /dev/null +++ b/local-cli/bundle/output/unbundle/util.js @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2016-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. + */ +'use strict'; + +const newline = /\r\n?|\n|\u2028|\u2029/g; +const countLines = + string => (string.match(newline) || []).length + 1; // fastest implementation + +function lineToLineSourceMap(source, filename) { + // The first line mapping in our package is the base64vlq code for zeros (A). + const firstLine = 'AAAA;'; + + // Most other lines in our mappings are all zeros (for module, column etc) + // except for the lineno mappinp: curLineno - prevLineno = 1; Which is C. + const line = 'AACA;'; + + return { + version: 3, + sources: [filename], + mappings: firstLine + Array(countLines(source)).join(line), + }; +} + +const wrapperEnd = wrappedCode => wrappedCode.indexOf('{') + 1; + +const Section = (line, column, map) => ({map, offset: {line, column}}); + +function combineSourceMaps({modules, withCustomOffsets}) { + let offsets; + const sections = []; + const sourceMap = { + version: 3, + sections, + }; + + if (withCustomOffsets) { + offsets = sourceMap.x_facebook_offsets = []; + } + + let line = 0; + modules.forEach(({code, id, map, name}) => { + const hasOffset = withCustomOffsets && id != null; + const column = hasOffset ? wrapperEnd(code) : 0; + sections.push(Section(line, column, map || lineToLineSourceMap(code, name))); + if (hasOffset) { + offsets[id] = line; + } + line += countLines(code); + }); + + return sourceMap; +} + +module.exports = {countLines, lineToLineSourceMap, combineSourceMaps}; diff --git a/packager/react-packager/src/Bundler/Bundle.js b/packager/react-packager/src/Bundler/Bundle.js index af265ebbc5b658..febac2dba33237 100644 --- a/packager/react-packager/src/Bundler/Bundle.js +++ b/packager/react-packager/src/Bundler/Bundle.js @@ -115,9 +115,7 @@ class Bundle extends BundleBase { return { startupCode, - modules: modules.map(({name, code, polyfill}) => - ({name, code, polyfill}) - ), + startupModules: allModules, modules, }; }