Untitled diff

Created Diff never expires
15 removals
Lines
Total
Removed
Words
Total
Removed
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
362 lines
29 additions
Lines
Total
Added
Words
Total
Added
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
378 lines
# CoffeeScript can be used both on the server, as a command-line compiler based
# 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
# on Node.js/V8, or to run CoffeeScript directly in the browser. This module
# contains the main entry functions for tokenizing, parsing, and compiling
# contains the main entry functions for tokenizing, parsing, and compiling
# source CoffeeScript into JavaScript.
# source CoffeeScript into JavaScript.


fs = require 'fs'
fs = require 'fs'
vm = require 'vm'
vm = require 'vm'
path = require 'path'
path = require 'path'
{Lexer} = require './lexer'
{Lexer} = require './lexer'
{parser} = require './parser'
{parser} = require './parser'
helpers = require './helpers'
helpers = require './helpers'
SourceMap = require './sourcemap'
SourceMap = require './sourcemap'
# Require `package.json`, which is two levels above this file, as this file is
# evaluated from `lib/coffee-script`.
packageJson = require '../../package.json'


# The current CoffeeScript version number.
# The current CoffeeScript version number.
exports.VERSION = '1.12.1'
exports.VERSION = packageJson.version


exports.FILE_EXTENSIONS = ['.coffee', '.litcoffee', '.coffee.md']
exports.FILE_EXTENSIONS = ['.coffee', '.litcoffee', '.coffee.md']


# Expose helpers for testing.
# Expose helpers for testing.
exports.helpers = helpers
exports.helpers = helpers


# Function that allows for btoa in both nodejs and the browser.
# Function that allows for btoa in both nodejs and the browser.
base64encode = (src) -> switch
base64encode = (src) -> switch
when typeof Buffer is 'function'
when typeof Buffer is 'function'
new Buffer(src).toString('base64')
new Buffer(src).toString('base64')
when typeof btoa is 'function'
when typeof btoa is 'function'
# The contents of a `<script>` block are encoded via UTF-16, so if any extended
# The contents of a `<script>` 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.
# characters are used in the block, btoa will fail as it maxes out at UTF-8.
# See https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_Unicode_Problem
# See https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_Unicode_Problem
# for the gory details, and for the solution implemented here.
# for the gory details, and for the solution implemented here.
btoa encodeURIComponent(src).replace /%([0-9A-F]{2})/g, (match, p1) ->
btoa encodeURIComponent(src).replace /%([0-9A-F]{2})/g, (match, p1) ->
String.fromCharCode '0x' + p1
String.fromCharCode '0x' + p1
else
else
throw new Error('Unable to base64 encode inline sourcemap.')
throw new Error('Unable to base64 encode inline sourcemap.')


# Function wrapper to add source file information to SyntaxErrors thrown by the
# Function wrapper to add source file information to SyntaxErrors thrown by the
# lexer/parser/compiler.
# lexer/parser/compiler.
withPrettyErrors = (fn) ->
withPrettyErrors = (fn) ->
(code, options = {}) ->
(code, options = {}) ->
try
try
fn.call @, code, options
fn.call @, code, options
catch err
catch err
throw err if typeof code isnt 'string' # Support `CoffeeScript.nodes(tokens)`.
throw err if typeof code isnt 'string' # Support `CoffeeScript.nodes(tokens)`.
throw helpers.updateSyntaxError err, code, options.filename
throw helpers.updateSyntaxError err, code, options.filename


# Compile CoffeeScript code to JavaScript, using the Coffee/Jison compiler.
# Compile CoffeeScript code to JavaScript, using the Coffee/Jison compiler.
#
#
# If `options.sourceMap` is specified, then `options.filename` must also be specified. All
# If `options.sourceMap` is specified, then `options.filename` must also be specified. All
# options that can be passed to `SourceMap#generate` may also be passed here.
# options that can be passed to `SourceMap#generate` may also be passed here.
#
#
# This returns a javascript string, unless `options.sourceMap` is passed,
# This returns a javascript string, unless `options.sourceMap` is passed,
# in which case this returns a `{js, v3SourceMap, sourceMap}`
# in which case this returns a `{js, v3SourceMap, sourceMap}`
# object, where sourceMap is a sourcemap.coffee#SourceMap object, handy for doing programatic
# object, where sourceMap is a sourcemap.coffee#SourceMap object, handy for doing programatic
# lookups.
# lookups.
exports.compile = compile = withPrettyErrors (code, options) ->
exports.compile = compile = withPrettyErrors (code, options) ->
{merge, extend} = helpers
{merge, extend} = helpers
options = extend {}, options
options = extend {}, options
generateSourceMap = options.sourceMap or options.inlineMap
generateSourceMap = options.sourceMap or options.inlineMap


