Add sourcemap support for asset-based random access bundles
Summary:This adds support for source maps that can be used for “random access modules” / “unbundles” - source maps contain an extra custom field: `x_facebook_offsets` - this field maps module IDs to line offsets - the source map is built as if all files were concatenated Decoding/symbolication works as follows: - when decoding a stack trace, and a stack frame comes from a filename that contains only numbers and ends with `.js`, look up the additionally needed line offset in the offset map and add it to the original line of the stack frame. - consume the source map as usual Reviewed By: martinbigio Differential Revision: D3072426 fb-gh-sync-id: 827e6dc13b1959f02903baafa7f9e4fc2e0d4bb9 shipit-source-id: 827e6dc13b1959f02903baafa7f9e4fc2e0d4bb9
This commit is contained in:
parent
7a2698475e
commit
5b5a89aefa
|
@ -12,6 +12,7 @@ const mkdirp = require('mkdirp');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const Promise = require('promise');
|
const Promise = require('promise');
|
||||||
|
|
||||||
|
const buildSourceMapWithMetaData = require('./build-unbundle-sourcemap-with-metadata');
|
||||||
const writeFile = require('../writeFile');
|
const writeFile = require('../writeFile');
|
||||||
const writeSourceMap = require('./write-sourcemap');
|
const writeSourceMap = require('./write-sourcemap');
|
||||||
const MAGIC_UNBUNDLE_NUMBER = require('./magic-number');
|
const MAGIC_UNBUNDLE_NUMBER = require('./magic-number');
|
||||||
|
@ -29,12 +30,11 @@ function saveAsAssets(bundle, options, log) {
|
||||||
const {
|
const {
|
||||||
'bundle-output': bundleOutput,
|
'bundle-output': bundleOutput,
|
||||||
'bundle-encoding': encoding,
|
'bundle-encoding': encoding,
|
||||||
dev,
|
|
||||||
'sourcemap-output': sourcemapOutput,
|
'sourcemap-output': sourcemapOutput,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
log('start');
|
log('start');
|
||||||
const {startupCode, modules} = bundle.getUnbundle({minify: !dev});
|
const {startupCode, startupModules, modules} = bundle.getUnbundle();
|
||||||
log('finish');
|
log('finish');
|
||||||
|
|
||||||
log('Writing bundle output to:', bundleOutput);
|
log('Writing bundle output to:', bundleOutput);
|
||||||
|
@ -49,7 +49,13 @@ function saveAsAssets(bundle, options, log) {
|
||||||
);
|
);
|
||||||
writeUnbundle.then(() => log('Done writing unbundle output'));
|
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) {
|
function createDir(dirName) {
|
||||||
|
|
|
@ -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,
|
||||||
|
});
|
||||||
|
};
|
|
@ -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};
|
|
@ -115,9 +115,7 @@ class Bundle extends BundleBase {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
startupCode,
|
startupCode,
|
||||||
modules: modules.map(({name, code, polyfill}) =>
|
startupModules: allModules,
|
||||||
({name, code, polyfill})
|
|
||||||
),
|
|
||||||
modules,
|
modules,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue