fs            = require 'fs'
vm            = require 'vm'
path          = require 'path'
{Lexer}       = require './lexer'
{parser}      = require './parser'
helpers       = require './helpers'
SourceMap     = require './sourcemap'CoffeeScriptは、サーバー上でNode.js/V8ベースのコマンドラインコンパイラとして使用することも、ブラウザで直接CoffeeScriptを実行することもできます。このモジュールには、ソースCoffeeScriptをトークン化、解析、およびJavaScriptにコンパイルするための主要なエントリ関数が含まれています。
fs            = require 'fs'
vm            = require 'vm'
path          = require 'path'
{Lexer}       = require './lexer'
{parser}      = require './parser'
helpers       = require './helpers'
SourceMap     = require './sourcemap'このファイルは`lib/coffee-script`から評価されるため、このファイルの2つ上のレベルにある`package.json`をrequireします。
packageJson   = require '../../package.json'現在のCoffeeScriptのバージョン番号。
exports.VERSION = packageJson.version
exports.FILE_EXTENSIONS = ['.coffee', '.litcoffee', '.coffee.md']テスト用のヘルパーを公開します。
exports.helpers = helpersnodejsとブラウザの両方でbtoaを可能にする関数。
base64encode = (src) -> switch
  when typeof Buffer is 'function'
    new Buffer(src).toString('base64')
  when typeof btoa is 'function'`<script>`ブロックの内容はUTF-16でエンコードされるため、ブロックで拡張文字が使用されている場合、btoaはUTF-8で最大になるため失敗します。詳細とここで実装されている解決策については、https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_Unicode_Problemを参照してください。
    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.')レクサー/パーサー/コンパイラによってスローされたSyntaxErrorsにソースファイル情報を追加するための関数ラッパー。
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コンパイルされた各ファイルについて、後で再コンパイルが必要になった場合に備えて、そのソースをメモリに保存します。最初のコンパイルでソースマップ(高速)が作成されなかったが、何か問題が発生し、スタックトレースが必要な場合は、再コンパイルが必要になる場合があります。ほとんどの場合、コードは例外をスローしないことを前提として、スタックトレースが必要な場合にのみ2回コンパイルする方が、使用される可能性が低い場合でも常にソースマップを生成するよりも効率的です。`ファイル名`:`(ソース)`の形式で保存します。
sources = {}生成された場合は、ソースマップも`ファイル名`:`(ソースマップ)`の形式で保存します。
sourceMaps = {}Coffee/Jisonコンパイラを使用して、CoffeeScriptコードをJavaScriptにコンパイルします。
`options.sourceMap`が指定されている場合は、`options.filename`も指定する必要があります。`SourceMap#generate`に渡すことができるすべてのオプションもここに渡すことができます。
これは、`options.sourceMap`が渡されない限り、javascript文字列を返します。その場合、これは`{js, v3SourceMap, sourceMap}`オブジェクトを返します。ここで、sourceMapはsourcemap.coffee#SourceMapオブジェクトであり、プログラムによるルックアップに便利です。
exports.compile = compile = withPrettyErrors (code, options) ->
  {merge, extend} = helpers
  options = extend {}, optionsファイル名が渡されない場合は、常にソースマップを生成します。ファイル名がないと、`prepareStackTrace`のソースマップを取得するために再コンパイルする必要がある場合に、後でこのソースを取得する方法がないためです。
  generateSourceMap = options.sourceMap or options.inlineMap or not options.filename?
  filename = options.filename or '<anonymous>'
  sources[filename] = code
  map = new SourceMap if generateSourceMap
  tokens = lexer.tokenize code, options参照される変数のリストを渡して、生成された変数が同じ名前にならないようにします。
  options.referencedVars = (
    token[1] for token in tokens when token[0] is 'IDENTIFIER'
  )importまたはexportをチェックします。見つかった場合は、ベアモードを強制します。
  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各フラグメントのデータでソースマップを更新します。
    if generateSourceMap空、空白、またはセミコロンのみのフラグメントを含めないでください。
      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各フラグメントのコードを最終的なJavaScriptにコピーします。
    js += fragment.code
  if options.header
    header = "Generated by CoffeeScript #{@VERSION}"
    js = "// #{header}\n#{js}"
  if generateSourceMap
    v3SourceMap = map.generate(options, code)
    sourceMaps[filename] = map
  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
    jsCoffeeScriptコードの文字列をトークン化し、トークンの配列を返します。