if generateSourceMap
if generateSourceMap
map = new SourceMap
map = new SourceMap


tokens = lexer.tokenize code, options
tokens = lexer.tokenize code, options


# Pass a list of referenced variables, so that generated variables won't get
# Pass a list of referenced variables, so that generated variables won't get
# the same name.
# the same name.
options.referencedVars = (
options.referencedVars = (
token[1] for token in tokens when token[0] is 'IDENTIFIER'
token[1] for token in tokens when token[0] is 'IDENTIFIER'
)
)


# Check for import or export; if found, force bare mode
# Check for import or export; if found, force bare mode
unless options.bare? and options.bare is yes
unless options.bare? and options.bare is yes
for token in tokens
for token in tokens
if token[0] in ['IMPORT', 'EXPORT']
if token[0] in ['IMPORT', 'EXPORT']
options.bare = yes
options.bare = yes
break
break


fragments = parser.parse(tokens).compileToFragments options
fragments = parser.parse(tokens).compileToFragments options


currentLine = 0
currentLine = 0
currentLine += 1 if options.header
currentLine += 1 if options.header
currentLine += 1 if options.shiftLine
currentLine += 1 if options.shiftLine
currentColumn = 0
currentColumn = 0
js = ""
js = ""
for fragment in fragments
for fragment in fragments
# Update the sourcemap with data from each fragment
# Update the sourcemap with data from each fragment
if generateSourceMap
if generateSourceMap
# Do not include empty, whitespace, or semicolon-only fragments.
# Do not include empty, whitespace, or semicolon-only fragments.
if fragment.locationData and not /^[;\s]*$/.test fragment.code
if fragment.locationData and not /^[;\s]*$/.test fragment.code
map.add(
map.add(
[fragment.locationData.first_line, fragment.locationData.first_column]
[fragment.locationData.first_line, fragment.locationData.first_column]
[currentLine, currentColumn]
[currentLine, currentColumn]
{noReplace: true})
{noReplace: true})
newLines = helpers.count fragment.code, "\n"
newLines = helpers.count fragment.code, "\n"
currentLine += newLines
currentLine += newLines
if newLines
if newLines
currentColumn = fragment.code.length - (fragment.code.lastIndexOf("\n") + 1)
currentColumn = fragment.code.length - (fragment.code.lastIndexOf("\n") + 1)
else
else
currentColumn += fragment.code.length
currentColumn += fragment.code.length


# Copy the code from each fragment into the final JavaScript.
# Copy the code from each fragment into the final JavaScript.
js += fragment.code
js += fragment.code


if options.header
if options.header
header = "Generated by CoffeeScript #{@VERSION}"
header = "Generated by CoffeeScript #{@VERSION}"
js = "// #{header}\n#{js}"
js = "// #{header}\n#{js}"


if generateSourceMap
if generateSourceMap
v3SourceMap = map.generate(options, code)
v3SourceMap = map.generate(options, code)


if options.inlineMap
if options.inlineMap
encoded = base64encode JSON.stringify v3SourceMap
encoded = base64encode JSON.stringify v3SourceMap
sourceMapDataURI = "//# sourceMappingURL=data:application/json;base64,#{encoded}"
sourceMapDataURI = "//# sourceMappingURL=data:application/json;base64,#{encoded}"
sourceURL = "//# sourceURL=#{options.filename ? 'coffeescript'}"
sourceURL = "//# sourceURL=#{options.filename ? 'coffeescript'}"
js = "#{js}\n#{sourceMapDataURI}\n#{sourceURL}"
js = "#{js}\n#{sourceMapDataURI}\n#{sourceURL}"


if options.sourceMap
if options.sourceMap
{
{
js
js
sourceMap: map
sourceMap: map
v3SourceMap: JSON.stringify v3SourceMap, null, 2
v3SourceMap: JSON.stringify v3SourceMap, null, 2
}
}
else
else
js
js


# Tokenize a string of CoffeeScript code, and return the array of tokens.
# Tokenize a string of CoffeeScript code, and return the array of tokens.
exports.tokens = withPrettyErrors (code, options) ->
exports.tokens = withPrettyErrors (code, options) ->
lexer.tokenize code, options
lexer.tokenize code, options


# Parse a string of CoffeeScript code or an array of lexed tokens, and
# Parse a string of CoffeeScript code or an array of lexed tokens, and
# return the AST. You can then compile it by calling `.compile()` on the root,
# return the AST. You can then compile it by calling `.compile()` on the root,
# or traverse it by using `.traverseChildren()` with a callback.
# or traverse it by using `.traverseChildren()` with a callback.
exports.nodes = withPrettyErrors (source, options) ->
exports.nodes = withPrettyErrors (source, options) ->
if typeof source is 'string'
if typeof source is 'string'
parser.parse lexer.tokenize source, options
parser.parse lexer.tokenize source, options
else
else
parser.parse source
parser.parse source


# Compile and execute a string of CoffeeScript (on the server), correctly
# Compile and execute a string of CoffeeScript (on the server), correctly
# setting `__filename`, `__dirname`, and relative `require()`.
# setting `__filename`, `__dirname`, and relative `require()`.
exports.run = (code, options = {}) ->
exports.run = (code, options = {}) ->
mainModule = require.main
mainModule = require.main


# Set the filename.
# Set the filename.
mainModule.filename = process.argv[1] =
mainModule.filename = process.argv[1] =
if options.filename then fs.realpathSync(options.filename) else '.'
if options.filename then fs.realpathSync(options.filename) else '.'


# Clear the module cache.
# Clear the module cache.
mainModule.moduleCache and= {}
mainModule.moduleCache and= {}


# Assign paths for node_modules loading
# Assign paths for node_modules loading
dir = if options.filename
dir = if options.filename
path.dirname fs.realpathSync options.filename
path.dirname fs.realpathSync options.filename
else
else
fs.realpathSync '.'
fs.realpathSync '.'
mainModule.paths = require('module')._nodeModulePaths dir
mainModule.paths = require('module')._nodeModulePaths dir


# Compile.
# Compile.
if not helpers.isCoffee(mainModule.filename) or require.extensions
if not helpers.isCoffee(mainModule.filename) or require.extensions
answer = compile code, options
answer = compile code, options
code = answer.js ? answer
code = answer.js ? answer


mainModule._compile code, mainModule.filename
mainModule._compile code, mainModule.filename


# Compile and evaluate a string of CoffeeScript (in a Node.js-like environment).
# Compile and evaluate a string of CoffeeScript (in a Node.js-like environment).
# The CoffeeScript REPL uses this to run the input.
# The CoffeeScript REPL uses this to run the input.
exports.eval = (code, options = {}) ->
exports.eval = (code, options = {}) ->
return unless code = code.trim()
return unless code = code.trim()
createContext = vm.Script.createContext ? vm.createContext
createContext = vm.Script.createContext ? vm.createContext


isContext = vm.isContext ? (ctx) ->
isContext = vm.isContext ? (ctx) ->
options.sandbox instanceof createContext().constructor
options.sandbox instanceof createContext().constructor


