jashkenas--coffeescript/docs/v1/annotated-source/sourcemap.html

524 lines
20 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<html>
<head>
<title>sourcemap.litcoffee</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0; maximum-scale=1.0; user-scalable=0;">
<link rel="stylesheet" media="all" href="docco.css" />
</head>
<body>
<div id="container">
<div id="background"></div>
<ul id="jump_to">
<li>
<a class="large" href="javascript:void(0);">Jump To &hellip;</a>
<a class="small" href="javascript:void(0);">+</a>
<div id="jump_wrapper">
2014-08-26 16:24:29 +00:00
<div id="jump_page_wrapper">
<div id="jump_page">
<a class="source" href="browser.html">
browser.coffee
</a>
<a class="source" href="cake.html">
cake.coffee
</a>
<a class="source" href="coffee-script.html">
coffee-script.coffee
</a>
<a class="source" href="command.html">
command.coffee
</a>
<a class="source" href="grammar.html">
grammar.coffee
</a>
<a class="source" href="helpers.html">
helpers.coffee
</a>
<a class="source" href="index.html">
index.coffee
</a>
<a class="source" href="lexer.html">
lexer.coffee
</a>
<a class="source" href="nodes.html">
nodes.coffee
</a>
<a class="source" href="optparse.html">
optparse.coffee
</a>
<a class="source" href="register.html">
register.coffee
</a>
<a class="source" href="repl.html">
repl.coffee
</a>
<a class="source" href="rewriter.html">
rewriter.coffee
</a>
<a class="source" href="scope.html">
scope.litcoffee
</a>
<a class="source" href="sourcemap.html">
sourcemap.litcoffee
</a>
</div>
</div>
</li>
</ul>
2013-03-18 05:06:33 +00:00
<ul class="sections">
<li id="title">
<div class="annotation">
<h1>sourcemap.litcoffee</h1>
2013-03-18 05:06:33 +00:00
</div>
</li>
<li id="section-1">
<div class="annotation">
2013-03-18 05:06:33 +00:00
<div class="pilwrap ">
<a class="pilcrow" href="#section-1">&#182;</a>
2013-03-18 05:06:33 +00:00
</div>
<p>Source maps allow JavaScript runtimes to match running JavaScript back to
2013-03-18 11:46:19 +00:00
the original source code that corresponds to it. This can be minified
2014-08-23 14:08:39 +00:00
JavaScript, but in our case, were concerned with mapping pretty-printed
2014-01-30 04:54:00 +00:00
JavaScript back to CoffeeScript.</p>
<p>In order to produce maps, we must keep track of positions (line number, column number)
2013-03-18 11:46:19 +00:00
that originated every node in the syntax tree, and be able to generate a
<a href="https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit">map file</a>
— which is a compact, VLQ-encoded representation of the JSON serialization
2014-01-30 04:54:00 +00:00
of this information — to write out alongside the generated JavaScript.</p>
<h2 id="linemap">LineMap</h2>
</div>
</li>
2013-03-18 05:06:33 +00:00
2014-01-30 04:54:00 +00:00
<li id="section-2">
2013-06-02 05:37:45 +00:00
<div class="annotation">
<div class="pilwrap ">
2014-01-30 04:54:00 +00:00
<a class="pilcrow" href="#section-2">&#182;</a>
2013-03-18 05:06:33 +00:00
</div>
2013-03-18 11:46:19 +00:00
<p>A <strong>LineMap</strong> object keeps track of information about original line and column
positions for a single line of output JavaScript code.
2014-01-30 04:54:00 +00:00
<strong>SourceMaps</strong> are implemented in terms of <strong>LineMaps</strong>.</p>
</div>
2014-01-30 04:54:00 +00:00
<div class="content"><div class='highlight'><pre><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LineMap</span></span>
2016-09-22 20:19:43 +00:00
constructor: <span class="hljs-function"><span class="hljs-params">(@line)</span> -&gt;</span>
@columns = []
2013-03-04 19:19:08 +00:00
2016-09-22 20:19:43 +00:00
add: <span class="hljs-function"><span class="hljs-params">(column, [sourceLine, sourceColumn], options={})</span> -&gt;</span>
<span class="hljs-keyword">return</span> <span class="hljs-keyword">if</span> @columns[column] <span class="hljs-keyword">and</span> options.noReplace
@columns[column] = {line: @line, column, sourceLine, sourceColumn}
2016-09-22 20:19:43 +00:00
sourceLocation: <span class="hljs-function"><span class="hljs-params">(column)</span> -&gt;</span>
column-- <span class="hljs-keyword">until</span> (mapping = @columns[column]) <span class="hljs-keyword">or</span> (column &lt;= <span class="hljs-number">0</span>)
2014-01-30 04:54:00 +00:00
mapping <span class="hljs-keyword">and</span> [mapping.sourceLine, mapping.sourceColumn]</pre></div></div>
2013-06-02 05:37:45 +00:00
</li>
2014-01-30 04:54:00 +00:00
<li id="section-3">
2013-06-02 05:37:45 +00:00
<div class="annotation">
<div class="pilwrap ">
2014-01-30 04:54:00 +00:00
<a class="pilcrow" href="#section-3">&#182;</a>
2013-03-18 05:06:33 +00:00
</div>
2014-01-30 04:54:00 +00:00
<h2 id="sourcemap">SourceMap</h2>
2013-06-02 05:37:45 +00:00
</div>
</li>
2014-01-30 04:54:00 +00:00
<li id="section-4">
2013-06-02 05:37:45 +00:00
<div class="annotation">
<div class="pilwrap ">
2014-01-30 04:54:00 +00:00
<a class="pilcrow" href="#section-4">&#182;</a>
2013-06-02 05:37:45 +00:00
</div>
2014-01-30 04:54:00 +00:00
<p>Maps locations in a single generated JavaScript file back to locations in
the original CoffeeScript source file.</p>
<p>This is intentionally agnostic towards how a source map might be represented on
2014-08-23 14:08:39 +00:00
disk. Once the compiler is ready to produce a “v3”-style source map, we can walk
2014-01-30 04:54:00 +00:00
through the arrays of line and column buffer to produce it.</p>
2013-03-04 19:19:08 +00:00
</div>
2014-01-30 04:54:00 +00:00
<div class="content"><div class='highlight'><pre><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SourceMap</span></span>
2016-09-22 20:19:43 +00:00
constructor: <span class="hljs-function">-&gt;</span>
@lines = []</pre></div></div>
</li>
2013-03-18 05:06:33 +00:00
2014-01-30 04:54:00 +00:00
<li id="section-5">
<div class="annotation">
2013-03-18 05:06:33 +00:00
<div class="pilwrap ">
2014-01-30 04:54:00 +00:00
<a class="pilcrow" href="#section-5">&#182;</a>
2013-03-18 05:06:33 +00:00
</div>
<p>Adds a mapping to this SourceMap. <code>sourceLocation</code> and <code>generatedLocation</code>
are both <code>[line, column]</code> arrays. If <code>options.noReplace</code> is true, then if there
is already a mapping for the specified <code>line</code> and <code>column</code>, this will have no
2014-01-30 04:54:00 +00:00
effect.</p>
</div>
2016-09-22 20:19:43 +00:00
<div class="content"><div class='highlight'><pre> add: <span class="hljs-function"><span class="hljs-params">(sourceLocation, generatedLocation, options = {})</span> -&gt;</span>
[line, column] = generatedLocation
2016-09-22 20:19:43 +00:00
lineMap = (@lines[line] <span class="hljs-keyword">or</span>= <span class="hljs-keyword">new</span> LineMap(line))
lineMap.add column, sourceLocation, options</pre></div></div>
</li>
2013-03-18 05:06:33 +00:00
2014-01-30 04:54:00 +00:00
<li id="section-6">
<div class="annotation">
2013-03-18 05:06:33 +00:00
<div class="pilwrap ">
2014-01-30 04:54:00 +00:00
<a class="pilcrow" href="#section-6">&#182;</a>
2013-03-18 05:06:33 +00:00
</div>
<p>Look up the original position of a given <code>line</code> and <code>column</code> in the generated
2014-01-30 04:54:00 +00:00
code.</p>
</div>
2016-09-22 20:19:43 +00:00
<div class="content"><div class='highlight'><pre> sourceLocation: <span class="hljs-function"><span class="hljs-params">([line, column])</span> -&gt;</span>
line-- <span class="hljs-keyword">until</span> (lineMap = @lines[line]) <span class="hljs-keyword">or</span> (line &lt;= <span class="hljs-number">0</span>)
2014-01-30 04:54:00 +00:00
lineMap <span class="hljs-keyword">and</span> lineMap.sourceLocation column</pre></div></div>
</li>
2013-03-18 05:06:33 +00:00
2014-01-30 04:54:00 +00:00
<li id="section-7">
<div class="annotation">
2013-03-18 05:06:33 +00:00
<div class="pilwrap ">
2014-01-30 04:54:00 +00:00
<a class="pilcrow" href="#section-7">&#182;</a>
2013-06-02 05:37:45 +00:00
</div>
2014-01-30 04:54:00 +00:00
<h2 id="v3-sourcemap-generation">V3 SourceMap Generation</h2>
2013-06-02 05:37:45 +00:00
</div>
</li>
2014-01-30 04:54:00 +00:00
<li id="section-8">
2013-06-02 05:37:45 +00:00
<div class="annotation">
<div class="pilwrap ">
2014-01-30 04:54:00 +00:00
<a class="pilcrow" href="#section-8">&#182;</a>
2013-03-18 05:06:33 +00:00
</div>
<p>Builds up a V3 source map, returning the generated JSON as a string.
<code>options.sourceRoot</code> may be used to specify the sourceRoot written to the source
map. Also, <code>options.sourceFiles</code> and <code>options.generatedFile</code> may be passed to
2014-08-23 14:08:39 +00:00
set “sources” and “file”, respectively.</p>
</div>
2016-09-22 20:19:43 +00:00
<div class="content"><div class='highlight'><pre> generate: <span class="hljs-function"><span class="hljs-params">(options = {}, code = <span class="hljs-literal">null</span>)</span> -&gt;</span>
2014-01-30 04:54:00 +00:00
writingline = <span class="hljs-number">0</span>
lastColumn = <span class="hljs-number">0</span>
lastSourceLine = <span class="hljs-number">0</span>
lastSourceColumn = <span class="hljs-number">0</span>
needComma = <span class="hljs-literal">no</span>
buffer = <span class="hljs-string">""</span>
2016-09-22 20:19:43 +00:00
<span class="hljs-keyword">for</span> lineMap, lineNumber <span class="hljs-keyword">in</span> @lines <span class="hljs-keyword">when</span> lineMap
2014-01-30 04:54:00 +00:00
<span class="hljs-keyword">for</span> mapping <span class="hljs-keyword">in</span> lineMap.columns <span class="hljs-keyword">when</span> mapping
<span class="hljs-keyword">while</span> writingline &lt; mapping.line
lastColumn = <span class="hljs-number">0</span>
needComma = <span class="hljs-literal">no</span>
buffer += <span class="hljs-string">";"</span>
2013-03-18 11:36:34 +00:00
writingline++</pre></div></div>
</li>
2013-03-18 05:06:33 +00:00
2014-01-30 04:54:00 +00:00
<li id="section-9">
<div class="annotation">
<div class="pilwrap ">
2014-01-30 04:54:00 +00:00
<a class="pilcrow" href="#section-9">&#182;</a>
2013-03-18 05:06:33 +00:00
</div>
2014-08-23 14:08:39 +00:00
<p>Write a comma if weve already written a segment on this line.</p>
</div>
2014-01-30 04:54:00 +00:00
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> needComma
buffer += <span class="hljs-string">","</span>
needComma = <span class="hljs-literal">no</span></pre></div></div>
</li>
2013-03-18 05:06:33 +00:00
2014-01-30 04:54:00 +00:00
<li id="section-10">
<div class="annotation">
2013-03-18 05:06:33 +00:00
<div class="pilwrap ">
2014-01-30 04:54:00 +00:00
<a class="pilcrow" href="#section-10">&#182;</a>
2013-03-18 05:06:33 +00:00
</div>
<p>Write the next segment. Segments can be 1, 4, or 5 values. If just one, then it
2014-08-23 14:08:39 +00:00
is a generated column which doesnt match anything in the source code.</p>
2014-01-30 04:54:00 +00:00
<p>The starting column in the generated source, relative to any previous recorded
column for the current line:</p>
2013-06-02 05:37:45 +00:00
</div>
2016-09-22 20:19:43 +00:00
<div class="content"><div class='highlight'><pre> buffer += @encodeVlq mapping.column - lastColumn
2013-03-18 11:36:34 +00:00
lastColumn = mapping.column</pre></div></div>
</li>
2013-03-18 05:06:33 +00:00
2014-01-30 04:54:00 +00:00
<li id="section-11">
<div class="annotation">
2013-03-18 05:06:33 +00:00
<div class="pilwrap ">
2014-01-30 04:54:00 +00:00
<a class="pilcrow" href="#section-11">&#182;</a>
2013-03-18 05:06:33 +00:00
</div>
2014-01-30 04:54:00 +00:00
<p>The index into the list of sources:</p>
</div>
2016-09-22 20:19:43 +00:00
<div class="content"><div class='highlight'><pre> buffer += @encodeVlq <span class="hljs-number">0</span></pre></div></div>
</li>
2013-03-18 05:06:33 +00:00
2014-01-30 04:54:00 +00:00
<li id="section-12">
<div class="annotation">
2013-03-18 05:06:33 +00:00
<div class="pilwrap ">
2014-01-30 04:54:00 +00:00
<a class="pilcrow" href="#section-12">&#182;</a>
2013-03-18 05:06:33 +00:00
</div>
2014-01-30 04:54:00 +00:00
<p>The starting line in the original source, relative to the previous source line.</p>
</div>
2016-09-22 20:19:43 +00:00
<div class="content"><div class='highlight'><pre> buffer += @encodeVlq mapping.sourceLine - lastSourceLine
2013-06-02 05:37:45 +00:00
lastSourceLine = mapping.sourceLine</pre></div></div>
</li>
2013-03-18 05:06:33 +00:00
2014-01-30 04:54:00 +00:00
<li id="section-13">
<div class="annotation">
2013-03-18 05:06:33 +00:00
<div class="pilwrap ">
2014-01-30 04:54:00 +00:00
<a class="pilcrow" href="#section-13">&#182;</a>
2013-03-18 05:06:33 +00:00
</div>
2014-01-30 04:54:00 +00:00
<p>The starting column in the original source, relative to the previous column.</p>
</div>
2016-09-22 20:19:43 +00:00
<div class="content"><div class='highlight'><pre> buffer += @encodeVlq mapping.sourceColumn - lastSourceColumn
2013-03-18 11:36:34 +00:00
lastSourceColumn = mapping.sourceColumn
2014-01-30 04:54:00 +00:00
needComma = <span class="hljs-literal">yes</span></pre></div></div>
</li>
2013-03-18 05:06:33 +00:00
2014-01-30 04:54:00 +00:00
<li id="section-14">
<div class="annotation">
2013-03-18 05:06:33 +00:00
<div class="pilwrap ">
2014-01-30 04:54:00 +00:00
<a class="pilcrow" href="#section-14">&#182;</a>
2013-03-18 05:06:33 +00:00
</div>
2014-08-23 14:08:39 +00:00
<p>Produce the canonical JSON object format for a “v3” source map.</p>
</div>
2014-01-30 04:54:00 +00:00
<div class="content"><div class='highlight'><pre> v3 =
2016-09-22 20:19:43 +00:00
version: <span class="hljs-number">3</span>
file: options.generatedFile <span class="hljs-keyword">or</span> <span class="hljs-string">''</span>
sourceRoot: options.sourceRoot <span class="hljs-keyword">or</span> <span class="hljs-string">''</span>
sources: options.sourceFiles <span class="hljs-keyword">or</span> [<span class="hljs-string">''</span>]
names: []
mappings: buffer
2016-09-22 20:19:43 +00:00
v3.sourcesContent = [code] <span class="hljs-keyword">if</span> options.inlineMap
2016-09-22 20:19:43 +00:00
v3</pre></div></div>
</li>
2013-03-18 05:06:33 +00:00
2014-01-30 04:54:00 +00:00
<li id="section-15">
<div class="annotation">
2013-03-18 05:06:33 +00:00
<div class="pilwrap ">
2014-01-30 04:54:00 +00:00
<a class="pilcrow" href="#section-15">&#182;</a>
2013-06-02 05:37:45 +00:00
</div>
2014-01-30 04:54:00 +00:00
<h2 id="base64-vlq-encoding">Base64 VLQ Encoding</h2>
2013-06-02 05:37:45 +00:00
</div>
</li>
2014-01-30 04:54:00 +00:00
<li id="section-16">
2013-06-02 05:37:45 +00:00
<div class="annotation">
<div class="pilwrap ">
2014-01-30 04:54:00 +00:00
<a class="pilcrow" href="#section-16">&#182;</a>
2013-03-18 05:06:33 +00:00
</div>
2014-08-23 14:08:39 +00:00
<p>Note that SourceMap VLQ encoding is “backwards”. MIDI-style VLQ encoding puts
the most-significant-bit (MSB) from the original value into the MSB of the VLQ
encoded value (see <a href="https://en.wikipedia.org/wiki/File:Uintvar_coding.svg">Wikipedia</a>).
SourceMap VLQ does things the other way around, with the least significat four
2014-01-30 04:54:00 +00:00
bits of the original value encoded into the first byte of the VLQ encoded value.</p>
</div>
2014-01-30 04:54:00 +00:00
<div class="content"><div class='highlight'><pre> VLQ_SHIFT = <span class="hljs-number">5</span>
VLQ_CONTINUATION_BIT = <span class="hljs-number">1</span> &lt;&lt; VLQ_SHIFT <span class="hljs-comment"># 0010 0000</span>
VLQ_VALUE_MASK = VLQ_CONTINUATION_BIT - <span class="hljs-number">1</span> <span class="hljs-comment"># 0001 1111</span>
2016-09-22 20:19:43 +00:00
encodeVlq: <span class="hljs-function"><span class="hljs-params">(value)</span> -&gt;</span>
2014-01-30 04:54:00 +00:00
answer = <span class="hljs-string">''</span></pre></div></div>
</li>
2013-03-18 05:06:33 +00:00
2014-01-30 04:54:00 +00:00
<li id="section-17">
<div class="annotation">
<div class="pilwrap ">
2014-01-30 04:54:00 +00:00
<a class="pilcrow" href="#section-17">&#182;</a>
2013-03-18 05:06:33 +00:00
</div>
2014-01-30 04:54:00 +00:00
<p>Least significant bit represents the sign.</p>
</div>
2014-01-30 04:54:00 +00:00
<div class="content"><div class='highlight'><pre> signBit = <span class="hljs-keyword">if</span> value &lt; <span class="hljs-number">0</span> <span class="hljs-keyword">then</span> <span class="hljs-number">1</span> <span class="hljs-keyword">else</span> <span class="hljs-number">0</span></pre></div></div>
</li>
2013-03-18 05:06:33 +00:00
2014-01-30 04:54:00 +00:00
<li id="section-18">
<div class="annotation">
<div class="pilwrap ">
2014-01-30 04:54:00 +00:00
<a class="pilcrow" href="#section-18">&#182;</a>
2013-03-18 05:06:33 +00:00
</div>
2014-01-30 04:54:00 +00:00
<p>The next bits are the actual value.</p>
</div>
2014-01-30 04:54:00 +00:00
<div class="content"><div class='highlight'><pre> valueToEncode = (Math.abs(value) &lt;&lt; <span class="hljs-number">1</span>) + signBit</pre></div></div>
</li>
2013-03-18 05:06:33 +00:00
2014-01-30 04:54:00 +00:00
<li id="section-19">
<div class="annotation">
2013-03-18 05:06:33 +00:00
<div class="pilwrap ">
2014-01-30 04:54:00 +00:00
<a class="pilcrow" href="#section-19">&#182;</a>
2013-03-18 05:06:33 +00:00
</div>
2014-01-30 04:54:00 +00:00
<p>Make sure we encode at least one character, even if valueToEncode is 0.</p>
</div>
2014-01-30 04:54:00 +00:00
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">while</span> valueToEncode <span class="hljs-keyword">or</span> <span class="hljs-keyword">not</span> answer
nextChunk = valueToEncode &amp; VLQ_VALUE_MASK
valueToEncode = valueToEncode &gt;&gt; VLQ_SHIFT
2014-01-30 04:54:00 +00:00
nextChunk |= VLQ_CONTINUATION_BIT <span class="hljs-keyword">if</span> valueToEncode
2016-09-22 20:19:43 +00:00
answer += @encodeBase64 nextChunk
2013-03-18 11:36:34 +00:00
answer</pre></div></div>
</li>
2013-03-18 05:06:33 +00:00
2014-01-30 04:54:00 +00:00
<li id="section-20">
<div class="annotation">
2014-01-30 04:54:00 +00:00
<div class="pilwrap ">
<a class="pilcrow" href="#section-20">&#182;</a>
2013-03-18 05:06:33 +00:00
</div>
2014-01-30 04:54:00 +00:00
<h2 id="regular-base64-encoding">Regular Base64 Encoding</h2>
</div>
</li>
2013-03-18 05:06:33 +00:00
2014-01-30 04:54:00 +00:00
<li id="section-21">
<div class="annotation">
2013-03-18 05:06:33 +00:00
<div class="pilwrap ">
2014-01-30 04:54:00 +00:00
<a class="pilcrow" href="#section-21">&#182;</a>
2013-03-18 05:06:33 +00:00
</div>
</div>
2014-01-30 04:54:00 +00:00
<div class="content"><div class='highlight'><pre> BASE64_CHARS = <span class="hljs-string">'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'</span>
2016-09-22 20:19:43 +00:00
encodeBase64: <span class="hljs-function"><span class="hljs-params">(value)</span> -&gt;</span>
2014-01-30 04:54:00 +00:00
BASE64_CHARS[value] <span class="hljs-keyword">or</span> <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Error <span class="hljs-string">"Cannot Base64 encode value: <span class="hljs-subst">#{value}</span>"</span></pre></div></div>
</li>
2013-03-18 05:06:33 +00:00
2014-01-30 04:54:00 +00:00
<li id="section-22">
<div class="annotation">
2013-03-18 05:06:33 +00:00
<div class="pilwrap ">
2014-01-30 04:54:00 +00:00
<a class="pilcrow" href="#section-22">&#182;</a>
2013-03-18 05:06:33 +00:00
</div>
2014-01-30 04:54:00 +00:00
<p>Our API for source maps is just the <code>SourceMap</code> class.</p>
2013-03-04 19:19:08 +00:00
</div>
2016-09-22 20:19:43 +00:00
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">module</span>.exports = SourceMap</pre></div></div>
</li>
2013-03-18 05:06:33 +00:00
</ul>
</div>
</body>
</html>