Created
April 5, 2018 18:01
-
-
Save treeform/3ac009556892b100779c6ed38882429b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import json, strutils, algorithm | |
| type | |
| Mapping* = object | |
| ## mapping object that represents the line | |
| generatedLine: int | |
| generatedColumn: int | |
| originalLine: int | |
| originalColumn: int | |
| source: string | |
| name: string | |
| SourceMap* = ref object | |
| ## represents complete source object | |
| file: string | |
| mappings: seq[Mapping] | |
| sources: seq[string] | |
| names: seq[string] | |
| proc newSourceMap*(file: string): SourceMap = | |
| ## start creation of a new javascript source map | |
| result = new SourceMap | |
| result.file = file | |
| result.mappings = newSeq[Mapping]() | |
| result.sources = newSeq[string]() | |
| result.names = newSeq[string]() | |
| proc cmp(mappingA, mappingB: Mapping): int = | |
| ## compair two source maps | |
| 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 integers = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" | |
| proc encodeVLQ(i: int): string = | |
| ## encode using VLQ see https://en.wikipedia.org/wiki/Variable-length_quantity | |
| result = "" | |
| var n = i | |
| if n < 0: | |
| n = (-n shl 1) or 1 | |
| else: | |
| n = n shl 1 | |
| var z = 0 | |
| while z == 0 or n > 0: | |
| var e = n and 31 | |
| n = n shr 5 | |
| if n > 0: | |
| e = e or 32 | |
| result.add(integers[e]) | |
| z += 1 | |
| assert encodeVLQ(1) == "C" | |
| assert encodeVLQ(-1) == "D" | |
| assert encodeVLQ(98) == "kG" | |
| proc serializeMappings(sourceMap: var 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) | |
| # sort the mappings | |
| sourceMap.mappings.sort(cmp) | |
| result = "" | |
| var | |
| prevGeneratedColumn = 0 | |
| prevGeneratedLine = 1 | |
| prevOriginalColumn = 0 | |
| prevOriginalLine = 0 | |
| prevName = 0 | |
| prevSource = 0 | |
| next = "" | |
| nameIdx: int | |
| for i, mapping in sourceMap.mappings: | |
| if mapping.generatedLine != prevGeneratedLine: | |
| prevGeneratedColumn = 0 | |
| while mapping.generatedLine != prevGeneratedLine: | |
| next &= ';' | |
| inc prevGeneratedLine | |
| else: | |
| if i > 0: | |
| if cmp(mapping, sourceMap.mappings[i - 1]) != 0: | |
| continue | |
| next &= ',' | |
| next &= encodeVLQ(mapping.generatedColumn - prevGeneratedColumn) | |
| prevGeneratedColumn = mapping.generatedColumn | |
| if mapping.source != nil: | |
| let sourceIdx = sourceMap.sources.find(mapping.source) | |
| next &= encodeVLQ(sourceIdx - prevSource) | |
| prevSource = sourceIdx | |
| next &= encodeVLQ(mapping.originalLine - 1 - prevOriginalLine) | |
| prevOriginalLine = mapping.originalLine - 1 | |
| next &= encodeVLQ(mapping.originalColumn - prevOriginalColumn) | |
| prevOriginalColumn = mapping.originalColumn | |
| result &= next | |
| proc generate*(sourceMap: var SourceMap): string = | |
| ## Generates a source map and returns it as a string | |
| let serializedMappings = sourceMap.serializeMappings() | |
| return $ %* { | |
| "version": 3, | |
| "sources": sourceMap.sources, | |
| "names": sourceMap.names, | |
| "mappings": serializedMappings, | |
| "file": sourceMap.file, | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment