Diff
checker
文本
文本
图像
文档
Excel
文件夹
Legal
Enterprise
桌面版
定价
登录
下载 Diffchecker 桌面版
比较文本
查找两个文本文件之间的差异
工具
历史
实时编辑器
折叠未更改行
关闭换行
视图
拆分
统一
比对精度
智能
单词
字符
语法高亮
选择语法
忽略
文本转换
转到第一个差异
编辑输入
Diffchecker Desktop
运行Diffchecker最安全的方式。获取Diffchecker桌面应用:您的差异永远不会离开您的电脑!
获取桌面版
Untitled diff
创建于
9年前
差异永不过期
清除
导出
分享
解释
16 删除
行
总计
删除
字符
总计
删除
要继续使用此功能,请升级到
Diff
checker
Pro
查看价格
362 行
全部复制
35 添加
行
总计
添加
字符
总计
添加
要继续使用此功能,请升级到
Diff
checker
Pro
查看价格
378 行
全部复制
# 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 object
s
.
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._
compile
File
filename,
true
answer =
compileCode
compile
d
File
s[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"
复制
已复制
复制
已复制
已保存差异
原始文本
打开文件
# 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. fs = require 'fs' vm = require 'vm' path = require 'path' {Lexer} = require './lexer' {parser} = require './parser' helpers = require './helpers' SourceMap = require './sourcemap' # The current CoffeeScript version number. exports.VERSION = '1.12.1' exports.FILE_EXTENSIONS = ['.coffee', '.litcoffee', '.coffee.md'] # Expose helpers for testing. exports.helpers = helpers # Function that allows for btoa in both nodejs and the browser. base64encode = (src) -> switch when typeof Buffer is 'function' new Buffer(src).toString('base64') when typeof btoa is 'function' # 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. # 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. btoa encodeURIComponent(src).replace /%([0-9A-F]{2})/g, (match, p1) -> String.fromCharCode '0x' + p1 else throw new Error('Unable to base64 encode inline sourcemap.') # Function wrapper to add source file information to SyntaxErrors thrown by the # lexer/parser/compiler. withPrettyErrors = (fn) -> (code, options = {}) -> try fn.call @, code, options catch err throw err if typeof code isnt 'string' # Support `CoffeeScript.nodes(tokens)`. throw helpers.updateSyntaxError err, code, options.filename # Compile CoffeeScript code to JavaScript, using the Coffee/Jison compiler. # # 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. # # This returns a javascript string, unless `options.sourceMap` is passed, # in which case this returns a `{js, v3SourceMap, sourceMap}` # object, where sourceMap is a sourcemap.coffee#SourceMap object, handy for doing programatic # lookups. exports.compile = compile = withPrettyErrors (code, options) -> {merge, extend} = helpers options = extend {}, options generateSourceMap = options.sourceMap or options.inlineMap if generateSourceMap map = new SourceMap tokens = lexer.tokenize code, options # Pass a list of referenced variables, so that generated variables won't get # the same name. options.referencedVars = ( token[1] for token in tokens when token[0] is 'IDENTIFIER' ) # Check for import or export; if found, force bare mode unless options.bare? and options.bare is yes for token in tokens if token[0] in ['IMPORT', 'EXPORT'] options.bare = yes break fragments = parser.parse(tokens).compileToFragments options currentLine = 0 currentLine += 1 if options.header currentLine += 1 if options.shiftLine currentColumn = 0 js = "" for fragment in fragments # Update the sourcemap with data from each fragment if generateSourceMap # Do not include empty, whitespace, or semicolon-only fragments. if fragment.locationData and not /^[;\s]*$/.test fragment.code map.add( [fragment.locationData.first_line, fragment.locationData.first_column] [currentLine, currentColumn] {noReplace: true}) newLines = helpers.count fragment.code, "\n" currentLine += newLines if newLines currentColumn = fragment.code.length - (fragment.code.lastIndexOf("\n") + 1) else currentColumn += fragment.code.length # Copy the code from each fragment into the final JavaScript. js += fragment.code if options.header header = "Generated by CoffeeScript #{@VERSION}" js = "// #{header}\n#{js}" if generateSourceMap v3SourceMap = map.generate(options, code) if options.inlineMap encoded = base64encode JSON.stringify v3SourceMap sourceMapDataURI = "//# sourceMappingURL=data:application/json;base64,#{encoded}" sourceURL = "//# sourceURL=#{options.filename ? 'coffeescript'}" js = "#{js}\n#{sourceMapDataURI}\n#{sourceURL}" if options.sourceMap { js sourceMap: map v3SourceMap: JSON.stringify v3SourceMap, null, 2 } else js # Tokenize a string of CoffeeScript code, and return the array of tokens. exports.tokens = withPrettyErrors (code, options) -> lexer.tokenize code, options # 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, # or traverse it by using `.traverseChildren()` with a callback. exports.nodes = withPrettyErrors (source, options) -> if typeof source is 'string' parser.parse lexer.tokenize source, options else parser.parse source # Compile and execute a string of CoffeeScript (on the server), correctly # setting `__filename`, `__dirname`, and relative `require()`. exports.run = (code, options = {}) -> mainModule = require.main # Set the filename. mainModule.filename = process.argv[1] = if options.filename then fs.realpathSync(options.filename) else '.' # Clear the module cache. mainModule.moduleCache and= {} # Assign paths for node_modules loading dir = if options.filename path.dirname fs.realpathSync options.filename else fs.realpathSync '.' mainModule.paths = require('module')._nodeModulePaths dir # Compile. if not helpers.isCoffee(mainModule.filename) or require.extensions answer = compile code, options code = answer.js ? answer mainModule._compile code, mainModule.filename # Compile and evaluate a string of CoffeeScript (in a Node.js-like environment). # The CoffeeScript REPL uses this to run the input. exports.eval = (code, options = {}) -> return unless code = code.trim() createContext = vm.Script.createContext ? vm.createContext isContext = vm.isContext ? (ctx) -> options.sandbox instanceof createContext().constructor if createContext if options.sandbox? if isContext options.sandbox sandbox = options.sandbox else sandbox = createContext() sandbox[k] = v for own k, v of options.sandbox sandbox.global = sandbox.root = sandbox.GLOBAL = sandbox else sandbox = global sandbox.__filename = options.filename || 'eval' sandbox.__dirname = path.dirname sandbox.__filename # define module/require only if they chose not to specify their own unless sandbox isnt global or sandbox.module or sandbox.require Module = require 'module' sandbox.module = _module = new Module(options.modulename || 'eval') sandbox.require = _require = (path) -> Module._load path, _module, true _module.filename = sandbox.__filename for r in Object.getOwnPropertyNames require when r not in ['paths', 'arguments', 'caller'] _require[r] = require[r] # use the same hack node currently uses for their own REPL _require.paths = _module.paths = Module._nodeModulePaths process.cwd() _require.resolve = (request) -> Module._resolveFilename request, _module o = {} o[k] = v for own k, v of options o.bare = on # ensure return value js = compile code, o if sandbox is global vm.runInThisContext js else vm.runInContext js, sandbox exports.register = -> require './register' # Throw error with deprecation warning when depending upon implicit `require.extensions` registration if require.extensions for ext in @FILE_EXTENSIONS then do (ext) -> require.extensions[ext] ?= -> throw new Error """ Use CoffeeScript.register() or require the coffee-script/register module to require #{ext} files. """ exports._compileFile = (filename, sourceMap = no, inlineMap = no) -> raw = fs.readFileSync filename, 'utf8' stripped = if raw.charCodeAt(0) is 0xFEFF then raw.substring 1 else raw try answer = compile stripped, { filename, sourceMap, inlineMap sourceFiles: [filename] literate: helpers.isLiterate filename } catch err # As the filename and code of a dynamically loaded file will be different # from the original file compiled with CoffeeScript.run, add that # information to error so it can be pretty-printed later. throw helpers.updateSyntaxError err, stripped, filename answer # Instantiate a Lexer for our use here. lexer = new Lexer # 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". parser.lexer = lex: -> token = parser.tokens[@pos++] if token [tag, @yytext, @yylloc] = token parser.errorToken = token.origin or token @yylineno = @yylloc.first_line else tag = '' tag setInput: (tokens) -> parser.tokens = tokens @pos = 0 upcomingInput: -> "" # Make all the AST nodes visible to the parser. parser.yy = require './nodes' # Override Jison's default error handling function. parser.yy.parseError = (message, {token}) -> # 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. {errorToken, tokens} = parser [errorTag, errorText, errorLoc] = errorToken errorText = switch when errorToken is tokens[tokens.length - 1] 'end of input' when errorTag in ['INDENT', 'OUTDENT'] 'indentation' when errorTag in ['IDENTIFIER', 'NUMBER', 'INFINITY', 'STRING', 'STRING_START', 'REGEX', 'REGEX_START'] errorTag.replace(/_START$/, '').toLowerCase() else helpers.nameWhitespaceCharacter errorText # The second argument has a `loc` property, which should have the location # 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 lexer. helpers.throwSyntaxError "unexpected #{errorText}", errorLoc # Based on http://v8.googlecode.com/svn/branches/bleeding_edge/src/messages.js # Modified to handle sourceMap formatSourcePosition = (frame, getSourceMapping) -> fileName = undefined fileLocation = '' if frame.isNative() fileLocation = "native" else if frame.isEval() fileName = frame.getScriptNameOrSourceURL() fileLocation = "#{frame.getEvalOrigin()}, " unless fileName else fileName = frame.getFileName() fileName or= "<anonymous>" line = frame.getLineNumber() column = frame.getColumnNumber() # Check for a sourceMap position source = getSourceMapping fileName, line, column fileLocation = if source "#{fileName}:#{source[0]}:#{source[1]}" else "#{fileName}:#{line}:#{column}" functionName = frame.getFunctionName() isConstructor = frame.isConstructor() isMethodCall = not (frame.isToplevel() or isConstructor) if isMethodCall methodName = frame.getMethodName() typeName = frame.getTypeName() if functionName tp = as = '' if typeName and functionName.indexOf typeName tp = "#{typeName}." if methodName and functionName.indexOf(".#{methodName}") isnt functionName.length - methodName.length - 1 as = " [as #{methodName}]" "#{tp}#{functionName}#{as} (#{fileLocation})" else "#{typeName}.#{methodName or '<anonymous>'} (#{fileLocation})" else if isConstructor "new #{functionName or '<anonymous>'} (#{fileLocation})" else if functionName "#{functionName} (#{fileLocation})" else fileLocation # Map of filenames -> sourceMap object. sourceMaps = {} # Generates the source map for a coffee file and stores it in the local cache variable. getSourceMap = (filename) -> return sourceMaps[filename] if sourceMaps[filename] for ext in exports.FILE_EXTENSIONS if helpers.ends filename, ext answer = exports._compileFile filename, true return sourceMaps[filename] = answer.sourceMap return null # Based on [michaelficarra/CoffeeScriptRedux](http://goo.gl/ZTx1p) # NodeJS / V8 have no support for transforming positions in stack traces using # sourceMap, so we must monkey-patch Error to display CoffeeScript source # positions. Error.prepareStackTrace = (err, stack) -> getSourceMapping = (filename, line, column) -> sourceMap = getSourceMap filename answer = sourceMap.sourceLocation [line - 1, column - 1] if sourceMap if answer then [answer[0] + 1, answer[1] + 1] else null frames = for frame in stack break if frame.getFunction() is exports.run " at #{formatSourcePosition frame, getSourceMapping}" "#{err.toString()}\n#{frames.join '\n'}\n"
更改后文本
打开文件
# 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. fs = require 'fs' vm = require 'vm' path = require 'path' {Lexer} = require './lexer' {parser} = require './parser' helpers = require './helpers' 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. exports.VERSION = packageJson.version exports.FILE_EXTENSIONS = ['.coffee', '.litcoffee', '.coffee.md'] # Expose helpers for testing. exports.helpers = helpers # Function that allows for btoa in both nodejs and the browser. base64encode = (src) -> switch when typeof Buffer is 'function' new Buffer(src).toString('base64') when typeof btoa is 'function' # 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. # 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. btoa encodeURIComponent(src).replace /%([0-9A-F]{2})/g, (match, p1) -> String.fromCharCode '0x' + p1 else throw new Error('Unable to base64 encode inline sourcemap.') # Function wrapper to add source file information to SyntaxErrors thrown by the # lexer/parser/compiler. withPrettyErrors = (fn) -> (code, options = {}) -> try fn.call @, code, options catch err throw err if typeof code isnt 'string' # Support `CoffeeScript.nodes(tokens)`. throw helpers.updateSyntaxError err, code, options.filename # Compile CoffeeScript code to JavaScript, using the Coffee/Jison compiler. # # 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. # # This returns a javascript string, unless `options.sourceMap` is passed, # in which case this returns a `{js, v3SourceMap, sourceMap}` # object, where sourceMap is a sourcemap.coffee#SourceMap object, handy for doing programatic # lookups. exports.compile = compile = withPrettyErrors (code, options) -> {merge, extend} = helpers options = extend {}, options generateSourceMap = options.sourceMap or options.inlineMap if generateSourceMap map = new SourceMap tokens = lexer.tokenize code, options # Pass a list of referenced variables, so that generated variables won't get # the same name. options.referencedVars = ( token[1] for token in tokens when token[0] is 'IDENTIFIER' ) # Check for import or export; if found, force bare mode unless options.bare? and options.bare is yes for token in tokens if token[0] in ['IMPORT', 'EXPORT'] options.bare = yes break fragments = parser.parse(tokens).compileToFragments options currentLine = 0 currentLine += 1 if options.header currentLine += 1 if options.shiftLine currentColumn = 0 js = "" for fragment in fragments # Update the sourcemap with data from each fragment if generateSourceMap # Do not include empty, whitespace, or semicolon-only fragments. if fragment.locationData and not /^[;\s]*$/.test fragment.code map.add( [fragment.locationData.first_line, fragment.locationData.first_column] [currentLine, currentColumn] {noReplace: true}) newLines = helpers.count fragment.code, "\n" currentLine += newLines if newLines currentColumn = fragment.code.length - (fragment.code.lastIndexOf("\n") + 1) else currentColumn += fragment.code.length # Copy the code from each fragment into the final JavaScript. js += fragment.code if options.header header = "Generated by CoffeeScript #{@VERSION}" js = "// #{header}\n#{js}" if generateSourceMap v3SourceMap = map.generate(options, code) if options.inlineMap encoded = base64encode JSON.stringify v3SourceMap sourceMapDataURI = "//# sourceMappingURL=data:application/json;base64,#{encoded}" sourceURL = "//# sourceURL=#{options.filename ? 'coffeescript'}" js = "#{js}\n#{sourceMapDataURI}\n#{sourceURL}" if options.sourceMap { js sourceMap: map v3SourceMap: JSON.stringify v3SourceMap, null, 2 } else js # Tokenize a string of CoffeeScript code, and return the array of tokens. exports.tokens = withPrettyErrors (code, options) -> lexer.tokenize code, options # 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, # or traverse it by using `.traverseChildren()` with a callback. exports.nodes = withPrettyErrors (source, options) -> if typeof source is 'string' parser.parse lexer.tokenize source, options else parser.parse source # Compile and execute a string of CoffeeScript (on the server), correctly # setting `__filename`, `__dirname`, and relative `require()`. exports.run = (code, options = {}) -> mainModule = require.main # Set the filename. mainModule.filename = process.argv[1] = if options.filename then fs.realpathSync(options.filename) else '.' # Clear the module cache. mainModule.moduleCache and= {} # Assign paths for node_modules loading dir = if options.filename path.dirname fs.realpathSync options.filename else fs.realpathSync '.' mainModule.paths = require('module')._nodeModulePaths dir # Compile. if not helpers.isCoffee(mainModule.filename) or require.extensions answer = compile code, options code = answer.js ? answer mainModule._compile code, mainModule.filename # Compile and evaluate a string of CoffeeScript (in a Node.js-like environment). # The CoffeeScript REPL uses this to run the input. exports.eval = (code, options = {}) -> return unless code = code.trim() createContext = vm.Script.createContext ? vm.createContext isContext = vm.isContext ? (ctx) -> options.sandbox instanceof createContext().constructor if createContext if options.sandbox? if isContext options.sandbox sandbox = options.sandbox else sandbox = createContext() sandbox[k] = v for own k, v of options.sandbox sandbox.global = sandbox.root = sandbox.GLOBAL = sandbox else sandbox = global sandbox.__filename = options.filename || 'eval' sandbox.__dirname = path.dirname sandbox.__filename # define module/require only if they chose not to specify their own unless sandbox isnt global or sandbox.module or sandbox.require Module = require 'module' sandbox.module = _module = new Module(options.modulename || 'eval') sandbox.require = _require = (path) -> Module._load path, _module, true _module.filename = sandbox.__filename for r in Object.getOwnPropertyNames require when r not in ['paths', 'arguments', 'caller'] _require[r] = require[r] # use the same hack node currently uses for their own REPL _require.paths = _module.paths = Module._nodeModulePaths process.cwd() _require.resolve = (request) -> Module._resolveFilename request, _module o = {} o[k] = v for own k, v of options o.bare = on # ensure return value js = compile code, o if sandbox is global vm.runInThisContext js else vm.runInContext js, sandbox exports.register = -> require './register' # Throw error with deprecation warning when depending upon implicit `require.extensions` registration if require.extensions for ext in @FILE_EXTENSIONS then do (ext) -> require.extensions[ext] ?= -> throw new Error """ 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) -> 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 compiledFiles[filename] = stripped compileCode stripped, filename, sourceMap, inlineMap compileCode = (code, filename, sourceMap = no, inlineMap = no) -> try answer = compile code, { filename, sourceMap, inlineMap sourceFiles: [filename] literate: helpers.isLiterate filename } catch err # As the filename and code of a dynamically loaded file will be different # from the original file compiled with CoffeeScript.run, add that # information to error so it can be pretty-printed later. throw helpers.updateSyntaxError err, code, filename answer # Instantiate a Lexer for our use here. lexer = new Lexer # 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". parser.lexer = lex: -> token = parser.tokens[@pos++] if token [tag, @yytext, @yylloc] = token parser.errorToken = token.origin or token @yylineno = @yylloc.first_line else tag = '' tag setInput: (tokens) -> parser.tokens = tokens @pos = 0 upcomingInput: -> "" # Make all the AST nodes visible to the parser. parser.yy = require './nodes' # Override Jison's default error handling function. parser.yy.parseError = (message, {token}) -> # 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. {errorToken, tokens} = parser [errorTag, errorText, errorLoc] = errorToken errorText = switch when errorToken is tokens[tokens.length - 1] 'end of input' when errorTag in ['INDENT', 'OUTDENT'] 'indentation' when errorTag in ['IDENTIFIER', 'NUMBER', 'INFINITY', 'STRING', 'STRING_START', 'REGEX', 'REGEX_START'] errorTag.replace(/_START$/, '').toLowerCase() else helpers.nameWhitespaceCharacter errorText # The second argument has a `loc` property, which should have the location # 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 lexer. helpers.throwSyntaxError "unexpected #{errorText}", errorLoc # Based on http://v8.googlecode.com/svn/branches/bleeding_edge/src/messages.js # Modified to handle sourceMap formatSourcePosition = (frame, getSourceMapping) -> fileName = undefined fileLocation = '' if frame.isNative() fileLocation = "native" else if frame.isEval() fileName = frame.getScriptNameOrSourceURL() fileLocation = "#{frame.getEvalOrigin()}, " unless fileName else fileName = frame.getFileName() fileName or= "<anonymous>" line = frame.getLineNumber() column = frame.getColumnNumber() # Check for a sourceMap position source = getSourceMapping fileName, line, column fileLocation = if source "#{fileName}:#{source[0]}:#{source[1]}" else "#{fileName}:#{line}:#{column}" functionName = frame.getFunctionName() isConstructor = frame.isConstructor() isMethodCall = not (frame.isToplevel() or isConstructor) if isMethodCall methodName = frame.getMethodName() typeName = frame.getTypeName() if functionName tp = as = '' if typeName and functionName.indexOf typeName tp = "#{typeName}." if methodName and functionName.indexOf(".#{methodName}") isnt functionName.length - methodName.length - 1 as = " [as #{methodName}]" "#{tp}#{functionName}#{as} (#{fileLocation})" else "#{typeName}.#{methodName or '<anonymous>'} (#{fileLocation})" else if isConstructor "new #{functionName or '<anonymous>'} (#{fileLocation})" else if functionName "#{functionName} (#{fileLocation})" else fileLocation # Map of filenames: sourceMap objects. sourceMaps = {} # Generates the source map for a coffee file and stores it in the local cache variable. getSourceMap = (filename) -> if sourceMaps[filename]? sourceMaps[filename] else if compiledFiles[filename]? answer = compileCode compiledFiles[filename], filename, yes, no sourceMaps[filename] = answer.sourceMap else null # Based on [michaelficarra/CoffeeScriptRedux](http://goo.gl/ZTx1p) # NodeJS / V8 have no support for transforming positions in stack traces using # sourceMap, so we must monkey-patch Error to display CoffeeScript source # positions. Error.prepareStackTrace = (err, stack) -> getSourceMapping = (filename, line, column) -> sourceMap = getSourceMap filename answer = sourceMap.sourceLocation [line - 1, column - 1] if sourceMap if answer then [answer[0] + 1, answer[1] + 1] else null frames = for frame in stack break if frame.getFunction() is exports.run " at #{formatSourcePosition frame, getSourceMapping}" "#{err.toString()}\n#{frames.join '\n'}\n"
查找差异