mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00

* Bump version to 2.0.0; bump dependencies versions * Make v2 docs the primary docs; jettison the v1 docs’ source: whenever the v1 docs need to be rebuild in the future, that can be done on the `1` branch and copied over; simplify folder tree * Updated v1 docs that reflect that v2 is out and have updated paths to reflect that the v2 docs are now the primary docs, and the v1 docs only live under /v1/ * Add Google Analytics; track navigation, editing code and running code * 2.0.0 changelog * Fix link to root docs * No more @next; installing local copy should be --save-dev * Analytics on the browser-based tests page should prove fascinating . . . * Update annotated source * Add note to changelog clarifying scope
530 lines
20 KiB
HTML
530 lines
20 KiB
HTML
<!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 …</a>
|
||
<a class="small" href="javascript:void(0);">+</a>
|
||
<div id="jump_wrapper">
|
||
<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="coffeescript.html">
|
||
coffeescript.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>
|
||
|
||
<ul class="sections">
|
||
|
||
<li id="title">
|
||
<div class="annotation">
|
||
<h1>sourcemap.litcoffee</h1>
|
||
</div>
|
||
</li>
|
||
|
||
|
||
|
||
<li id="section-1">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-1">¶</a>
|
||
</div>
|
||
<p>Source maps allow JavaScript runtimes to match running JavaScript back to
|
||
the original source code that corresponds to it. This can be minified
|
||
JavaScript, but in our case, we’re concerned with mapping pretty-printed
|
||
JavaScript back to CoffeeScript.</p>
|
||
<p>In order to produce maps, we must keep track of positions (line number, column number)
|
||
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
|
||
of this information — to write out alongside the generated JavaScript.</p>
|
||
<h2 id="linemap">LineMap</h2>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-2">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-2">¶</a>
|
||
</div>
|
||
<p>A <strong>LineMap</strong> object keeps track of information about original line and column
|
||
positions for a single line of output JavaScript code.
|
||
<strong>SourceMaps</strong> are implemented in terms of <strong>LineMaps</strong>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LineMap</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@line)</span> -></span>
|
||
@columns = []
|
||
|
||
add: <span class="hljs-function"><span class="hljs-params">(column, [sourceLine, sourceColumn], options={})</span> -></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}
|
||
|
||
sourceLocation: <span class="hljs-function"><span class="hljs-params">(column)</span> -></span>
|
||
column-- <span class="hljs-keyword">until</span> (mapping = @columns[column]) <span class="hljs-keyword">or</span> (column <= <span class="hljs-number">0</span>)
|
||
mapping <span class="hljs-keyword">and</span> [mapping.sourceLine, mapping.sourceColumn]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-3">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-3">¶</a>
|
||
</div>
|
||
<h2 id="sourcemap">SourceMap</h2>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-4">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-4">¶</a>
|
||
</div>
|
||
<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
|
||
disk. Once the compiler is ready to produce a “v3”-style source map, we can walk
|
||
through the arrays of line and column buffer to produce it.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SourceMap</span></span>
|
||
constructor: <span class="hljs-function">-></span>
|
||
@lines = []</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-5">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-5">¶</a>
|
||
</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
|
||
effect.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> add: <span class="hljs-function"><span class="hljs-params">(sourceLocation, generatedLocation, options = {})</span> -></span>
|
||
[line, column] = generatedLocation
|
||
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>
|
||
|
||
|
||
<li id="section-6">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-6">¶</a>
|
||
</div>
|
||
<p>Look up the original position of a given <code>line</code> and <code>column</code> in the generated
|
||
code.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> sourceLocation: <span class="hljs-function"><span class="hljs-params">([line, column])</span> -></span>
|
||
line-- <span class="hljs-keyword">until</span> (lineMap = @lines[line]) <span class="hljs-keyword">or</span> (line <= <span class="hljs-number">0</span>)
|
||
lineMap <span class="hljs-keyword">and</span> lineMap.sourceLocation column</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-7">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-7">¶</a>
|
||
</div>
|
||
<h2 id="v3-sourcemap-generation">V3 SourceMap Generation</h2>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-8">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-8">¶</a>
|
||
</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
|
||
set “sources” and “file”, respectively.</p>
|
||
|
||
</div>
|
||
|
||
<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> -></span>
|
||
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>
|
||
|
||
<span class="hljs-keyword">for</span> lineMap, lineNumber <span class="hljs-keyword">in</span> @lines <span class="hljs-keyword">when</span> lineMap
|
||
<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 < mapping.line
|
||
lastColumn = <span class="hljs-number">0</span>
|
||
needComma = <span class="hljs-literal">no</span>
|
||
buffer += <span class="hljs-string">";"</span>
|
||
writingline++</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-9">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-9">¶</a>
|
||
</div>
|
||
<p>Write a comma if we’ve already written a segment on this line.</p>
|
||
|
||
</div>
|
||
|
||
<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>
|
||
|
||
|
||
<li id="section-10">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-10">¶</a>
|
||
</div>
|
||
<p>Write the next segment. Segments can be 1, 4, or 5 values. If just one, then it
|
||
is a generated column which doesn’t match anything in the source code.</p>
|
||
<p>The starting column in the generated source, relative to any previous recorded
|
||
column for the current line:</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> buffer += @encodeVlq mapping.column - lastColumn
|
||
lastColumn = mapping.column</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-11">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-11">¶</a>
|
||
</div>
|
||
<p>The index into the list of sources:</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> buffer += @encodeVlq <span class="hljs-number">0</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-12">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-12">¶</a>
|
||
</div>
|
||
<p>The starting line in the original source, relative to the previous source line.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> buffer += @encodeVlq mapping.sourceLine - lastSourceLine
|
||
lastSourceLine = mapping.sourceLine</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-13">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-13">¶</a>
|
||
</div>
|
||
<p>The starting column in the original source, relative to the previous column.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> buffer += @encodeVlq mapping.sourceColumn - lastSourceColumn
|
||
lastSourceColumn = mapping.sourceColumn
|
||
needComma = <span class="hljs-literal">yes</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-14">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-14">¶</a>
|
||
</div>
|
||
<p>Produce the canonical JSON object format for a “v3” source map.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> sources = <span class="hljs-keyword">if</span> options.sourceFiles
|
||
options.sourceFiles
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> options.filename
|
||
[options.filename]
|
||
<span class="hljs-keyword">else</span>
|
||
[<span class="hljs-string">'<anonymous>'</span>]
|
||
|
||
v3 =
|
||
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: sources
|
||
names: []
|
||
mappings: buffer
|
||
|
||
v3.sourcesContent = [code] <span class="hljs-keyword">if</span> options.sourceMap <span class="hljs-keyword">or</span> options.inlineMap
|
||
|
||
v3</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-15">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-15">¶</a>
|
||
</div>
|
||
<h2 id="base64-vlq-encoding">Base64 VLQ Encoding</h2>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-16">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-16">¶</a>
|
||
</div>
|
||
<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
|
||
bits of the original value encoded into the first byte of the VLQ encoded value.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> VLQ_SHIFT = <span class="hljs-number">5</span>
|
||
VLQ_CONTINUATION_BIT = <span class="hljs-number">1</span> << 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>
|
||
|
||
encodeVlq: <span class="hljs-function"><span class="hljs-params">(value)</span> -></span>
|
||
answer = <span class="hljs-string">''</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-17">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-17">¶</a>
|
||
</div>
|
||
<p>Least significant bit represents the sign.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> signBit = <span class="hljs-keyword">if</span> value < <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>
|
||
|
||
|
||
<li id="section-18">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-18">¶</a>
|
||
</div>
|
||
<p>The next bits are the actual value.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> valueToEncode = (Math.abs(value) << <span class="hljs-number">1</span>) + signBit</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-19">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-19">¶</a>
|
||
</div>
|
||
<p>Make sure we encode at least one character, even if valueToEncode is 0.</p>
|
||
|
||
</div>
|
||
|
||
<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 & VLQ_VALUE_MASK
|
||
valueToEncode = valueToEncode >> VLQ_SHIFT
|
||
nextChunk |= VLQ_CONTINUATION_BIT <span class="hljs-keyword">if</span> valueToEncode
|
||
answer += @encodeBase64 nextChunk
|
||
|
||
answer</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-20">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-20">¶</a>
|
||
</div>
|
||
<h2 id="regular-base64-encoding">Regular Base64 Encoding</h2>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-21">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-21">¶</a>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> BASE64_CHARS = <span class="hljs-string">'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'</span>
|
||
|
||
encodeBase64: <span class="hljs-function"><span class="hljs-params">(value)</span> -></span>
|
||
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>
|
||
|
||
|
||
<li id="section-22">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-22">¶</a>
|
||
</div>
|
||
<p>Our API for source maps is just the <code>SourceMap</code> class.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">module</span>.exports = SourceMap</pre></div></div>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</div>
|
||
</body>
|
||
</html>
|