if createContext
if createContext
if options.sandbox?
if options.sandbox?
if isContext options.sandbox
if isContext options.sandbox
sandbox = options.sandbox
sandbox = options.sandbox
else
else
sandbox = createContext()
sandbox = createContext()
sandbox[k] = v for own k, v of options.sandbox
sandbox[k] = v for own k, v of options.sandbox
sandbox.global = sandbox.root = sandbox.GLOBAL = sandbox
sandbox.global = sandbox.root = sandbox.GLOBAL = sandbox
else
else
sandbox = global
sandbox = global
sandbox.__filename = options.filename || 'eval'
sandbox.__filename = options.filename || 'eval'
sandbox.__dirname = path.dirname sandbox.__filename
sandbox.__dirname = path.dirname sandbox.__filename
# define module/require only if they chose not to specify their own
# define module/require only if they chose not to specify their own
unless sandbox isnt global or sandbox.module or sandbox.require
unless sandbox isnt global or sandbox.module or sandbox.require
Module = require 'module'
Module = require 'module'
sandbox.module = _module = new Module(options.modulename || 'eval')
sandbox.module = _module = new Module(options.modulename || 'eval')
sandbox.require = _require = (path) -> Module._load path, _module, true
sandbox.require = _require = (path) -> Module._load path, _module, true
_module.filename = sandbox.__filename
_module.filename = sandbox.__filename
for r in Object.getOwnPropertyNames require when r not in ['paths', 'arguments', 'caller']
for r in Object.getOwnPropertyNames require when r not in ['paths', 'arguments', 'caller']
_require[r] = require[r]
_require[r] = require[r]
# use the same hack node currently uses for their own REPL
# use the same hack node currently uses for their own REPL
_require.paths = _module.paths = Module._nodeModulePaths process.cwd()
_require.paths = _module.paths = Module._nodeModulePaths process.cwd()
_require.resolve = (request) -> Module._resolveFilename request, _module
_require.resolve = (request) -> Module._resolveFilename request, _module
o = {}
o = {}
o[k] = v for own k, v of options
o[k] = v for own k, v of options
o.bare = on # ensure return value
o.bare = on # ensure return value
js = compile code, o
js = compile code, o
if sandbox is global
if sandbox is global
vm.runInThisContext js
vm.runInThisContext js
else
else
vm.runInContext js, sandbox
vm.runInContext js, sandbox


exports.register = -> require './register'
exports.register = -> require './register'


# Throw error with deprecation warning when depending upon implicit `require.extensions` registration
# Throw error with deprecation warning when depending upon implicit `require.extensions` registration
if require.extensions
if require.extensions
for ext in @FILE_EXTENSIONS then do (ext) ->
for ext in @FILE_EXTENSIONS then do (ext) ->
require.extensions[ext] ?= ->
require.extensions[ext] ?= ->
throw new Error """
throw new Error """
Use CoffeeScript.register() or require the coffee-script/register module to require #{ext} files.
Use CoffeeScript.register() or require the coffee-script/register module to require #{ext} files.
"""
"""


# 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.
compiledFiles = {}

exports._compileFile = (filename, sourceMap = no, inlineMap = no) ->
exports._compileFile = (filename, sourceMap = no, inlineMap = no) ->
raw = fs.readFileSync filename, 'utf8'
raw = fs.readFileSync filename, 'utf8'
# Strip the Unicode byte order mark, if this file begins with one.
stripped = if raw.charCodeAt(0) is 0xFEFF then raw.substring 1 else raw
stripped = if raw.charCodeAt(0) is 0xFEFF then raw.substring 1 else raw
compiledFiles[filename] = stripped
compileCode stripped, filename, sourceMap, inlineMap


compileCode = (code, filename, sourceMap = no, inlineMap = no) ->
try
try
answer = compile stripped, {
answer = compile code, {
filename, sourceMap, inlineMap
filename, sourceMap, inlineMap
sourceFiles: [filename]
sourceFiles: [filename]
literate: helpers.isLiterate filename
literate: helpers.isLiterate filename
}
}
catch err
catch err
# As the filename and code of a dynamically loaded file will be different
# As the filename and code of a dynamically loaded file will be different
# from the original file compiled with CoffeeScript.run, add that
# from the original file compiled with CoffeeScript.run, add that
# information to error so it can be pretty-printed later.
# information to error so it can be pretty-printed later.
throw helpers.updateSyntaxError err, stripped, filename
throw helpers.updateSyntaxError err, code, filename


answer
answer


# Instantiate a Lexer for our use here.
# Instantiate a Lexer for our use here.
lexer = new Lexer
lexer = new Lexer


