• ジャンプ先 +
    browser.coffee cake.coffee coffee-script.coffee command.coffee grammar.coffee helpers.coffee index.coffee lexer.coffee nodes.coffee optparse.coffee register.coffee repl.coffee rewriter.coffee scope.litcoffee sourcemap.litcoffee
  • sourcemap.litcoffee

  • ¶

    ソースマップを使用すると、実行中の JavaScript を、それに対応する元のソースコードに JavaScript ランタイムで一致させることができます。JavaScript は最小化できますが、この場合は、奇麗書された JavaScript を CoffeeScript にマッピングする必要があります。

    マップを作成するには、構文ツリーのすべてのノードを発した位置(行番号、列番号)を追跡し、この情報の JSON シリアル化のコンパクトな VLQ エンコード表現である マップファイル を、生成された JavaScript と並べて書き出せるようにする必要があります。

    LineMap

  • ¶

    LineMap オブジェクトは、1 行の出力 JavaScript コードの元の行と列の位置に関する情報を追跡します。SourceMaps は LineMaps に基づいて実装されます。

    class LineMap
      constructor: (@line) ->
        @columns = []
    
      add: (column, [sourceLine, sourceColumn], options={}) ->
        return if @columns[column] and options.noReplace
        @columns[column] = {line: @line, column, sourceLine, sourceColumn}
    
      sourceLocation: (column) ->
        column-- until (mapping = @columns[column]) or (column <= 0)
        mapping and [mapping.sourceLine, mapping.sourceColumn]
  • ¶

    SourceMap

  • ¶

    生成された 1 つの JavaScript ファイル内の位置を、元の CoffeeScript ソースファイル内の位置にマップします。

    これは、ソースマップをディスクにどのように表現するかに対して、意図的に順応的ではありません。コンパイラが「v3」スタイルのソースマップを作成する準備が整ったら、行と列のバッファーの配列を参照してマップを作成できます。

    class SourceMap
      constructor: ->
        @lines = []
  • ¶

    この SourceMap にマッピングを追加します。sourceLocation と generatedLocation はどちらも [line, column] 配列です。options.noReplace が true の場合、指定した line と column にマッピングがすでにある場合、これは影響しません。

      add: (sourceLocation, generatedLocation, options = {}) ->
        [line, column] = generatedLocation
        lineMap = (@lines[line] or= new LineMap(line))
        lineMap.add column, sourceLocation, options
  • ¶

    生成されたコード内の特定の line と column の元の位置を検索します。

      sourceLocation: ([line, column]) ->
        line-- until (lineMap = @lines[line]) or (line <= 0)
        lineMap and lineMap.sourceLocation column
  • ¶

    V3 SourceMap の生成

  • ¶

    V3 ソースマップを作成し、生成された JSON を文字列として返します。options.sourceRoot を使用して、ソースマップに書き込まれる sourceRoot を指定できます。また、options.sourceFiles と options.generatedFile を渡して、「sources」と「file」を設定できます。

      generate: (options = {}, code = null) ->
        writingline       = 0
        lastColumn        = 0
        lastSourceLine    = 0
        lastSourceColumn  = 0
        needComma         = no
        buffer            = ""
    
        for lineMap, lineNumber in @lines when lineMap
          for mapping in lineMap.columns when mapping
            while writingline < mapping.line
              lastColumn = 0
              needComma = no
              buffer += ";"
              writingline++
  • ¶

    この行にセグメントをすでに書いた場合、コンマを書きます。

            if needComma
              buffer += ","
              needComma = no
  • ¶

    次のセグメントを書きます。セグメントは、1、4、または 5 つの値になります。1 つのみの場合、ソースコード内の何にも一致しない生成された列です。

    現在の行で記録された以前の列に関連する、生成されたソース内の開始列

            buffer += @encodeVlq mapping.column - lastColumn
            lastColumn = mapping.column
  • ¶

    ソースのリスト内のインデックス

            buffer += @encodeVlq 0
  • ¶

    前のソース行に関連する、元のソース内の開始行。

            buffer += @encodeVlq mapping.sourceLine - lastSourceLine
            lastSourceLine = mapping.sourceLine
  • ¶

    前の列に関連する、元のソース内の開始列。

            buffer += @encodeVlq mapping.sourceColumn - lastSourceColumn
            lastSourceColumn = mapping.sourceColumn
            needComma = yes
  • ¶

    「v3」ソースマップの正規の JSON オブジェクト形式を作成します。

        v3 =
          version:    3
          file:       options.generatedFile or ''
          sourceRoot: options.sourceRoot or ''
          sources:    options.sourceFiles or ['']
          names:      []
          mappings:   buffer
    
        v3.sourcesContent = [code] if options.inlineMap
    
        v3
  • ¶

    Base64 VLQ エンコード

  • ¶

    SourceMap VLQ エンコードは「逆」であることに注意してください。MIDI スタイルの VLQ エンコードは、元の値の最上位ビット (MSB) を VLQ エンコード値の MSB に入れます(Wikipedia を参照)。SourceMap VLQ ではその逆が行われ、元の値の下位 4 ビットが、VLQ エンコード値の最初のバイトにエンコードされます。

      VLQ_SHIFT            = 5
      VLQ_CONTINUATION_BIT = 1 << VLQ_SHIFT             # 0010 0000
      VLQ_VALUE_MASK       = VLQ_CONTINUATION_BIT - 1   # 0001 1111
    
      encodeVlq: (value) ->
        answer = ''
  • ¶

    下位ビットは符号を表します。

        signBit = if value < 0 then 1 else 0
  • ¶

    次のビットは実際の値です。

        valueToEncode = (Math.abs(value) << 1) + signBit
  • ¶

    valueToEncode が 0 の場合でも、少なくとも 1 文字エンコードしていることを確認します。

        while valueToEncode or not answer
          nextChunk = valueToEncode & VLQ_VALUE_MASK
          valueToEncode = valueToEncode >> VLQ_SHIFT
          nextChunk |= VLQ_CONTINUATION_BIT if valueToEncode
          answer += @encodeBase64 nextChunk
    
        answer
  • ¶

    通常の Base64 エンコード

  • ¶
      BASE64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
    
      encodeBase64: (value) ->
        BASE64_CHARS[value] or throw new Error "Cannot Base64 encode value: #{value}"
  • ¶

    ソースマップの API は SourceMap クラスだけです。

    module.exports = SourceMap