├── .gitignore ├── .npmignore ├── .travis.yml ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── README.md ├── bench ├── bench_decode.node.js ├── bench_decode_fast.node.js ├── index.html ├── scala.js.map └── scalajs-runtime-sourcemap.js ├── bin ├── decode-source-map └── encode-source-map ├── package.json ├── rollup.config.cli-decode.js ├── rollup.config.cli-encode.js ├── rollup.config.js ├── rollup.config.tests.js ├── src ├── base64.ts ├── cli │ ├── decode-source-map.ts │ └── encode-source-map.ts ├── concat.ts ├── decode.ts ├── decoder.ts ├── encode.ts ├── encoder.ts ├── index.ts ├── int-buffer-reader.ts ├── int-buffer-writer.ts ├── interfaces.ts ├── mapping-factories.ts ├── mappings-decoder.ts ├── mappings-encoder.ts ├── reader.ts ├── tests │ ├── concat-tests.ts │ ├── encoder-tests.ts │ ├── fixtures │ │ ├── map1-2.ts │ │ ├── map1.ts │ │ ├── map2.ts │ │ ├── map3-4-1.ts │ │ └── map3-4.ts │ ├── index.ts │ └── vlq-tests.ts ├── utils │ ├── read-file.ts │ ├── to-buffer.ts │ └── to-string.ts ├── vlq.ts └── writer.ts ├── tsconfig.json ├── tslint.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | /lib/ 3 | /package/ 4 | /dist/ 5 | *.cfg 6 | *.asm 7 | *.tgz 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /src/ 2 | /dist/tests/ 3 | /lib/tests/ 4 | /.vscode/ 5 | /bench/ 6 | /vendor/ 7 | node_modules/ 8 | /package/ 9 | *.cfg 10 | *.asm 11 | *.tgz 12 | /.gitignore 13 | /.npmignore 14 | /rollup.* 15 | /tsconfig.json 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - "8" 5 | - "6" 6 | - "4" 7 | 8 | install: 9 | - npm install 10 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Launch", 6 | "type": "node", 7 | "request": "launch", 8 | "program": "${workspaceRoot}/src/tests/index.ts", 9 | "stopOnEntry": false, 10 | "args": [], 11 | "cwd": "${workspaceRoot}", 12 | "preLaunchTask": "build", 13 | "runtimeExecutable": null, 14 | "runtimeArgs": [ 15 | "--nolazy", 16 | "${workspaceRoot}/node_modules/.bin/_mocha" 17 | ], 18 | "env": { 19 | "NODE_ENV": "development" 20 | }, 21 | "externalConsole": false, 22 | "sourceMaps": true, 23 | "outDir": "${workspaceRoot}/dist" 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "**/.git": true, 4 | "**/node_modules": true 5 | }, 6 | "search.exclude": { 7 | "dist/**": true, 8 | "**/node_modules": true, 9 | "**/vendor": true, 10 | "lib/**": true, 11 | "*.asm": true, 12 | "*.cfg": true 13 | }, 14 | "typescript.tsdk": "./node_modules/typescript/lib", 15 | "json.schemas": [{ 16 | "fileMatch": [ 17 | "/tsconfig.json" 18 | ], 19 | "url": "http://json.schemastore.org/tsconfig?update" 20 | }] 21 | } 22 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0", 3 | "command": "npm", 4 | "isShellCommand": true, 5 | "args": ["run"], 6 | "tasks": [{ 7 | "taskName": "build", 8 | "isBuildCommand": true, 9 | "showOutput": "always", 10 | "isWatching": false 11 | }] 12 | } 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fast Source Map [![Build Status](https://travis-ci.org/krisselden/fast-source-map.svg?branch=master)](https://travis-ci.org/krisselden/fast-source-map) 2 | 3 | Keeping the JIT gods happy since circa 2015 4 | 5 | When working with sourcemaps, VLQ encoding and decoding typically dominiates 6 | cost. To address this, this library provides a Fast VLQ encoder & decoder for 7 | JavaScript. By reducing allocations, minimizing transformations, and writing 8 | JIT friendly code we are able to drammatically improve performance. 9 | 10 | How much faster? At the time this README was written, decoding scala.js.map took: 11 | 12 | ``` 13 | v8: 4.6.85.28 14 | node: 5.0.0 15 | os: 10.10.3 16 | hw: 2.5 GHz Intel Core i7, 16gb 17 | ``` 18 | 19 | * source-map: ~5,000ms 20 | * fast-source-map ~ 500ms 21 | 22 | # Usage 23 | 24 | First we will need to have our `sourceMap.mappings` stored as a buffer. We have 25 | future plans to work directly on a sourceMap buffer, as that will allow for 26 | more ideal performance. Even without, the performance improvements are still 27 | well worth it. 28 | 29 | ```js 30 | var buffer = /* try to leave the map inert, and in buffer form. Otherwise convert to buffer */ 31 | ``` 32 | 33 | Typically, the following steps are required: 34 | 35 | Setup the reader 36 | 37 | The reader can read mappings from any array of ascii values. 38 | We will use Uint8Array in this example. 39 | 40 | ```js 41 | let map = JSON.parse(fs.readFileSync('path/to/source/to/decode.js.map', 'utf8')); 42 | 43 | let byteArray = new Uint8Array(map.mappings.length); 44 | let buffer = Buffer.from(byteArray.buffer); 45 | buffer.write(map.mappings, 0, 'ascii'); 46 | 47 | const SM = require("fast-source-map"); 48 | let reader = new SM.IntBufferReader(byteArray, 0, byteArray.length); 49 | ``` 50 | 51 | Setup the decoder 52 | 53 | ```js 54 | let decoder = new SM.Decoder(); 55 | let mappingsDecoder = new SM.MappingsDecoder(decoder); 56 | ``` 57 | 58 | Now for some actual decoding 59 | 60 | ```js 61 | mappingsDecoder.decode(reader); 62 | decoder.mappings // => is the quickly decoded 63 | ``` 64 | 65 | To concatenate multiple source maps 66 | 67 | ```js 68 | const decodeFile = SM.decodeFile; 69 | 70 | let concatenator = new SM.SourceMapConcatenator(); 71 | concatenator.push(decodeFile('path/to/file-1.js.map')); 72 | concatenator.push(decodeFile('path/to/file-2.js.map')); 73 | concatenator.push(decodeFile('path/to/file-3.js.map')); 74 | 75 | concatenator.toJSON(); // => the concatenated source maps 76 | ``` 77 | 78 | Now to reconcatenate the source maps after a source file is removed. 79 | 80 | ```js 81 | // file-2.js.map is removed 82 | concatenator.splice(1, 1); 83 | concatenator.toJSON(); // => the concatenated source maps 84 | ``` 85 | 86 | And reconcatenate again after adding back other files. 87 | 88 | ```js 89 | // an updated file 2 and a new file 4 are added 90 | concatenator.splice(1, 0, decodeFile('path/to/file-2-updated.js.map')); 91 | concatenator.push(decodeFile('path/to/file-4.js.map')); 92 | concatenator.toJSON(); // => the concatenated source maps 93 | ``` 94 | -------------------------------------------------------------------------------- /bench/bench_decode.node.js: -------------------------------------------------------------------------------- 1 | var SourceMapConsumer = require('source-map').SourceMapConsumer; 2 | var fs = require('fs'); 3 | 4 | function test() { 5 | var parsedSourceMap = JSON.parse(fs.readFileSync('bench/scala.js.map')); 6 | 7 | var start = Date.now(); 8 | var sourceMapConsumer = new SourceMapConsumer(parsedSourceMap); 9 | sourceMapConsumer.eachMapping(function () {}); 10 | 11 | return { 12 | decoded: sourceMapConsumer, 13 | duration: Date.now() - start, 14 | }; 15 | } 16 | 17 | console.log('first run:', test().duration + 'ms'); // prime 18 | setTimeout(function() { 19 | console.log('second run:', test().duration + 'ms'); 20 | }); 21 | -------------------------------------------------------------------------------- /bench/bench_decode_fast.node.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var decode = require('../dist/index').decode; 3 | var assert = require('assert'); 4 | 5 | function test() { 6 | var parsed = JSON.parse(fs.readFileSync('bench/scala.js.map', 'utf8')); 7 | var start = Date.now(); 8 | var decoded = decode(parsed); 9 | 10 | return { 11 | duration: Date.now() - start, 12 | decoded: decoded, 13 | } 14 | } 15 | 16 | console.log('first run:', test().duration + 'ms'); // warm up 17 | 18 | // let the world settle down 19 | setTimeout(function() { 20 | var result = test(); // actual test run 21 | var decoded = result.decoded; 22 | console.log('second run:', result.duration + 'ms'); // actual run 23 | 24 | // make sure the output appears reasonable 25 | assert(decoded.mappings.length === 379201, 'correct number of mappings') 26 | assert.deepEqual(decoded.mappings[0], 27 | [ 28 | { fieldCount: 4, col: 0, src: 0, srcLine: 0, srcCol: 0, name: 0 }, 29 | ], 30 | ); 31 | assert.deepEqual(decoded.mappings[379200], []); 32 | }, 100); 33 | -------------------------------------------------------------------------------- /bench/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /bin/decode-source-map: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('../dist/cli/decode-source-map'); 3 | -------------------------------------------------------------------------------- /bin/encode-source-map: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('../dist/cli/encode-source-map'); 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fast-source-map", 3 | "version": "0.1.0", 4 | "description": "fast source mapping decode/encode", 5 | "main": "dist/index.js", 6 | "browser": "dist/browser.js", 7 | "jsnext:main": "lib/index.js", 8 | "typings": "lib/index.d.ts", 9 | "bin": { 10 | "decode-source-map": "./bin/decode-source-map", 11 | "encode-source-map": "./bin/encode-source-map" 12 | }, 13 | "devDependencies": { 14 | "@types/chai": "^4.0.4", 15 | "@types/mocha": "^2.2.43", 16 | "@types/node": "^8.0.28", 17 | "chai": "^4.1.2", 18 | "mocha": "^3.5.3", 19 | "rollup": "^0.50.0", 20 | "sorcery": "^0.10.0", 21 | "source-map": "^0.5.6", 22 | "tslint": "^5.7.0", 23 | "typescript": "^2.5.2" 24 | }, 25 | "scripts": { 26 | "build": "npm run build:typescript && npm run build:rollup && npm run build:rollup-tests && npm run build:rollup-cli-decode && npm run build:rollup-cli-encode", 27 | "build:typescript": "tsc", 28 | "build:rollup": "rollup -c rollup.config.js && sorcery -i dist/index.js && sorcery -i dist/browser.js", 29 | "build:rollup-tests": "rollup -c rollup.config.tests.js && sorcery -i dist/tests/index.js", 30 | "build:rollup-cli-decode": "rollup -c rollup.config.cli-decode.js && sorcery -i dist/cli/decode-source-map.js", 31 | "build:rollup-cli-encode": "rollup -c rollup.config.cli-encode.js && sorcery -i dist/cli/encode-source-map.js", 32 | "lint": "tslint src/**.ts", 33 | "prepublish": "npm run build", 34 | "pretest": "npm run lint", 35 | "test": "mocha dist/tests/index.js", 36 | "test:debug": "mocha debug dist/tests/index.js" 37 | }, 38 | "keywords": [ 39 | "source-map", 40 | "sourcemap", 41 | "decode", 42 | "encode", 43 | "vlq" 44 | ], 45 | "author": "Kris Selden ", 46 | "license": "MIT", 47 | "repository": { 48 | "type": "git", 49 | "url": "https://github.com/krisselden/fast-source-map.git" 50 | }, 51 | "dependencies": { 52 | "concat-stream": "^1.5.1" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /rollup.config.cli-decode.js: -------------------------------------------------------------------------------- 1 | var DIST = __dirname + '/dist/index'; 2 | 3 | var index = { 4 | resolveId: function (importee, importer) { 5 | if (importee === '../index') { 6 | return DIST; 7 | } 8 | // fallthrough 9 | } 10 | }; 11 | 12 | export default { 13 | entry: 'lib/cli/decode-source-map.js', 14 | plugins: [index], 15 | format: 'cjs', 16 | dest: 'dist/cli/decode-source-map.js', 17 | sourceMap: 'dist/cli/decode-source-map.js.map', 18 | external: [DIST] 19 | }; 20 | -------------------------------------------------------------------------------- /rollup.config.cli-encode.js: -------------------------------------------------------------------------------- 1 | var DIST = __dirname + '/dist/index'; 2 | 3 | var index = { 4 | resolveId: function (importee, importer) { 5 | if (importee === '../index') { 6 | return DIST; 7 | } 8 | // fallthrough 9 | } 10 | }; 11 | 12 | export default { 13 | entry: 'lib/cli/encode-source-map.js', 14 | plugins: [index], 15 | format: 'cjs', 16 | dest: 'dist/cli/encode-source-map.js', 17 | sourceMap: 'dist/cli/encode-source-map.js.map', 18 | external: [DIST] 19 | }; 20 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | entry: 'lib/index.js', 3 | targets: [ 4 | { 5 | dest: 'dist/index.js', 6 | sourceMap: 'dist/index.js.map', 7 | format: 'cjs' 8 | }, 9 | { 10 | dest: 'dist/browser.js', 11 | sourceMap: 'dist/browser.js.map', 12 | format: 'iife', 13 | moduleName: 'SM' 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /rollup.config.tests.js: -------------------------------------------------------------------------------- 1 | var DIST = __dirname + '/dist/index'; 2 | 3 | var index = { 4 | resolveId: function (importee, importer) { 5 | if (importee === '../index') { 6 | return DIST; 7 | } 8 | // fallthrough 9 | } 10 | }; 11 | 12 | export default { 13 | entry: 'lib/tests/index.js', 14 | plugins: [index], 15 | format: 'cjs', 16 | dest: 'dist/tests/index.js', 17 | sourceMap: 'dist/tests/index.js.map', 18 | external: ['chai', DIST], 19 | }; 20 | -------------------------------------------------------------------------------- /src/base64.ts: -------------------------------------------------------------------------------- 1 | export let uint6ToASCII = new Uint8Array(64); 2 | export let asciiToUint6 = new Uint8Array(127); 3 | 4 | const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; 5 | for (let i = 0; i < 64; i++) { 6 | const ascii = chars.charCodeAt(i); 7 | uint6ToASCII[i] = ascii; 8 | asciiToUint6[ascii] = i; 9 | } 10 | -------------------------------------------------------------------------------- /src/cli/decode-source-map.ts: -------------------------------------------------------------------------------- 1 | import { decode, decodeFile } from '../decode'; 2 | 3 | const concat = require('concat-stream'); 4 | const length = process.argv.length; 5 | 6 | if (length === 3) { 7 | console.log(JSON.stringify(decodeFile(process.argv[2]))); 8 | } else if (length === 2) { 9 | process.stdin.pipe(concat((data: string) => { 10 | console.log(JSON.stringify(decode(JSON.parse(data)))); 11 | })); 12 | } else { 13 | console.error('USAGE: decode FILE'); 14 | process.exit(1); 15 | } 16 | -------------------------------------------------------------------------------- /src/cli/encode-source-map.ts: -------------------------------------------------------------------------------- 1 | import { encode, encodeFile } from '../index'; 2 | 3 | const concat = require('concat-stream'); 4 | const length = process.argv.length; 5 | 6 | if (length === 3) { 7 | console.log(JSON.stringify(encodeFile(process.argv[2]))); 8 | } else if (length === 2) { 9 | process.stdin.pipe(concat((data: string) => { 10 | console.log(JSON.stringify(encode(JSON.parse(data)))); 11 | })); 12 | } else { 13 | console.error('USAGE: decode FILE'); 14 | process.exit(1); 15 | } 16 | -------------------------------------------------------------------------------- /src/concat.ts: -------------------------------------------------------------------------------- 1 | import { DecodedMappings, DecodedSourceMap } from './interfaces'; 2 | 3 | /** 4 | * A function that concatenates source maps. 5 | * 6 | * Source maps are expected to be in the following format: 7 | * 8 | * ```js 9 | * { 10 | * version: , 11 | * sources: [], 12 | * sourcesContent: [], 13 | * names: [], 14 | * mappings: [ 15 | * [{ 16 | * col: , 17 | * src: , 18 | * srcLine: , 19 | * srcCol: , 20 | * }] 21 | * ], 22 | * file: 23 | * } 24 | * ``` 25 | */ 26 | export default function concat(maps: DecodedSourceMap[]): DecodedSourceMap { 27 | const sources: string[] = maps.reduce((acc: string[], map: DecodedSourceMap) => { 28 | return acc.concat(map.sources); 29 | }, []); 30 | const sourcesContent = maps.reduce((acc: string[], map: DecodedSourceMap) => { 31 | return acc.concat(map.sourcesContent); 32 | }, []); 33 | const names = maps.reduce((acc: string[], map: DecodedSourceMap) => { 34 | return acc.concat(map.names); 35 | }, []); 36 | 37 | let srcOffset = 0; 38 | let nameOffset = 0; 39 | const mappings = maps.reduce((acc: DecodedMappings, map) => { 40 | acc = acc.concat(map.mappings.map((lineMappings) => { 41 | return lineMappings.map((mapping) => ({ 42 | col: mapping.col, 43 | fieldCount: mapping.fieldCount, 44 | name: mapping.name + nameOffset, 45 | src: mapping.src + srcOffset, 46 | srcCol: mapping.srcCol, 47 | srcLine: mapping.srcLine, 48 | })); 49 | })); 50 | 51 | srcOffset += map.sources.length; 52 | nameOffset += map.names.length; 53 | 54 | return acc; 55 | }, []); 56 | 57 | return { 58 | file: '', 59 | mappings, 60 | names, 61 | sources, 62 | sourcesContent, 63 | version: '3', 64 | }; 65 | } 66 | -------------------------------------------------------------------------------- /src/decode.ts: -------------------------------------------------------------------------------- 1 | import Decoder from './decoder'; 2 | import IntBufferReader from './int-buffer-reader'; 3 | import { DecodedSourceMap } from './interfaces'; 4 | import MappingsDecoder from './mappings-decoder'; 5 | import readFile from './utils/read-file'; 6 | import toBuffer from './utils/to-buffer'; 7 | 8 | export function decode(map: any): DecodedSourceMap { 9 | const buffer = toBuffer(map.mappings); 10 | const reader = new IntBufferReader(buffer, 0, buffer.length); 11 | const decoder = new Decoder(); 12 | const mappingsDecoder = new MappingsDecoder(decoder); 13 | 14 | mappingsDecoder.decode(reader); 15 | 16 | map.mappings = decoder.mappings; 17 | 18 | return map; 19 | } 20 | 21 | export function decodeFile(path: string) { 22 | return decode(JSON.parse(readFile(path))); 23 | } 24 | -------------------------------------------------------------------------------- /src/decoder.ts: -------------------------------------------------------------------------------- 1 | import { DecodedMappings, LineMappings } from './interfaces'; 2 | import { createMapping1, createMapping4, createMapping5 } from './mapping-factories'; 3 | import { Delegate } from './mappings-decoder'; 4 | 5 | export default class Decoder implements Delegate { 6 | public currentLine: LineMappings = []; 7 | 8 | public mappings: DecodedMappings = [this.currentLine]; 9 | 10 | public newline() { 11 | this.currentLine = []; 12 | this.mappings.push(this.currentLine); 13 | } 14 | 15 | public mapping1(col: number) { 16 | this.currentLine.push(createMapping1(col)); 17 | } 18 | 19 | public mapping4(col: number, src: number, srcLine: number, srcCol: number) { 20 | this.currentLine.push(createMapping4(col, src, srcLine, srcCol)); 21 | } 22 | 23 | public mapping5(col: number, src: number, srcLine: number, srcCol: number, name: number) { 24 | this.currentLine.push(createMapping5(col, src, srcLine, srcCol, name)); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/encode.ts: -------------------------------------------------------------------------------- 1 | import Encoder from './encoder'; 2 | import IntBufferWriter from './int-buffer-writer'; 3 | import MappingsEncoder from './mappings-encoder'; 4 | import readFile from './utils/read-file'; 5 | import toString from './utils/to-string'; 6 | 7 | export function encode(map: any) { 8 | const buffer: number[] = []; 9 | const writer = new IntBufferWriter(buffer, 0); 10 | const encoder = new Encoder(writer); 11 | const mappingsEncoder = new MappingsEncoder(encoder); 12 | 13 | mappingsEncoder.encode(map.mappings); 14 | 15 | map.mappings = toString(buffer, 0, buffer.length); 16 | 17 | return map; 18 | } 19 | 20 | /** 21 | * Takes a source map file with a decoded `mappings` section and reencodes 22 | * `mappings`. 23 | */ 24 | export function encodeFile(path: string) { 25 | const map = JSON.parse(readFile(path)); 26 | return encode(map); 27 | } 28 | -------------------------------------------------------------------------------- /src/encoder.ts: -------------------------------------------------------------------------------- 1 | import { Delegate } from './mappings-encoder'; 2 | import { encodeVLQ } from './vlq'; 3 | import Writer from './writer'; 4 | 5 | export default class Encoder implements Delegate { 6 | public writer: Writer; 7 | 8 | constructor(writer: Writer) { 9 | this.writer = writer; 10 | } 11 | 12 | public separator() { 13 | this.writer.write(44); /* , */ 14 | } 15 | 16 | public newline() { 17 | this.writer.write(59); /* ; */ 18 | } 19 | 20 | public write5(column: number, source: number, sourceLine: number, sourceColumn: number, name: number) { 21 | encodeVLQ(this.writer, column); 22 | encodeVLQ(this.writer, source); 23 | encodeVLQ(this.writer, sourceLine); 24 | encodeVLQ(this.writer, sourceColumn); 25 | encodeVLQ(this.writer, name); 26 | } 27 | 28 | public write4(column: number, source: number, sourceLine: number, sourceColumn: number) { 29 | encodeVLQ(this.writer, column); 30 | encodeVLQ(this.writer, source); 31 | encodeVLQ(this.writer, sourceLine); 32 | encodeVLQ(this.writer, sourceColumn); 33 | } 34 | 35 | public write1(column: number) { 36 | encodeVLQ(this.writer, column); 37 | } 38 | 39 | get length() { 40 | return this.writer.length; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { encodeVLQ, decodeVLQ } from './vlq'; 2 | export { default as IntBufferReader } from './int-buffer-reader'; 3 | export { default as IntBufferWriter } from './int-buffer-writer'; 4 | export { default as MappingsEncoder, Delegate as MappingsEncoderDelegate } from './mappings-encoder'; 5 | export { default as MappingsDecoder, Delegate as MappingsDecoderDelegate } from './mappings-decoder'; 6 | export { default as Encoder } from './encoder'; 7 | export { default as Decoder } from './decoder'; 8 | export { default as concat } from './concat'; 9 | export { decode, decodeFile } from './decode'; 10 | export { encode, encodeFile } from './encode'; 11 | -------------------------------------------------------------------------------- /src/int-buffer-reader.ts: -------------------------------------------------------------------------------- 1 | import Reader from './reader'; 2 | 3 | export default class IntBufferReader implements Reader { 4 | public buf: number[] | Uint8Array; 5 | public ptr: number; 6 | public limit: number; 7 | 8 | constructor(buf: number[] | Uint8Array, ptr: number, len: number) { 9 | this.buf = buf; 10 | this.ptr = ptr | 0; 11 | this.limit = (ptr + len) | 0; 12 | } 13 | 14 | public peek() { 15 | return this.buf[this.ptr | 0] | 0; 16 | } 17 | 18 | public read() { 19 | const n = this.buf[this.ptr | 0] | 0; 20 | this.ptr = (this.ptr + 1) | 0; 21 | return n; 22 | } 23 | 24 | public next() { 25 | this.ptr = (this.ptr + 1) | 0; 26 | } 27 | 28 | public hasNext(): boolean { 29 | return this.ptr < this.limit; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/int-buffer-writer.ts: -------------------------------------------------------------------------------- 1 | import Writer from './writer'; 2 | 3 | export default class IntBufferWriter implements Writer { 4 | public buf: number[] | Uint8Array; 5 | public ptr: number; 6 | 7 | constructor(buf: number[] | Uint8Array, ptr: number) { 8 | this.buf = buf; 9 | this.ptr = ptr | 0; 10 | } 11 | 12 | public write(n: number) { 13 | this.buf[this.ptr++] = n | 0; 14 | } 15 | 16 | public get length() { 17 | return this.buf.length; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/interfaces.ts: -------------------------------------------------------------------------------- 1 | export interface DecodedMapping { 2 | fieldCount: number; 3 | col: number; 4 | src: number; 5 | srcLine: number; 6 | srcCol: number; 7 | name: number; 8 | } 9 | 10 | export type LineMappings = DecodedMapping[]; 11 | 12 | export type DecodedMappings = LineMappings[]; 13 | 14 | export interface DecodedSourceMap { 15 | version: string; 16 | sources: string[]; 17 | sourcesContent: string[]; 18 | names: string[]; 19 | mappings: DecodedMappings; 20 | file: string; 21 | } 22 | -------------------------------------------------------------------------------- /src/mapping-factories.ts: -------------------------------------------------------------------------------- 1 | import { DecodedMapping } from './interfaces'; 2 | 3 | export function createMapping1(col: number): DecodedMapping { 4 | return { 5 | col, 6 | fieldCount: 1, 7 | name: 0, 8 | src: 0, 9 | srcCol: 0, 10 | srcLine: 0, 11 | }; 12 | } 13 | 14 | export function createMapping4(col: number, src: number, srcLine: number, srcCol: number): DecodedMapping { 15 | return { 16 | col, 17 | fieldCount: 4, 18 | name: 0, 19 | src, 20 | srcCol, 21 | srcLine, 22 | }; 23 | } 24 | 25 | export function createMapping5(col: number, 26 | src: number, 27 | srcLine: number, 28 | srcCol: number, 29 | name: number): DecodedMapping { 30 | return { 31 | col, 32 | fieldCount: 5, 33 | name, 34 | src, 35 | srcCol, 36 | srcLine, 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /src/mappings-decoder.ts: -------------------------------------------------------------------------------- 1 | import Reader from './reader'; 2 | import { decodeVLQ } from './vlq'; 3 | 4 | export interface Delegate { 5 | newline(): void; 6 | mapping1(column: number): void; 7 | mapping4(column: number, source: number, sourceLine: number, sourceColumn: number): void; 8 | mapping5(column: number, source: number, sourceLine: number, sourceColumn: number, name: number): void; 9 | } 10 | 11 | export default class MappingsDecoder { 12 | // absolutes 13 | public line = 0; 14 | public column = 0; 15 | public source = 0; 16 | public sourceLine = 0; 17 | public sourceColumn = 0; 18 | public name = 0; 19 | 20 | public fieldCount = 0; 21 | 22 | public delegate: Delegate; 23 | 24 | constructor(delegate: Delegate) { 25 | this.delegate = delegate; 26 | } 27 | 28 | public decode(reader: Reader) { 29 | while (reader.hasNext()) { 30 | switch (reader.peek()) { 31 | case 59: // semicolon 32 | if (this.fieldCount > 0) { 33 | this.emitMapping(); 34 | } 35 | this.emitNewline(); 36 | this.column = 0; 37 | this.fieldCount = 0; 38 | reader.next(); 39 | break; 40 | case 44: // comma 41 | this.emitMapping(); 42 | this.fieldCount = 0; 43 | reader.next(); 44 | break; 45 | default: 46 | this.decodeField(reader); 47 | break; 48 | } 49 | } 50 | if (this.fieldCount > 0) { 51 | this.emitMapping(); 52 | } 53 | } 54 | 55 | public emitNewline() { 56 | this.delegate.newline(); 57 | } 58 | 59 | public emitMapping() { 60 | switch (this.fieldCount) { 61 | case 1: 62 | this.delegate.mapping1(this.column); 63 | break; 64 | case 4: 65 | this.delegate.mapping4(this.column, this.source, this.sourceLine, this.sourceColumn); 66 | break; 67 | case 5: 68 | this.delegate.mapping5(this.column, this.source, this.sourceLine, this.sourceColumn, this.name); 69 | break; 70 | } 71 | } 72 | 73 | public decodeField(reader: Reader) { 74 | const value = decodeVLQ(reader) | 0; 75 | switch (this.fieldCount) { 76 | case 0: 77 | this.column += value; 78 | this.fieldCount = 1; 79 | break; 80 | case 1: 81 | this.source += value; 82 | this.fieldCount = 2; 83 | break; 84 | case 2: 85 | this.sourceLine += value; 86 | this.fieldCount = 3; 87 | break; 88 | case 3: 89 | this.sourceColumn += value; 90 | this.fieldCount = 4; 91 | break; 92 | case 4: 93 | this.name += value; 94 | this.fieldCount = 5; 95 | break; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/mappings-encoder.ts: -------------------------------------------------------------------------------- 1 | import { DecodedMappings } from './interfaces'; 2 | 3 | export interface Delegate { 4 | length: number; 5 | newline(): void; 6 | separator(): void; 7 | write1(column: number): void; 8 | write4(column: number, source: number, sourceLine: number, sourceColumn: number): void; 9 | write5(column: number, source: number, sourceLine: number, sourceColumn: number, name: number): void; 10 | } 11 | 12 | export default class MappingsEncoder { 13 | public column = 0; 14 | public source = 0; 15 | public sourceLine = 0; 16 | public sourceColumn = 0; 17 | public name = 0; 18 | 19 | public delegate: Delegate; 20 | 21 | constructor(delegate: Delegate) { 22 | this.delegate = delegate; 23 | } 24 | 25 | public encode(mappings: DecodedMappings) { 26 | for (let i = 0; i < mappings.length; i++) { 27 | const line = mappings[i]; 28 | 29 | for (let j = 0; j < line.length; j++) { 30 | const mapping = line[j]; 31 | 32 | switch (mapping.fieldCount) { 33 | case 1: 34 | this.write1(mapping); 35 | break; 36 | case 4: 37 | this.write4(mapping); 38 | break; 39 | case 5: 40 | this.write5(mapping); 41 | break; 42 | default: 43 | missingFieldCount(); 44 | } 45 | 46 | if (j < line.length - 1) { 47 | // no trailing segment separator 48 | this.separator(); 49 | } 50 | } 51 | 52 | if (i < mappings.length - 1 ) { 53 | // skip trailing line separator 54 | this.newline(); 55 | } 56 | 57 | this.column = 0; 58 | } 59 | 60 | return this.delegate.length; 61 | } 62 | 63 | public separator() { 64 | this.delegate.separator(); 65 | } 66 | 67 | public newline() { 68 | this.delegate.newline(); 69 | } 70 | 71 | public write5(mapping: { 72 | col: number; 73 | src: number; 74 | srcLine: number; 75 | srcCol: number; 76 | name: number; 77 | }) { 78 | this.delegate.write5( 79 | mapping.col - this.column, 80 | mapping.src - this.source, 81 | mapping.srcLine - this.sourceLine, 82 | mapping.srcCol - this.sourceColumn, 83 | mapping.name - this.name); 84 | 85 | this.column = mapping.col; 86 | this.source = mapping.src; 87 | this.sourceLine = mapping.srcLine; 88 | this.sourceColumn = mapping.srcCol; 89 | this.name = mapping.name; 90 | } 91 | 92 | public write4(mapping: { 93 | col: number; 94 | src: number; 95 | srcLine: number; 96 | srcCol: number; 97 | }) { 98 | this.delegate.write4( 99 | mapping.col - this.column, 100 | mapping.src - this.source, 101 | mapping.srcLine - this.sourceLine, 102 | mapping.srcCol - this.sourceColumn); 103 | 104 | this.column = mapping.col; 105 | this.source = mapping.src; 106 | this.sourceLine = mapping.srcLine; 107 | this.sourceColumn = mapping.srcCol; 108 | } 109 | 110 | public write1(mapping: { 111 | col: number; 112 | }) { 113 | this.delegate.write1(mapping.col - this.column); 114 | 115 | this.column = mapping.col; 116 | } 117 | } 118 | 119 | function missingFieldCount() { 120 | throw new TypeError('mappings to encode require fieldCount'); 121 | } 122 | -------------------------------------------------------------------------------- /src/reader.ts: -------------------------------------------------------------------------------- 1 | interface Reader { 2 | /** 3 | * Returns the next byte without moving the pointer. 4 | */ 5 | peek(): number; 6 | 7 | /** 8 | * Returns the next byte and moves the pointer forward. 9 | */ 10 | read(): number; 11 | 12 | /** 13 | * Moves the pointer forward without reading any bytes. 14 | */ 15 | next(): void; 16 | 17 | /** 18 | * Returns whether the pointer has reached the end of the buffer. 19 | */ 20 | hasNext(): boolean; 21 | } 22 | 23 | export default Reader; 24 | -------------------------------------------------------------------------------- /src/tests/concat-tests.ts: -------------------------------------------------------------------------------- 1 | import { concat } from '../index'; 2 | 3 | import { expect } from 'chai'; 4 | import map1 from './fixtures/map1'; 5 | import map1_2 from './fixtures/map1-2'; 6 | import map2 from './fixtures/map2'; 7 | import map3_4 from './fixtures/map3-4'; 8 | import map3_4_1 from './fixtures/map3-4-1'; 9 | 10 | describe('concat()', () => { 11 | it('can output an empty source map', () => { 12 | expect(concat([])).to.deep.equal({ 13 | file: '', 14 | mappings: [], 15 | names: [], 16 | sources: [], 17 | sourcesContent: [], 18 | version: '3', 19 | }, 'concatenator can output the empty case'); 20 | }); 21 | 22 | it('can output a single source map', () => { 23 | const map = { 24 | file: 'map1.js', 25 | mappings: [[{ 26 | col: 0, 27 | fieldCount: 4, 28 | name: 0, 29 | src: 0, 30 | srcCol: 0, 31 | srcLine: 1, 32 | }]], 33 | names: [] as string[], 34 | sources: [ 'file1.js' ], 35 | sourcesContent: [] as string[], 36 | version: '3', 37 | }; 38 | 39 | expect(concat([map])).to.deep.equal({ 40 | file: '', 41 | mappings: [[{ 42 | col: 0, 43 | fieldCount: 4, 44 | src: 0, 45 | srcCol: 0, 46 | srcLine: 1, 47 | }]], 48 | names: [], 49 | sources: [ 'file1.js' ], 50 | sourcesContent: [], 51 | version: '3', 52 | }, 'concatenator can output a single source map'); 53 | }); 54 | 55 | it('can produce simple merged source maps', () => { 56 | expect(concat([map1, map2])).to.deep.equal(map1_2); 57 | }); 58 | 59 | it('can merge source maps with multiple sources', () => { 60 | expect(concat([map3_4, map1])).to.deep.equal(map3_4_1); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /src/tests/encoder-tests.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { MappingsEncoder, MappingsEncoderDelegate } from '../index'; 3 | 4 | class Encoder implements MappingsEncoderDelegate { 5 | public writes: Array = []; 6 | 7 | public write1(n: number): void { 8 | this.writes.push(n); 9 | } 10 | 11 | public write4(a: number, b: number, c: number, d: number): void { 12 | this.writes.push(a, b, c, d); 13 | } 14 | 15 | public write5(a: number, b: number, c: number, d: number, e: number): void { 16 | this.writes.push(a, b, c, d, e); 17 | } 18 | 19 | public separator(): void { 20 | this.writes.push(','); 21 | } 22 | 23 | public newline(): void { 24 | this.writes.push(';'); 25 | } 26 | 27 | get length(): number { 28 | return this.writes.length; 29 | } 30 | } 31 | 32 | describe('Encoder', () => { 33 | let encoder: Encoder; 34 | let mapper: MappingsEncoder; 35 | let mapping: { col: number, src: number, srcLine: number, srcCol: number, name: number }; 36 | 37 | beforeEach( () => { 38 | encoder = new Encoder(); 39 | mapper = new MappingsEncoder(encoder); 40 | 41 | mapping = { 42 | col: 197, 43 | name: 2, 44 | src: 0, 45 | srcCol: 29, 46 | srcLine: 7, 47 | }; 48 | }); 49 | 50 | describe('write1', () => { 51 | it('writes the `col` field', () => { 52 | expect(encoder.writes).to.deep.equal([]); 53 | 54 | mapper.write1(mapping); 55 | 56 | expect(encoder.writes).to.deep.equal([ 197 ]); 57 | }); 58 | }); 59 | 60 | describe('write4', () => { 61 | it('writes the col, src, srcLine and srcCol fields in order', () => { 62 | expect(encoder.writes).to.deep.equal([]); 63 | 64 | mapper.write4(mapping); 65 | 66 | expect(encoder.writes).to.deep.equal([ 197, 0, 7, 29 ]); 67 | }); 68 | }); 69 | 70 | describe('write5', () => { 71 | it('writes the col, src, srcLine, srcCol and name fields in order', () => { 72 | expect(encoder.writes).to.deep.equal([]); 73 | 74 | mapper.write5(mapping); 75 | 76 | expect(encoder.writes).to.deep.equal([ 197, 0, 7, 29, 2 ]); 77 | }); 78 | }); 79 | 80 | describe('encode', () => { 81 | it('encodes sequences of the same field length', () => { 82 | expect(encoder.writes).to.deep.equal([]); 83 | 84 | mapper.encode([[{ 85 | col: 105, 86 | fieldCount: 1, 87 | name: 0, 88 | src: 0, 89 | srcCol: 0, 90 | srcLine: 0, 91 | }, { 92 | col: 200, 93 | fieldCount: 1, 94 | name: 0, 95 | src: 0, 96 | srcCol: 0, 97 | srcLine: 0, 98 | }, { 99 | col: 300, 100 | fieldCount: 1, 101 | name: 0, 102 | src: 0, 103 | srcCol: 0, 104 | srcLine: 0, 105 | }]]); 106 | 107 | expect(encoder.writes).to.deep.equal([ 105, ',', 95, ',', 100 ]); 108 | }); 109 | 110 | it('encodes sequences of mixed field lengths', () => { 111 | expect(encoder.writes).to.deep.equal([]); 112 | 113 | mapper.encode([[{ 114 | col: 10, 115 | fieldCount: 5, 116 | name: 14, 117 | src: 11, 118 | srcCol: 13, 119 | srcLine: 12, 120 | }, { 121 | col: 20, 122 | fieldCount: 1, 123 | name: 0, 124 | src: 0, 125 | srcCol: 0, 126 | srcLine: 0, 127 | }, { 128 | col: 30, 129 | fieldCount: 4, 130 | name: 0, 131 | src: 31, 132 | srcCol: 33, 133 | srcLine: 32, 134 | }]]); 135 | 136 | expect(encoder.writes).to.deep.equal([ 137 | 10, 11, 12, 13, 14, ',', 138 | 10, ',', 139 | 10, 20, 20, 20, 140 | ]); 141 | }); 142 | 143 | it('encodes multiple lines with single segments', () => { 144 | expect(encoder.writes).to.deep.equal([]); 145 | 146 | mapper.encode([[ 147 | { 148 | col: 10, 149 | fieldCount: 1, 150 | name: 0, 151 | src: 0, 152 | srcCol: 0, 153 | srcLine: 0, 154 | }, { 155 | col: 20, 156 | fieldCount: 1, 157 | name: 0, 158 | src: 0, 159 | srcCol: 0, 160 | srcLine: 0, 161 | }, 162 | ], [ 163 | { 164 | col: 100, 165 | fieldCount: 1, 166 | name: 0, 167 | src: 0, 168 | srcCol: 0, 169 | srcLine: 0, 170 | }, 171 | ]]); 172 | 173 | expect(encoder.writes).to.deep.equal([ 174 | 10, ',', 10, ';', 175 | 100, 176 | ]); 177 | }); 178 | 179 | it('encodes multiple lines with multiple mixed segments', () => { 180 | expect(encoder.writes).to.deep.equal([]); 181 | 182 | mapper.encode([[ 183 | { 184 | col: 10, 185 | fieldCount: 1, 186 | name: 0, 187 | src: 0, 188 | srcCol: 0, 189 | srcLine: 0, 190 | }, { 191 | col: 20, 192 | fieldCount: 1, 193 | name: 0, 194 | src: 0, 195 | srcCol: 0, 196 | srcLine: 0, 197 | }, 198 | ], [ 199 | { 200 | col: 100, 201 | fieldCount: 5, 202 | name: 104, 203 | src: 101, 204 | srcCol: 103, 205 | srcLine: 102, 206 | }, 207 | ], [ 208 | { 209 | col: 200, 210 | fieldCount: 4, 211 | name: 0, 212 | src: 201, 213 | srcCol: 203, 214 | srcLine: 202, 215 | }, { 216 | col: 300, 217 | fieldCount: 4, 218 | name: 0, 219 | src: 301, 220 | srcCol: 303, 221 | srcLine: 302, 222 | }, 223 | ]]); 224 | 225 | expect(encoder.writes).to.deep.equal([ 226 | 10, ',', 10, ';', 227 | 100, 101, 102, 103, 104, ';', 228 | 200, 100, 100, 100, ',', 100, 100, 100, 100, 229 | ]); 230 | }); 231 | }); 232 | }); 233 | -------------------------------------------------------------------------------- /src/tests/fixtures/map1-2.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | file: '', 3 | mappings: [ 4 | // first.js mappings 5 | [ 6 | { 7 | col: 0, 8 | fieldCount: 4, 9 | name: 0, 10 | src: 0, 11 | srcCol: 0, 12 | srcLine: 0, 13 | }, 14 | ], 15 | [ 16 | { 17 | col: 0, 18 | fieldCount: 4, 19 | name: 0, 20 | src: 0, 21 | srcCol: 0, 22 | srcLine: 1, 23 | }, 24 | ], 25 | [ 26 | { 27 | col: 0, 28 | fieldCount: 4, 29 | name: 0, 30 | src: 0, 31 | srcCol: 0, 32 | srcLine: 2, 33 | }, 34 | ], 35 | [ 36 | { 37 | col: 0, 38 | fieldCount: 4, 39 | name: 0, 40 | src: 0, 41 | srcCol: 0, 42 | srcLine: 3, 43 | }, 44 | ], 45 | [ 46 | { 47 | col: 0, 48 | fieldCount: 4, 49 | name: 0, 50 | src: 0, 51 | srcCol: 0, 52 | srcLine: 4, 53 | }, 54 | ], 55 | [ 56 | { 57 | col: 0, 58 | fieldCount: 4, 59 | name: 0, 60 | src: 0, 61 | srcCol: 0, 62 | srcLine: 5, 63 | }, 64 | ], 65 | [ 66 | { 67 | col: 0, 68 | fieldCount: 4, 69 | name: 0, 70 | src: 0, 71 | srcCol: 0, 72 | srcLine: 6, 73 | }, 74 | ], 75 | // second.js mappings 76 | [ 77 | { 78 | col: 0, 79 | fieldCount: 4, 80 | name: 0, 81 | src: 1, 82 | srcCol: 0, 83 | srcLine: 0, 84 | }, 85 | ], 86 | [ 87 | { 88 | col: 0, 89 | fieldCount: 4, 90 | name: 0, 91 | src: 1, 92 | srcCol: 0, 93 | srcLine: 1, 94 | }, 95 | ], 96 | [ 97 | { 98 | col: 0, 99 | fieldCount: 4, 100 | name: 0, 101 | src: 1, 102 | srcCol: 0, 103 | srcLine: 2, 104 | }, 105 | ], 106 | ], 107 | names: [] as string[], 108 | sources: [ 109 | 'test/fixtures/inner/first.js', 110 | 'test/fixtures/inner/second.js', 111 | ], 112 | sourcesContent: [ 113 | 'function meaningOfLife() {\n throw new Error(42);\n}\n\nfunction boom() {\n throw new Error(\'boom\');\n}\n', 114 | 'function somethingElse() {\n throw new Error("somethign else");\n}\n', 115 | ], 116 | version: '3', 117 | }; 118 | -------------------------------------------------------------------------------- /src/tests/fixtures/map1.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | file: 'map1.js', 3 | mappings: [ 4 | [ 5 | { 6 | col: 0, 7 | fieldCount: 4, 8 | name: 0, 9 | src: 0, 10 | srcCol: 0, 11 | srcLine: 0, 12 | }, 13 | ], 14 | [ 15 | { 16 | col: 0, 17 | fieldCount: 4, 18 | name: 0, 19 | src: 0, 20 | srcCol: 0, 21 | srcLine: 1, 22 | }, 23 | ], 24 | [ 25 | { 26 | col: 0, 27 | fieldCount: 4, 28 | name: 0, 29 | src: 0, 30 | srcCol: 0, 31 | srcLine: 2, 32 | }, 33 | ], 34 | [ 35 | { 36 | col: 0, 37 | fieldCount: 4, 38 | name: 0, 39 | src: 0, 40 | srcCol: 0, 41 | srcLine: 3, 42 | }, 43 | ], 44 | [ 45 | { 46 | col: 0, 47 | fieldCount: 4, 48 | name: 0, 49 | src: 0, 50 | srcCol: 0, 51 | srcLine: 4, 52 | }, 53 | ], 54 | [ 55 | { 56 | col: 0, 57 | fieldCount: 4, 58 | name: 0, 59 | src: 0, 60 | srcCol: 0, 61 | srcLine: 5, 62 | }, 63 | ], 64 | [ 65 | { 66 | col: 0, 67 | fieldCount: 4, 68 | name: 0, 69 | src: 0, 70 | srcCol: 0, 71 | srcLine: 6, 72 | }, 73 | ], 74 | ], 75 | names: [], 76 | sources: [ 'test/fixtures/inner/first.js' ], 77 | sourcesContent: [ 78 | 'function meaningOfLife() {\n throw new Error(42);\n}\n\nfunction boom() {\n throw new Error(\'boom\');\n}\n', 79 | ], 80 | version: '3', 81 | }; 82 | -------------------------------------------------------------------------------- /src/tests/fixtures/map2.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | file: 'map2.js', 3 | mappings: [ 4 | [ 5 | { 6 | col: 0, 7 | fieldCount: 4, 8 | name: 0, 9 | src: 0, 10 | srcCol: 0, 11 | srcLine: 0, 12 | }, 13 | ], 14 | [ 15 | { 16 | col: 0, 17 | fieldCount: 4, 18 | name: 0, 19 | src: 0, 20 | srcCol: 0, 21 | srcLine: 1, 22 | }, 23 | ], 24 | [ 25 | { 26 | col: 0, 27 | fieldCount: 4, 28 | name: 0, 29 | src: 0, 30 | srcCol: 0, 31 | srcLine: 2, 32 | }, 33 | ], 34 | ], 35 | names: [], 36 | sources: [ 37 | 'test/fixtures/inner/second.js', 38 | ], 39 | sourcesContent: [ 40 | 'function somethingElse() {\n throw new Error("somethign else");\n}\n', 41 | ], 42 | version: '3', 43 | }; 44 | -------------------------------------------------------------------------------- /src/tests/fixtures/map3-4-1.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | file: '', 3 | mappings: [ 4 | // third.js mappings 5 | [ 6 | { 7 | col: 0, 8 | fieldCount: 4, 9 | name: 0, 10 | src: 0, 11 | srcCol: 0, 12 | srcLine: 0, 13 | }, 14 | ], 15 | [ 16 | { 17 | col: 0, 18 | fieldCount: 4, 19 | name: 0, 20 | src: 0, 21 | srcCol: 0, 22 | srcLine: 1, 23 | }, 24 | ], 25 | [ 26 | { 27 | col: 0, 28 | fieldCount: 4, 29 | name: 0, 30 | src: 0, 31 | srcCol: 0, 32 | srcLine: 2, 33 | }, 34 | ], 35 | [ 36 | { 37 | col: 0, 38 | fieldCount: 4, 39 | name: 0, 40 | src: 0, 41 | srcCol: 0, 42 | srcLine: 3, 43 | }, 44 | ], 45 | [ 46 | { 47 | col: 0, 48 | fieldCount: 4, 49 | name: 0, 50 | src: 0, 51 | srcCol: 0, 52 | srcLine: 4, 53 | }, 54 | ], 55 | [ 56 | { 57 | col: 0, 58 | fieldCount: 4, 59 | name: 0, 60 | src: 0, 61 | srcCol: 0, 62 | srcLine: 5, 63 | }, 64 | ], 65 | [ 66 | { 67 | col: 0, 68 | fieldCount: 4, 69 | name: 0, 70 | src: 0, 71 | srcCol: 0, 72 | srcLine: 6, 73 | }, 74 | ], 75 | // fourth.js mappings 76 | [ 77 | { 78 | col: 0, 79 | fieldCount: 4, 80 | name: 0, 81 | src: 1, 82 | srcCol: 0, 83 | srcLine: 0, 84 | }, 85 | ], 86 | [ 87 | { 88 | col: 0, 89 | fieldCount: 4, 90 | name: 0, 91 | src: 1, 92 | srcCol: 0, 93 | srcLine: 1, 94 | }, 95 | ], 96 | [ 97 | { 98 | col: 0, 99 | fieldCount: 4, 100 | name: 0, 101 | src: 1, 102 | srcCol: 0, 103 | srcLine: 2, 104 | }, 105 | ], 106 | // first.js mappings 107 | [ 108 | { 109 | col: 0, 110 | fieldCount: 4, 111 | name: 0, 112 | src: 2, 113 | srcCol: 0, 114 | srcLine: 0, 115 | }, 116 | ], 117 | [ 118 | { 119 | col: 0, 120 | fieldCount: 4, 121 | name: 0, 122 | src: 2, 123 | srcCol: 0, 124 | srcLine: 1, 125 | }, 126 | ], 127 | [ 128 | { 129 | col: 0, 130 | fieldCount: 4, 131 | name: 0, 132 | src: 2, 133 | srcCol: 0, 134 | srcLine: 2, 135 | }, 136 | ], 137 | [ 138 | { 139 | col: 0, 140 | fieldCount: 4, 141 | name: 0, 142 | src: 2, 143 | srcCol: 0, 144 | srcLine: 3, 145 | }, 146 | ], 147 | [ 148 | { 149 | col: 0, 150 | fieldCount: 4, 151 | name: 0, 152 | src: 2, 153 | srcCol: 0, 154 | srcLine: 4, 155 | }, 156 | ], 157 | [ 158 | { 159 | col: 0, 160 | fieldCount: 4, 161 | name: 0, 162 | src: 2, 163 | srcCol: 0, 164 | srcLine: 5, 165 | }, 166 | ], 167 | [ 168 | { 169 | col: 0, 170 | fieldCount: 4, 171 | name: 0, 172 | src: 2, 173 | srcCol: 0, 174 | srcLine: 6, 175 | }, 176 | ], 177 | ], 178 | names: [], 179 | sources: [ 180 | 'test/fixtures/inner/third.js', 181 | 'test/fixtures/inner/fourth.js', 182 | 'test/fixtures/inner/first.js', 183 | ], 184 | sourcesContent: [ 185 | 'function meaningOfLife() {\n throw new Error(42);\n}\n\nfunction boom() {\n throw new Error(\'boom\');\n}\n', 186 | 'function somethingElse() {\n throw new Error("somethign else");\n}\n', 187 | 'function meaningOfLife() {\n throw new Error(42);\n}\n\nfunction boom() {\n throw new Error(\'boom\');\n}\n', 188 | ], 189 | version: '3', 190 | }; 191 | -------------------------------------------------------------------------------- /src/tests/fixtures/map3-4.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | file: '', 3 | mappings: [ 4 | // third.js mappings 5 | [ 6 | { 7 | col: 0, 8 | fieldCount: 4, 9 | name: 0, 10 | src: 0, 11 | srcCol: 0, 12 | srcLine: 0, 13 | }, 14 | ], 15 | [ 16 | { 17 | col: 0, 18 | fieldCount: 4, 19 | name: 0, 20 | src: 0, 21 | srcCol: 0, 22 | srcLine: 1, 23 | }, 24 | ], 25 | [ 26 | { 27 | col: 0, 28 | fieldCount: 4, 29 | name: 0, 30 | src: 0, 31 | srcCol: 0, 32 | srcLine: 2, 33 | }, 34 | ], 35 | [ 36 | { 37 | col: 0, 38 | fieldCount: 4, 39 | name: 0, 40 | src: 0, 41 | srcCol: 0, 42 | srcLine: 3, 43 | }, 44 | ], 45 | [ 46 | { 47 | col: 0, 48 | fieldCount: 4, 49 | name: 0, 50 | src: 0, 51 | srcCol: 0, 52 | srcLine: 4, 53 | }, 54 | ], 55 | [ 56 | { 57 | col: 0, 58 | fieldCount: 4, 59 | name: 0, 60 | src: 0, 61 | srcCol: 0, 62 | srcLine: 5, 63 | }, 64 | ], 65 | [ 66 | { 67 | col: 0, 68 | fieldCount: 4, 69 | name: 0, 70 | src: 0, 71 | srcCol: 0, 72 | srcLine: 6, 73 | }, 74 | ], 75 | // fourth.js mappings 76 | [ 77 | { 78 | col: 0, 79 | fieldCount: 4, 80 | name: 0, 81 | src: 1, 82 | srcCol: 0, 83 | srcLine: 0, 84 | }, 85 | ], 86 | [ 87 | { 88 | col: 0, 89 | fieldCount: 4, 90 | name: 0, 91 | src: 1, 92 | srcCol: 0, 93 | srcLine: 1, 94 | }, 95 | ], 96 | [ 97 | { 98 | col: 0, 99 | fieldCount: 4, 100 | name: 0, 101 | src: 1, 102 | srcCol: 0, 103 | srcLine: 2, 104 | }, 105 | ], 106 | ], 107 | names: [], 108 | sources: [ 109 | 'test/fixtures/inner/third.js', 110 | 'test/fixtures/inner/fourth.js', 111 | ], 112 | sourcesContent: [ 113 | 'function meaningOfLife() {\n throw new Error(42);\n}\n\nfunction boom() {\n throw new Error(\'boom\');\n}\n', 114 | 'function somethingElse() {\n throw new Error("somethign else");\n}\n', 115 | ], 116 | version: '3', 117 | }; 118 | -------------------------------------------------------------------------------- /src/tests/index.ts: -------------------------------------------------------------------------------- 1 | export * from './concat-tests'; 2 | export * from './encoder-tests'; 3 | export * from './vlq-tests'; 4 | -------------------------------------------------------------------------------- /src/tests/vlq-tests.ts: -------------------------------------------------------------------------------- 1 | import toBuffer from '../utils/to-buffer'; 2 | import toString from '../utils/to-string'; 3 | 4 | import { 5 | Decoder, 6 | decodeVLQ, 7 | Encoder, 8 | encodeVLQ, 9 | IntBufferReader, 10 | IntBufferWriter, 11 | MappingsDecoder, 12 | MappingsEncoder, 13 | } from '../index'; 14 | 15 | import { expect } from 'chai'; 16 | 17 | describe('test encode', () => { 18 | it('encodeVLQ', () => { 19 | let writer = new IntBufferWriter([], 0); 20 | 21 | [ 123, 456, 789, 987, 654, 321 ].forEach((n) => { 22 | encodeVLQ(writer, n); 23 | }); 24 | 25 | expect(toString(writer.buf, 0, writer.ptr)).to.equal('2HwcqxB29B8oBiU'); 26 | 27 | writer = new IntBufferWriter(new Int32Array(10), 0); 28 | 29 | [ -1, 2, 1, 7, -1, 2, 6, 2 ].forEach((n) => { 30 | encodeVLQ(writer, n); 31 | }); 32 | expect(toString(writer.buf, 0, writer.ptr)).to.equal('DECODEME'); 33 | }); 34 | 35 | it('decodeVLQ', () => { 36 | // tslint:disable-next-line:max-line-length 37 | const output = { 38 | buf: new Int32Array(10), 39 | ptr: 0, 40 | }; 41 | 42 | const buffer = toBuffer('DECODEME'); 43 | const reader = new IntBufferReader(buffer, 0, buffer.length); 44 | 45 | while (reader.ptr < reader.buf.length) { 46 | output.buf[output.ptr++] = decodeVLQ(reader); 47 | } 48 | 49 | expect(output.buf).to.deep.equal(new Int32Array([ -1, 2, 1, 7, -1, 2, 6, 2, 0, 0 ])); 50 | expect(output.ptr).to.equal(8); 51 | }); 52 | 53 | it('mappings decoder', () => { 54 | // tslint:disable-next-line:max-line-length 55 | const buffer = toBuffer('uLAOA,SAASA,GAAcC,EAAMC,EAAIC,GACjC,OAAUF,GACV,IAAS,SAAT,MAA0B,IAAIG,GAAOF,EAAIC,EAAzC,KACS,cAAT,MAA+B'); 56 | 57 | const reader = new IntBufferReader(buffer, 0, buffer.length); 58 | 59 | const decoder = new Decoder(); 60 | const mappingsDecoder = new MappingsDecoder(decoder); 61 | 62 | mappingsDecoder.decode(reader); 63 | 64 | expect(decoder.mappings).to.deep.equal([[ 65 | { fieldCount: 4, col: 183, src: 0, srcLine: 7, srcCol: 0, name: 0 }, 66 | { fieldCount: 5, col: 192, src: 0, srcLine: 7, srcCol: 9, name: 0 }, 67 | { fieldCount: 5, col: 195, src: 0, srcLine: 7, srcCol: 23, name: 1 }, 68 | { fieldCount: 5, col: 197, src: 0, srcLine: 7, srcCol: 29, name: 2 }, 69 | { fieldCount: 5, col: 199, src: 0, srcLine: 7, srcCol: 33, name: 3 }, 70 | { fieldCount: 4, col: 202, src: 0, srcLine: 8, srcCol: 0, name: 0 }, 71 | { fieldCount: 5, col: 209, src: 0, srcLine: 8, srcCol: 10, name: 1 }, 72 | { fieldCount: 4, col: 212, src: 0, srcLine: 9, srcCol: 0, name: 0 }, 73 | { fieldCount: 4, col: 216, src: 0, srcLine: 9, srcCol: 9, name: 0 }, 74 | { fieldCount: 4, col: 225, src: 0, srcLine: 9, srcCol: 0, name: 0 }, 75 | { fieldCount: 4, col: 231, src: 0, srcLine: 9, srcCol: 26, name: 0 }, 76 | { fieldCount: 5, col: 235, src: 0, srcLine: 9, srcCol: 30, name: 4 }, 77 | { fieldCount: 5, col: 238, src: 0, srcLine: 9, srcCol: 37, name: 2 }, 78 | { fieldCount: 5, col: 240, src: 0, srcLine: 9, srcCol: 41, name: 3 }, 79 | { fieldCount: 4, col: 242, src: 0, srcLine: 9, srcCol: 0, name: 0 }, 80 | { fieldCount: 4, col: 247, src: 0, srcLine: 10, srcCol: 9, name: 0 }, 81 | { fieldCount: 4, col: 261, src: 0, srcLine: 10, srcCol: 0, name: 0 }, 82 | { fieldCount: 4, col: 267, src: 0, srcLine: 10, srcCol: 31, name: 0 }, 83 | ]]); 84 | }); 85 | 86 | it('mappings decoder (another)', () => { 87 | // tslint:disable-next-line:max-line-length 88 | const buffer = toBuffer(',YAAY;;AAArB,WAAS,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE;AACjD,QAAI,KAAK,GAAG,CAAC,CAAC;AACd,QAAI,GAAG,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;AAC5B,QAAI,MAAM,EAAE,CAAC,CAAC;;AAEd,WAAO,KAAK,GAAG,GAAG,EAAE;;;AAGlB,OAAC,GAAG,CAAC,GAAG,GAAG,KAAK,CAAA,GAAI,CAAC,CAAC;;;;AAItB,YAAM,GAAG,KAAK,GAAG,CAAC,GAAI,CAAC,GAAG,CAAC,AAAC,CAAC;;AAE7B,UAAI,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,EAAE;AAC1B,aAAK,GAAG,MAAM,GAAG,CAAC,CAAC;OACpB,MAAM;AACL,WAAG,GAAG,MAAM,CAAC;OACd;KACF;;AAED,WAAO,AAAC,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,GAAI,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC;GACpD'); 89 | 90 | const decoder = new Decoder(); 91 | const reader = new IntBufferReader(buffer, 0, buffer.length); 92 | 93 | new MappingsDecoder(decoder).decode(reader); 94 | 95 | const mappings = decoder.mappings; 96 | 97 | expect(mappings.length, 'mappings.length').to.equal(25); 98 | expect(mappings[0].length).to.equal(1); 99 | expect(mappings[0][0], 'YAAY').to.deep.equal({ fieldCount: 4, srcLine: 0, srcCol: 12, src: 0, col: 12, name: 0 }); 100 | 101 | expect(mappings[1].length).to.equal(0); 102 | expect(mappings[2].length).to.equal(8); 103 | 104 | expect(mappings[2][0], 'AAArB').to.deep.equal({ fieldCount: 4, srcLine: 0, srcCol: -9, src: 0, col: 0, name: 0 }); 105 | expect(mappings[2][1], 'WAAS').to.deep.equal({ fieldCount: 4, srcLine: 0, srcCol: 0, src: 0, col: 11, name: 0 }); 106 | expect(mappings[2][2], 'YAAY').to.deep.equal({ fieldCount: 4, srcLine: 0, srcCol: 12, src: 0, col: 23, name: 0 }); 107 | expect(mappings[2][3], 'CAAC').to.deep.equal({ fieldCount: 4, srcLine: 0, srcCol: 13, src: 0, col: 24, name: 0 }); 108 | expect(mappings[2][4], 'IAAI').to.deep.equal({ fieldCount: 4, srcLine: 0, srcCol: 17, src: 0, col: 28, name: 0 }); 109 | expect(mappings[2][5], 'EAAE').to.deep.equal({ fieldCount: 4, srcLine: 0, srcCol: 19, src: 0, col: 30, name: 0 }); 110 | }); 111 | 112 | it('encoder', () => { 113 | // (lines + segemnts * 6) = byte_count 114 | const decoded = [[ 115 | { fieldCount: 4, col: 183, src: 0, srcLine: 7, srcCol: 0, name: 0 }, 116 | { fieldCount: 5, col: 192, src: 0, srcLine: 7, srcCol: 9, name: 0 }, 117 | { fieldCount: 5, col: 195, src: 0, srcLine: 7, srcCol: 23, name: 1 }, 118 | { fieldCount: 5, col: 197, src: 0, srcLine: 7, srcCol: 29, name: 2 }, 119 | { fieldCount: 5, col: 199, src: 0, srcLine: 7, srcCol: 33, name: 3 }, 120 | { fieldCount: 4, col: 202, src: 0, srcLine: 8, srcCol: 0, name: 0 }, 121 | { fieldCount: 5, col: 209, src: 0, srcLine: 8, srcCol: 10, name: 1 }, 122 | { fieldCount: 4, col: 212, src: 0, srcLine: 9, srcCol: 0, name: 0 }, 123 | { fieldCount: 4, col: 216, src: 0, srcLine: 9, srcCol: 9, name: 0 }, 124 | { fieldCount: 4, col: 225, src: 0, srcLine: 9, srcCol: 0, name: 0 }, 125 | { fieldCount: 4, col: 231, src: 0, srcLine: 9, srcCol: 26, name: 0 }, 126 | { fieldCount: 5, col: 235, src: 0, srcLine: 9, srcCol: 30, name: 4 }, 127 | { fieldCount: 5, col: 238, src: 0, srcLine: 9, srcCol: 37, name: 2 }, 128 | { fieldCount: 5, col: 240, src: 0, srcLine: 9, srcCol: 41, name: 3 }, 129 | { fieldCount: 4, col: 242, src: 0, srcLine: 9, srcCol: 0, name: 0 }, 130 | { fieldCount: 4, col: 247, src: 0, srcLine: 10, srcCol: 9, name: 0 }, 131 | { fieldCount: 4, col: 261, src: 0, srcLine: 10, srcCol: 0, name: 0 }, 132 | { fieldCount: 4, col: 267, src: 0, srcLine: 10, srcCol: 31, name: 0 }, 133 | ]]; 134 | 135 | // TODO: pretty sure we can do a Uint8Array here 136 | // let buffer = new Uint32Array(estimatedSize); 137 | const buffer: number[] = []; 138 | const writer = new IntBufferWriter(buffer, 0); 139 | const encoder = new Encoder(writer); 140 | const mappingsEncoder = new MappingsEncoder(encoder); 141 | 142 | mappingsEncoder.encode(decoded); 143 | 144 | expect(buffer.length, 'mapper.encode(decoded)').to.deep.equal(102); // TODO: this number is likely not right... 145 | expect(toString(buffer, 0, buffer.length)).to.deep.equal( 146 | 'uLAOA,SAASA,GAAcC,EAAMC,EAAIC,GACjC,OAAUF,GACV,IAAS,SAAT,MAA0B,IAAIG,GAAOF,EAAIC,EAAzC,KACS,cAAT,MAA+B'); 147 | }); 148 | }); 149 | -------------------------------------------------------------------------------- /src/utils/read-file.ts: -------------------------------------------------------------------------------- 1 | const fs: FS | undefined = (() => { 2 | if (typeof module === 'object' && 3 | typeof module.exports === 'object' && 4 | typeof require === 'function') { 5 | return require('fs'); 6 | } 7 | })(); 8 | 9 | const readFile: (path: string) => string = fs === undefined ? () => { throw new Error('readFile not supported'); } : 10 | (path) => fs.readFileSync(path, { encoding: 'utf8' }); 11 | 12 | interface FS { 13 | readFileSync(path: string, options: { encoding: string }): string; 14 | } 15 | 16 | export default readFile; 17 | -------------------------------------------------------------------------------- /src/utils/to-buffer.ts: -------------------------------------------------------------------------------- 1 | export default function toBuffer(str: string) { 2 | const buffer = new Uint8Array(str.length); 3 | for (let i = 0; i < buffer.length; i++) { 4 | // this is for base64 so we know these are all < 123 5 | buffer[i] = str.charCodeAt(i) | 0; 6 | } 7 | return buffer; 8 | } 9 | -------------------------------------------------------------------------------- /src/utils/to-string.ts: -------------------------------------------------------------------------------- 1 | export default function toString(buffer: number[] | Uint8Array, offset: number, len: number) { 2 | let str = ''; 3 | for (let i = offset; i < len; i++) { 4 | str += String.fromCharCode(buffer[i]); 5 | } 6 | return str; 7 | } 8 | -------------------------------------------------------------------------------- /src/vlq.ts: -------------------------------------------------------------------------------- 1 | import { asciiToUint6, uint6ToASCII } from './base64'; 2 | import Reader from './reader'; 3 | import Writer from './writer'; 4 | 5 | // 0 - 63 (6-bit 0 - 111111) 6 | // 32 100000 continuation bit 7 | // 31 011111 mask 5 bits 8 | // 1 is the sign bit 9 | export function encodeVLQ(writer: Writer, v: number) { 10 | let num = v < 0 ? (-v << 1) | 1 : v << 1; 11 | let cont = false; 12 | do { 13 | let digit = num & 31; 14 | num >>= 5; 15 | cont = num > 0; 16 | if (cont) { 17 | digit |= 32; 18 | } 19 | writer.write(uint6ToASCII[digit]); 20 | } while (cont); 21 | } 22 | 23 | export function decodeVLQ(reader: Reader) { 24 | let num = 0; 25 | let shift = 0; 26 | let digit = 0; 27 | let cont = 0; 28 | do { 29 | digit = asciiToUint6[reader.read()]; 30 | cont = digit & 32; 31 | digit = digit & 31; 32 | num = num + (digit << shift); 33 | shift += 5; 34 | } while (cont > 0); 35 | return num & 1 ? -(num >> 1) : (num >> 1); 36 | } 37 | -------------------------------------------------------------------------------- /src/writer.ts: -------------------------------------------------------------------------------- 1 | interface Writer { 2 | length: number; 3 | write(n: number): void; 4 | } 5 | 6 | export default Writer; 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "es2015", 4 | "target": "es5", 5 | "sourceMap": true, 6 | "outDir": "lib", 7 | "rootDir": "src", 8 | "declaration": true, 9 | "strict": true 10 | }, 11 | "include": [ 12 | "src/**/*.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "jsRules": {}, 7 | "rules": { 8 | "quotemark": { 9 | "options": "single" 10 | }, 11 | "no-console": false, 12 | "no-var-requires": false, 13 | "no-bitwise": false, 14 | "interface-name": false 15 | }, 16 | "rulesDirectory": [] 17 | } -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@types/chai@^4.0.4": 6 | version "4.0.4" 7 | resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.0.4.tgz#fe86315d9a66827feeb16f73bc954688ec950e18" 8 | 9 | "@types/mocha@^2.2.43": 10 | version "2.2.43" 11 | resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.43.tgz#03c54589c43ad048cbcbfd63999b55d0424eec27" 12 | 13 | "@types/node@^8.0.28": 14 | version "8.0.28" 15 | resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.28.tgz#86206716f8d9251cf41692e384264cbd7058ad60" 16 | 17 | ansi-regex@^2.0.0: 18 | version "2.1.1" 19 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" 20 | 21 | ansi-styles@^2.2.1: 22 | version "2.2.1" 23 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" 24 | 25 | assertion-error@^1.0.1: 26 | version "1.0.2" 27 | resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.0.2.tgz#13ca515d86206da0bac66e834dd397d87581094c" 28 | 29 | babel-code-frame@^6.22.0: 30 | version "6.26.0" 31 | resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" 32 | dependencies: 33 | chalk "^1.1.3" 34 | esutils "^2.0.2" 35 | js-tokens "^3.0.2" 36 | 37 | balanced-match@^1.0.0: 38 | version "1.0.0" 39 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 40 | 41 | brace-expansion@^1.1.7: 42 | version "1.1.8" 43 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" 44 | dependencies: 45 | balanced-match "^1.0.0" 46 | concat-map "0.0.1" 47 | 48 | browser-stdout@1.3.0: 49 | version "1.3.0" 50 | resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" 51 | 52 | buffer-crc32@^0.2.5: 53 | version "0.2.13" 54 | resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" 55 | 56 | chai@^4.1.2: 57 | version "4.1.2" 58 | resolved "https://registry.yarnpkg.com/chai/-/chai-4.1.2.tgz#0f64584ba642f0f2ace2806279f4f06ca23ad73c" 59 | dependencies: 60 | assertion-error "^1.0.1" 61 | check-error "^1.0.1" 62 | deep-eql "^3.0.0" 63 | get-func-name "^2.0.0" 64 | pathval "^1.0.0" 65 | type-detect "^4.0.0" 66 | 67 | chalk@^1.1.3: 68 | version "1.1.3" 69 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" 70 | dependencies: 71 | ansi-styles "^2.2.1" 72 | escape-string-regexp "^1.0.2" 73 | has-ansi "^2.0.0" 74 | strip-ansi "^3.0.0" 75 | supports-color "^2.0.0" 76 | 77 | check-error@^1.0.1: 78 | version "1.0.2" 79 | resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" 80 | 81 | colors@^1.1.2: 82 | version "1.1.2" 83 | resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" 84 | 85 | commander@2.9.0: 86 | version "2.9.0" 87 | resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" 88 | dependencies: 89 | graceful-readlink ">= 1.0.0" 90 | 91 | commander@^2.9.0: 92 | version "2.11.0" 93 | resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" 94 | 95 | concat-map@0.0.1: 96 | version "0.0.1" 97 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 98 | 99 | concat-stream@^1.5.1: 100 | version "1.6.0" 101 | resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" 102 | dependencies: 103 | inherits "^2.0.3" 104 | readable-stream "^2.2.2" 105 | typedarray "^0.0.6" 106 | 107 | core-util-is@~1.0.0: 108 | version "1.0.2" 109 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 110 | 111 | debug@2.6.8: 112 | version "2.6.8" 113 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" 114 | dependencies: 115 | ms "2.0.0" 116 | 117 | deep-eql@^3.0.0: 118 | version "3.0.1" 119 | resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" 120 | dependencies: 121 | type-detect "^4.0.0" 122 | 123 | diff@3.2.0: 124 | version "3.2.0" 125 | resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" 126 | 127 | diff@^3.2.0: 128 | version "3.3.1" 129 | resolved "https://registry.yarnpkg.com/diff/-/diff-3.3.1.tgz#aa8567a6eed03c531fc89d3f711cd0e5259dec75" 130 | 131 | es6-promise@^3.1.2: 132 | version "3.3.1" 133 | resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613" 134 | 135 | escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2: 136 | version "1.0.5" 137 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 138 | 139 | esutils@^2.0.2: 140 | version "2.0.2" 141 | resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" 142 | 143 | fs.realpath@^1.0.0: 144 | version "1.0.0" 145 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 146 | 147 | get-func-name@^2.0.0: 148 | version "2.0.0" 149 | resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" 150 | 151 | glob@7.1.1: 152 | version "7.1.1" 153 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" 154 | dependencies: 155 | fs.realpath "^1.0.0" 156 | inflight "^1.0.4" 157 | inherits "2" 158 | minimatch "^3.0.2" 159 | once "^1.3.0" 160 | path-is-absolute "^1.0.0" 161 | 162 | glob@^7.0.5, glob@^7.1.1: 163 | version "7.1.2" 164 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" 165 | dependencies: 166 | fs.realpath "^1.0.0" 167 | inflight "^1.0.4" 168 | inherits "2" 169 | minimatch "^3.0.4" 170 | once "^1.3.0" 171 | path-is-absolute "^1.0.0" 172 | 173 | graceful-fs@^4.1.3: 174 | version "4.1.11" 175 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" 176 | 177 | "graceful-readlink@>= 1.0.0": 178 | version "1.0.1" 179 | resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" 180 | 181 | growl@1.9.2: 182 | version "1.9.2" 183 | resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" 184 | 185 | has-ansi@^2.0.0: 186 | version "2.0.0" 187 | resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" 188 | dependencies: 189 | ansi-regex "^2.0.0" 190 | 191 | has-flag@^1.0.0: 192 | version "1.0.0" 193 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" 194 | 195 | he@1.1.1: 196 | version "1.1.1" 197 | resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" 198 | 199 | inflight@^1.0.4: 200 | version "1.0.6" 201 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 202 | dependencies: 203 | once "^1.3.0" 204 | wrappy "1" 205 | 206 | inherits@2, inherits@^2.0.3, inherits@~2.0.3: 207 | version "2.0.3" 208 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 209 | 210 | isarray@~1.0.0: 211 | version "1.0.0" 212 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 213 | 214 | js-tokens@^3.0.2: 215 | version "3.0.2" 216 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" 217 | 218 | json3@3.3.2: 219 | version "3.3.2" 220 | resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" 221 | 222 | lodash._baseassign@^3.0.0: 223 | version "3.2.0" 224 | resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" 225 | dependencies: 226 | lodash._basecopy "^3.0.0" 227 | lodash.keys "^3.0.0" 228 | 229 | lodash._basecopy@^3.0.0: 230 | version "3.0.1" 231 | resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" 232 | 233 | lodash._basecreate@^3.0.0: 234 | version "3.0.3" 235 | resolved "https://registry.yarnpkg.com/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz#1bc661614daa7fc311b7d03bf16806a0213cf821" 236 | 237 | lodash._getnative@^3.0.0: 238 | version "3.9.1" 239 | resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" 240 | 241 | lodash._isiterateecall@^3.0.0: 242 | version "3.0.9" 243 | resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" 244 | 245 | lodash.create@3.1.1: 246 | version "3.1.1" 247 | resolved "https://registry.yarnpkg.com/lodash.create/-/lodash.create-3.1.1.tgz#d7f2849f0dbda7e04682bb8cd72ab022461debe7" 248 | dependencies: 249 | lodash._baseassign "^3.0.0" 250 | lodash._basecreate "^3.0.0" 251 | lodash._isiterateecall "^3.0.0" 252 | 253 | lodash.isarguments@^3.0.0: 254 | version "3.1.0" 255 | resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" 256 | 257 | lodash.isarray@^3.0.0: 258 | version "3.0.4" 259 | resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" 260 | 261 | lodash.keys@^3.0.0: 262 | version "3.1.2" 263 | resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" 264 | dependencies: 265 | lodash._getnative "^3.0.0" 266 | lodash.isarguments "^3.0.0" 267 | lodash.isarray "^3.0.0" 268 | 269 | minimatch@^3.0.2, minimatch@^3.0.4: 270 | version "3.0.4" 271 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 272 | dependencies: 273 | brace-expansion "^1.1.7" 274 | 275 | minimist@0.0.8: 276 | version "0.0.8" 277 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 278 | 279 | minimist@^1.2.0: 280 | version "1.2.0" 281 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" 282 | 283 | mkdirp@0.5.1, mkdirp@^0.5.1: 284 | version "0.5.1" 285 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 286 | dependencies: 287 | minimist "0.0.8" 288 | 289 | mocha@^3.5.3: 290 | version "3.5.3" 291 | resolved "https://registry.yarnpkg.com/mocha/-/mocha-3.5.3.tgz#1e0480fe36d2da5858d1eb6acc38418b26eaa20d" 292 | dependencies: 293 | browser-stdout "1.3.0" 294 | commander "2.9.0" 295 | debug "2.6.8" 296 | diff "3.2.0" 297 | escape-string-regexp "1.0.5" 298 | glob "7.1.1" 299 | growl "1.9.2" 300 | he "1.1.1" 301 | json3 "3.3.2" 302 | lodash.create "3.1.1" 303 | mkdirp "0.5.1" 304 | supports-color "3.1.2" 305 | 306 | ms@2.0.0: 307 | version "2.0.0" 308 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 309 | 310 | once@^1.3.0: 311 | version "1.4.0" 312 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 313 | dependencies: 314 | wrappy "1" 315 | 316 | path-is-absolute@^1.0.0: 317 | version "1.0.1" 318 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 319 | 320 | path-parse@^1.0.5: 321 | version "1.0.5" 322 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" 323 | 324 | pathval@^1.0.0: 325 | version "1.1.0" 326 | resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" 327 | 328 | process-nextick-args@~1.0.6: 329 | version "1.0.7" 330 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" 331 | 332 | readable-stream@^2.2.2: 333 | version "2.3.3" 334 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" 335 | dependencies: 336 | core-util-is "~1.0.0" 337 | inherits "~2.0.3" 338 | isarray "~1.0.0" 339 | process-nextick-args "~1.0.6" 340 | safe-buffer "~5.1.1" 341 | string_decoder "~1.0.3" 342 | util-deprecate "~1.0.1" 343 | 344 | resolve@^1.3.2: 345 | version "1.4.0" 346 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.4.0.tgz#a75be01c53da25d934a98ebd0e4c4a7312f92a86" 347 | dependencies: 348 | path-parse "^1.0.5" 349 | 350 | rimraf@^2.5.2: 351 | version "2.6.2" 352 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" 353 | dependencies: 354 | glob "^7.0.5" 355 | 356 | rollup@^0.50.0: 357 | version "0.50.0" 358 | resolved "https://registry.yarnpkg.com/rollup/-/rollup-0.50.0.tgz#4c158f4e780e6cb33ff0dbfc184a52cc58cd5f3b" 359 | 360 | safe-buffer@~5.1.0, safe-buffer@~5.1.1: 361 | version "5.1.1" 362 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" 363 | 364 | sander@^0.5.0: 365 | version "0.5.1" 366 | resolved "https://registry.yarnpkg.com/sander/-/sander-0.5.1.tgz#741e245e231f07cafb6fdf0f133adfa216a502ad" 367 | dependencies: 368 | es6-promise "^3.1.2" 369 | graceful-fs "^4.1.3" 370 | mkdirp "^0.5.1" 371 | rimraf "^2.5.2" 372 | 373 | semver@^5.3.0: 374 | version "5.4.1" 375 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" 376 | 377 | sorcery@^0.10.0: 378 | version "0.10.0" 379 | resolved "https://registry.yarnpkg.com/sorcery/-/sorcery-0.10.0.tgz#8ae90ad7d7cb05fc59f1ab0c637845d5c15a52b7" 380 | dependencies: 381 | buffer-crc32 "^0.2.5" 382 | minimist "^1.2.0" 383 | sander "^0.5.0" 384 | sourcemap-codec "^1.3.0" 385 | 386 | source-map@^0.5.6: 387 | version "0.5.7" 388 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" 389 | 390 | sourcemap-codec@^1.3.0: 391 | version "1.3.1" 392 | resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.3.1.tgz#9ad6f9bdbd691931016e30939dbc868673323146" 393 | dependencies: 394 | vlq "^0.2.1" 395 | 396 | string_decoder@~1.0.3: 397 | version "1.0.3" 398 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" 399 | dependencies: 400 | safe-buffer "~5.1.0" 401 | 402 | strip-ansi@^3.0.0: 403 | version "3.0.1" 404 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" 405 | dependencies: 406 | ansi-regex "^2.0.0" 407 | 408 | supports-color@3.1.2: 409 | version "3.1.2" 410 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5" 411 | dependencies: 412 | has-flag "^1.0.0" 413 | 414 | supports-color@^2.0.0: 415 | version "2.0.0" 416 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" 417 | 418 | tslib@^1.7.1: 419 | version "1.7.1" 420 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.7.1.tgz#bc8004164691923a79fe8378bbeb3da2017538ec" 421 | 422 | tslint@^5.7.0: 423 | version "5.7.0" 424 | resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.7.0.tgz#c25e0d0c92fa1201c2bc30e844e08e682b4f3552" 425 | dependencies: 426 | babel-code-frame "^6.22.0" 427 | colors "^1.1.2" 428 | commander "^2.9.0" 429 | diff "^3.2.0" 430 | glob "^7.1.1" 431 | minimatch "^3.0.4" 432 | resolve "^1.3.2" 433 | semver "^5.3.0" 434 | tslib "^1.7.1" 435 | tsutils "^2.8.1" 436 | 437 | tsutils@^2.8.1: 438 | version "2.8.2" 439 | resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.8.2.tgz#2c1486ba431260845b0ac6f902afd9d708a8ea6a" 440 | dependencies: 441 | tslib "^1.7.1" 442 | 443 | type-detect@^4.0.0: 444 | version "4.0.3" 445 | resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.3.tgz#0e3f2670b44099b0b46c284d136a7ef49c74c2ea" 446 | 447 | typedarray@^0.0.6: 448 | version "0.0.6" 449 | resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" 450 | 451 | typescript@^2.5.2: 452 | version "2.5.2" 453 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.5.2.tgz#038a95f7d9bbb420b1bf35ba31d4c5c1dd3ffe34" 454 | 455 | util-deprecate@~1.0.1: 456 | version "1.0.2" 457 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 458 | 459 | vlq@^0.2.1: 460 | version "0.2.2" 461 | resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.2.tgz#e316d5257b40b86bb43cb8d5fea5d7f54d6b0ca1" 462 | 463 | wrappy@1: 464 | version "1.0.2" 465 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 466 | --------------------------------------------------------------------------------