[Pkg-javascript-commits] [node-vlq] 01/02: New upstream version 0.2.0

Julien Puydt julien.puydt at laposte.net
Tue Nov 15 06:32:46 UTC 2016


This is an automated email from the git hooks/post-receive script.

jpuydt-guest pushed a commit to branch master
in repository node-vlq.

commit 1122fc3fdb0444452630e99ba92128d22c8473e3
Author: Julien Puydt <julien.puydt at laposte.net>
Date:   Tue Nov 1 14:39:47 2016 +0100

    New upstream version 0.2.0
---
 .gitignore                   |   3 +
 CHANGELOG.md                 |   9 ++
 README.md                    |  73 ++++++++++++++++
 bower.json                   |  15 ++++
 dist/vlq.js                  |  96 +++++++++++++++++++++
 package.json                 |  25 ++++++
 sourcemaps/Chrome.png        | Bin 0 -> 81479 bytes
 sourcemaps/Firefox.png       | Bin 0 -> 48697 bytes
 sourcemaps/README.md         | 193 +++++++++++++++++++++++++++++++++++++++++++
 sourcemaps/helloworld.coffee |   1 +
 sourcemaps/helloworld.js     |   6 ++
 sourcemaps/helloworld.js.map |   9 ++
 sourcemaps/index.html        |   5 ++
 src/vlq.js                   |  85 +++++++++++++++++++
 test/decode.js               |  13 +++
 test/encode.js               |  13 +++
 test/index.js                |   2 +
 17 files changed, 548 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0a33161
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+.DS_Store
+tmp*
+node_modules
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..211e53b
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,9 @@
+# changelog
+
+## 0.2.0
+
+* Author as ES6 module, accessible to ES6-aware systems via the `jsnext:main` field in `package.json`
+
+## 0.1.0
+
+* First release
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..347dfd9
--- /dev/null
+++ b/README.md
@@ -0,0 +1,73 @@
+# vlq.js
+
+Convert integers to a Base64-encoded VLQ string, and vice versa. No dependencies, works in node.js or browsers, supports AMD.
+
+
+## Why would you want to do that?
+
+Sourcemaps are the most likely use case. Mappings from original source to generated content are encoded as a sequence of VLQ strings.
+
+
+## What is a VLQ string?
+
+A [variable-length quantity](http://en.wikipedia.org/wiki/Variable-length_quantity) is a compact way of encoding large integers in text (i.e. in situations where you can't transmit raw binary data). An integer represented as digits will always take up more space than the equivalent VLQ representation:
+
+| Integer             | VLQ        |
+| :------------------ | :--------- |
+| 0                   | A          |
+| 1                   | C          |
+| -1                  | D          |
+| 123                 | 2H         |
+| 123456789           | qxmvrH     |
+| 123456789123456789  | gxvh6sB    |
+
+
+## Installation
+
+```bash
+npm install vlq
+```
+
+...or...
+
+```bash
+bower install vlq
+```
+
+...or grab the vlq.js file and include it with a `<script src='vlq.js'>` tag.
+
+
+## Usage
+
+### Encoding
+
+`vlq.encode` accepts an integer, or an array of integers, and returns a string:
+
+```js
+vlq.encode( 123 ); // '2H';
+vlq.encode([ 123, 456, 789 ]); // '2HwcqxB'
+```
+
+### Decoding
+
+`vlq.decode` accepts a string and always returns an array:
+
+```js
+vlq.decode( '2H' ); // [ 123 ]
+vlq.decode( '2HwcqxB' ); // [ 123, 456, 789 ]
+```
+
+
+## Using vlq.js with sourcemaps
+
+[See here for an example of using vlq.js with sourcemaps.](https://github.com/Rich-Harris/vlq/tree/master/sourcemaps)
+
+
+## Credits
+
+Adapted from [murzwin.com/base64vlq.html](http://murzwin.com/base64vlq.html) by Alexander Pavlov.
+
+
+## License
+
+MIT.
\ No newline at end of file
diff --git a/bower.json b/bower.json
new file mode 100644
index 0000000..89ea154
--- /dev/null
+++ b/bower.json
@@ -0,0 +1,15 @@
+{
+	"name": "vlq",
+	"description": "Generate, and decode, base64 VLQ mappings for source maps and other uses",
+	"author": "Rich Harris",
+	"repository": "https://github.com/Rich-Harris/vlq",
+	"version": "0.2.0",
+	"main": "dist/vlq.js",
+	"ignore": [
+		".*",
+		"*.json",
+		"test",
+		"sourcemaps",
+		"README.md"
+	]
+}
diff --git a/dist/vlq.js b/dist/vlq.js
new file mode 100644
index 0000000..60748d9
--- /dev/null
+++ b/dist/vlq.js
@@ -0,0 +1,96 @@
+(function (global, factory) {
+	typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
+	typeof define === 'function' && define.amd ? define(['exports'], factory) :
+	factory((global.vlq = {}))
+}(this, function (exports) { 'use strict';
+
+	exports.decode = decode;
+	exports.encode = encode;
+
+	var charToInteger = {};
+	var integerToChar = {};
+
+	'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split( '' ).forEach( function ( char, i ) {
+		charToInteger[ char ] = i;
+		integerToChar[ i ] = char;
+	});
+
+	function decode ( string ) {
+		var result = [],
+			len = string.length,
+			i,
+			hasContinuationBit,
+			shift = 0,
+			value = 0,
+			integer,
+			shouldNegate;
+
+		for ( i = 0; i < len; i += 1 ) {
+			integer = charToInteger[ string[i] ];
+
+			if ( integer === undefined ) {
+				throw new Error( 'Invalid character (' + string[i] + ')' );
+			}
+
+			hasContinuationBit = integer & 32;
+
+			integer &= 31;
+			value += integer << shift;
+
+			if ( hasContinuationBit ) {
+				shift += 5;
+			} else {
+				shouldNegate = value & 1;
+				value >>= 1;
+
+				result.push( shouldNegate ? -value : value );
+
+				// reset
+				value = shift = 0;
+			}
+		}
+
+		return result;
+	}
+
+	function encode ( value ) {
+		var result;
+
+		if ( typeof value === 'number' ) {
+			result = encodeInteger( value );
+		} else if ( Array.isArray( value ) ) {
+			result = '';
+			value.forEach( function ( num ) {
+				result += encodeInteger( num );
+			});
+		} else {
+			throw new Error( 'vlq.encode accepts an integer or an array of integers' );
+		}
+
+		return result;
+	}
+
+	function encodeInteger ( num ) {
+		var result = '', clamped;
+
+		if ( num < 0 ) {
+			num = ( -num << 1 ) | 1;
+		} else {
+			num <<= 1;
+		}
+
+		do {
+			clamped = num & 31;
+			num >>= 5;
+
+			if ( num > 0 ) {
+				clamped |= 32;
+			}
+
+			result += integerToChar[ clamped ];
+		} while ( num > 0 );
+
+		return result;
+	}
+
+}));
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..1a6a5d4
--- /dev/null
+++ b/package.json
@@ -0,0 +1,25 @@
+{
+  "name": "vlq",
+  "description": "Generate, and decode, base64 VLQ mappings for source maps and other uses",
+  "author": "Rich Harris",
+  "repository": "https://github.com/Rich-Harris/vlq",
+  "license": "MIT",
+  "version": "0.2.0",
+  "main": "dist/vlq.js",
+  "jsnext:main": "src/vlq.js",
+  "files": [
+    "README.md",
+    "src/vlq.js",
+    "dist/vlq.js"
+  ],
+  "devDependencies": {
+    "esperanto": "^0.6.8",
+    "jshint": "^2.6.0"
+  },
+  "scripts": {
+  	"build": "esperanto -i src/vlq.js -o dist/vlq.js -t umd -n vlq -s",
+  	"lint": "jshint src",
+  	"test": "node test",
+  	"prepublish": "npm run lint; npm run build; npm test"
+  }
+}
diff --git a/sourcemaps/Chrome.png b/sourcemaps/Chrome.png
new file mode 100644
index 0000000..4ff6029
Binary files /dev/null and b/sourcemaps/Chrome.png differ
diff --git a/sourcemaps/Firefox.png b/sourcemaps/Firefox.png
new file mode 100644
index 0000000..fc2c72d
Binary files /dev/null and b/sourcemaps/Firefox.png differ
diff --git a/sourcemaps/README.md b/sourcemaps/README.md
new file mode 100644
index 0000000..ffb3d2e
--- /dev/null
+++ b/sourcemaps/README.md
@@ -0,0 +1,193 @@
+# Using vlq.js with source maps
+
+This library doesn't include any special magic for dealing with source maps, just the low-level encoding/decoding. But it's actually fairly straightforward to convert an incomprehensible-looking string like this...
+
+```
+AAAA;AAAA,EAAA,OAAO,CAAC,GAAR,CAAY,aAAZ,CAAA,CAAA;AAAA
+```
+
+...into an array of mappings. Suppose we had some CoffeeScript code:
+
+**helloworld.coffee**
+```coffee
+console.log 'hello world'
+```
+
+It would get transpiled into this:
+
+**helloworld.js**
+```js
+(function() {
+  console.log('hello world');
+
+}).call(this);
+```
+
+And CoffeeScript would (if you asked it to) generate a sourcemap like this:
+
+**helloworld.js.map**
+```js
+{
+  "version": 3,
+  "file": "helloworld.js",
+  "sources": [
+    "helloworld.coffee"
+  ],
+  "names": [],
+  "mappings": "AAAA;AAAA,EAAA,OAAO,CAAC,GAAR,CAAY,aAAZ,CAAA,CAAA;AAAA"
+}
+```
+
+(A source map simply a JSON object that adheres to a particular specification, [which you can find here](https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?hl=en_US&pli=1&pli=1).)
+
+Each line in the generated JavaScript (`helloworld.js`) is represented as a series of VLQ-encoded *segments*, separated by the `,` character. The lines themselves are separated by `;` characters. So we could represent the mapping like so:
+
+```js
+mappings = 'AAAA;AAAA,EAAA,OAAO,CAAC,GAAR,CAAY,aAAZ,CAAA,CAAA;AAAA';
+vlqs = mappings.split( ';' ).map( function ( line ) {
+	return line.split( ',' );
+});
+
+[
+	// line 0 of helloworld.js (everything is zero-based)
+	[ 'AAAA' ],
+
+	// line 1
+	[ 'AAAA', 'EAAA', 'OAAO', 'CAAC', 'GAAR', 'CAAY', 'aAAZ', 'CAAA', 'CAAA' ],
+
+	// line 2
+	[ 'AAAA' ]
+]
+```
+
+Using vlq.js to decode each segment, we can convert that into the following:
+
+```js
+decoded = vlqs.map( function ( line ) {
+	return line.map( vlq.decode );
+});
+
+[
+  // line 0
+  [ [ 0, 0, 0, 0 ] ],
+
+  // line 1
+  [
+    [ 0, 0, 0, 0 ],
+    [ 2, 0, 0, 0 ],
+    [ 7, 0, 0, 7 ],
+    [ 1, 0, 0, 1 ],
+    [ 3, 0, 0, -8 ],
+    [ 1, 0, 0, 12 ],
+    [ 13, 0, 0, -12 ],
+    [ 1, 0, 0, 0 ],
+    [ 1, 0, 0, 0 ]
+  ],
+
+  // line 2
+  [ [ 0, 0, 0, 0 ] ]
+]
+```
+
+Each segment has 4 *fields* in this case, though the [spec](https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?hl=en_US&pli=1&pli=1) allows segments to have either 1, 4 or 5 fields (in other words, 2, 3, 4 and 5 below are optional - in our CoffeeScript example, the fifth field is never used). They are:
+
+1. The zero-based starting column of the current line. If this is the first segment of the line, it's absolute, otherwise it's relative to the same field in the previous segment.
+2. The zero-based index of the original **source file**, as listed in the source map object's `sources` array (since the generated code may be the result of combining several files), *relative to the previous value*.
+3. The zero-based starting **line** in the original source code that this segment corresponds to, *relative to the previous value*.
+4. The zero-based starting **column** in the original source code that this segment corresponds to, *relative to the previous value*.
+5. The zero-based index of the **name**, as listed in the source map object's `names` array, that this mapping corresponds to, *relative to the previous value*. (This isn't used here because no names are changed, but it's useful when minifying JavaScript, since `myVeryLongVarName` will get changed to `a` or similar.)
+
+We can now decode our mappings a bit further:
+
+```js
+var sourceFileIndex = 0,   // second field
+    sourceCodeLine = 0,    // third field
+    sourceCodeColumn = 0,  // fourth field
+    nameIndex = 0;         // fifth field
+
+decoded = decoded.map( function ( line ) {
+  var generatedCodeColumn = 0; // first field - reset each time
+
+  return line.map( function ( segment ) {
+    var result;
+
+    generatedCodeColumn += segment[0];
+
+    result = [ generatedCodeColumn ];
+
+    if ( segment.length === 1 ) {
+      // only one field!
+      return result;
+    }
+
+    sourceFileIndex  += segment[1];
+    sourceCodeLine   += segment[2];
+    sourceCodeColumn += segment[3];
+
+    result.push( sourceFileIndex, sourceCodeLine, sourceCodeColumn );
+
+    if ( segment.length === 5 ) {
+      nameIndex += segment[4];
+      result.push( nameIndex );
+    }
+
+    return result;
+  });
+});
+
+[
+  // line 0
+  [ [ 0, 0, 0, 0 ] ],
+
+  // line 1
+  [
+    [ 0, 0, 0, 0 ],
+    [ 2, 0, 0, 0 ],
+    [ 9, 0, 0, 7 ],
+    [ 10, 0, 0, 8 ],
+    [ 13, 0, 0, 0 ],
+    [ 14, 0, 0, 12 ],
+    [ 27, 0, 0, 0 ],
+    [ 28, 0, 0, 0 ],
+    [ 29, 0, 0, 0 ]
+  ],
+
+  // line 2
+  [ [ 0, 0, 0, 0 ] ]
+]
+```
+
+The first and third lines don't really contain any interesting information. But the second line does. Let's take the first three segments:
+
+```js
+// line 1 (the second line - still zero-based, remember)
+[
+  // Column 0 of line 1 corresponds to source file 0, line 0, column 0
+  [ 0, 0, 0, 0 ],
+
+  // Column 2 of line 1 also corresponds to 0, 0, 0! In other words, the
+  // two spaces before `console` in helloworld.js don't correspond to
+  // anything in helloworld.coffee
+  [ 2, 0, 0, 0 ],
+
+  // Column 9 of line 1 corresponds to 0, 0, 7. Taken together with the
+  // previous segment, this means that columns 2-9 of line 1 in the
+  // generated helloworld.js file correspond to columns 0-7 of line 0
+  // in the original helloworld.coffee
+  [ 9, 0, 0, 7 ],
+
+  ...
+]
+```
+
+It's through this fairly convoluted process that your browser (assuming it's one of the good ones) is able to read `helloworld.js` and an accompanying source map (typically `helloworld.js.map`) and do this:
+
+### Chrome
+
+![](https://github.com/Rich-Harris/vlq/blob/master/sourcemaps/Chrome.png)
+
+### Firefox
+
+![](https://github.com/Rich-Harris/vlq/blob/master/sourcemaps/Firefox.png)
+
+You can try this for yourself by cloning this repo and opening the `sourcemaps/index.html` file.
\ No newline at end of file
diff --git a/sourcemaps/helloworld.coffee b/sourcemaps/helloworld.coffee
new file mode 100644
index 0000000..c4855f6
--- /dev/null
+++ b/sourcemaps/helloworld.coffee
@@ -0,0 +1 @@
+console.log 'hello world'
\ No newline at end of file
diff --git a/sourcemaps/helloworld.js b/sourcemaps/helloworld.js
new file mode 100644
index 0000000..8972927
--- /dev/null
+++ b/sourcemaps/helloworld.js
@@ -0,0 +1,6 @@
+(function() {
+  console.log('hello world');
+
+}).call(this);
+
+//# sourceMappingURL=helloworld.js.map
\ No newline at end of file
diff --git a/sourcemaps/helloworld.js.map b/sourcemaps/helloworld.js.map
new file mode 100644
index 0000000..c328fa5
--- /dev/null
+++ b/sourcemaps/helloworld.js.map
@@ -0,0 +1,9 @@
+{
+  "version": 3,
+  "file": "helloworld.js",
+  "sources": [
+    "helloworld.coffee"
+  ],
+  "names": [],
+  "mappings": "AAAA;AAAA,EAAA,OAAO,CAAC,GAAR,CAAY,aAAZ,CAAA,CAAA;AAAA"
+}
\ No newline at end of file
diff --git a/sourcemaps/index.html b/sourcemaps/index.html
new file mode 100644
index 0000000..f04c7be
--- /dev/null
+++ b/sourcemaps/index.html
@@ -0,0 +1,5 @@
+<p>Check the console! It should print 'hello world', and the source of the log should be reported as <code>helloworld.coffee</code>, if you're in a modern browser (you may need to refresh the page with devtools open).</p>
+
+<p>Clicking on <code>helloworld.coffee</code> should open the original CoffeeScript file in the Sources pane of your devtools.</p>
+
+<script src='helloworld.js'></script>
\ No newline at end of file
diff --git a/src/vlq.js b/src/vlq.js
new file mode 100644
index 0000000..e99ce87
--- /dev/null
+++ b/src/vlq.js
@@ -0,0 +1,85 @@
+var charToInteger = {};
+var integerToChar = {};
+
+'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split( '' ).forEach( function ( char, i ) {
+	charToInteger[ char ] = i;
+	integerToChar[ i ] = char;
+});
+
+export function decode ( string ) {
+	var result = [],
+		len = string.length,
+		i,
+		hasContinuationBit,
+		shift = 0,
+		value = 0,
+		integer,
+		shouldNegate;
+
+	for ( i = 0; i < len; i += 1 ) {
+		integer = charToInteger[ string[i] ];
+
+		if ( integer === undefined ) {
+			throw new Error( 'Invalid character (' + string[i] + ')' );
+		}
+
+		hasContinuationBit = integer & 32;
+
+		integer &= 31;
+		value += integer << shift;
+
+		if ( hasContinuationBit ) {
+			shift += 5;
+		} else {
+			shouldNegate = value & 1;
+			value >>= 1;
+
+			result.push( shouldNegate ? -value : value );
+
+			// reset
+			value = shift = 0;
+		}
+	}
+
+	return result;
+}
+
+export function encode ( value ) {
+	var result;
+
+	if ( typeof value === 'number' ) {
+		result = encodeInteger( value );
+	} else if ( Array.isArray( value ) ) {
+		result = '';
+		value.forEach( function ( num ) {
+			result += encodeInteger( num );
+		});
+	} else {
+		throw new Error( 'vlq.encode accepts an integer or an array of integers' );
+	}
+
+	return result;
+}
+
+function encodeInteger ( num ) {
+	var result = '', clamped;
+
+	if ( num < 0 ) {
+		num = ( -num << 1 ) | 1;
+	} else {
+		num <<= 1;
+	}
+
+	do {
+		clamped = num & 31;
+		num >>= 5;
+
+		if ( num > 0 ) {
+			clamped |= 32;
+		}
+
+		result += integerToChar[ clamped ];
+	} while ( num > 0 );
+
+	return result;
+}
\ No newline at end of file
diff --git a/test/decode.js b/test/decode.js
new file mode 100644
index 0000000..3dc0c5f
--- /dev/null
+++ b/test/decode.js
@@ -0,0 +1,13 @@
+var assert = require( 'assert' ),
+	vlq = require( '../' );
+
+var tests = [
+	[ 'AAAA', [ 0, 0, 0, 0 ] ],
+	[ 'AAgBC', [ 0, 0, 16, 1 ] ]
+];
+
+tests.forEach( function ( test ) {
+	assert.deepEqual( vlq.decode( test[0] ), test[1] );
+});
+
+console.log( 'all vlq.decode tests passed' );
diff --git a/test/encode.js b/test/encode.js
new file mode 100644
index 0000000..7b64587
--- /dev/null
+++ b/test/encode.js
@@ -0,0 +1,13 @@
+var assert = require( 'assert' ),
+	vlq = require( '../' );
+
+var tests = [
+	[ [ 0, 0, 0, 0 ], 'AAAA' ],
+	[ [ 0, 0, 16, 1 ], 'AAgBC' ]
+];
+
+tests.forEach( function ( test ) {
+	assert.equal( vlq.encode( test[0] ), test[1] );
+});
+
+console.log( 'all vlq.encode tests passed' );
diff --git a/test/index.js b/test/index.js
new file mode 100644
index 0000000..26cc237
--- /dev/null
+++ b/test/index.js
@@ -0,0 +1,2 @@
+require( './encode' );
+require( './decode' );

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-vlq.git



More information about the Pkg-javascript-commits mailing list