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
911 lines
42 KiB
HTML
911 lines
42 KiB
HTML
<!DOCTYPE html>
|
||
|
||
<html>
|
||
<head>
|
||
<title>coffeescript.coffee</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>coffeescript.coffee</h1>
|
||
</div>
|
||
</li>
|
||
|
||
|
||
|
||
<li id="section-1">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-1">¶</a>
|
||
</div>
|
||
<p>CoffeeScript can be used both on the server, as a command-line compiler based
|
||
on Node.js/V8, or to run CoffeeScript directly in the browser. This module
|
||
contains the main entry functions for tokenizing, parsing, and compiling
|
||
source CoffeeScript into JavaScript.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>
|
||
{Lexer} = <span class="hljs-built_in">require</span> <span class="hljs-string">'./lexer'</span>
|
||
{parser} = <span class="hljs-built_in">require</span> <span class="hljs-string">'./parser'</span>
|
||
helpers = <span class="hljs-built_in">require</span> <span class="hljs-string">'./helpers'</span>
|
||
SourceMap = <span class="hljs-built_in">require</span> <span class="hljs-string">'./sourcemap'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-2">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-2">¶</a>
|
||
</div>
|
||
<p>Require <code>package.json</code>, which is two levels above this file, as this file is
|
||
evaluated from <code>lib/coffeescript</code>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>packageJson = <span class="hljs-built_in">require</span> <span class="hljs-string">'../../package.json'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-3">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-3">¶</a>
|
||
</div>
|
||
<p>The current CoffeeScript version number.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>exports.VERSION = packageJson.version
|
||
|
||
exports.FILE_EXTENSIONS = FILE_EXTENSIONS = [<span class="hljs-string">'.coffee'</span>, <span class="hljs-string">'.litcoffee'</span>, <span class="hljs-string">'.coffee.md'</span>]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-4">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-4">¶</a>
|
||
</div>
|
||
<p>Expose helpers for testing.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>exports.helpers = helpers</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-5">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-5">¶</a>
|
||
</div>
|
||
<p>Function that allows for btoa in both nodejs and the browser.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">base64encode</span> = <span class="hljs-params">(src)</span> -></span> <span class="hljs-keyword">switch</span>
|
||
<span class="hljs-keyword">when</span> <span class="hljs-keyword">typeof</span> Buffer <span class="hljs-keyword">is</span> <span class="hljs-string">'function'</span>
|
||
Buffer.<span class="hljs-keyword">from</span>(src).toString(<span class="hljs-string">'base64'</span>)
|
||
<span class="hljs-keyword">when</span> <span class="hljs-keyword">typeof</span> btoa <span class="hljs-keyword">is</span> <span class="hljs-string">'function'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-6">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-6">¶</a>
|
||
</div>
|
||
<p>The contents of a <code><script></code> block are encoded via UTF-16, so if any extended
|
||
characters are used in the block, btoa will fail as it maxes out at UTF-8.
|
||
See <a href="https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_Unicode_Problem">https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_Unicode_Problem</a>
|
||
for the gory details, and for the solution implemented here.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> btoa encodeURIComponent(src).replace <span class="hljs-regexp">/%([0-9A-F]{2})/g</span>, <span class="hljs-function"><span class="hljs-params">(match, p1)</span> -></span>
|
||
String.fromCharCode <span class="hljs-string">'0x'</span> + p1
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Error(<span class="hljs-string">'Unable to base64 encode inline sourcemap.'</span>)</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-7">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-7">¶</a>
|
||
</div>
|
||
<p>Function wrapper to add source file information to SyntaxErrors thrown by the
|
||
lexer/parser/compiler.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">withPrettyErrors</span> = <span class="hljs-params">(fn)</span> -></span>
|
||
(code, options = {}) ->
|
||
<span class="hljs-keyword">try</span>
|
||
fn.call @, code, options
|
||
<span class="hljs-keyword">catch</span> err
|
||
<span class="hljs-keyword">throw</span> err <span class="hljs-keyword">if</span> <span class="hljs-keyword">typeof</span> code <span class="hljs-keyword">isnt</span> <span class="hljs-string">'string'</span> <span class="hljs-comment"># Support `CoffeeScript.nodes(tokens)`.</span>
|
||
<span class="hljs-keyword">throw</span> helpers.updateSyntaxError err, code, options.filename</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-8">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-8">¶</a>
|
||
</div>
|
||
<p>For each compiled file, save its source in memory in case we need to
|
||
recompile it later. We might need to recompile if the first compilation
|
||
didn’t create a source map (faster) but something went wrong and we need
|
||
a stack trace. Assuming that most of the time, code isn’t throwing
|
||
exceptions, it’s probably more efficient to compile twice only when we
|
||
need a stack trace, rather than always generating a source map even when
|
||
it’s not likely to be used. Save in form of <code>filename</code>: [<code>(source)</code>]</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>sources = {}</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-9">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-9">¶</a>
|
||
</div>
|
||
<p>Also save source maps if generated, in form of <code>(source)</code>: [<code>(source map)</code>].</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>sourceMaps = {}</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-10">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-10">¶</a>
|
||
</div>
|
||
<p>Compile CoffeeScript code to JavaScript, using the Coffee/Jison compiler.</p>
|
||
<p>If <code>options.sourceMap</code> is specified, then <code>options.filename</code> must also be
|
||
specified. All options that can be passed to <code>SourceMap#generate</code> may also
|
||
be passed here.</p>
|
||
<p>This returns a javascript string, unless <code>options.sourceMap</code> is passed,
|
||
in which case this returns a <code>{js, v3SourceMap, sourceMap}</code>
|
||
object, where sourceMap is a sourcemap.coffee#SourceMap object, handy for
|
||
doing programmatic lookups.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>exports.compile = compile = withPrettyErrors (code, options = {}) -></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-11">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-11">¶</a>
|
||
</div>
|
||
<p>Clone <code>options</code>, to avoid mutating the <code>options</code> object passed in.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> options = Object.assign {}, options</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-12">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-12">¶</a>
|
||
</div>
|
||
<p>Always generate a source map if no filename is passed in, since without a
|
||
a filename we have no way to retrieve this source later in the event that
|
||
we need to recompile it to get a source map for <code>prepareStackTrace</code>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> generateSourceMap = options.sourceMap <span class="hljs-keyword">or</span> options.inlineMap <span class="hljs-keyword">or</span> <span class="hljs-keyword">not</span> options.filename?
|
||
filename = options.filename <span class="hljs-keyword">or</span> <span class="hljs-string">'<anonymous>'</span>
|
||
|
||
checkShebangLine filename, code
|
||
|
||
sources[filename] ?= []
|
||
sources[filename].push code
|
||
map = <span class="hljs-keyword">new</span> SourceMap <span class="hljs-keyword">if</span> generateSourceMap
|
||
|
||
tokens = lexer.tokenize code, options</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-13">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-13">¶</a>
|
||
</div>
|
||
<p>Pass a list of referenced variables, so that generated variables won’t get
|
||
the same name.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> options.referencedVars = (
|
||
token[<span class="hljs-number">1</span>] <span class="hljs-keyword">for</span> token <span class="hljs-keyword">in</span> tokens <span class="hljs-keyword">when</span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'IDENTIFIER'</span>
|
||
)</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-14">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-14">¶</a>
|
||
</div>
|
||
<p>Check for import or export; if found, force bare mode.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">unless</span> options.bare? <span class="hljs-keyword">and</span> options.bare <span class="hljs-keyword">is</span> <span class="hljs-literal">yes</span>
|
||
<span class="hljs-keyword">for</span> token <span class="hljs-keyword">in</span> tokens
|
||
<span class="hljs-keyword">if</span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> [<span class="hljs-string">'IMPORT'</span>, <span class="hljs-string">'EXPORT'</span>]
|
||
options.bare = <span class="hljs-literal">yes</span>
|
||
<span class="hljs-keyword">break</span>
|
||
|
||
fragments = parser.parse(tokens).compileToFragments options
|
||
|
||
currentLine = <span class="hljs-number">0</span>
|
||
currentLine += <span class="hljs-number">1</span> <span class="hljs-keyword">if</span> options.header
|
||
currentLine += <span class="hljs-number">1</span> <span class="hljs-keyword">if</span> options.shiftLine
|
||
currentColumn = <span class="hljs-number">0</span>
|
||
js = <span class="hljs-string">""</span>
|
||
<span class="hljs-keyword">for</span> fragment <span class="hljs-keyword">in</span> fragments</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-15">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-15">¶</a>
|
||
</div>
|
||
<p>Update the sourcemap with data from each fragment.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> generateSourceMap</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-16">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-16">¶</a>
|
||
</div>
|
||
<p>Do not include empty, whitespace, or semicolon-only fragments.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> fragment.locationData <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> <span class="hljs-regexp">/^[;\s]*$/</span>.test fragment.code
|
||
map.add(
|
||
[fragment.locationData.first_line, fragment.locationData.first_column]
|
||
[currentLine, currentColumn]
|
||
{noReplace: <span class="hljs-literal">true</span>})
|
||
newLines = helpers.count fragment.code, <span class="hljs-string">"\n"</span>
|
||
currentLine += newLines
|
||
<span class="hljs-keyword">if</span> newLines
|
||
currentColumn = fragment.code.length - (fragment.code.lastIndexOf(<span class="hljs-string">"\n"</span>) + <span class="hljs-number">1</span>)
|
||
<span class="hljs-keyword">else</span>
|
||
currentColumn += fragment.code.length</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-17">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-17">¶</a>
|
||
</div>
|
||
<p>Copy the code from each fragment into the final JavaScript.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> js += fragment.code
|
||
|
||
<span class="hljs-keyword">if</span> options.header
|
||
header = <span class="hljs-string">"Generated by CoffeeScript <span class="hljs-subst">#{@VERSION}</span>"</span>
|
||
js = <span class="hljs-string">"// <span class="hljs-subst">#{header}</span>\n<span class="hljs-subst">#{js}</span>"</span>
|
||
|
||
<span class="hljs-keyword">if</span> generateSourceMap
|
||
v3SourceMap = map.generate options, code
|
||
sourceMaps[filename] ?= []
|
||
sourceMaps[filename].push map
|
||
|
||
<span class="hljs-keyword">if</span> options.transpile
|
||
<span class="hljs-keyword">if</span> <span class="hljs-keyword">typeof</span> options.transpile <span class="hljs-keyword">isnt</span> <span class="hljs-string">'object'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-18">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-18">¶</a>
|
||
</div>
|
||
<p>This only happens if run via the Node API and <code>transpile</code> is set to
|
||
something other than an object.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Error <span class="hljs-string">'The transpile option must be given an object with options to pass to Babel'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-19">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-19">¶</a>
|
||
</div>
|
||
<p>Get the reference to Babel that we have been passed if this compiler
|
||
is run via the CLI or Node API.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> transpiler = options.transpile.transpile
|
||
<span class="hljs-keyword">delete</span> options.transpile.transpile</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-20">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-20">¶</a>
|
||
</div>
|
||
<p>See <a href="https://github.com/babel/babel/issues/827#issuecomment-77573107">https://github.com/babel/babel/issues/827#issuecomment-77573107</a>:
|
||
Babel can take a v3 source map object as input in <code>inputSourceMap</code>
|
||
and it will return an <em>updated</em> v3 source map object in its output.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> v3SourceMap <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> options.transpile.inputSourceMap?
|
||
options.transpile.inputSourceMap = v3SourceMap
|
||
transpilerOutput = transpiler js, options.transpile
|
||
js = transpilerOutput.code
|
||
<span class="hljs-keyword">if</span> v3SourceMap <span class="hljs-keyword">and</span> transpilerOutput.map
|
||
v3SourceMap = transpilerOutput.map
|
||
|
||
<span class="hljs-keyword">if</span> options.inlineMap
|
||
encoded = base64encode JSON.stringify v3SourceMap
|
||
sourceMapDataURI = <span class="hljs-string">"//# sourceMappingURL=data:application/json;base64,<span class="hljs-subst">#{encoded}</span>"</span>
|
||
sourceURL = <span class="hljs-string">"//# sourceURL=<span class="hljs-subst">#{options.filename ? <span class="hljs-string">'coffeescript'</span>}</span>"</span>
|
||
js = <span class="hljs-string">"<span class="hljs-subst">#{js}</span>\n<span class="hljs-subst">#{sourceMapDataURI}</span>\n<span class="hljs-subst">#{sourceURL}</span>"</span>
|
||
|
||
<span class="hljs-keyword">if</span> options.sourceMap
|
||
{
|
||
js
|
||
sourceMap: map
|
||
v3SourceMap: JSON.stringify v3SourceMap, <span class="hljs-literal">null</span>, <span class="hljs-number">2</span>
|
||
}
|
||
<span class="hljs-keyword">else</span>
|
||
js</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-21">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-21">¶</a>
|
||
</div>
|
||
<p>Tokenize a string of CoffeeScript code, and return the array of tokens.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>exports.tokens = withPrettyErrors (code, options) ->
|
||
lexer.tokenize code, options</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-22">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-22">¶</a>
|
||
</div>
|
||
<p>Parse a string of CoffeeScript code or an array of lexed tokens, and
|
||
return the AST. You can then compile it by calling <code>.compile()</code> on the root,
|
||
or traverse it by using <code>.traverseChildren()</code> with a callback.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>exports.nodes = withPrettyErrors (source, options) ->
|
||
<span class="hljs-keyword">if</span> <span class="hljs-keyword">typeof</span> source <span class="hljs-keyword">is</span> <span class="hljs-string">'string'</span>
|
||
parser.parse lexer.tokenize source, options
|
||
<span class="hljs-keyword">else</span>
|
||
parser.parse source</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-23">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-23">¶</a>
|
||
</div>
|
||
<p>This file used to export these methods; leave stubs that throw warnings
|
||
instead. These methods have been moved into <code>index.coffee</code> to provide
|
||
separate entrypoints for Node and non-Node environments, so that static
|
||
analysis tools don’t choke on Node packages when compiling for a non-Node
|
||
environment.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>exports.run = exports.eval = exports.register = <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Error <span class="hljs-string">'require index.coffee, not this file'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-24">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-24">¶</a>
|
||
</div>
|
||
<p>Instantiate a Lexer for our use here.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>lexer = <span class="hljs-keyword">new</span> Lexer</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-25">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-25">¶</a>
|
||
</div>
|
||
<p>The real Lexer produces a generic stream of tokens. This object provides a
|
||
thin wrapper around it, compatible with the Jison API. We can then pass it
|
||
directly as a “Jison lexer”.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>parser.lexer =
|
||
lex: <span class="hljs-function">-></span>
|
||
token = parser.tokens[@pos++]
|
||
<span class="hljs-keyword">if</span> token
|
||
[tag, @yytext, @yylloc] = token
|
||
parser.errorToken = token.origin <span class="hljs-keyword">or</span> token
|
||
@yylineno = @yylloc.first_line
|
||
<span class="hljs-keyword">else</span>
|
||
tag = <span class="hljs-string">''</span>
|
||
tag
|
||
setInput: <span class="hljs-function"><span class="hljs-params">(tokens)</span> -></span>
|
||
parser.tokens = tokens
|
||
@pos = <span class="hljs-number">0</span>
|
||
upcomingInput: <span class="hljs-function">-></span> <span class="hljs-string">''</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-26">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-26">¶</a>
|
||
</div>
|
||
<p>Make all the AST nodes visible to the parser.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>parser.yy = <span class="hljs-built_in">require</span> <span class="hljs-string">'./nodes'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-27">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-27">¶</a>
|
||
</div>
|
||
<p>Override Jison’s default error handling function.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>parser.yy.parseError = <span class="hljs-function"><span class="hljs-params">(message, {token})</span> -></span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-28">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-28">¶</a>
|
||
</div>
|
||
<p>Disregard Jison’s message, it contains redundant line number information.
|
||
Disregard the token, we take its value directly from the lexer in case
|
||
the error is caused by a generated token which might refer to its origin.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> {errorToken, tokens} = parser
|
||
[errorTag, errorText, errorLoc] = errorToken
|
||
|
||
errorText = <span class="hljs-keyword">switch</span>
|
||
<span class="hljs-keyword">when</span> errorToken <span class="hljs-keyword">is</span> tokens[tokens.length - <span class="hljs-number">1</span>]
|
||
<span class="hljs-string">'end of input'</span>
|
||
<span class="hljs-keyword">when</span> errorTag <span class="hljs-keyword">in</span> [<span class="hljs-string">'INDENT'</span>, <span class="hljs-string">'OUTDENT'</span>]
|
||
<span class="hljs-string">'indentation'</span>
|
||
<span class="hljs-keyword">when</span> errorTag <span class="hljs-keyword">in</span> [<span class="hljs-string">'IDENTIFIER'</span>, <span class="hljs-string">'NUMBER'</span>, <span class="hljs-string">'INFINITY'</span>, <span class="hljs-string">'STRING'</span>, <span class="hljs-string">'STRING_START'</span>, <span class="hljs-string">'REGEX'</span>, <span class="hljs-string">'REGEX_START'</span>]
|
||
errorTag.replace(<span class="hljs-regexp">/_START$/</span>, <span class="hljs-string">''</span>).toLowerCase()
|
||
<span class="hljs-keyword">else</span>
|
||
helpers.nameWhitespaceCharacter errorText</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-29">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-29">¶</a>
|
||
</div>
|
||
<p>The second argument has a <code>loc</code> property, which should have the location
|
||
data for this token. Unfortunately, Jison seems to send an outdated <code>loc</code>
|
||
(from the previous token), so we take the location information directly
|
||
from the lexer.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> helpers.throwSyntaxError <span class="hljs-string">"unexpected <span class="hljs-subst">#{errorText}</span>"</span>, errorLoc</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-30">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-30">¶</a>
|
||
</div>
|
||
<p>Based on <a href="http://v8.googlecode.com/svn/branches/bleeding_edge/src/messages.js">http://v8.googlecode.com/svn/branches/bleeding_edge/src/messages.js</a>
|
||
Modified to handle sourceMap</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">formatSourcePosition</span> = <span class="hljs-params">(frame, getSourceMapping)</span> -></span>
|
||
filename = <span class="hljs-literal">undefined</span>
|
||
fileLocation = <span class="hljs-string">''</span>
|
||
|
||
<span class="hljs-keyword">if</span> frame.isNative()
|
||
fileLocation = <span class="hljs-string">"native"</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">if</span> frame.isEval()
|
||
filename = frame.getScriptNameOrSourceURL()
|
||
fileLocation = <span class="hljs-string">"<span class="hljs-subst">#{frame.getEvalOrigin()}</span>, "</span> <span class="hljs-keyword">unless</span> filename
|
||
<span class="hljs-keyword">else</span>
|
||
filename = frame.getFileName()
|
||
|
||
filename <span class="hljs-keyword">or</span>= <span class="hljs-string">"<anonymous>"</span>
|
||
|
||
line = frame.getLineNumber()
|
||
column = frame.getColumnNumber()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-31">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-31">¶</a>
|
||
</div>
|
||
<p>Check for a sourceMap position</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> source = getSourceMapping filename, line, column
|
||
fileLocation =
|
||
<span class="hljs-keyword">if</span> source
|
||
<span class="hljs-string">"<span class="hljs-subst">#{filename}</span>:<span class="hljs-subst">#{source[<span class="hljs-number">0</span>]}</span>:<span class="hljs-subst">#{source[<span class="hljs-number">1</span>]}</span>"</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-string">"<span class="hljs-subst">#{filename}</span>:<span class="hljs-subst">#{line}</span>:<span class="hljs-subst">#{column}</span>"</span>
|
||
|
||
functionName = frame.getFunctionName()
|
||
isConstructor = frame.isConstructor()
|
||
isMethodCall = <span class="hljs-keyword">not</span> (frame.isToplevel() <span class="hljs-keyword">or</span> isConstructor)
|
||
|
||
<span class="hljs-keyword">if</span> isMethodCall
|
||
methodName = frame.getMethodName()
|
||
typeName = frame.getTypeName()
|
||
|
||
<span class="hljs-keyword">if</span> functionName
|
||
tp = <span class="hljs-keyword">as</span> = <span class="hljs-string">''</span>
|
||
<span class="hljs-keyword">if</span> typeName <span class="hljs-keyword">and</span> functionName.indexOf typeName
|
||
tp = <span class="hljs-string">"<span class="hljs-subst">#{typeName}</span>."</span>
|
||
<span class="hljs-keyword">if</span> methodName <span class="hljs-keyword">and</span> functionName.indexOf(<span class="hljs-string">".<span class="hljs-subst">#{methodName}</span>"</span>) <span class="hljs-keyword">isnt</span> functionName.length - methodName.length - <span class="hljs-number">1</span>
|
||
<span class="hljs-keyword">as</span> = <span class="hljs-string">" [as <span class="hljs-subst">#{methodName}</span>]"</span>
|
||
|
||
<span class="hljs-string">"<span class="hljs-subst">#{tp}</span><span class="hljs-subst">#{functionName}</span><span class="hljs-subst">#{<span class="hljs-keyword">as</span>}</span> (<span class="hljs-subst">#{fileLocation}</span>)"</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-string">"<span class="hljs-subst">#{typeName}</span>.<span class="hljs-subst">#{methodName <span class="hljs-keyword">or</span> <span class="hljs-string">'<anonymous>'</span>}</span> (<span class="hljs-subst">#{fileLocation}</span>)"</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> isConstructor
|
||
<span class="hljs-string">"new <span class="hljs-subst">#{functionName <span class="hljs-keyword">or</span> <span class="hljs-string">'<anonymous>'</span>}</span> (<span class="hljs-subst">#{fileLocation}</span>)"</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> functionName
|
||
<span class="hljs-string">"<span class="hljs-subst">#{functionName}</span> (<span class="hljs-subst">#{fileLocation}</span>)"</span>
|
||
<span class="hljs-keyword">else</span>
|
||
fileLocation
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">getSourceMap</span> = <span class="hljs-params">(filename, line, column)</span> -></span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-32">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-32">¶</a>
|
||
</div>
|
||
<p>Skip files that we didn’t compile, like Node system files that appear in
|
||
the stack trace, as they never have source maps.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span> <span class="hljs-keyword">unless</span> filename <span class="hljs-keyword">is</span> <span class="hljs-string">'<anonymous>'</span> <span class="hljs-keyword">or</span> filename.slice(filename.lastIndexOf(<span class="hljs-string">'.'</span>)) <span class="hljs-keyword">in</span> FILE_EXTENSIONS
|
||
|
||
<span class="hljs-keyword">if</span> filename <span class="hljs-keyword">isnt</span> <span class="hljs-string">'<anonymous>'</span> <span class="hljs-keyword">and</span> sourceMaps[filename]?
|
||
<span class="hljs-keyword">return</span> sourceMaps[filename][sourceMaps[filename].length - <span class="hljs-number">1</span>]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-33">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-33">¶</a>
|
||
</div>
|
||
<p>CoffeeScript compiled in a browser or via <code>CoffeeScript.compile</code> or <code>.run</code>
|
||
may get compiled with <code>options.filename</code> that’s missing, which becomes
|
||
<code><anonymous></code>; but the runtime might request the stack trace with the
|
||
filename of the script file. See if we have a source map cached under
|
||
<code><anonymous></code> that matches the error.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> sourceMaps[<span class="hljs-string">'<anonymous>'</span>]?</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-34">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-34">¶</a>
|
||
</div>
|
||
<p>Work backwards from the most recent anonymous source maps, until we find
|
||
one that works. This isn’t foolproof; there is a chance that multiple
|
||
source maps will have line/column pairs that match. But we have no other
|
||
way to match them. <code>frame.getFunction().toString()</code> doesn’t always work,
|
||
and it’s not foolproof either.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">for</span> map <span class="hljs-keyword">in</span> sourceMaps[<span class="hljs-string">'<anonymous>'</span>] <span class="hljs-keyword">by</span> <span class="hljs-number">-1</span>
|
||
sourceLocation = map.sourceLocation [line - <span class="hljs-number">1</span>, column - <span class="hljs-number">1</span>]
|
||
<span class="hljs-keyword">return</span> map <span class="hljs-keyword">if</span> sourceLocation?[<span class="hljs-number">0</span>]? <span class="hljs-keyword">and</span> sourceLocation[<span class="hljs-number">1</span>]?</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-35">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-35">¶</a>
|
||
</div>
|
||
<p>If all else fails, recompile this source to get a source map. We need the
|
||
previous section (for <code><anonymous></code>) despite this option, because after it
|
||
gets compiled we will still need to look it up from
|
||
<code>sourceMaps['<anonymous>']</code> in order to find and return it. That’s why we
|
||
start searching from the end in the previous block, because most of the
|
||
time the source map we want is the last one.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> sources[filename]?
|
||
answer = compile sources[filename][sources[filename].length - <span class="hljs-number">1</span>],
|
||
filename: filename
|
||
sourceMap: <span class="hljs-literal">yes</span>
|
||
literate: helpers.isLiterate filename
|
||
answer.sourceMap
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-literal">null</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-36">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-36">¶</a>
|
||
</div>
|
||
<p>Based on <a href="http://goo.gl/ZTx1p">michaelficarra/CoffeeScriptRedux</a>
|
||
NodeJS / V8 have no support for transforming positions in stack traces using
|
||
sourceMap, so we must monkey-patch Error to display CoffeeScript source
|
||
positions.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>Error.prepareStackTrace = <span class="hljs-function"><span class="hljs-params">(err, stack)</span> -></span>
|
||
<span class="hljs-function"> <span class="hljs-title">getSourceMapping</span> = <span class="hljs-params">(filename, line, column)</span> -></span>
|
||
sourceMap = getSourceMap filename, line, column
|
||
answer = sourceMap.sourceLocation [line - <span class="hljs-number">1</span>, column - <span class="hljs-number">1</span>] <span class="hljs-keyword">if</span> sourceMap?
|
||
<span class="hljs-keyword">if</span> answer? <span class="hljs-keyword">then</span> [answer[<span class="hljs-number">0</span>] + <span class="hljs-number">1</span>, answer[<span class="hljs-number">1</span>] + <span class="hljs-number">1</span>] <span class="hljs-keyword">else</span> <span class="hljs-literal">null</span>
|
||
|
||
frames = <span class="hljs-keyword">for</span> frame <span class="hljs-keyword">in</span> stack
|
||
<span class="hljs-keyword">break</span> <span class="hljs-keyword">if</span> frame.getFunction() <span class="hljs-keyword">is</span> exports.run
|
||
<span class="hljs-string">" at <span class="hljs-subst">#{formatSourcePosition frame, getSourceMapping}</span>"</span>
|
||
|
||
<span class="hljs-string">"<span class="hljs-subst">#{err.toString()}</span>\n<span class="hljs-subst">#{frames.join <span class="hljs-string">'\n'</span>}</span>\n"</span>
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">checkShebangLine</span> = <span class="hljs-params">(file, input)</span> -></span>
|
||
firstLine = input.split(<span class="hljs-regexp">/$/m</span>)[<span class="hljs-number">0</span>]
|
||
rest = firstLine?.match(<span class="hljs-regexp">/^#!\s*([^\s]+\s*)(.*)/</span>)
|
||
args = rest?[<span class="hljs-number">2</span>]?.split(<span class="hljs-regexp">/\s/</span>).filter (s) -> s <span class="hljs-keyword">isnt</span> <span class="hljs-string">''</span>
|
||
<span class="hljs-keyword">if</span> args?.length > <span class="hljs-number">1</span>
|
||
<span class="hljs-built_in">console</span>.error <span class="hljs-string">'''
|
||
The script to be run begins with a shebang line with more than one
|
||
argument. This script will fail on platforms such as Linux which only
|
||
allow a single argument.
|
||
'''</span>
|
||
<span class="hljs-built_in">console</span>.error <span class="hljs-string">"The shebang line was: '<span class="hljs-subst">#{firstLine}</span>' in file '<span class="hljs-subst">#{file}</span>'"</span>
|
||
<span class="hljs-built_in">console</span>.error <span class="hljs-string">"The arguments were: <span class="hljs-subst">#{JSON.stringify args}</span>"</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</div>
|
||
</body>
|
||
</html>
|