# The real Lexer produces a generic stream of tokens. This object provides a
# 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
# thin wrapper around it, compatible with the Jison API. We can then pass it
# directly as a "Jison lexer".
# directly as a "Jison lexer".
parser.lexer =
parser.lexer =
lex: ->
lex: ->
token = parser.tokens[@pos++]
token = parser.tokens[@pos++]
if token
if token
[tag, @yytext, @yylloc] = token
[tag, @yytext, @yylloc] = token
parser.errorToken = token.origin or token
parser.errorToken = token.origin or token
@yylineno = @yylloc.first_line
@yylineno = @yylloc.first_line
else
else
tag = ''
tag = ''


tag
tag
setInput: (tokens) ->
setInput: (tokens) ->
parser.tokens = tokens
parser.tokens = tokens
@pos = 0
@pos = 0
upcomingInput: ->
upcomingInput: ->
""
""
# Make all the AST nodes visible to the parser.
# Make all the AST nodes visible to the parser.
parser.yy = require './nodes'
parser.yy = require './nodes'


# Override Jison's default error handling function.
# Override Jison's default error handling function.
parser.yy.parseError = (message, {token}) ->
parser.yy.parseError = (message, {token}) ->
# Disregard Jison's message, it contains redundant line number information.
# Disregard Jison's message, it contains redundant line number information.
# Disregard the token, we take its value directly from the lexer in case
# 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.
# the error is caused by a generated token which might refer to its origin.
{errorToken, tokens} = parser
{errorToken, tokens} = parser
[errorTag, errorText, errorLoc] = errorToken
[errorTag, errorText, errorLoc] = errorToken


errorText = switch
errorText = switch
when errorToken is tokens[tokens.length - 1]
when errorToken is tokens[tokens.length - 1]
'end of input'
'end of input'
when errorTag in ['INDENT', 'OUTDENT']
when errorTag in ['INDENT', 'OUTDENT']
'indentation'
'indentation'
when errorTag in ['IDENTIFIER', 'NUMBER', 'INFINITY', 'STRING', 'STRING_START', 'REGEX', 'REGEX_START']
when errorTag in ['IDENTIFIER', 'NUMBER', 'INFINITY', 'STRING', 'STRING_START', 'REGEX', 'REGEX_START']
errorTag.replace(/_START$/, '').toLowerCase()
errorTag.replace(/_START$/, '').toLowerCase()
else
else
helpers.nameWhitespaceCharacter errorText
helpers.nameWhitespaceCharacter errorText


# The second argument has a `loc` property, which should have the location
# The second argument has a `loc` property, which should have the location
# data for this token. Unfortunately, Jison seems to send an outdated `loc`
# data for this token. Unfortunately, Jison seems to send an outdated `loc`
# (from the previous token), so we take the location information directly
# (from the previous token), so we take the location information directly
# from the lexer.
# from the lexer.
helpers.throwSyntaxError "unexpected #{errorText}", errorLoc
helpers.throwSyntaxError "unexpected #{errorText}", errorLoc


# Based on http://v8.googlecode.com/svn/branches/bleeding_edge/src/messages.js
# Based on http://v8.googlecode.com/svn/branches/bleeding_edge/src/messages.js
# Modified to handle sourceMap
# Modified to handle sourceMap
formatSourcePosition = (frame, getSourceMapping) ->
formatSourcePosition = (frame, getSourceMapping) ->
fileName = undefined
fileName = undefined
fileLocation = ''
fileLocation = ''


if frame.isNative()
if frame.isNative()
fileLocation = "native"
fileLocation = "native"
else
else
if frame.isEval()
if frame.isEval()
fileName = frame.getScriptNameOrSourceURL()
fileName = frame.getScriptNameOrSourceURL()
fileLocation = "#{frame.getEvalOrigin()}, " unless fileName
fileLocation = "#{frame.getEvalOrigin()}, " unless fileName
else
else
fileName = frame.getFileName()
fileName = frame.getFileName()


fileName or= "<anonymous>"
fileName or= "<anonymous>"


line = frame.getLineNumber()
line = frame.getLineNumber()
column = frame.getColumnNumber()
column = frame.getColumnNumber()


