跳转到内容

sourcemap 可视化工具

https://github.com/evanw/source-map-visualization

const vlqTable = new Uint8Array(128);
const vlqChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
for (let i = 0; i < vlqTable.length; i++) vlqTable[i] = 0xFF;
for (let i = 0; i < vlqChars.length; i++) vlqTable[vlqChars.charCodeAt(i)] = i;
function generateInverseMappings(sources, data) {
let longestDataLength = 0;
// Scatter the mappings to the individual sources
for (let i = 0, n = data.length; i < n; i += 6) {
const originalSource = data[i + 2];
if (originalSource === -1) continue;
const source = sources[originalSource];
let inverseData = source.data;
let j = source.dataLength;
// Append the mapping to the typed array
if (j + 6 > inverseData.length) {
const newLength = inverseData.length << 1;
const newData = new Int32Array(newLength > 1024 ? newLength : 1024);
newData.set(inverseData);
source.data = inverseData = newData;
}
inverseData[j] = data[i];
inverseData[j + 1] = data[i + 1];
inverseData[j + 2] = originalSource;
inverseData[j + 3] = data[i + 3];
inverseData[j + 4] = data[i + 4];
inverseData[j + 5] = data[i + 5];
j += 6;
source.dataLength = j;
if (j > longestDataLength) longestDataLength = j;
}
// Sort the mappings for each individual source
const temp = new Int32Array(longestDataLength);
for (const source of sources) {
const data = source.data.subarray(0, source.dataLength);
// Sort lazily for performance
let isSorted = false;
Object.defineProperty(source, 'data', {
get() {
if (!isSorted) {
temp.set(data);
topDownSplitMerge(temp, 0, data.length, data);
isSorted = true;
}
return data;
},
})
}
// From: https://en.wikipedia.org/wiki/Merge_sort
function topDownSplitMerge(B, iBegin, iEnd, A) {
if (iEnd - iBegin <= 6) return;
const iMiddle = ((iEnd / 6 + iBegin / 6) >> 1) * 6;
topDownSplitMerge(A, iBegin, iMiddle, B);
topDownSplitMerge(A, iMiddle, iEnd, B);
topDownMerge(B, iBegin, iMiddle, iEnd, A);
}
// From: https://en.wikipedia.org/wiki/Merge_sort
function topDownMerge(A, iBegin, iMiddle, iEnd, B) {
let i = iBegin, j = iMiddle;
for (let k = iBegin; k < iEnd; k += 6) {
if (i < iMiddle && (j >= iEnd ||
// Compare mappings first by original line (index 3) and then by original column (index 4)
A[i + 3] < A[j + 3] ||
(A[i + 3] === A[j + 3] && A[i + 4] <= A[j + 4])
)) {
B[k] = A[i];
B[k + 1] = A[i + 1];
B[k + 2] = A[i + 2];
B[k + 3] = A[i + 3];
B[k + 4] = A[i + 4];
B[k + 5] = A[i + 5];
i += 6;
} else {
B[k] = A[j];
B[k + 1] = A[j + 1];
B[k + 2] = A[j + 2];
B[k + 3] = A[j + 3];
B[k + 4] = A[j + 4];
B[k + 5] = A[j + 5];
j += 6;
}
}
}
}
function decodeMappings(mappings, sourcesCount) {
const n = mappings.length;
let data = new Int32Array(1024);
let dataLength = 0;
let generatedLine = 0;
let generatedLineStart = 0;
let generatedColumn = 0;
let originalSource = 0;
let originalLine = 0;
let originalColumn = 0;
let originalName = 0;
let needToSortGeneratedColumns = false;
let i = 0;
function decodeError(text) {
const error = `Invalid VLQ data at index ${i}: ${text}`;
showLoadingError(`The "mappings" field of the imported source map contains invalid data. ${error}.`);
throw new Error(error);
}
function decodeVLQ() {
let shift = 0;
let vlq = 0;
// Scan over the input
while (true) {
// Read a byte
if (i >= mappings.length) decodeError('Expected extra data');
const c = mappings.charCodeAt(i);
if ((c & 0x7F) !== c) decodeError('Invalid character');
const index = vlqTable[c & 0x7F];
if (index === 0xFF) decodeError('Invalid character');
i++;
// Decode the byte
vlq |= (index & 31) << shift;
shift += 5;
// Stop if there's no continuation bit
if ((index & 32) === 0) break;
}
// Recover the signed value
return vlq & 1 ? -(vlq >> 1) : vlq >> 1;
}
while (i < n) {
let c = mappings.charCodeAt(i);
// Handle a line break
if (c === 59 /* ; */) {
// The generated columns are very rarely out of order. In that case,
// sort them with insertion since they are very likely almost ordered.
if (needToSortGeneratedColumns) {
for (let j = generatedLineStart + 6; j < dataLength; j += 6) {
const genL = data[j];
const genC = data[j + 1];
const origS = data[j + 2];
const origL = data[j + 3];
const origC = data[j + 4];
const origN = data[j + 5];
let k = j - 6;
for (; k >= generatedLineStart && data[k + 1] > genC; k -= 6) {
data[k + 6] = data[k];
data[k + 7] = data[k + 1];
data[k + 8] = data[k + 2];
data[k + 9] = data[k + 3];
data[k + 10] = data[k + 4];
data[k + 11] = data[k + 5];
}
data[k + 6] = genL;
data[k + 7] = genC;
data[k + 8] = origS;
data[k + 9] = origL;
data[k + 10] = origC;
data[k + 11] = origN;
}
}
generatedLine++;
generatedColumn = 0;
generatedLineStart = dataLength;
needToSortGeneratedColumns = false;
i++;
continue;
}
// Ignore stray commas
if (c === 44 /* , */) {
i++;
continue;
}
// Read the generated column
const generatedColumnDelta = decodeVLQ();
if (generatedColumnDelta < 0) needToSortGeneratedColumns = true;
generatedColumn += generatedColumnDelta;
if (generatedColumn < 0) decodeError('Invalid generated column');
// It's valid for a mapping to have 1, 4, or 5 variable-length fields
let isOriginalSourceMissing = true;
let isOriginalNameMissing = true;
if (i < n) {
c = mappings.charCodeAt(i);
if (c === 44 /* , */) {
i++;
} else if (c !== 59 /* ; */) {
isOriginalSourceMissing = false;
// Read the original source
const originalSourceDelta = decodeVLQ();
originalSource += originalSourceDelta;
if (originalSource < 0 || originalSource >= sourcesCount) decodeError('Invalid original source');
// Read the original line
const originalLineDelta = decodeVLQ();
originalLine += originalLineDelta;
if (originalLine < 0) decodeError('Invalid original line');
// Read the original column
const originalColumnDelta = decodeVLQ();
originalColumn += originalColumnDelta;
if (originalColumn < 0) decodeError('Invalid original column');
// Check for the optional name index
if (i < n) {
c = mappings.charCodeAt(i);
if (c === 44 /* , */) {
i++;
} else if (c !== 59 /* ; */) {
isOriginalNameMissing = false;
// Read the optional name index
const originalNameDelta = decodeVLQ();
originalName += originalNameDelta;
if (originalName < 0) decodeError('Invalid original name');
// Handle the next character
if (i < n) {
c = mappings.charCodeAt(i);
if (c === 44 /* , */) {
i++;
} else if (c !== 59 /* ; */) {
decodeError('Invalid character after mapping');
}
}
}
}
}
}
// Append the mapping to the typed array
if (dataLength + 6 > data.length) {
const newData = new Int32Array(data.length << 1);
newData.set(data);
data = newData;
}
data[dataLength] = generatedLine;
data[dataLength + 1] = generatedColumn;
if (isOriginalSourceMissing) {
data[dataLength + 2] = -1;
data[dataLength + 3] = -1;
data[dataLength + 4] = -1;
} else {
data[dataLength + 2] = originalSource;
data[dataLength + 3] = originalLine;
data[dataLength + 4] = originalColumn;
}
data[dataLength + 5] = isOriginalNameMissing ? -1 : originalName;
dataLength += 6;
}
return data.subarray(0, dataLength);
}
function parseSourceMap(json) {
try {
json = JSON.parse(json);
} catch (e) {
throw e;
}
if (json.version !== 3) {
throw new Error('Invalid source map');
}
if (!(json.sources instanceof Array) || json.sources.some(x => typeof x !== 'string')) {
throw new Error('Invalid source map');
}
if (typeof json.mappings !== 'string') {
throw new Error('Invalid source map');
}
const { sources, sourcesContent, names, mappings } = json;
const emptyData = new Int32Array(0);
for (let i = 0; i < sources.length; i++) {
sources[i] = {
name: sources[i],
content: sourcesContent && sourcesContent[i] || '',
data: emptyData,
dataLength: 0,
};
}
const data = decodeMappings(mappings, sources.length);
generateInverseMappings(sources, data);
return { sources, names, data };
}
parseSourceMap(`{
"version": 3,
"file": "original.js",
"sourceRoot": "",
"sources": [
"original.coffee"
],
"names": [],
"mappings": ";AAAa;EAAA;AAAA,MAAA,KAAA,EAAA,IAAA,EAAA,IAAA,EAAA,GAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA;;EACb,MAAA,GAAW;;EACX,QAAA,GAAW;;EAGX,IAAgB,QAAhB;;IAAA,MAAA,GAAS,CAAC,GAAV;GALa;;;EAQb,MAAA,GAAS,QAAA,CAAC,CAAD,CAAA;WAAO,CAAA,GAAI;EAAX,EARI;;;EAWb,IAAA,GAAO,CAAC,CAAD,EAAI,CAAJ,EAAO,CAAP,EAAU,CAAV,EAAa,CAAb,EAXM;;;EAcb,IAAA,GACE;IAAA,IAAA,EAAQ,IAAI,CAAC,IAAb;IACA,MAAA,EAAQ,MADR;IAEA,IAAA,EAAQ,QAAA,CAAC,CAAD,CAAA;aAAO,CAAA,GAAI,MAAA,CAAO,CAAP;IAAX;EAFR,EAfW;;;EAoBb,IAAA,GAAO,QAAA,CAAC,MAAD,EAAA,GAAS,OAAT,CAAA;WACL,KAAA,CAAM,MAAN,EAAc,OAAd;EADK;;EAIP,IAAsB,8CAAtB;;IAAA,KAAA,CAAM,YAAN,EAAA;GAxBa;;;EA2Bb,KAAA;;AAAS;IAAA,KAAA,sCAAA;;mBAAA,IAAI,CAAC,IAAL,CAAU,GAAV;IAAA,CAAA;;;AA3BI",
"sourcesContent": [
"# Assignment:\\nnumber = 42\\nopposite = true\\n\\n# Conditions:\\nnumber = -42 if opposite\\n\\n# Functions:\\nsquare = (x) -> x * x\\n\\n# Arrays:\\nlist = [1, 2, 3, 4, 5]\\n\\n# Objects:\\nmath =\\n root: Math.sqrt\\n square: square\\n cube: (x) -> x * square x\\n\\n# Splats:\\nrace = (winner, runners...) ->\\n print winner, runners\\n\\n# Existence:\\nalert \\"I knew it!\\" if elvis?\\n\\n# Array comprehensions:\\ncubes = (math.cube num for num in list)\\n"
]
}`)