exports.tokens = withPrettyErrors (code, options) ->
  lexer.tokenize code, optionsCoffeeScriptコードの文字列または字句解析されたトークンの配列を解析し、ASTを返します。その後、ルートで`.compile()`を呼び出すことでコンパイルするか、コールバックを使用して`.traverseChildren()`を使用してトラバースできます。
exports.nodes = withPrettyErrors (source, options) ->
  if typeof source is 'string'
    parser.parse lexer.tokenize source, options
  else
    parser.parse sourceCoffeeScriptの文字列を(サーバー上で)コンパイルして実行し、`__filename`、`__dirname`、および相対`require()`を正しく設定します。
exports.run = (code, options = {}) ->
  mainModule = require.mainファイル名を設定します。
  mainModule.filename = process.argv[1] =
    if options.filename then fs.realpathSync(options.filename) else '<anonymous>'モジュールキャッシュをクリアします。
  mainModule.moduleCache and= {}node_modulesの読み込みパスを割り当てます
  dir = if options.filename?
    path.dirname fs.realpathSync options.filename
  else
    fs.realpathSync '.'
  mainModule.paths = require('module')._nodeModulePaths dirコンパイルします。
  if not helpers.isCoffee(mainModule.filename) or require.extensions
    answer = compile code, options
    code = answer.js ? answer
  mainModule._compile code, mainModule.filenameCoffeeScriptの文字列を(Node.jsのような環境で)コンパイルして評価します。CoffeeScript REPLはこれを使用して入力を実行します。
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独自のモジュール/ requireを指定しない場合にのみ、モジュール/ requireを定義します
    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]ノードが現在独自の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'暗黙的な`require.extensions`登録に依存している場合は、非推奨の警告とともにエラーをスローします
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'このファイルがUnicodeバイトオーダーマークで始まる場合は、それを削除します。
  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動的にロードされたファイルのファイル名とコードは、CoffeeScript.runでコンパイルされた元のファイルとは異なるため、その情報をエラーに追加して、後で適切に印刷できるようにします。
    throw helpers.updateSyntaxError err, stripped, filename
  answerここで使用するためにLexerをインスタンス化します。
lexer = new Lexer実際のLexerは、トークンの汎用ストリームを生成します。このオブジェクトは、Jison APIと互換性のある薄いラッパーを提供します。その後、それを「Jisonレクサー」として直接渡すことができます。
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: ->
    ""すべてのASTノードをパーサーから見えるようにします。
parser.yy = require './nodes'Jisonのデフォルトのエラー処理関数をオーバーライドします。
parser.yy.parseError = (message, {token}) ->Jisonのメッセージは無視してください。冗長な行番号情報が含まれています。トークンは無視してください。エラーの原因が、その起源を参照する可能性のある生成されたトークンである場合に備えて、レクサーから直接その値を取得します。
  {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 errorText2番目の引数には`loc`プロパティがあり、このトークンの位置データが含まれている必要があります。残念ながら、Jisonは古い`loc`(前のトークンからの)を送信しているように見えるため、レクサーから直接位置情報を取得します。
  helpers.throwSyntaxError "unexpected #{errorText}", errorLochttp://v8.googlecode.com/svn/branches/bleeding_edge/src/messages.jsに基づいて、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()sourceMapの位置を確認します
    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
getSourceMap = (filename) ->
  if sourceMaps[filename]?
    sourceMaps[filename]ブラウザでコンパイルされたCoffeeScriptは、`<anonymous>`の`options.filename`でコンパイルされる場合がありますが、ブラウザはスクリプトファイルのファイル名でスタックトレースを要求する場合があります。
  else if sourceMaps['<anonymous>']?
    sourceMaps['<anonymous>']
  else if sources[filename]?
    answer = compile sources[filename],
      filename: filename
      sourceMap: yes
      literate: helpers.isLiterate filename
    answer.sourceMap
  else
    nullmichaelficarra/CoffeeScriptReduxに基づいて、NodeJS / V8はsourceMapを使用してスタックトレースの位置を変換する機能をサポートしていないため、CoffeeScriptのソース位置を表示するにはErrorをモンキーパッチする必要があります。
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"