JavaScript Source Maps

Anyone who has tried to debug minified code knows how mind-bogglingly painful it is. Variables are one- or two-letters long, everything is on one line or a series of compacted lines, and stack-traces are down-right useless. This is where source maps can help. A source map is an external file created during the build process that maps the generated code back into the original sources. Tools can take the minified code, read in the source map, and take you from the minified gobbledygook right to the original point in your well-formatted and [hopefully] commented sources.

Creating a source map with AjaxMin is easy: specify the –map:MTYPE MFILE switch on the command-line, with the MTYPE value either V3 or XML. The source map will be created at the file path pointed to by MFILE. Be sure you have write access to that path.  Currently only two formats have been implemented: the Script# XML source map format (“XML”), and the Google JSON V3 Source Map format (“V3”). [Note: if the MTYPE value is left off the switch, XML will be assumed for backwards-compatibility.]

 

JSON V3 Source Map Format, –map:v3

The V3 source map format generates a JSON object that can be consumed by applications such as Chrome. This format is quickly becoming an Internet standard; other browsers will or will soon have support for this format as well. A quick Bing search can deliver a wealth of information on how to set up the various browsers or tools to read these source map files. The format itself is well-documented on the Google website: Source Map Revision 3 Proposal. The gist is that the output is a UTF-8-encoded JSON object literal with a few specific properties defined upon it. The mappings are encoded in relative VLQ Base-64 sequences to provide for more compacted output than previous formats. A typical example may look like:

{
"version":3,
"file":"minifiedCode.js",
"lineCount":1,
"mappings":"AAEAA,SAASA,cAAc,CAAA,CAAG,CACtBC,UAAU,CAAC,eAAe,CAAE,GAAlB,CADY,CAI1BC,SAASA,WAAW,CAAA,CAAG,CACnBC,KAAK,CAAC,OAAD,CADc,CCDvBC,SAASA,KAAK,CAACC,CAAD,CAAI,CACdC,QAAQF,MAAM,CAAC,gBAAiB,CAAEC,CAApB,CAAsB,CACpCC,QAAQF,MAAM,CAAC,QAAD,CAFA,CDLlBJ,cAAc,CAAA,CAAE,CCChB,IADA,IAAIK,EAAI,EACHA,EAAI,CAAC,CAAEA,CAAE,EAAG,GAAG,CAAEA,CAAC,EAAvB,CACQA,CAAE,CAAE,CAAE,EAAG,C,EAAGD,KAAK,CAACC,CAAD",
"sources":["SourceFile1.js","SourceFile2.js"],
"names":["alertOnTimeout","setTimeout","invokeAlert","alert","write","i","document"]
}

In this example, two source files (SourceFile1.js and SourceFile2.js) have been minified to the file minifiedCode.js. This source map maps the code in minifiedCode.js to the original code in the two source files.

 

XML Script# Source Map Format, –map:xml

Many thanks to Microsoft contributor Rafael Correa for implementing the XML Script# format feature. His description from the check-in notes:

Given a particular position in the output script file, these symbols will be able to tell us its corresponding position on the source file. That would allow us to translate Chrome callstacks back to their original (pre-AjaxMin) format. In fact, for deeply minified scripts output to single line, Chrome would be the only browser that can leverage this map for callstack translation as it outputs both line and column number for each frame on the stack.

The Script# source map format is XML-based. A typical source map file may look like:

<map>
<
headers>DstStartLine,DstStartColumn,DstEndLine,DstEndColumn,SrcStartPosition,SrcEndPosition,SrcStartLine,SrcStartColumn,SrcEndLine,SrcEndColumn,SrcFileId,SymbolType,ParentFunction</headers> <scriptFiles> <scriptFile path="Test.min.js"> <s>1,10,1,18,9,17,1,9,1,17,1,Lookup,addClass</s> <s>1,25,1,27,270,313,10,7,10,50,1,VariableDeclaration,addClass</s> <s>1,28,1,29,328,333,12,13,12,18,1,VariableDeclaration,addClass</s> <!—more removed for brevity -->
      <s>1,1,1,448,0,861,1,0,35,2,1,FunctionObject,addClass</s>
<
s>1,1,1,448,0,861,1,0,1,861,1,Block,</s> <checksum value="93-80-C3-F2-41-DE-04-E4-1D-8E-5C-B0-16-7F-50-FD" /> </scriptFile> </scriptFiles> <sourceFiles> <sourceFile id="1" path="Test.js" /> </sourceFiles> </map>

Each <s> element is a symbol in the minified file and contains a comma-separated list of values indicating the destination (minified) location, the source location, an index into the list of source files used to generate the output file, the symbol type, and an optional parent function indicator.

 

AjaxMin MSBuild Task

Source map generation is also supported in the AjaxMin MSBuild Task via the SourceMapType attribute (type String). To generate a V3 source map, set the property to “V3”; to create an XML Script# source map, set it to “XML”. The source map will be created in the same file as the output file(s), and the file name will be outputfile.js.map (the exact same name as the output file, but with “.map” appended to the end). Unfortunately, the build task does not allow for the name of the generated source map file to be specified. If your build requires that kind of control over the output, it is recommended to use the AjaxMin Manifest build task instead.

 

AjaxMin.dll Usage

To generate a Source Map while using the AjaxMin.dll library, a little more work is needed than just setting a couple switches. A source map implementation must be instantiated and assigned to the SymbolsMap property of the CodeSettings object used to minify the source code. The implementations provided by AjaxMin implement the ISymbolsMap interface, and take a TextWriter as the sole parameter to their constructors. That means your code will be responsible for creating the TextWriter to which the source map will be written. Be aware that the text encoding should be UTF-8 with no Byte-Order Mark (BOM) written at the beginning. V3 source map files that include a BOM will not be read by Chrome. You an either instantiate one of the two implementation classes directly (V3SourceMap or ScriptSharpSourceMap), or you can use the Create method on the static SourceMapFactory class. The ISourceMap.StartPackage and ISourceMap.EndPackage methods should be called before and after the minification call.

Sample code will be added to this documentation in the future. Or you can browse the sources: see the  files AjaxMinDll/JavaScript/SourceMapFactory.cs for instantiation, and AjaxMinTask/AjaxManifestTask.cs (ProcessJavaScript method) for the utilization of the implementation object.

Last edited Oct 24, 2012 at 1:03 AM by ronlo, version 9

Comments

DotNetWise Jul 30, 2014 at 10:52 PM 
what about inlined sources in sourcemaps? (instead of referencing the original file)

russrut Jul 17, 2012 at 11:08 PM 
Is there a tool available to take the crunched callstack and convert to uncrunched for you?