# Check for a sourceMap position
# Check for a sourceMap position
source = getSourceMapping fileName, line, column
source = getSourceMapping fileName, line, column
fileLocation =
fileLocation =
if source
if source
"#{fileName}:#{source[0]}:#{source[1]}"
"#{fileName}:#{source[0]}:#{source[1]}"
else
else
"#{fileName}:#{line}:#{column}"
"#{fileName}:#{line}:#{column}"


functionName = frame.getFunctionName()
functionName = frame.getFunctionName()
isConstructor = frame.isConstructor()
isConstructor = frame.isConstructor()
isMethodCall = not (frame.isToplevel() or isConstructor)
isMethodCall = not (frame.isToplevel() or isConstructor)


if isMethodCall
if isMethodCall
methodName = frame.getMethodName()
methodName = frame.getMethodName()
typeName = frame.getTypeName()
typeName = frame.getTypeName()


if functionName
if functionName
tp = as = ''
tp = as = ''
if typeName and functionName.indexOf typeName
if typeName and functionName.indexOf typeName
tp = "#{typeName}."
tp = "#{typeName}."
if methodName and functionName.indexOf(".#{methodName}") isnt functionName.length - methodName.length - 1
if methodName and functionName.indexOf(".#{methodName}") isnt functionName.length - methodName.length - 1
as = " [as #{methodName}]"
as = " [as #{methodName}]"


"#{tp}#{functionName}#{as} (#{fileLocation})"
"#{tp}#{functionName}#{as} (#{fileLocation})"
else
else
"#{typeName}.#{methodName or '<anonymous>'} (#{fileLocation})"
"#{typeName}.#{methodName or '<anonymous>'} (#{fileLocation})"
else if isConstructor
else if isConstructor
"new #{functionName or '<anonymous>'} (#{fileLocation})"
"new #{functionName or '<anonymous>'} (#{fileLocation})"
else if functionName
else if functionName
"#{functionName} (#{fileLocation})"
"#{functionName} (#{fileLocation})"
else
else
fileLocation
fileLocation


# Map of filenames -> sourceMap object.
# Map of filenames: sourceMap objects.
sourceMaps = {}
sourceMaps = {}

# Generates the source map for a coffee file and stores it in the local cache variable.
# Generates the source map for a coffee file and stores it in the local cache variable.
getSourceMap = (filename) ->
getSourceMap = (filename) ->
return sourceMaps[filename] if sourceMaps[filename]
if sourceMaps[filename]?
for ext in exports.FILE_EXTENSIONS
sourceMaps[filename]
if helpers.ends filename, ext
else if compiledFiles[filename]?
answer = exports._compileFile filename, true
answer = compileCode compiledFiles[filename], filename, yes, no
return sourceMaps[filename] = answer.sourceMap
sourceMaps[filename] = answer.sourceMap
return null
else
null


# Based on [michaelficarra/CoffeeScriptRedux](http://goo.gl/ZTx1p)
# Based on [michaelficarra/CoffeeScriptRedux](http://goo.gl/ZTx1p)
# NodeJS / V8 have no support for transforming positions in stack traces using
# NodeJS / V8 have no support for transforming positions in stack traces using
# sourceMap, so we must monkey-patch Error to display CoffeeScript source
# sourceMap, so we must monkey-patch Error to display CoffeeScript source
# positions.
# positions.
Error.prepareStackTrace = (err, stack) ->
Error.prepareStackTrace = (err, stack) ->
getSourceMapping = (filename, line, column) ->
getSourceMapping = (filename, line, column) ->
sourceMap = getSourceMap filename
sourceMap = getSourceMap filename
answer = sourceMap.sourceLocation [line - 1, column - 1] if sourceMap
answer = sourceMap.sourceLocation [line - 1, column - 1] if sourceMap
if answer then [answer[0] + 1, answer[1] + 1] else null
if answer then [answer[0] + 1, answer[1] + 1] else null


frames = for frame in stack
frames = for frame in stack
break if frame.getFunction() is exports.run
break if frame.getFunction() is exports.run
" at #{formatSourcePosition frame, getSourceMapping}"
" at #{formatSourcePosition frame, getSourceMapping}"


"#{err.toString()}\n#{frames.join '\n'}\n"
"#{err.toString()}\n#{frames.join '\n'}\n"