Skip to content

Instantly share code, notes, and snippets.

@treeform
Created April 5, 2018 17:21
Show Gist options
  • Save treeform/c95e4073a6f5942d8192a4d425cb885f to your computer and use it in GitHub Desktop.
Save treeform/c95e4073a6f5942d8192a4d425cb885f to your computer and use it in GitHub Desktop.
import json
import strutils
type
Mapping = object
generatedLine: int
generatedColumn: int
originalLine: int
originalColumn: int
source: string
name: string
SourceMap = ref object
file: string
mappings: seq[Mapping]
sources: seq[string]
names: seq[string]
proc newSourceMap(file: string): SourceMap =
result = new SourceMap
result.file = file
result.mappings = newSeq[Mapping]()
result.sources = newSeq[string]()
result.names = newSeq[string]()
proc compareByGeneratedPositionsInflated(mappingA, mappingB: Mapping): int =
var cmp = mappingA.generatedLine - mappingB.generatedLine;
if cmp != 0:
return cmp
cmp = mappingA.generatedColumn - mappingB.generatedColumn;
if cmp != 0:
return cmp;
cmp = cmp(mappingA.source, mappingB.source);
if cmp != 0:
return cmp;
cmp = mappingA.originalLine - mappingB.originalLine;
if cmp != 0:
return cmp
cmp = mappingA.originalColumn - mappingB.originalColumn;
if cmp != 0:
return cmp
return cmp(mappingA.name, mappingB.name);
const
VLQ_BASE_SHIFT = 5
# binary: 100000
VLQ_BASE = 1 shl VLQ_BASE_SHIFT
#binary: 011111
VLQ_BASE_MASK = VLQ_BASE - 1
# binary: 100000
VLQ_CONTINUATION_BIT = VLQ_BASE
proc toVLQSigned(aValue: int): int =
if aValue < 0:
((-aValue) shl 1) + 1
else:
(aValue shl 1) + 0;
assert toVLQSigned(1) == 2
assert toVLQSigned(2) == 4
assert toVLQSigned(-1) == 3
assert toVLQSigned(-2) == 5
assert toVLQSigned(0) == 0
const intToCharMap = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
proc digit64encode(digit: int): char =
## Encode an integer in the range of 0 to 63 to a single base 64 digit.
assert 0 <= digit or digit < intToCharMap.len, "digit out of range"
return intToCharMap[digit]
assert digit64encode(0) == 'A'
assert digit64encode(13) == 'N'
proc base64VLQencode(aValue: int): string =
var encoded = ""
var digit = 0
var vlq = toVLQSigned(aValue)
while true:
#echo "hi", vlq, digit, encoded
#digit = vlq & VLQ_BASE_MASK;
#vlq >>>= VLQ_BASE_SHIFT;
digit = vlq and VLQ_BASE_MASK
vlq = vlq shr VLQ_BASE_SHIFT
if vlq > 0:
# There are still more digits in this value, so we must make sure the
# continuation bit is marked.
digit = digit or VLQ_CONTINUATION_BIT
encoded &= digit64encode(digit)
#echo encoded
if not (vlq > 0):
break
return encoded
assert base64VLQencode(1) == "C"
assert base64VLQencode(-1) == "D"
assert base64VLQencode(98) == "kG"
proc serializeMappings(sourceMap: SourceMap): string =
# update the source arrays
for i, mapping in sourceMap.mappings:
if mapping.source != nil:
let sourceIdx = sourceMap.sources.find(mapping.source)
if sourceIdx == -1:
sourceMap.sources.add(mapping.source)
# update the name arrays
for i, mapping in sourceMap.mappings:
if mapping.name != nil:
let nameIdx = sourceMap.names.find(mapping.name)
if nameIdx == -1:
sourceMap.names.add(mapping.name)
result = ""
var
previousGeneratedColumn = 0
previousGeneratedLine = 1
previousOriginalColumn = 0
previousOriginalLine = 0
previousName = 0
previousSource = 0
next = ""
nameIdx: int
sourceIdx: int
for i, mapping in sourceMap.mappings:
#echo mapping
if mapping.generatedLine != previousGeneratedLine:
previousGeneratedColumn = 0
while mapping.generatedLine != previousGeneratedLine:
next &= ';'
inc previousGeneratedLine
else:
if i > 0:
if compareByGeneratedPositionsInflated(mapping, sourceMap.mappings[i - 1]) != 0:
continue
next &= ','
next &= base64VLQencode(mapping.generatedColumn - previousGeneratedColumn)
previousGeneratedColumn = mapping.generatedColumn
if mapping.source != nil:
sourceIdx = sourceMap.sources.find(mapping.source)
next &= base64VLQencode(sourceIdx - previousSource)
previousSource = sourceIdx
# lines are stored 0-based in SourceMap spec version 3
next &= base64VLQencode(mapping.originalLine - 1 - previousOriginalLine)
previousOriginalLine = mapping.originalLine - 1
next &= base64VLQencode(mapping.originalColumn - previousOriginalColumn)
previousOriginalColumn = mapping.originalColumn
result &= next
proc toJson(sourceMap: SourceMap): JsonNode =
let serializedMappings = sourceMap.serializeMappings()
return %* {
"version": 3,
"sources": sourceMap.sources,
"names": sourceMap.names,
"mappings": serializedMappings,
"file": sourceMap.file,
}
var s = newSourceMap("test.js.map")
#[
s.mappings.add Mapping(
source: "test.nim",
originalLine: 1,
originalColumn: 1,
generatedLine: 79,
generatedColumn: 1
)
s.mappings.add Mapping(
source: "test.nim",
originalLine: 2,
originalColumn: 2,
generatedLine: 82,
generatedColumn: 5
)
s.mappings.add Mapping(
source: "test.nim",
originalLine: 3,
originalColumn: 2,
generatedLine: 84,
generatedColumn: 5
)
s.mappings.add Mapping(
source: "test.nim",
originalLine: 4,
originalColumn: 2,
generatedLine: 86,
generatedColumn: 5
)
s.mappings.add Mapping(
source: "test.nim",
originalLine: 5,
originalColumn: 4,
generatedLine: 87,
generatedColumn: 5
)
s.mappings.add Mapping(
source: "test.nim",
originalLine: 7,
originalColumn: 1,
generatedLine: 95,
generatedColumn: 1
)
]#
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment