简体   繁体   中英

Decoding (javascript) source maps

I have been puzzling over the following image found here

解码源地图

And the following description how it was supposed to work:

The above diagram AAgBC once processed further would return 0, 0, 32, 16, 1 – the 32 being the continuation bit that helps build the following value of 16. B purely decoded in Base64 is 1. So the important values that are used are 0, 0, 16, 1. This then lets us know that line 1 (lines are kept count by the semi colons) column 0 of the generated file maps to file 0 (array of files 0 is foo.js), line 16 at column 1.

I vaguely understands why B was converted to 16 , but what is C ? and how did that get converted to 1 ?

The explanation on that page is very confusing. A key thing it forgets to mention is that the base64 and VLQ system is based on a 6-bit alphabet instead of the usual 8-bit alphabet; for example the base64 data is incompatible with "normal" base64 decoders like JavaScript's own atob function ( atob("AAgBC") produces an error). I think this page offers a better explanation.

Suppose we have a function decodeLine() to decode the numbers for a single line, "IACH,mBAA0B,CAAS,EAAE,SAAgB" . This is only half the battle, as each of the arrays-of-4 (or arrays-of-5) is relative to the previous array-of-4.

In this example the first base64 string "IACH" represents the array [4,0,1,-3] , but this information is useless by itself because it is relative to the previous array-of-4 (except for the first number, which is reset to 0 at the beginning of each line). So if the sum of the previous arrays-of-4 was [11,1,10,3] , we can compute that the real meaning of "IACH" is [4, 1+0, 10+1,3+-3] = [4,1,11,0] - which means "output column 4 corresponds to source file 1, source line 11, source column 0". Similarly, "mBAA0B" decodes to [19,0,0,26] which is added to [4,1,11,0] to get [23,1,11,26] which means "output column 23 corresponds to source file 1, source line 11, source column 26".

So here I have written code to decode an entire "mappings" string. It has two parts, decodeLine to decode a single line and scanMappings to decode all the lines and call a function for each decoded set of 6 values (line, column, source file, source line, source column, and optional name index):

 function decodeLine(line) { function digit(c) { return atob("AAA"+c).charCodeAt(2); } if (line==="") return []; return line.split(",").map(token => { var output = [], num; for (var i = 0; i < token.length;) { var v = 0, shift = 0; do { var d = digit(token[i++]); // d = next 6-bit value decoded from base64 v |= (d & 31) << shift; // put lowest 5 bits of d into v } while (shift += 5, d & 32); // repeat if high bit of d is set output.push(v & 1 ? -(v >> 1) : v >> 1); // low bit is sign } return output; }); } function scanMappings(data, callback) { var lines = data.split(";"), output = []; var sum = [0,0,0,0,0]; for (var line = 0; line < lines.length; line++) { sum[0] = 0; decodeLine(lines[line]).forEach(p => { for (var i = 0; i < p.length; i++) sum[i] += p[i]; callback(line, ...sum); }) } } // Example taken from one of my TypeScript files console.log(JSON.stringify(decodeLine("IACH,mBAA0B,CAAS,EAAE,SAAgB"))); scanMappings( ";;;;;;;;;;;ICAA;;;;;;;;;;OAUG;IACH,mBAA0B,CAAS,EAAE,SAAgB;QACnD,uEAAuE", (line,col,srcFile,srcLine,srcCol) => console.log(`(${line},${col}) is from src file ${srcFile} at (${srcLine},${srcCol})`)); 

The lines and columns are zero-based. The specification is here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM