[Pkg-javascript-commits] [node-buble] 01/05: New upstream version 0.18.0

Praveen Arimbrathodiyil praveen at moszumanska.debian.org
Fri Jan 19 15:25:16 UTC 2018


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

praveen pushed a commit to branch master
in repository node-buble.

commit f1f241ed047104acc0dee7ea483b1979af3b2b4a
Author: Pirate Praveen <praveen at debian.org>
Date:   Fri Jan 19 18:44:55 2018 +0530

    New upstream version 0.18.0
---
 .eslintrc                                          |    1 -
 CHANGELOG.md                                       |   28 +
 bin/buble                                          |   20 +-
 bin/handleError.js                                 |   56 +-
 bin/runBuble.js                                    |  137 +--
 bin/showHelp.js                                    |   17 +-
 package-lock.json                                  | 1171 +++++++++++---------
 package.json                                       |   35 +-
 register.js                                        |    2 +-
 rollup.config.js                                   |    3 +
 src/index.js                                       |   93 +-
 src/program/BlockStatement.js                      |  331 +++---
 src/program/Node.js                                |  108 +-
 src/program/Program.js                             |   24 +-
 src/program/Scope.js                               |   97 +-
 src/program/extractNames.js                        |   32 +-
 src/program/keys.js                                |    2 +-
 src/program/types/ArrayExpression.js               |   61 +-
 src/program/types/ArrowFunctionExpression.js       |   44 +-
 src/program/types/AssignmentExpression.js          |  372 ++++---
 src/program/types/BinaryExpression.js              |   12 +-
 src/program/types/BreakStatement.js                |   20 +-
 src/program/types/CallExpression.js                |   95 +-
 src/program/types/ClassBody.js                     |  219 ++--
 src/program/types/ClassDeclaration.js              |   83 +-
 src/program/types/ClassExpression.js               |   56 +-
 src/program/types/ContinueStatement.js             |   14 +-
 src/program/types/ExportDefaultDeclaration.js      |    7 +-
 src/program/types/ExportNamedDeclaration.js        |    7 +-
 src/program/types/ForInStatement.js                |   32 +-
 src/program/types/ForOfStatement.js                |   77 +-
 src/program/types/ForStatement.js                  |   50 +-
 src/program/types/FunctionDeclaration.js           |   20 +-
 src/program/types/FunctionExpression.js            |   58 +-
 src/program/types/Identifier.js                    |   40 +-
 src/program/types/IfStatement.js                   |   31 +-
 src/program/types/ImportDeclaration.js             |    7 +-
 src/program/types/ImportDefaultSpecifier.js        |    6 +-
 src/program/types/ImportSpecifier.js               |    6 +-
 src/program/types/JSXAttribute.js                  |   12 +-
 src/program/types/JSXClosingElement.js             |   17 +-
 src/program/types/JSXElement.js                    |   42 +-
 src/program/types/JSXExpressionContainer.js        |    8 +-
 src/program/types/JSXOpeningElement.js             |   71 +-
 src/program/types/JSXSpreadAttribute.js            |    8 +-
 src/program/types/Literal.js                       |   38 +-
 src/program/types/MemberExpression.js              |   10 +-
 src/program/types/NewExpression.js                 |   39 +-
 src/program/types/ObjectExpression.js              |  215 +++-
 src/program/types/Property.js                      |   54 +-
 src/program/types/ReturnStatement.js               |   30 +-
 src/program/types/SpreadElement.js                 |   12 +
 src/program/types/SpreadProperty.js                |   10 -
 src/program/types/Super.js                         |   85 +-
 src/program/types/TaggedTemplateExpression.js      |   44 +-
 src/program/types/TemplateElement.js               |    4 +-
 src/program/types/TemplateLiteral.js               |   76 +-
 src/program/types/ThisExpression.js                |   27 +-
 src/program/types/UpdateExpression.js              |   24 +-
 src/program/types/VariableDeclaration.js           |   87 +-
 src/program/types/VariableDeclarator.js            |   46 +-
 src/program/types/index.js                         |    4 +-
 src/program/types/shared/LoopStatement.js          |   94 +-
 src/program/types/shared/ModuleDeclaration.js      |    7 +-
 src/program/wrap.js                                |   31 +-
 src/support.js                                     |   52 +-
 src/utils/CompileError.js                          |   21 +-
 src/utils/array.js                                 |   10 +-
 src/utils/deindent.js                              |   20 +-
 src/utils/destructure.js                           |  333 ++++--
 src/utils/getSnippet.js                            |   28 +-
 src/utils/isReference.js                           |   30 +-
 src/utils/locate.js                                |   12 +-
 src/utils/removeTrailingComma.js                   |   10 +
 src/utils/reserved.js                              |    7 +-
 src/utils/spread.js                                |   58 +-
 test/cli/compiles-directory/expected/bar.js        |    2 +-
 test/cli/compiles-directory/expected/foo.js        |    5 +-
 test/cli/compiles-directory/expected/foo.js.map    |    2 +-
 test/cli/compiles-directory/src/foo.js             |    2 +-
 .../creates-inline-sourcemap/expected/output.js    |    3 +-
 test/cli/creates-inline-sourcemap/input.js         |    2 +-
 test/cli/creates-sourcemap/expected/output.js      |    3 +-
 test/cli/creates-sourcemap/expected/output.js.map  |    2 +-
 test/cli/creates-sourcemap/input.js                |    2 +-
 .../supports-jsx-pragma-comment/expected/output.js |    2 +-
 test/cli/supports-jsx-pragma-comment/input.js      |    2 +-
 test/cli/supports-jsx-pragma/expected/output.js    |    2 +-
 test/cli/supports-jsx-pragma/input.js              |    2 +-
 test/cli/writes-to-stdout/expected/output.js       |    1 +
 test/samples/arrow-functions.js                    |   25 +-
 test/samples/async.js                              |   11 +
 test/samples/block-scoping.js                      |    2 +-
 .../classes-no-named-function-expressions.js       |   45 +-
 test/samples/classes.js                            |   48 +-
 test/samples/computed-properties.js                |   59 +-
 test/samples/destructuring.js                      |   50 +-
 test/samples/exponentiation-operator.js            |   35 +-
 test/samples/for-of.js                             |    4 +-
 test/samples/generators.js                         |   12 +-
 test/samples/jsx.js                                |   23 +-
 test/samples/loops.js                              |   67 +-
 test/samples/misc.js                               |   10 +-
 test/samples/modules.js                            |   39 +-
 ...ect-properties-no-named-function-expressions.js |   29 +-
 test/samples/object-properties.js                  |   19 +-
 test/samples/object-rest-spread.js                 |  122 +-
 test/samples/spread-operator.js                    |   45 +-
 test/samples/template-strings.js                   |   18 +-
 test/samples/trailing-function-commas.js           |   68 ++
 test/test.js                                       |  364 +++---
 test/utils/getLocation.js                          |   18 +-
 112 files changed, 3777 insertions(+), 2518 deletions(-)

diff --git a/.eslintrc b/.eslintrc
index ed8a063..04919df 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -5,7 +5,6 @@
         "semi": [ 2, "always" ],
         "keyword-spacing": [ 2, { "before": true, "after": true } ],
         "space-before-blocks": [ 2, "always" ],
-        "space-before-function-paren": [ 2, "always" ],
         "no-mixed-spaces-and-tabs": [ 2, "smart-tabs" ],
         "no-cond-assign": [ 0 ]
     },
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d1cb165..2377c3e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,33 @@
 # buble changelog
 
+## 0.18.0
+
+* Allow anonymous functions and classes as default exports ([#37](https://github.com/Rich-Harris/buble/issues/37))
+* Handle non-breaking spaces in JSX ([#46](https://github.com/Rich-Harris/buble/issues/46))
+* Allow anonymous classes to be assigned to properties ([#33](https://github.com/Rich-Harris/buble/issues/33))
+* Add `trailingFunctionCommas` transformation ([#50](https://github.com/Rich-Harris/buble/issues/50))
+
+## 0.17.3
+
+* Move `chalk` to dependencies ([#35](https://github.com/Rich-Harris/buble/issues/35))
+
+## 0.17.2
+
+* Fix nested template literals regression ([#39](https://github.com/Rich-Harris/buble/issues/39))
+
+## 0.17.1
+
+* Error on nested rest elements ([#31](https://github.com/Rich-Harris/buble/pull/31))
+* Allow destructuring with computed properties ([#34](https://github.com/Rich-Harris/buble/pull/34))
+
+## 0.17.0
+
+* Update dependencies
+* Transpile arrow functions unconditionally if spread arguments need transpilation ([#22](https://github.com/Rich-Harris/buble/pull/22))
+* Better object spread support ([#25](https://github.com/Rich-Harris/buble/pull/25))
+* Rest properties ([#30](https://github.com/Rich-Harris/buble/pull/30))
+* Fix ([#24](https://github.com/Rich-Harris/buble/pull/24))
+
 ## 0.16.0
 
 * Allow truthy dash-cased props ([#2](https://github.com/Rich-Harris/buble/pull/2))
diff --git a/bin/buble b/bin/buble
index 35643d9..95af636 100755
--- a/bin/buble
+++ b/bin/buble
@@ -1,7 +1,7 @@
 #!/usr/bin/env node
-var minimist = require( 'minimist' );
+var minimist = require('minimist');
 
-var command = minimist( process.argv.slice( 2 ), {
+var command = minimist(process.argv.slice(2), {
 	alias: {
 		// Short options
 		h: 'help',
@@ -15,14 +15,10 @@ var command = minimist( process.argv.slice( 2 ), {
 	}
 });
 
-if ( command.help || ( process.argv.length <= 2 && process.stdin.isTTY ) ) {
-	require( './showHelp' )();
-}
-
-else if ( command.version ) {
-	console.log( 'Bublé version ' + require( '../package.json' ).version );
-}
-
-else {
-	require( './runBuble' )( command );
+if (command.help || (process.argv.length <= 2 && process.stdin.isTTY)) {
+	require('./showHelp')();
+} else if (command.version) {
+	console.log('Bublé version ' + require('../package.json').version); // eslint-disable-line no-console
+} else {
+	require('./runBuble')(command);
 }
diff --git a/bin/handleError.js b/bin/handleError.js
index ab9b69f..4888adb 100644
--- a/bin/handleError.js
+++ b/bin/handleError.js
@@ -1,46 +1,58 @@
-var chalk = require( 'chalk' );
+var chalk = require('chalk');
+
+function print(msg) {
+	console.error(chalk.red(msg)); // eslint-disable-line no-console
+}
 
 var handlers = {
-	MISSING_INPUT_OPTION: function () {
-		console.error( chalk.red( 'You must specify an --input (-i) option' ) );
+	MISSING_INPUT_OPTION: () => {
+		print('You must specify an --input (-i) option');
 	},
 
-	MISSING_OUTPUT_DIR: function () {
-		console.error( chalk.red( 'You must specify an --output (-o) option when compiling a directory of files' ) );
+	MISSING_OUTPUT_DIR: () => {
+		print(
+			'You must specify an --output (-o) option when compiling a directory of files'
+		);
 	},
 
-	MISSING_OUTPUT_FILE: function () {
-		console.error( chalk.red( 'You must specify an --output (-o) option when creating a file with a sourcemap' ) );
+	MISSING_OUTPUT_FILE: () => {
+		print(
+			'You must specify an --output (-o) option when creating a file with a sourcemap'
+		);
 	},
 
-	ONE_AT_A_TIME: function ( err ) {
-		console.error( chalk.red( 'Bublé can only compile one file/directory at a time' ) );
+	ONE_AT_A_TIME: () => {
+		print('Bublé can only compile one file/directory at a time');
 	},
 
-	DUPLICATE_IMPORT_OPTIONS: function ( err ) {
-		console.error( chalk.red( 'use --input, or pass input path as argument – not both' ) );
+	DUPLICATE_IMPORT_OPTIONS: () => {
+		print('use --input, or pass input path as argument – not both');
 	},
 
-	BAD_TARGET: function ( err ) {
-		console.error( chalk.red( 'illegal --target option' ) );
+	BAD_TARGET: () => {
+		print('illegal --target option');
 	}
 };
 
-module.exports = function handleError ( err ) {
+module.exports = function handleError(err) {
 	var handler;
 
-	if ( handler = handlers[ err && err.code ] ) {
-		handler( err );
+	if ((handler = handlers[err && err.code])) {
+		handler(err);
 	} else {
-		if ( err.snippet ) console.error( chalk.red( '---\n' + err.snippet ) );
-		console.error( chalk.red( err.message || err ) );
+		if (err.snippet) print('---\n' + err.snippet);
+		print(err.message || err);
 
-		if ( err.stack ) {
-			console.error( chalk.grey( err.stack ) );
+		if (err.stack) {
+			console.error(chalk.grey(err.stack)); // eslint-disable-line no-console
 		}
 	}
 
-	console.error( 'Type ' + chalk.cyan( 'buble --help' ) + ' for help, or visit https://buble.surge.sh/guide/' );
+	console.error( // eslint-disable-line no-console
+		'Type ' +
+			chalk.cyan('buble --help') +
+			' for help, or visit https://buble.surge.sh/guide/'
+	);
 
-	process.exit( 1 );
+	process.exit(1);
 };
diff --git a/bin/runBuble.js b/bin/runBuble.js
index 0e57474..cb621e3 100644
--- a/bin/runBuble.js
+++ b/bin/runBuble.js
@@ -1,48 +1,49 @@
-var fs = require( 'fs' );
-var path = require( 'path' );
-var buble = require( '../dist/buble.deps.js' );
-var handleError = require( './handleError.js' );
+var fs = require('fs');
+var path = require('path');
+var buble = require('../dist/buble.deps.js');
+var handleError = require('./handleError.js');
 var EOL = require('os').EOL;
 
-function compile ( from, to, command, options ) {
+function compile(from, to, command, options) {
 	try {
-		var stats = fs.statSync( from );
-		if ( stats.isDirectory() ) {
-			compileDir( from, to, command, options );
+		var stats = fs.statSync(from);
+		if (stats.isDirectory()) {
+			compileDir(from, to, command, options);
 		} else {
-			compileFile( from, to, command, options );
+			compileFile(from, to, command, options);
 		}
-	} catch ( err ) {
-		handleError( err );
+	} catch (err) {
+		handleError(err);
 	}
 }
 
-function compileDir ( from, to, command, options ) {
-	if ( !command.output ) handleError({ code: 'MISSING_OUTPUT_DIR' });
+function compileDir(from, to, command, options) {
+	if (!command.output) handleError({ code: 'MISSING_OUTPUT_DIR' });
 
 	try {
-		fs.mkdirSync( to )
-	} catch ( e ) {
-		if ( e.code !== 'EEXIST' ) throw e
+		fs.mkdirSync(to);
+	} catch (e) {
+		if (e.code !== 'EEXIST') throw e;
 	}
 
-	fs.readdirSync( from ).forEach( function ( file ) {
-		compile( path.resolve( from, file ), path.resolve( to, file ), command, options );
+	fs.readdirSync(from).forEach(function(file) {
+		compile(path.resolve(from, file), path.resolve(to, file), command, options);
 	});
 }
 
-function compileFile ( from, to, command, options ) {
-	var ext = path.extname( from );
+function compileFile(from, to, command, options) {
+	var ext = path.extname(from);
 
-	if ( ext !== '.js' && ext !== '.jsm' && ext !== '.es6' && ext !== '.jsx') return;
+	if (ext !== '.js' && ext !== '.jsm' && ext !== '.es6' && ext !== '.jsx')
+		return;
 
-	if ( to ) {
-		var extTo = path.extname( to );
-		to = to.slice( 0, -extTo.length ) + '.js';
+	if (to) {
+		var extTo = path.extname(to);
+		to = to.slice(0, -extTo.length) + '.js';
 	}
 
-	var source = fs.readFileSync( from, 'utf-8' );
-	var result = buble.transform( source, {
+	var source = fs.readFileSync(from, 'utf-8');
+	var result = buble.transform(source, {
 		target: options.target,
 		transforms: options.transforms,
 		source: from,
@@ -52,35 +53,35 @@ function compileFile ( from, to, command, options ) {
 		namedFunctionExpressions: options.namedFunctionExpressions
 	});
 
-	write( result, to, command );
+	write(result, to, command);
 }
 
-function write ( result, to, command ) {
-	if ( command.sourcemap === 'inline' ) {
+function write(result, to, command) {
+	if (command.sourcemap === 'inline') {
 		result.code += EOL + '//# sourceMappingURL=' + result.map.toUrl();
-	} else if ( command.sourcemap ) {
-		if ( !to ) {
+	} else if (command.sourcemap) {
+		if (!to) {
 			handleError({ code: 'MISSING_OUTPUT_FILE' });
 		}
 
-		result.code += EOL + '//# sourceMappingURL=' + path.basename( to ) + '.map';
-		fs.writeFileSync( to + '.map', result.map.toString() );
+		result.code += EOL + '//# sourceMappingURL=' + path.basename(to) + '.map';
+		fs.writeFileSync(to + '.map', result.map.toString());
 	}
 
-	if ( to ) {
-		fs.writeFileSync( to, result.code );
+	if (to) {
+		fs.writeFileSync(to, result.code);
 	} else {
-		console.log( result.code ); // eslint-disable-line no-console
+		console.log(result.code); // eslint-disable-line no-console
 	}
 }
 
-module.exports = function ( command ) {
-	if ( command._.length > 1 ) {
+module.exports = function(command) {
+	if (command._.length > 1) {
 		handleError({ code: 'ONE_AT_A_TIME' });
 	}
 
-	if ( command._.length === 1 ) {
-		if ( command.input ) {
+	if (command._.length === 1) {
+		if (command.input) {
 			handleError({ code: 'DUPLICATE_IMPORT_OPTIONS' });
 		}
 
@@ -91,58 +92,58 @@ module.exports = function ( command ) {
 		target: {},
 		transforms: {},
 		jsx: command.jsx,
-		objectAssign: command.objectAssign === true ? "Object.assign" : command.objectAssign,
-		namedFunctionExpressions: command["named-function-expr"] !== false
+		objectAssign:
+			command.objectAssign === true ? 'Object.assign' : command.objectAssign,
+		namedFunctionExpressions: command['named-function-expr'] !== false
 	};
 
-	if ( command.target ) {
-		if ( !/^(?:(\w+):([\d\.]+),)*(\w+):([\d\.]+)$/.test( command.target ) ) {
+	if (command.target) {
+		if (!/^(?:(\w+):([\d\.]+),)*(\w+):([\d\.]+)$/.test(command.target)) {
 			handleError({ code: 'BAD_TARGET' });
 		}
 
-		command.target.split( ',' )
-			.map( function ( target ) {
-				return target.split( ':' );
+		command.target
+			.split(',')
+			.map(function(target) {
+				return target.split(':');
 			})
-			.forEach( function ( pair ) {
-				options.target[ pair[0] ] = pair[1];
+			.forEach(function(pair) {
+				options.target[pair[0]] = pair[1];
 			});
 	}
 
-	if ( command.yes ) {
-		command.yes.split( ',' ).forEach( function ( transform ) {
-			options.transforms[ transform ] = true;
+	if (command.yes) {
+		command.yes.split(',').forEach(function(transform) {
+			options.transforms[transform] = true;
 		});
 	}
 
-	if ( command.no ) {
-		command.no.split( ',' ).forEach( function ( transform ) {
-			options.transforms[ transform ] = false;
+	if (command.no) {
+		command.no.split(',').forEach(function(transform) {
+			options.transforms[transform] = false;
 		});
 	}
 
-	if ( command.input ) {
-		compile( command.input, command.output, command, options );
-	}
-
-	else {
+	if (command.input) {
+		compile(command.input, command.output, command, options);
+	} else {
 		process.stdin.resume();
-		process.stdin.setEncoding( 'utf8' );
+		process.stdin.setEncoding('utf8');
 
 		var source = '';
 
-		process.stdin.on( 'data', function ( chunk ) {
+		process.stdin.on('data', function(chunk) {
 			source += chunk;
 		});
 
-		process.stdin.on( 'end', function () {
-			options.source = command.input = "stdin";
+		process.stdin.on('end', function() {
+			options.source = command.input = 'stdin';
 			options.file = command.output;
 			try {
-				var result = buble.transform( source, options );
-				write( result, command.output, command );
-			} catch ( err ) {
-				handleError( err );
+				var result = buble.transform(source, options);
+				write(result, command.output, command);
+			} catch (err) {
+				handleError(err);
 			}
 		});
 	}
diff --git a/bin/showHelp.js b/bin/showHelp.js
index 1cc19b6..770c566 100644
--- a/bin/showHelp.js
+++ b/bin/showHelp.js
@@ -1,13 +1,16 @@
-var fs = require( 'fs' );
-var path = require( 'path' );
+var fs = require('fs');
+var path = require('path');
 
-module.exports = function () {
-	fs.readFile( path.join( __dirname, 'help.md' ), function ( err, result ) {
+module.exports = function() {
+	fs.readFile(path.join(__dirname, 'help.md'), (err, result) => {
 		var help;
 
-		if ( err ) throw err;
+		if (err) throw err;
 
-		help = result.toString().replace( '<%= version %>', require( '../package.json' ).version );
-		console.log( '\n' + help + '\n' );
+		help = result
+			.toString()
+			.replace('<%= version %>', require('../package.json').version);
+
+		console.log('\n' + help + '\n'); // eslint-disable-line no-console
 	});
 };
diff --git a/package-lock.json b/package-lock.json
index 97c2a30..43849da 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,13 +1,13 @@
 {
   "name": "buble",
-  "version": "0.15.2",
+  "version": "0.17.2",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
     "acorn": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
-      "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo="
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.2.1.tgz",
+      "integrity": "sha512-jG0u7c4Ly+3QkkW18V+NRDN+4bWHdln30NL1ZL2AvFZZmQe/BfopYCtghCKKVBUSetZ4QKcyA0pY6/4Gw8Pv8w=="
     },
     "acorn-jsx": {
       "version": "3.0.1",
@@ -15,47 +15,60 @@
       "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=",
       "requires": {
         "acorn": "3.3.0"
+      },
+      "dependencies": {
+        "acorn": {
+          "version": "3.3.0",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
+          "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo="
+        }
       }
     },
-    "acorn-object-spread": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/acorn-object-spread/-/acorn-object-spread-1.0.0.tgz",
-      "integrity": "sha1-SOrQ9KjrFplaF6Dbn/xqyq2kumg=",
+    "acorn5-object-spread": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/acorn5-object-spread/-/acorn5-object-spread-4.0.0.tgz",
+      "integrity": "sha1-1XWAge7ZcSGrC+R+Mcqu8qo5lpc=",
       "requires": {
-        "acorn": "3.3.0"
+        "acorn": "5.2.1"
       }
     },
     "ajv": {
-      "version": "4.11.8",
-      "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz",
-      "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=",
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.3.0.tgz",
+      "integrity": "sha1-RBT/dKUIecII7l/cgm4ywwNUnto=",
       "dev": true,
       "requires": {
         "co": "4.6.0",
-        "json-stable-stringify": "1.0.1"
+        "fast-deep-equal": "1.0.0",
+        "fast-json-stable-stringify": "2.0.0",
+        "json-schema-traverse": "0.3.1"
       }
     },
     "ajv-keywords": {
-      "version": "1.5.1",
-      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz",
-      "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=",
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz",
+      "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=",
       "dev": true
     },
     "ansi-escapes": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz",
-      "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.0.0.tgz",
+      "integrity": "sha512-O/klc27mWNUigtv0F8NJWbLF00OcegQalkqKURWdosW08YZKi4m6CnSUSvIZG1otNJbTWhN01Hhz389DW7mvDQ==",
       "dev": true
     },
     "ansi-regex": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+      "dev": true
     },
     "ansi-styles": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-      "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz",
+      "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
+      "requires": {
+        "color-convert": "1.9.1"
+      }
     },
     "argparse": {
       "version": "1.0.9",
@@ -108,6 +121,44 @@
       "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
       "dev": true
     },
+    "babel-code-frame": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
+      "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
+      "dev": true,
+      "requires": {
+        "chalk": "1.1.3",
+        "esutils": "2.0.2",
+        "js-tokens": "3.0.2"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "2.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+          "dev": true
+        },
+        "chalk": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "2.2.1",
+            "escape-string-regexp": "1.0.5",
+            "has-ansi": "2.0.0",
+            "strip-ansi": "3.0.1",
+            "supports-color": "2.0.0"
+          }
+        },
+        "supports-color": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+          "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+          "dev": true
+        }
+      }
+    },
     "balanced-match": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
@@ -152,6 +203,12 @@
         }
       }
     },
+    "browser-stdout": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz",
+      "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=",
+      "dev": true
+    },
     "buble": {
       "version": "0.8.2",
       "resolved": "https://registry.npmjs.org/buble/-/buble-0.8.2.tgz",
@@ -164,14 +221,45 @@
         "minimist": "1.2.0"
       },
       "dependencies": {
+        "acorn": {
+          "version": "3.3.0",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
+          "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=",
+          "dev": true
+        },
+        "ansi-styles": {
+          "version": "2.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+          "dev": true
+        },
+        "chalk": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "2.2.1",
+            "escape-string-regexp": "1.0.5",
+            "has-ansi": "2.0.0",
+            "strip-ansi": "3.0.1",
+            "supports-color": "2.0.0"
+          }
+        },
         "magic-string": {
           "version": "0.13.1",
           "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.13.1.tgz",
           "integrity": "sha1-QEuYyVFhmndKLJJXDA07HmZ3nj4=",
           "dev": true,
           "requires": {
-            "vlq": "0.2.2"
+            "vlq": "0.2.3"
           }
+        },
+        "supports-color": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+          "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+          "dev": true
         }
       }
     },
@@ -197,15 +285,13 @@
       "dev": true
     },
     "chalk": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-      "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz",
+      "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==",
       "requires": {
-        "ansi-styles": "2.2.1",
+        "ansi-styles": "3.2.0",
         "escape-string-regexp": "1.0.5",
-        "has-ansi": "2.0.0",
-        "strip-ansi": "3.0.1",
-        "supports-color": "2.0.0"
+        "supports-color": "4.5.0"
       }
     },
     "circular-json": {
@@ -215,12 +301,12 @@
       "dev": true
     },
     "cli-cursor": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz",
-      "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
+      "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
       "dev": true,
       "requires": {
-        "restore-cursor": "1.0.1"
+        "restore-cursor": "2.0.0"
       }
     },
     "cli-width": {
@@ -235,16 +321,23 @@
       "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
       "dev": true
     },
-    "code-point-at": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
-      "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
-      "dev": true
+    "color-convert": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
+      "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
+      "requires": {
+        "color-name": "1.1.3"
+      }
+    },
+    "color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
     },
     "commander": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-2.3.0.tgz",
-      "integrity": "sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM=",
+      "version": "2.11.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz",
+      "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==",
       "dev": true
     },
     "concat-map": {
@@ -265,9 +358,9 @@
       }
     },
     "console-group": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/console-group/-/console-group-0.2.1.tgz",
-      "integrity": "sha1-zK3NMVN51OIqqxk1V1tafWWh4a4=",
+      "version": "0.3.3",
+      "resolved": "https://registry.npmjs.org/console-group/-/console-group-0.3.3.tgz",
+      "integrity": "sha1-bY62uda3V6KJUoT2LQnHrUPyu84=",
       "dev": true
     },
     "core-util-is": {
@@ -276,19 +369,21 @@
       "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
       "dev": true
     },
-    "d": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz",
-      "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=",
+    "cross-spawn": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
+      "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
       "dev": true,
       "requires": {
-        "es5-ext": "0.10.30"
+        "lru-cache": "4.1.1",
+        "shebang-command": "1.2.0",
+        "which": "1.3.0"
       }
     },
     "debug": {
-      "version": "2.6.9",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+      "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
       "dev": true,
       "requires": {
         "ms": "2.0.0"
@@ -316,165 +411,106 @@
       }
     },
     "diff": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz",
-      "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=",
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz",
+      "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==",
       "dev": true
     },
     "doctrine": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
-      "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz",
+      "integrity": "sha1-xz2NKQnSIpHhoAejlYBNqLZl/mM=",
       "dev": true,
       "requires": {
         "esutils": "2.0.2",
         "isarray": "1.0.0"
       }
     },
-    "es5-ext": {
-      "version": "0.10.30",
-      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.30.tgz",
-      "integrity": "sha1-cUGhaDZpfbq/qq7uQUlc4p9SyTk=",
-      "dev": true,
-      "requires": {
-        "es6-iterator": "2.0.1",
-        "es6-symbol": "3.1.1"
-      }
-    },
-    "es6-iterator": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.1.tgz",
-      "integrity": "sha1-jjGcnwRTv1ddN0lAplWSDlnKVRI=",
-      "dev": true,
-      "requires": {
-        "d": "1.0.0",
-        "es5-ext": "0.10.30",
-        "es6-symbol": "3.1.1"
-      }
-    },
-    "es6-map": {
-      "version": "0.1.5",
-      "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz",
-      "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=",
-      "dev": true,
-      "requires": {
-        "d": "1.0.0",
-        "es5-ext": "0.10.30",
-        "es6-iterator": "2.0.1",
-        "es6-set": "0.1.5",
-        "es6-symbol": "3.1.1",
-        "event-emitter": "0.3.5"
-      }
-    },
-    "es6-set": {
-      "version": "0.1.5",
-      "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz",
-      "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=",
-      "dev": true,
-      "requires": {
-        "d": "1.0.0",
-        "es5-ext": "0.10.30",
-        "es6-iterator": "2.0.1",
-        "es6-symbol": "3.1.1",
-        "event-emitter": "0.3.5"
-      }
-    },
-    "es6-symbol": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz",
-      "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=",
-      "dev": true,
-      "requires": {
-        "d": "1.0.0",
-        "es5-ext": "0.10.30"
-      }
-    },
-    "es6-weak-map": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz",
-      "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=",
-      "dev": true,
-      "requires": {
-        "d": "1.0.0",
-        "es5-ext": "0.10.30",
-        "es6-iterator": "2.0.1",
-        "es6-symbol": "3.1.1"
-      }
-    },
     "escape-string-regexp": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
       "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
     },
-    "escope": {
-      "version": "3.6.0",
-      "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz",
-      "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=",
-      "dev": true,
-      "requires": {
-        "es6-map": "0.1.5",
-        "es6-weak-map": "2.0.2",
-        "esrecurse": "4.2.0",
-        "estraverse": "4.2.0"
-      }
-    },
     "eslint": {
-      "version": "2.13.1",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-2.13.1.tgz",
-      "integrity": "sha1-5MyPoPAJ+4KaquI4VaKTYL4fbBE=",
+      "version": "4.11.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.11.0.tgz",
+      "integrity": "sha512-UWbhQpaKlm8h5x/VLwm0S1kheMrDj8jPwhnBMjr/Dlo3qqT7MvcN/UfKAR3E1N4lr4YNtOvS4m3hwsrVc/ky7g==",
       "dev": true,
       "requires": {
-        "chalk": "1.1.3",
+        "ajv": "5.3.0",
+        "babel-code-frame": "6.26.0",
+        "chalk": "2.3.0",
         "concat-stream": "1.6.0",
-        "debug": "2.6.9",
-        "doctrine": "1.5.0",
-        "es6-map": "0.1.5",
-        "escope": "3.6.0",
-        "espree": "3.5.1",
+        "cross-spawn": "5.1.0",
+        "debug": "3.1.0",
+        "doctrine": "2.0.0",
+        "eslint-scope": "3.7.1",
+        "espree": "3.5.2",
+        "esquery": "1.0.0",
         "estraverse": "4.2.0",
         "esutils": "2.0.2",
-        "file-entry-cache": "1.3.1",
+        "file-entry-cache": "2.0.0",
+        "functional-red-black-tree": "1.0.1",
         "glob": "7.1.2",
         "globals": "9.18.0",
-        "ignore": "3.3.5",
+        "ignore": "3.3.7",
         "imurmurhash": "0.1.4",
-        "inquirer": "0.12.0",
-        "is-my-json-valid": "2.16.1",
+        "inquirer": "3.3.0",
         "is-resolvable": "1.0.0",
         "js-yaml": "3.10.0",
-        "json-stable-stringify": "1.0.1",
+        "json-stable-stringify-without-jsonify": "1.0.1",
         "levn": "0.3.0",
         "lodash": "4.17.4",
+        "minimatch": "3.0.4",
         "mkdirp": "0.5.1",
+        "natural-compare": "1.4.0",
         "optionator": "0.8.2",
-        "path-is-absolute": "1.0.1",
         "path-is-inside": "1.0.2",
-        "pluralize": "1.2.1",
-        "progress": "1.1.8",
+        "pluralize": "7.0.0",
+        "progress": "2.0.0",
         "require-uncached": "1.0.3",
-        "shelljs": "0.6.1",
-        "strip-json-comments": "1.0.4",
-        "table": "3.8.3",
-        "text-table": "0.2.0",
-        "user-home": "2.0.0"
+        "semver": "5.4.1",
+        "strip-ansi": "4.0.0",
+        "strip-json-comments": "2.0.1",
+        "table": "4.0.2",
+        "text-table": "0.2.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+          "dev": true
+        },
+        "strip-ansi": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "3.0.0"
+          }
+        }
+      }
+    },
+    "eslint-scope": {
+      "version": "3.7.1",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz",
+      "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=",
+      "dev": true,
+      "requires": {
+        "esrecurse": "4.2.0",
+        "estraverse": "4.2.0"
       }
     },
     "espree": {
-      "version": "3.5.1",
-      "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.1.tgz",
-      "integrity": "sha1-DJiLirRttTEAoZVK5LqZXd0n2H4=",
+      "version": "3.5.2",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.2.tgz",
+      "integrity": "sha512-sadKeYwaR/aJ3stC2CdvgXu1T16TdYN+qwCpcWbMnGJ8s0zNWemzrvb2GbD4OhmJ/fwpJjudThAlLobGbWZbCQ==",
       "dev": true,
       "requires": {
-        "acorn": "5.1.2",
+        "acorn": "5.2.1",
         "acorn-jsx": "3.0.1"
-      },
-      "dependencies": {
-        "acorn": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.1.2.tgz",
-          "integrity": "sha512-o96FZLJBPY1lvTuJylGA9Bk3t/GKPPJG8H0ydQQl01crzwJgspa4AEIq/pVTXigmK0PHVQhiAtn8WMBLL9D2WA==",
-          "dev": true
-        }
       }
     },
     "esprima": {
@@ -483,6 +519,15 @@
       "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==",
       "dev": true
     },
+    "esquery": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz",
+      "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=",
+      "dev": true,
+      "requires": {
+        "estraverse": "4.2.0"
+      }
+    },
     "esrecurse": {
       "version": "4.2.0",
       "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz",
@@ -511,22 +556,6 @@
       "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
       "dev": true
     },
-    "event-emitter": {
-      "version": "0.3.5",
-      "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
-      "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=",
-      "dev": true,
-      "requires": {
-        "d": "1.0.0",
-        "es5-ext": "0.10.30"
-      }
-    },
-    "exit-hook": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz",
-      "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=",
-      "dev": true
-    },
     "expand-brackets": {
       "version": "0.1.5",
       "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
@@ -545,6 +574,17 @@
         "fill-range": "2.2.3"
       }
     },
+    "external-editor": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.0.5.tgz",
+      "integrity": "sha512-Msjo64WT5W+NhOpQXh0nOHm+n0RfU1QUwDnKYvJ8dEJ8zlwLrqXNTv5mSUTJpepf41PDJGyhueTw2vNZW+Fr/w==",
+      "dev": true,
+      "requires": {
+        "iconv-lite": "0.4.19",
+        "jschardet": "1.6.0",
+        "tmp": "0.0.33"
+      }
+    },
     "extglob": {
       "version": "0.3.2",
       "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
@@ -554,6 +594,18 @@
         "is-extglob": "1.0.0"
       }
     },
+    "fast-deep-equal": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz",
+      "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=",
+      "dev": true
+    },
+    "fast-json-stable-stringify": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
+      "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
+      "dev": true
+    },
     "fast-levenshtein": {
       "version": "2.0.6",
       "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
@@ -561,22 +613,21 @@
       "dev": true
     },
     "figures": {
-      "version": "1.7.0",
-      "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
-      "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=",
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
+      "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
       "dev": true,
       "requires": {
-        "escape-string-regexp": "1.0.5",
-        "object-assign": "4.1.1"
+        "escape-string-regexp": "1.0.5"
       }
     },
     "file-entry-cache": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-1.3.1.tgz",
-      "integrity": "sha1-RMYepgeuS+nBQC9B9EJwy/4zT/g=",
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz",
+      "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=",
       "dev": true,
       "requires": {
-        "flat-cache": "1.2.2",
+        "flat-cache": "1.3.0",
         "object-assign": "4.1.1"
       }
     },
@@ -600,9 +651,9 @@
       }
     },
     "flat-cache": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.2.tgz",
-      "integrity": "sha1-+oZxTnLCHbiGAXYezy9VXRq8a5Y=",
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz",
+      "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=",
       "dev": true,
       "requires": {
         "circular-json": "0.3.3",
@@ -632,21 +683,12 @@
       "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
       "dev": true
     },
-    "generate-function": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz",
-      "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=",
+    "functional-red-black-tree": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+      "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
       "dev": true
     },
-    "generate-object-property": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz",
-      "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=",
-      "dev": true,
-      "requires": {
-        "is-property": "1.0.2"
-      }
-    },
     "glob": {
       "version": "7.1.2",
       "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
@@ -707,23 +749,41 @@
       "dev": true
     },
     "growl": {
-      "version": "1.9.2",
-      "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz",
-      "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=",
+      "version": "1.10.3",
+      "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz",
+      "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==",
       "dev": true
     },
     "has-ansi": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
       "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+      "dev": true,
       "requires": {
         "ansi-regex": "2.1.1"
       }
     },
+    "has-flag": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
+      "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE="
+    },
+    "he": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
+      "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
+      "dev": true
+    },
+    "iconv-lite": {
+      "version": "0.4.19",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
+      "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==",
+      "dev": true
+    },
     "ignore": {
-      "version": "3.3.5",
-      "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.5.tgz",
-      "integrity": "sha512-JLH93mL8amZQhh/p6mfQgVBH3M6epNq3DfsXsTSuSrInVjwyYlFE1nv2AgfRCC8PoOhM0jwQ5v8s9LgbK7yGDw==",
+      "version": "3.3.7",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.7.tgz",
+      "integrity": "sha512-YGG3ejvBNHRqu0559EOxxNFihD0AjpvHlC/pdGKd3X3ofe+CoJkYazwNJYTNebqpPKN+VVQbh4ZFn1DivMNuHA==",
       "dev": true
     },
     "imurmurhash": {
@@ -749,30 +809,48 @@
       "dev": true
     },
     "inquirer": {
-      "version": "0.12.0",
-      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz",
-      "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=",
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz",
+      "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==",
       "dev": true,
       "requires": {
-        "ansi-escapes": "1.4.0",
-        "ansi-regex": "2.1.1",
-        "chalk": "1.1.3",
-        "cli-cursor": "1.0.2",
+        "ansi-escapes": "3.0.0",
+        "chalk": "2.3.0",
+        "cli-cursor": "2.1.0",
         "cli-width": "2.2.0",
-        "figures": "1.7.0",
+        "external-editor": "2.0.5",
+        "figures": "2.0.0",
         "lodash": "4.17.4",
-        "readline2": "1.0.1",
-        "run-async": "0.1.0",
-        "rx-lite": "3.1.2",
-        "string-width": "1.0.2",
-        "strip-ansi": "3.0.1",
+        "mute-stream": "0.0.7",
+        "run-async": "2.3.0",
+        "rx-lite": "4.0.8",
+        "rx-lite-aggregates": "4.0.8",
+        "string-width": "2.1.1",
+        "strip-ansi": "4.0.0",
         "through": "2.3.8"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+          "dev": true
+        },
+        "strip-ansi": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "3.0.0"
+          }
+        }
       }
     },
     "is-buffer": {
-      "version": "1.1.5",
-      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz",
-      "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=",
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+      "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
       "dev": true
     },
     "is-dotfile": {
@@ -803,13 +881,10 @@
       "dev": true
     },
     "is-fullwidth-code-point": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
-      "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
-      "dev": true,
-      "requires": {
-        "number-is-nan": "1.0.1"
-      }
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+      "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+      "dev": true
     },
     "is-glob": {
       "version": "2.0.1",
@@ -820,17 +895,11 @@
         "is-extglob": "1.0.0"
       }
     },
-    "is-my-json-valid": {
-      "version": "2.16.1",
-      "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz",
-      "integrity": "sha512-ochPsqWS1WXj8ZnMIV0vnNXooaMhp7cyL4FMSIPKTtnV0Ha/T19G2b9kkhcNsabV9bxYkze7/aLZJb/bYuFduQ==",
-      "dev": true,
-      "requires": {
-        "generate-function": "2.0.0",
-        "generate-object-property": "1.2.0",
-        "jsonpointer": "4.0.1",
-        "xtend": "4.0.1"
-      }
+    "is-module": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
+      "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=",
+      "dev": true
     },
     "is-number": {
       "version": "2.1.0",
@@ -877,10 +946,10 @@
       "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=",
       "dev": true
     },
-    "is-property": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
-      "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=",
+    "is-promise": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
+      "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
       "dev": true
     },
     "is-resolvable": {
@@ -898,6 +967,12 @@
       "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
       "dev": true
     },
+    "isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+      "dev": true
+    },
     "isobject": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
@@ -907,29 +982,11 @@
         "isarray": "1.0.0"
       }
     },
-    "jade": {
-      "version": "0.26.3",
-      "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz",
-      "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=",
-      "dev": true,
-      "requires": {
-        "commander": "0.6.1",
-        "mkdirp": "0.3.0"
-      },
-      "dependencies": {
-        "commander": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz",
-          "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=",
-          "dev": true
-        },
-        "mkdirp": {
-          "version": "0.3.0",
-          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz",
-          "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=",
-          "dev": true
-        }
-      }
+    "js-tokens": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
+      "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
+      "dev": true
     },
     "js-yaml": {
       "version": "3.10.0",
@@ -941,31 +998,28 @@
         "esprima": "4.0.0"
       }
     },
+    "jschardet": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/jschardet/-/jschardet-1.6.0.tgz",
+      "integrity": "sha512-xYuhvQ7I9PDJIGBWev9xm0+SMSed3ZDBAmvVjbFR1ZRLAF+vlXcQu6cRI9uAlj81rzikElRVteehwV7DuX2ZmQ==",
+      "dev": true
+    },
     "jsesc": {
       "version": "0.5.0",
       "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
       "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
       "dev": true
     },
-    "json-stable-stringify": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
-      "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=",
-      "dev": true,
-      "requires": {
-        "jsonify": "0.0.0"
-      }
-    },
-    "jsonify": {
-      "version": "0.0.0",
-      "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
-      "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
+    "json-schema-traverse": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
+      "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=",
       "dev": true
     },
-    "jsonpointer": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz",
-      "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=",
+    "json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+      "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
       "dev": true
     },
     "kind-of": {
@@ -974,7 +1028,7 @@
       "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
       "dev": true,
       "requires": {
-        "is-buffer": "1.1.5"
+        "is-buffer": "1.1.6"
       }
     },
     "levn": {
@@ -994,17 +1048,21 @@
       "dev": true
     },
     "lru-cache": {
-      "version": "2.7.3",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz",
-      "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=",
-      "dev": true
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz",
+      "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==",
+      "dev": true,
+      "requires": {
+        "pseudomap": "1.0.2",
+        "yallist": "2.1.2"
+      }
     },
     "magic-string": {
-      "version": "0.14.0",
-      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.14.0.tgz",
-      "integrity": "sha1-VyJK7xcByu7Sc7F6OalW5ysXJGI=",
+      "version": "0.22.4",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.4.tgz",
+      "integrity": "sha512-kxBL06p6iO2qPBHsqGK2b3cRwiRGpnmSuVWNhwHcMX7qJOUr1HvricYP1LZOCdkQBUp0jiWg2d6WJwR3vYgByw==",
       "requires": {
-        "vlq": "0.2.2"
+        "vlq": "0.2.3"
       }
     },
     "micromatch": {
@@ -1028,6 +1086,12 @@
         "regex-cache": "0.4.4"
       }
     },
+    "mimic-fn": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.1.0.tgz",
+      "integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg=",
+      "dev": true
+    },
     "minimatch": {
       "version": "3.0.4",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
@@ -1060,69 +1124,31 @@
       }
     },
     "mocha": {
-      "version": "2.5.3",
-      "resolved": "https://registry.npmjs.org/mocha/-/mocha-2.5.3.tgz",
-      "integrity": "sha1-FhvlvetJZ3HrmzV0UFC2IrWu/Fg=",
-      "dev": true,
-      "requires": {
-        "commander": "2.3.0",
-        "debug": "2.2.0",
-        "diff": "1.4.0",
-        "escape-string-regexp": "1.0.2",
-        "glob": "3.2.11",
-        "growl": "1.9.2",
-        "jade": "0.26.3",
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.0.1.tgz",
+      "integrity": "sha512-evDmhkoA+cBNiQQQdSKZa2b9+W2mpLoj50367lhy+Klnx9OV8XlCIhigUnn1gaTFLQCa0kdNhEGDr0hCXOQFDw==",
+      "dev": true,
+      "requires": {
+        "browser-stdout": "1.3.0",
+        "commander": "2.11.0",
+        "debug": "3.1.0",
+        "diff": "3.3.1",
+        "escape-string-regexp": "1.0.5",
+        "glob": "7.1.2",
+        "growl": "1.10.3",
+        "he": "1.1.1",
         "mkdirp": "0.5.1",
-        "supports-color": "1.2.0",
-        "to-iso-string": "0.0.2"
+        "supports-color": "4.4.0"
       },
       "dependencies": {
-        "debug": {
-          "version": "2.2.0",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
-          "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
-          "dev": true,
-          "requires": {
-            "ms": "0.7.1"
-          }
-        },
-        "escape-string-regexp": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz",
-          "integrity": "sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE=",
-          "dev": true
-        },
-        "glob": {
-          "version": "3.2.11",
-          "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
-          "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=",
-          "dev": true,
-          "requires": {
-            "inherits": "2.0.3",
-            "minimatch": "0.3.0"
-          }
-        },
-        "minimatch": {
-          "version": "0.3.0",
-          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
-          "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=",
+        "supports-color": {
+          "version": "4.4.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz",
+          "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==",
           "dev": true,
           "requires": {
-            "lru-cache": "2.7.3",
-            "sigmund": "1.0.1"
+            "has-flag": "2.0.0"
           }
-        },
-        "ms": {
-          "version": "0.7.1",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
-          "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-1.2.0.tgz",
-          "integrity": "sha1-/x7R5hFp0Gs88tWI4YixjYhH4X4=",
-          "dev": true
         }
       }
     },
@@ -1133,9 +1159,15 @@
       "dev": true
     },
     "mute-stream": {
-      "version": "0.0.5",
-      "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz",
-      "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=",
+      "version": "0.0.7",
+      "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
+      "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
+      "dev": true
+    },
+    "natural-compare": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+      "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
       "dev": true
     },
     "normalize-path": {
@@ -1147,12 +1179,6 @@
         "remove-trailing-separator": "1.1.0"
       }
     },
-    "number-is-nan": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
-      "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
-      "dev": true
-    },
     "object-assign": {
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -1179,10 +1205,13 @@
       }
     },
     "onetime": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
-      "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=",
-      "dev": true
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
+      "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
+      "dev": true,
+      "requires": {
+        "mimic-fn": "1.1.0"
+      }
     },
     "optionator": {
       "version": "0.8.2",
@@ -1203,6 +1232,12 @@
       "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
       "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
     },
+    "os-tmpdir": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+      "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+      "dev": true
+    },
     "parse-glob": {
       "version": "3.0.4",
       "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz",
@@ -1255,9 +1290,9 @@
       }
     },
     "pluralize": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz",
-      "integrity": "sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU=",
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz",
+      "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==",
       "dev": true
     },
     "prelude-ls": {
@@ -1279,9 +1314,15 @@
       "dev": true
     },
     "progress": {
-      "version": "1.1.8",
-      "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz",
-      "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=",
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz",
+      "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=",
+      "dev": true
+    },
+    "pseudomap": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+      "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
       "dev": true
     },
     "randomatic": {
@@ -1309,7 +1350,7 @@
               "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
               "dev": true,
               "requires": {
-                "is-buffer": "1.1.5"
+                "is-buffer": "1.1.6"
               }
             }
           }
@@ -1320,7 +1361,7 @@
           "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
           "dev": true,
           "requires": {
-            "is-buffer": "1.1.5"
+            "is-buffer": "1.1.6"
           }
         }
       }
@@ -1340,23 +1381,21 @@
         "util-deprecate": "1.0.2"
       }
     },
-    "readline2": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz",
-      "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=",
-      "dev": true,
-      "requires": {
-        "code-point-at": "1.1.0",
-        "is-fullwidth-code-point": "1.0.0",
-        "mute-stream": "0.0.5"
-      }
-    },
     "regenerate": {
       "version": "1.3.3",
       "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz",
       "integrity": "sha512-jVpo1GadrDAK59t/0jRx5VxYWQEDkkEKi6+HjE3joFVLfDOh9Xrdh0dF1eSq+BI/SwvTQ44gSscJ8N5zYL61sg==",
       "dev": true
     },
+    "regenerate-unicode-properties": {
+      "version": "5.1.3",
+      "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-5.1.3.tgz",
+      "integrity": "sha512-Yjy6t7jFQczDhYE+WVm7pg6gWYE258q4sUkk9qDErwXJIqx7jU9jGrMFHutJK/SRfcg7MEkXjGaYiVlOZyev/A==",
+      "dev": true,
+      "requires": {
+        "regenerate": "1.3.3"
+      }
+    },
     "regex-cache": {
       "version": "0.4.4",
       "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz",
@@ -1367,26 +1406,29 @@
       }
     },
     "regexpu-core": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz",
-      "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=",
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.1.3.tgz",
+      "integrity": "sha512-mB+njEzO7oezA57IbQxxd6fVPOeWKDmnGvJ485CwmfNchjHe5jWwqKepapmzUEj41yxIAqOg+C4LbXuJlkiO8A==",
       "dev": true,
       "requires": {
         "regenerate": "1.3.3",
-        "regjsgen": "0.2.0",
-        "regjsparser": "0.1.5"
+        "regenerate-unicode-properties": "5.1.3",
+        "regjsgen": "0.3.0",
+        "regjsparser": "0.2.1",
+        "unicode-match-property-ecmascript": "1.0.3",
+        "unicode-match-property-value-ecmascript": "1.0.1"
       }
     },
     "regjsgen": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz",
-      "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=",
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.3.0.tgz",
+      "integrity": "sha1-DuSj6SdkMM2iXx54nqbBW4ewy0M=",
       "dev": true
     },
     "regjsparser": {
-      "version": "0.1.5",
-      "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz",
-      "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=",
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.2.1.tgz",
+      "integrity": "sha1-w3h1U/rwTndcMCEC7zRtmVAA7Bw=",
       "dev": true,
       "requires": {
         "jsesc": "0.5.0"
@@ -1421,9 +1463,9 @@
       }
     },
     "resolve": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz",
-      "integrity": "sha512-aW7sVKPufyHqOmyyLzg/J+8606v5nevBgaliIlV7nUpVMsDnoBGV/cbSLNjZAg9q0Cfd/+easKVKQ8vOu8fn1Q==",
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz",
+      "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==",
       "dev": true,
       "requires": {
         "path-parse": "1.0.5"
@@ -1436,13 +1478,13 @@
       "dev": true
     },
     "restore-cursor": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz",
-      "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=",
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
+      "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
       "dev": true,
       "requires": {
-        "exit-hook": "1.1.1",
-        "onetime": "1.1.0"
+        "onetime": "2.0.1",
+        "signal-exit": "3.0.2"
       }
     },
     "rimraf": {
@@ -1455,9 +1497,9 @@
       }
     },
     "rollup": {
-      "version": "0.50.0",
-      "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.50.0.tgz",
-      "integrity": "sha512-7RqCBQ9iwsOBPkjYgoIaeUij606mSkDMExP0NT7QDI3bqkHYQHrQ83uoNIXwPcQm/vP2VbsUz3kiyZZ1qPlLTQ==",
+      "version": "0.51.3",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.51.3.tgz",
+      "integrity": "sha512-K9oygeFN+DhZTyWxAu0S/6cYMKSE9F2YXjXItwzBf8lRBRFRiQLurXkeUbqE+LCxvMtauiMFMApPb8YNyHT0Vw==",
       "dev": true
     },
     "rollup-plugin-buble": {
@@ -1471,31 +1513,40 @@
       }
     },
     "rollup-plugin-commonjs": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-2.2.1.tgz",
-      "integrity": "sha1-jJ+qBI6Ky3BlT3yTe8ne3sUKazk=",
+      "version": "8.2.6",
+      "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-8.2.6.tgz",
+      "integrity": "sha512-qK0+uhktmnAgZkHkqFuajNmPw93fjrO7+CysDaxWE5jrUR9XSlSvuao5ZJP+XizxA8weakhgYYBtbVz9SGBpjA==",
       "dev": true,
       "requires": {
-        "acorn": "2.7.0",
-        "estree-walker": "0.2.1",
-        "magic-string": "0.10.2",
-        "resolve": "1.4.0",
-        "rollup-pluginutils": "1.5.2"
+        "acorn": "5.2.1",
+        "estree-walker": "0.5.0",
+        "magic-string": "0.22.4",
+        "resolve": "1.5.0",
+        "rollup-pluginutils": "2.0.1"
       },
       "dependencies": {
-        "acorn": {
-          "version": "2.7.0",
-          "resolved": "https://registry.npmjs.org/acorn/-/acorn-2.7.0.tgz",
-          "integrity": "sha1-q259nYhqrKiwhbwzEreaGYQz8Oc=",
+        "estree-walker": {
+          "version": "0.5.0",
+          "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.5.0.tgz",
+          "integrity": "sha512-/bEAy+yKAZQrEWUhGmS3H9XpGqSDBtRzX0I2PgMw9kA2n1jN22uV5B5p7MFdZdvWdXCRJztXAfx6ZeRfgkEETg==",
           "dev": true
         },
-        "magic-string": {
-          "version": "0.10.2",
-          "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.10.2.tgz",
-          "integrity": "sha1-8l8cPZ5ITw2K1gbWwvr0BKO2z50=",
+        "rollup-pluginutils": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.0.1.tgz",
+          "integrity": "sha1-fslbNXP2VDpGpkYb2afFRFJdD8A=",
           "dev": true,
           "requires": {
-            "vlq": "0.2.2"
+            "estree-walker": "0.3.1",
+            "micromatch": "2.3.11"
+          },
+          "dependencies": {
+            "estree-walker": {
+              "version": "0.3.1",
+              "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.3.1.tgz",
+              "integrity": "sha1-5rGlHPcpJSTnI3wxLl/mZgwc4ao=",
+              "dev": true
+            }
           }
         }
       }
@@ -1528,14 +1579,15 @@
       }
     },
     "rollup-plugin-node-resolve": {
-      "version": "1.7.3",
-      "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-1.7.3.tgz",
-      "integrity": "sha1-chLyw3G4SZ/PsKF3JDGTGyQPEuQ=",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-3.0.0.tgz",
+      "integrity": "sha1-i4l8TDAw1QASd7BRSyXSygloPuA=",
       "dev": true,
       "requires": {
         "browser-resolve": "1.11.2",
         "builtin-modules": "1.1.1",
-        "resolve": "1.4.0"
+        "is-module": "1.0.0",
+        "resolve": "1.5.0"
       }
     },
     "rollup-pluginutils": {
@@ -1549,57 +1601,84 @@
       }
     },
     "run-async": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz",
-      "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=",
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
+      "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
       "dev": true,
       "requires": {
-        "once": "1.4.0"
+        "is-promise": "2.1.0"
       }
     },
     "rx-lite": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz",
-      "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=",
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz",
+      "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=",
       "dev": true
     },
+    "rx-lite-aggregates": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz",
+      "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=",
+      "dev": true,
+      "requires": {
+        "rx-lite": "4.0.8"
+      }
+    },
     "safe-buffer": {
       "version": "5.1.1",
       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
       "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
       "dev": true
     },
-    "shelljs": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.6.1.tgz",
-      "integrity": "sha1-7GIRvtGSBEIIj+D3Cyg3Iy7SyKg=",
+    "semver": {
+      "version": "5.4.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
+      "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==",
       "dev": true
     },
-    "sigmund": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
-      "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=",
+    "shebang-command": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+      "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+      "dev": true,
+      "requires": {
+        "shebang-regex": "1.0.0"
+      }
+    },
+    "shebang-regex": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+      "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
       "dev": true
     },
-    "slice-ansi": {
-      "version": "0.0.4",
-      "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz",
-      "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=",
+    "signal-exit": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+      "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
       "dev": true
     },
+    "slice-ansi": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz",
+      "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==",
+      "dev": true,
+      "requires": {
+        "is-fullwidth-code-point": "2.0.0"
+      }
+    },
     "source-map": {
-      "version": "0.5.7",
-      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-      "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
       "dev": true
     },
     "source-map-support": {
-      "version": "0.4.18",
-      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
-      "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.0.tgz",
+      "integrity": "sha512-vUoN3I7fHQe0R/SJLKRdKYuEdRGogsviXFkHHo17AWaTGv17VLnxw+CFXvqy+y4ORZ3doWLQcxRYfwKrsd/H7Q==",
       "dev": true,
       "requires": {
-        "source-map": "0.5.7"
+        "source-map": "0.6.1"
       }
     },
     "sprintf-js": {
@@ -1609,14 +1688,30 @@
       "dev": true
     },
     "string-width": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
-      "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+      "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
       "dev": true,
       "requires": {
-        "code-point-at": "1.1.0",
-        "is-fullwidth-code-point": "1.0.0",
-        "strip-ansi": "3.0.1"
+        "is-fullwidth-code-point": "2.0.0",
+        "strip-ansi": "4.0.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+          "dev": true
+        },
+        "strip-ansi": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "3.0.0"
+          }
+        }
       }
     },
     "string_decoder": {
@@ -1632,66 +1727,37 @@
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
       "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+      "dev": true,
       "requires": {
         "ansi-regex": "2.1.1"
       }
     },
     "strip-json-comments": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz",
-      "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=",
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+      "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
       "dev": true
     },
     "supports-color": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-      "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz",
+      "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=",
+      "requires": {
+        "has-flag": "2.0.0"
+      }
     },
     "table": {
-      "version": "3.8.3",
-      "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz",
-      "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=",
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz",
+      "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==",
       "dev": true,
       "requires": {
-        "ajv": "4.11.8",
-        "ajv-keywords": "1.5.1",
-        "chalk": "1.1.3",
+        "ajv": "5.3.0",
+        "ajv-keywords": "2.1.1",
+        "chalk": "2.3.0",
         "lodash": "4.17.4",
-        "slice-ansi": "0.0.4",
+        "slice-ansi": "1.0.0",
         "string-width": "2.1.1"
-      },
-      "dependencies": {
-        "ansi-regex": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
-          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
-          "dev": true
-        },
-        "is-fullwidth-code-point": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
-          "dev": true
-        },
-        "string-width": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
-          "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
-          "dev": true,
-          "requires": {
-            "is-fullwidth-code-point": "2.0.0",
-            "strip-ansi": "4.0.0"
-          }
-        },
-        "strip-ansi": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
-          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "3.0.0"
-          }
-        }
       }
     },
     "text-table": {
@@ -1706,11 +1772,14 @@
       "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
       "dev": true
     },
-    "to-iso-string": {
-      "version": "0.0.2",
-      "resolved": "https://registry.npmjs.org/to-iso-string/-/to-iso-string-0.0.2.tgz",
-      "integrity": "sha1-TcGeZk38y+Jb2NtQiwDG2hWCVdE=",
-      "dev": true
+    "tmp": {
+      "version": "0.0.33",
+      "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+      "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+      "dev": true,
+      "requires": {
+        "os-tmpdir": "1.0.2"
+      }
     },
     "tryit": {
       "version": "1.0.3",
@@ -1733,15 +1802,34 @@
       "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
       "dev": true
     },
-    "user-home": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz",
-      "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=",
+    "unicode-canonical-property-names-ecmascript": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.3.tgz",
+      "integrity": "sha512-iG/2t0F2LAU8aZYPkX5gi7ebukHnr3sWFESpb+zPQeeaQwOkfoO6ZW17YX7MdRPNG9pCy+tjzGill+Ah0Em0HA==",
+      "dev": true
+    },
+    "unicode-match-property-ecmascript": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.3.tgz",
+      "integrity": "sha512-nFcaBFcr08UQNF15ZgI5ISh3yUnQm7SJRRxwYrL5VYX46pS+6Q7TCTv4zbK+j6/l7rQt0mMiTL2zpmeygny6rA==",
       "dev": true,
       "requires": {
-        "os-homedir": "1.0.2"
+        "unicode-canonical-property-names-ecmascript": "1.0.3",
+        "unicode-property-aliases-ecmascript": "1.0.3"
       }
     },
+    "unicode-match-property-value-ecmascript": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.0.1.tgz",
+      "integrity": "sha512-lM8B0FDZQh9yYGgiabRQcyWicB27VLOolSBRIxsO7FeQPtg+79Oe7sC8Mzr8BObDs+G9CeYmC/shHo6OggNEog==",
+      "dev": true
+    },
+    "unicode-property-aliases-ecmascript": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.3.tgz",
+      "integrity": "sha512-TdDmDOTxEf2ad1g3ZBpM6cqKIb2nJpVlz1Q++casDryKz18tpeMBhSng9hjC1CTQCkOV9Rw2knlSB6iRo7ad1w==",
+      "dev": true
+    },
     "util-deprecate": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -1749,9 +1837,18 @@
       "dev": true
     },
     "vlq": {
-      "version": "0.2.2",
-      "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.2.tgz",
-      "integrity": "sha1-4xbVJXtAuGu0PLjV/qXX9U1rDKE="
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz",
+      "integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow=="
+    },
+    "which": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz",
+      "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==",
+      "dev": true,
+      "requires": {
+        "isexe": "2.0.0"
+      }
     },
     "wordwrap": {
       "version": "1.0.0",
@@ -1774,10 +1871,10 @@
         "mkdirp": "0.5.1"
       }
     },
-    "xtend": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
-      "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
+    "yallist": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+      "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
       "dev": true
     }
   }
diff --git a/package.json b/package.json
index 6051ccf..bc6211b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "buble",
-  "version": "0.16.0",
+  "version": "0.18.0",
   "description": "The blazing fast, batteries-included ES2015 compiler",
   "main": "dist/buble.umd.js",
   "module": "dist/buble.es.js",
@@ -24,7 +24,7 @@
   },
   "repository": {
     "type": "git",
-    "url": "git+https://gitlab.com/Rich-Harris/buble.git"
+    "url": "git+https://github.com/Rich-Harris/buble.git"
   },
   "keywords": [
     "javascript",
@@ -39,32 +39,31 @@
   "author": "Rich Harris",
   "license": "MIT",
   "bugs": {
-    "url": "https://gitlab.com/Rich-Harris/buble/issues"
+    "url": "https://github.com/Rich-Harris/buble/issues"
   },
-  "homepage": "https://gitlab.com/Rich-Harris/buble#README",
+  "homepage": "https://github.com/Rich-Harris/buble#README",
   "devDependencies": {
     "buble": "0.8.2",
-    "console-group": "^0.2.1",
-    "eslint": "^2.10.1",
+    "console-group": "^0.3.3",
+    "eslint": "^4.7.2",
     "glob": "^7.0.3",
-    "mocha": "^2.4.5",
-    "regexpu-core": "^2.0.0",
+    "mocha": "^4.0.1",
+    "regexpu-core": "^4.1.3",
     "rimraf": "^2.5.2",
-    "rollup": "^0.50.0",
+    "rollup": "^0.51.3",
     "rollup-plugin-buble": "^0.8.0",
-    "rollup-plugin-commonjs": "^2.2.1",
+    "rollup-plugin-commonjs": "^8.2.1",
     "rollup-plugin-json": "^2.0.0",
-    "rollup-plugin-node-resolve": "^1.5.0",
-    "source-map": "^0.5.6",
-    "source-map-support": "^0.4.0"
+    "rollup-plugin-node-resolve": "^3.0.0",
+    "source-map": "^0.6.1",
+    "source-map-support": "^0.5.0"
   },
   "dependencies": {
-    "acorn": "^3.3.0",
+    "acorn": "^5.1.2",
     "acorn-jsx": "^3.0.1",
-    "acorn-object-spread": "^1.0.0",
-    "chalk": "^1.1.3",
-    "vlq": "0.2.1",
-    "magic-string": "^0.14.0",
+    "acorn5-object-spread": "^4.0.0",
+    "chalk": "^2.1.0",
+    "magic-string": "^0.22.4",
     "minimist": "^1.2.0",
     "os-homedir": "^1.0.1",
     "vlq": "^0.2.2"
diff --git a/register.js b/register.js
index 806db61..dff1f82 100644
--- a/register.js
+++ b/register.js
@@ -14,7 +14,7 @@ if ( !~versions.indexOf( nodeVersion ) ) {
 	if ( +nodeVersion > 6 ) {
 		nodeVersion = '6';
 	} else {
-		throw new Error( 'Unsupported version (' + nodeVersion + '). Please raise an issue at https://gitlab.com/Rich-Harris/buble/issues' );
+		throw new Error( 'Unsupported version (' + nodeVersion + '). Please raise an issue at https://github.com/Rich-Harris/buble/issues' );
 	}
 }
 
diff --git a/rollup.config.js b/rollup.config.js
index 0fab8b5..6528ebb 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -10,6 +10,9 @@ const config = {
 		json(),
 		commonjs(),
 		buble({
+			target: {
+				node: 4
+			},
 			include: [
 				'src/**',
 				'node_modules/acorn-object-spread/**',
diff --git a/src/index.js b/src/index.js
index 445385f..3c707fc 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,62 +1,69 @@
-import acorn from 'acorn/dist/acorn.js';
+import * as acorn from 'acorn';
 import acornJsx from 'acorn-jsx/inject';
-import acornObjectSpread from 'acorn-object-spread/inject';
+import acornObjectSpread from 'acorn5-object-spread/inject';
 import Program from './program/Program.js';
 import { features, matrix } from './support.js';
 import getSnippet from './utils/getSnippet.js';
 
-const { parse } = [
-	acornObjectSpread,
-	acornJsx
-].reduce( ( final, plugin ) => plugin( final ), acorn );
+const { parse } = [acornObjectSpread, acornJsx].reduce(
+	(final, plugin) => plugin(final),
+	acorn
+);
 
-const dangerousTransforms = [
-	'dangerousTaggedTemplateString',
-	'dangerousForOf'
-];
+const dangerousTransforms = ['dangerousTaggedTemplateString', 'dangerousForOf'];
 
-export function target ( target ) {
-	const targets = Object.keys( target );
-	let bitmask = targets.length ?
-		0b1111111111111111111111111111111 :
-		0b1000000000000000000000000000000;
+export function target(target) {
+	const targets = Object.keys(target);
+	let bitmask = targets.length
+		? 0b11111111111111111111111111111111
+		: 0b01000000000000000000000000000000;
 
-	Object.keys( target ).forEach( environment => {
-		const versions = matrix[ environment ];
-		if ( !versions ) throw new Error( `Unknown environment '${environment}'. Please raise an issue at https://gitlab.com/Rich-Harris/buble/issues` );
+	Object.keys(target).forEach(environment => {
+		const versions = matrix[environment];
+		if (!versions)
+			throw new Error(
+				`Unknown environment '${environment}'. Please raise an issue at https://github.com/Rich-Harris/buble/issues`
+			);
 
-		const targetVersion = target[ environment ];
-		if ( !( targetVersion in versions ) ) throw new Error( `Support data exists for the following versions of ${environment}: ${Object.keys( versions ).join( ', ')}. Please raise an issue at https://gitlab.com/Rich-Harris/buble/issues` );
-		const support = versions[ targetVersion ];
+		const targetVersion = target[environment];
+		if (!(targetVersion in versions))
+			throw new Error(
+				`Support data exists for the following versions of ${environment}: ${Object.keys(
+					versions
+				).join(
+					', '
+				)}. Please raise an issue at https://github.com/Rich-Harris/buble/issues`
+			);
+		const support = versions[targetVersion];
 
 		bitmask &= support;
 	});
 
-	let transforms = Object.create( null );
-	features.forEach( ( name, i ) => {
-		transforms[ name ] = !( bitmask & 1 << i );
+	let transforms = Object.create(null);
+	features.forEach((name, i) => {
+		transforms[name] = !(bitmask & (1 << i));
 	});
 
-	dangerousTransforms.forEach( name => {
-		transforms[ name ] = false;
+	dangerousTransforms.forEach(name => {
+		transforms[name] = false;
 	});
 
 	return transforms;
 }
 
-export function transform ( source, options = {} ) {
+export function transform(source, options = {}) {
 	let ast;
 	let jsx = null;
 
 	try {
-		ast = parse( source, {
-			ecmaVersion: 7,
+		ast = parse(source, {
+			ecmaVersion: 8,
 			preserveParens: true,
 			sourceType: 'module',
 			onComment: (block, text) => {
-				if ( !jsx ) {
-					let match = /@jsx\s+([^\s]+)/.exec( text );
-					if ( match ) jsx = match[1];
+				if (!jsx) {
+					let match = /@jsx\s+([^\s]+)/.exec(text);
+					if (match) jsx = match[1];
 				}
 			},
 			plugins: {
@@ -65,25 +72,27 @@ export function transform ( source, options = {} ) {
 			}
 		});
 		options.jsx = jsx || options.jsx;
-	} catch ( err ) {
-		err.snippet = getSnippet( source, err.loc );
+	} catch (err) {
+		err.snippet = getSnippet(source, err.loc);
 		err.toString = () => `${err.name}: ${err.message}\n${err.snippet}`;
 		throw err;
 	}
 
-	let transforms = target( options.target || {} );
-	Object.keys( options.transforms || {} ).forEach( name => {
-		if ( name === 'modules' ) {
-			if ( !( 'moduleImport' in options.transforms ) ) transforms.moduleImport = options.transforms.modules;
-			if ( !( 'moduleExport' in options.transforms ) ) transforms.moduleExport = options.transforms.modules;
+	let transforms = target(options.target || {});
+	Object.keys(options.transforms || {}).forEach(name => {
+		if (name === 'modules') {
+			if (!('moduleImport' in options.transforms))
+				transforms.moduleImport = options.transforms.modules;
+			if (!('moduleExport' in options.transforms))
+				transforms.moduleExport = options.transforms.modules;
 			return;
 		}
 
-		if ( !( name in transforms ) ) throw new Error( `Unknown transform '${name}'` );
-		transforms[ name ] = options.transforms[ name ];
+		if (!(name in transforms)) throw new Error(`Unknown transform '${name}'`);
+		transforms[name] = options.transforms[name];
 	});
 
-	return new Program( source, ast, transforms, options ).export( options );
+	return new Program(source, ast, transforms, options).export(options);
 }
 
 export { version as VERSION } from '../package.json';
diff --git a/src/program/BlockStatement.js b/src/program/BlockStatement.js
index 719ca59..0c887ce 100644
--- a/src/program/BlockStatement.js
+++ b/src/program/BlockStatement.js
@@ -3,97 +3,100 @@ import Node from './Node.js';
 import Scope from './Scope.js';
 import destructure from '../utils/destructure.js';
 
-function isUseStrict ( node ) {
-	if ( !node ) return false;
-	if ( node.type !== 'ExpressionStatement' ) return false;
-	if ( node.expression.type !== 'Literal' ) return false;
+function isUseStrict(node) {
+	if (!node) return false;
+	if (node.type !== 'ExpressionStatement') return false;
+	if (node.expression.type !== 'Literal') return false;
 	return node.expression.value === 'use strict';
 }
 
 export default class BlockStatement extends Node {
-	createScope () {
-		this.parentIsFunction = /Function/.test( this.parent.type );
+	createScope() {
+		this.parentIsFunction = /Function/.test(this.parent.type);
 		this.isFunctionBlock = this.parentIsFunction || this.parent.type === 'Root';
 		this.scope = new Scope({
 			block: !this.isFunctionBlock,
-			parent: this.parent.findScope( false )
+			parent: this.parent.findScope(false)
 		});
 
-		if ( this.parentIsFunction ) {
-			this.parent.params.forEach( node => {
-				this.scope.addDeclaration( node, 'param' );
+		if (this.parentIsFunction) {
+			this.parent.params.forEach(node => {
+				this.scope.addDeclaration(node, 'param');
 			});
 		}
 	}
 
-	initialise ( transforms ) {
+	initialise(transforms) {
 		this.thisAlias = null;
 		this.argumentsAlias = null;
 		this.defaultParameters = [];
+		this.createdDeclarations = [];
 
 		// normally the scope gets created here, during initialisation,
 		// but in some cases (e.g. `for` statements), we need to create
 		// the scope early, as it pertains to both the init block and
 		// the body of the statement
-		if ( !this.scope ) this.createScope();
+		if (!this.scope) this.createScope();
 
-		this.body.forEach( node => node.initialise( transforms ) );
+		this.body.forEach(node => node.initialise(transforms));
 
 		this.scope.consolidate();
 	}
 
-	findLexicalBoundary () {
-		if ( this.type === 'Program' ) return this;
-		if ( /^Function/.test( this.parent.type ) ) return this;
+	findLexicalBoundary() {
+		if (this.type === 'Program') return this;
+		if (/^Function/.test(this.parent.type)) return this;
 
 		return this.parent.findLexicalBoundary();
 	}
 
-	findScope ( functionScope ) {
-		if ( functionScope && !this.isFunctionBlock ) return this.parent.findScope( functionScope );
+	findScope(functionScope) {
+		if (functionScope && !this.isFunctionBlock)
+			return this.parent.findScope(functionScope);
 		return this.scope;
 	}
 
-	getArgumentsAlias () {
-		if ( !this.argumentsAlias ) {
-			this.argumentsAlias = this.scope.createIdentifier( 'arguments' );
+	getArgumentsAlias() {
+		if (!this.argumentsAlias) {
+			this.argumentsAlias = this.scope.createIdentifier('arguments');
 		}
 
 		return this.argumentsAlias;
 	}
 
-	getArgumentsArrayAlias () {
-		if ( !this.argumentsArrayAlias ) {
-			this.argumentsArrayAlias = this.scope.createIdentifier( 'argsArray' );
+	getArgumentsArrayAlias() {
+		if (!this.argumentsArrayAlias) {
+			this.argumentsArrayAlias = this.scope.createIdentifier('argsArray');
 		}
 
 		return this.argumentsArrayAlias;
 	}
 
-	getThisAlias () {
-		if ( !this.thisAlias ) {
-			this.thisAlias = this.scope.createIdentifier( 'this' );
+	getThisAlias() {
+		if (!this.thisAlias) {
+			this.thisAlias = this.scope.createIdentifier('this');
 		}
 
 		return this.thisAlias;
 	}
 
-	getIndentation () {
-		if ( this.indentation === undefined ) {
+	getIndentation() {
+		if (this.indentation === undefined) {
 			const source = this.program.magicString.original;
 
 			const useOuter = this.synthetic || !this.body.length;
 			let c = useOuter ? this.start : this.body[0].start;
 
-			while ( c && source[c] !== '\n' ) c -= 1;
+			while (c && source[c] !== '\n') c -= 1;
 
 			this.indentation = '';
 
-			while ( true ) { // eslint-disable-line no-constant-condition
+			// eslint-disable-next-line no-constant-condition
+			while (true) {
 				c += 1;
 				const char = source[c];
 
-				if ( char !== ' ' && char !== '\t' ) break;
+				if (char !== ' ' && char !== '\t') break;
 
 				this.indentation += char;
 			}
@@ -102,81 +105,99 @@ export default class BlockStatement extends Node {
 
 			// account for dedented class constructors
 			let parent = this.parent;
-			while ( parent ) {
-				if ( parent.kind === 'constructor' && !parent.parent.parent.superClass ) {
-					this.indentation = this.indentation.replace( indentString, '' );
+			while (parent) {
+				if (parent.kind === 'constructor' && !parent.parent.parent.superClass) {
+					this.indentation = this.indentation.replace(indentString, '');
 				}
 
 				parent = parent.parent;
 			}
 
-			if ( useOuter ) this.indentation += indentString;
+			if (useOuter) this.indentation += indentString;
 		}
 
 		return this.indentation;
 	}
 
-	transpile ( code, transforms ) {
+	transpile(code, transforms) {
 		const indentation = this.getIndentation();
 
 		let introStatementGenerators = [];
 
-		if ( this.argumentsAlias ) {
-			introStatementGenerators.push( ( start, prefix, suffix ) => {
-				const assignment = `${prefix}var ${this.argumentsAlias} = arguments${suffix}`;
-				code.insertLeft( start, assignment );
+		if (this.argumentsAlias) {
+			introStatementGenerators.push((start, prefix, suffix) => {
+				const assignment = `${prefix}var ${this.argumentsAlias} = arguments${
+					suffix
+				}`;
+				code.appendLeft(start, assignment);
 			});
 		}
 
-		if ( this.thisAlias ) {
-			introStatementGenerators.push( ( start, prefix, suffix ) => {
+		if (this.thisAlias) {
+			introStatementGenerators.push((start, prefix, suffix) => {
 				const assignment = `${prefix}var ${this.thisAlias} = this${suffix}`;
-				code.insertLeft( start, assignment );
+				code.appendLeft(start, assignment);
 			});
 		}
 
-		if ( this.argumentsArrayAlias ) {
-			introStatementGenerators.push( ( start, prefix, suffix ) => {
-				const i = this.scope.createIdentifier( 'i' );
-				const assignment = `${prefix}var ${i} = arguments.length, ${this.argumentsArrayAlias} = Array(${i});\n${indentation}while ( ${i}-- ) ${this.argumentsArrayAlias}[${i}] = arguments[${i}]${suffix}`;
-				code.insertLeft( start, assignment );
+		if (this.argumentsArrayAlias) {
+			introStatementGenerators.push((start, prefix, suffix) => {
+				const i = this.scope.createIdentifier('i');
+				const assignment = `${prefix}var ${i} = arguments.length, ${
+					this.argumentsArrayAlias
+				} = Array(${i});\n${indentation}while ( ${i}-- ) ${
+					this.argumentsArrayAlias
+				}[${i}] = arguments[${i}]${suffix}`;
+				code.appendLeft(start, assignment);
 			});
 		}
 
-		if ( /Function/.test( this.parent.type ) ) {
-			this.transpileParameters( code, transforms, indentation, introStatementGenerators );
+		if (/Function/.test(this.parent.type)) {
+			this.transpileParameters(
+				code,
+				transforms,
+				indentation,
+				introStatementGenerators
+			);
 		}
 
-		if ( transforms.letConst && this.isFunctionBlock ) {
-			this.transpileBlockScopedIdentifiers( code );
+		if (transforms.letConst && this.isFunctionBlock) {
+			this.transpileBlockScopedIdentifiers(code);
 		}
 
-		super.transpile( code, transforms );
+		super.transpile(code, transforms);
 
-		if ( this.synthetic ) {
-			if ( this.parent.type === 'ArrowFunctionExpression' ) {
+		if (this.createdDeclarations.length) {
+			introStatementGenerators.push((start, prefix, suffix) => {
+				const assignment = `${prefix}var ${this.createdDeclarations.join(', ')}${suffix}`;
+				code.appendLeft(start, assignment);
+			});
+		}
+
+		if (this.synthetic) {
+			if (this.parent.type === 'ArrowFunctionExpression') {
 				const expr = this.body[0];
 
-				if ( introStatementGenerators.length ) {
-					code.insertLeft( this.start, `{` ).insertRight( this.end, `${this.parent.getIndentation()}}` );
+				if (introStatementGenerators.length) {
+					code
+						.appendLeft(this.start, `{`)
+						.prependRight(this.end, `${this.parent.getIndentation()}}`);
 
-					code.insertRight( expr.start, `\n${indentation}return ` );
-					code.insertLeft( expr.end, `;\n` );
-				} else if ( transforms.arrow ) {
-					code.insertLeft( expr.start, `{ return ` );
-					code.insertLeft( expr.end, `; }` );
+					code.prependRight(expr.start, `\n${indentation}return `);
+					code.appendLeft(expr.end, `;\n`);
+				} else if (transforms.arrow) {
+					code.prependRight(expr.start, `{ return `);
+					code.appendLeft(expr.end, `; }`);
 				}
-			}
-
-			else if ( introStatementGenerators.length ) {
-				code.insertLeft( this.start, `{` ).insertRight( this.end, `}` );
+			} else if (introStatementGenerators.length) {
+				code.prependRight(this.start, `{`).appendLeft(this.end, `}`);
 			}
 		}
 
 		let start;
-		if ( isUseStrict( this.body[0] ) ) {
+		if (isUseStrict(this.body[0])) {
 			start = this.body[0].end;
-		} else if ( this.synthetic || this.parent.type === 'Root' ) {
+		} else if (this.synthetic || this.parent.type === 'Root') {
 			start = this.start;
 		} else {
 			start = this.start + 1;
@@ -184,100 +205,139 @@ export default class BlockStatement extends Node {
 
 		let prefix = `\n${indentation}`;
 		let suffix = ';';
-		introStatementGenerators.forEach( ( fn, i ) => {
-			if ( i === introStatementGenerators.length - 1 ) suffix = `;\n`;
-			fn( start, prefix, suffix );
+		introStatementGenerators.forEach((fn, i) => {
+			if (i === introStatementGenerators.length - 1) suffix = `;\n`;
+			fn(start, prefix, suffix);
 		});
 	}
 
-	transpileParameters ( code, transforms, indentation, introStatementGenerators ) {
+	declareIdentifier(name) {
+		const id = this.scope.createIdentifier(name);
+		this.createdDeclarations.push(id);
+		return id;
+	}
+
+	transpileParameters(code, transforms, indentation, introStatementGenerators) {
 		const params = this.parent.params;
 
-		params.forEach( param => {
-			if ( param.type === 'AssignmentPattern' && param.left.type === 'Identifier' ) {
-				if ( transforms.defaultParameter ) {
-					introStatementGenerators.push( ( start, prefix, suffix ) => {
-						const lhs = `${prefix}if ( ${param.left.name} === void 0 ) ${param.left.name}`;
+		params.forEach(param => {
+			if (
+				param.type === 'AssignmentPattern' &&
+				param.left.type === 'Identifier'
+			) {
+				if (transforms.defaultParameter) {
+					introStatementGenerators.push((start, prefix, suffix) => {
+						const lhs = `${prefix}if ( ${param.left.name} === void 0 ) ${
+							param.left.name
+						}`;
 
 						code
-							.insertRight( param.left.end, lhs )
-							.move( param.left.end, param.right.end, start )
-							.insertLeft( param.right.end, suffix );
+							.prependRight(param.left.end, lhs)
+							.move(param.left.end, param.right.end, start)
+							.appendLeft(param.right.end, suffix);
 					});
 				}
-			}
-
-			else if ( param.type === 'RestElement' ) {
-				if ( transforms.spreadRest ) {
-					introStatementGenerators.push( ( start, prefix, suffix ) => {
-						const penultimateParam = params[ params.length - 2 ];
-
-						if ( penultimateParam ) {
-							code.remove( penultimateParam ? penultimateParam.end : param.start, param.end );
+			} else if (param.type === 'RestElement') {
+				if (transforms.spreadRest) {
+					introStatementGenerators.push((start, prefix, suffix) => {
+						const penultimateParam = params[params.length - 2];
+
+						if (penultimateParam) {
+							code.remove(
+								penultimateParam ? penultimateParam.end : param.start,
+								param.end
+							);
 						} else {
-							let start = param.start, end = param.end; // TODO https://gitlab.com/Rich-Harris/buble/issues/8
+							let start = param.start,
+								end = param.end; // TODO https://gitlab.com/Rich-Harris/buble/issues/8
 
-							while ( /\s/.test( code.original[ start - 1 ] ) ) start -= 1;
-							while ( /\s/.test( code.original[ end ] ) ) end += 1;
+							while (/\s/.test(code.original[start - 1])) start -= 1;
+							while (/\s/.test(code.original[end])) end += 1;
 
-							code.remove( start, end );
+							code.remove(start, end);
 						}
 
 						const name = param.argument.name;
-						const len = this.scope.createIdentifier( 'len' );
+						const len = this.scope.createIdentifier('len');
 						const count = params.length - 1;
 
-						if ( count ) {
-							code.insertLeft( start, `${prefix}var ${name} = [], ${len} = arguments.length - ${count};\n${indentation}while ( ${len}-- > 0 ) ${name}[ ${len} ] = arguments[ ${len} + ${count} ]${suffix}` );
+						if (count) {
+							code.prependRight(
+								start,
+								`${prefix}var ${name} = [], ${len} = arguments.length - ${
+									count
+								};\n${indentation}while ( ${len}-- > 0 ) ${name}[ ${
+									len
+								} ] = arguments[ ${len} + ${count} ]${suffix}`
+							);
 						} else {
-							code.insertLeft( start, `${prefix}var ${name} = [], ${len} = arguments.length;\n${indentation}while ( ${len}-- ) ${name}[ ${len} ] = arguments[ ${len} ]${suffix}` );
+							code.prependRight(
+								start,
+								`${prefix}var ${name} = [], ${len} = arguments.length;\n${
+									indentation
+								}while ( ${len}-- ) ${name}[ ${len} ] = arguments[ ${len} ]${
+									suffix
+								}`
+							);
 						}
 					});
 				}
-			}
-
-			else if ( param.type !== 'Identifier' ) {
-				if ( transforms.parameterDestructuring ) {
-					const ref = this.scope.createIdentifier( 'ref' );
-					destructure( code, this.scope, param, ref, false, introStatementGenerators );
-					code.insertLeft( param.start, ref );
+			} else if (param.type !== 'Identifier') {
+				if (transforms.parameterDestructuring) {
+					const ref = this.scope.createIdentifier('ref');
+					destructure(
+						code,
+						this.scope,
+						param,
+						ref,
+						false,
+						introStatementGenerators
+					);
+					code.prependRight(param.start, ref);
 				}
 			}
 		});
 	}
 
-	transpileBlockScopedIdentifiers ( code ) {
-		Object.keys( this.scope.blockScopedDeclarations ).forEach( name => {
-			const declarations = this.scope.blockScopedDeclarations[ name ];
+	transpileBlockScopedIdentifiers(code) {
+		Object.keys(this.scope.blockScopedDeclarations).forEach(name => {
+			const declarations = this.scope.blockScopedDeclarations[name];
 
-			for ( let declaration of declarations ) {
+			for (let declaration of declarations) {
 				let cont = false; // TODO implement proper continue...
 
-				if ( declaration.kind === 'for.let' ) {
+				if (declaration.kind === 'for.let') {
 					// special case
-					const forStatement = declaration.node.findNearest( 'ForStatement' );
+					const forStatement = declaration.node.findNearest('ForStatement');
 
-					if ( forStatement.shouldRewriteAsFunction ) {
-						const outerAlias = this.scope.createIdentifier( name );
-						const innerAlias = forStatement.reassigned[ name ] ?
-							this.scope.createIdentifier( name ) :
-							name;
+					if (forStatement.shouldRewriteAsFunction) {
+						const outerAlias = this.scope.createIdentifier(name);
+						const innerAlias = forStatement.reassigned[name]
+							? this.scope.createIdentifier(name)
+							: name;
 
 						declaration.name = outerAlias;
-						code.overwrite( declaration.node.start, declaration.node.end, outerAlias, true );
-
-						forStatement.aliases[ name ] = {
+						code.overwrite(
+							declaration.node.start,
+							declaration.node.end,
+							outerAlias,
+							{ storeName: true }
+						);
+
+						forStatement.aliases[name] = {
 							outer: outerAlias,
 							inner: innerAlias
 						};
 
-						for ( const identifier of declaration.instances ) {
-							const alias = forStatement.body.contains( identifier ) ?
-								innerAlias :
-								outerAlias;
+						for (const identifier of declaration.instances) {
+							const alias = forStatement.body.contains(identifier)
+								? innerAlias
+								: outerAlias;
 
-							if ( name !== alias ) {
-								code.overwrite( identifier.start, identifier.end, alias, true );
+							if (name !== alias) {
+								code.overwrite(identifier.start, identifier.end, alias, {
+									storeName: true
+								});
 							}
 						}
 
@@ -285,16 +345,23 @@ export default class BlockStatement extends Node {
 					}
 				}
 
-				if ( !cont ) {
-					const alias = this.scope.createIdentifier( name );
+				if (!cont) {
+					const alias = this.scope.createIdentifier(name);
 
-					if ( name !== alias ) {
+					if (name !== alias) {
 						declaration.name = alias;
-						code.overwrite( declaration.node.start, declaration.node.end, alias, true );
-
-						for ( const identifier of declaration.instances ) {
+						code.overwrite(
+							declaration.node.start,
+							declaration.node.end,
+							alias,
+							{ storeName: true }
+						);
+
+						for (const identifier of declaration.instances) {
 							identifier.rewritten = true;
-							code.overwrite( identifier.start, identifier.end, alias, true );
+							code.overwrite(identifier.start, identifier.end, alias, {
+								storeName: true
+							});
 						}
 					}
 				}
diff --git a/src/program/Node.js b/src/program/Node.js
index 1b29677..5a65fd1 100644
--- a/src/program/Node.js
+++ b/src/program/Node.js
@@ -3,18 +3,24 @@ import keys from './keys.js';
 
 // used for debugging, without the noise created by
 // circular references
-function toJSON ( node ) {
+function toJSON(node) {
 	var obj = {};
 
-	Object.keys( node ).forEach( key => {
-		if ( key === 'parent' || key === 'program' || key === 'keys' || key === '__wrapped' ) return;
-
-		if ( Array.isArray( node[ key ] ) ) {
-			obj[ key ] = node[ key ].map( toJSON );
-		} else if ( node[ key ] && node[ key ].toJSON ) {
-			obj[ key ] = node[ key ].toJSON();
+	Object.keys(node).forEach(key => {
+		if (
+			key === 'parent' ||
+			key === 'program' ||
+			key === 'keys' ||
+			key === '__wrapped'
+		)
+			return;
+
+		if (Array.isArray(node[key])) {
+			obj[key] = node[key].map(toJSON);
+		} else if (node[key] && node[key].toJSON) {
+			obj[key] = node[key].toJSON();
 		} else {
-			obj[ key ] = node[ key ];
+			obj[key] = node[key];
 		}
 	});
 
@@ -22,102 +28,102 @@ function toJSON ( node ) {
 }
 
 export default class Node {
-	constructor ( raw, parent ) {
+	constructor(raw, parent) {
 		raw.parent = parent;
 		raw.program = parent.program || parent;
 		raw.depth = parent.depth + 1;
-		raw.keys = keys[ raw.type ];
+		raw.keys = keys[raw.type];
 		raw.indentation = undefined;
 
-		for ( const key of keys[ raw.type ] ) {
-			wrap( raw[ key ], raw );
+		for (const key of keys[raw.type]) {
+			wrap(raw[key], raw);
 		}
 
-		raw.program.magicString.addSourcemapLocation( raw.start );
-		raw.program.magicString.addSourcemapLocation( raw.end );
+		raw.program.magicString.addSourcemapLocation(raw.start);
+		raw.program.magicString.addSourcemapLocation(raw.end);
 	}
 
-	ancestor ( level ) {
+	ancestor(level) {
 		let node = this;
-		while ( level-- ) {
+		while (level--) {
 			node = node.parent;
-			if ( !node ) return null;
+			if (!node) return null;
 		}
 
 		return node;
 	}
 
-	contains ( node ) {
-		while ( node ) {
-			if ( node === this ) return true;
+	contains(node) {
+		while (node) {
+			if (node === this) return true;
 			node = node.parent;
 		}
 
 		return false;
 	}
 
-	findLexicalBoundary () {
+	findLexicalBoundary() {
 		return this.parent.findLexicalBoundary();
 	}
 
-	findNearest ( type ) {
-		if ( typeof type === 'string' ) type = new RegExp( `^${type}$` );
-		if ( type.test( this.type ) ) return this;
-		return this.parent.findNearest( type );
+	findNearest(type) {
+		if (typeof type === 'string') type = new RegExp(`^${type}$`);
+		if (type.test(this.type)) return this;
+		return this.parent.findNearest(type);
 	}
 
-	unparenthesizedParent () {
+	unparenthesizedParent() {
 		let node = this.parent;
-		while ( node && node.type === 'ParenthesizedExpression' ) {
+		while (node && node.type === 'ParenthesizedExpression') {
 			node = node.parent;
 		}
 		return node;
 	}
 
-	unparenthesize () {
+	unparenthesize() {
 		let node = this;
-		while ( node.type === 'ParenthesizedExpression' ) {
+		while (node.type === 'ParenthesizedExpression') {
 			node = node.expression;
 		}
 		return node;
 	}
 
-	findScope ( functionScope ) {
-		return this.parent.findScope( functionScope );
+	findScope(functionScope) {
+		return this.parent.findScope(functionScope);
 	}
 
-	getIndentation () {
+	getIndentation() {
 		return this.parent.getIndentation();
 	}
 
-	initialise ( transforms ) {
-		for ( var key of this.keys ) {
-			const value = this[ key ];
+	initialise(transforms) {
+		for (var key of this.keys) {
+			const value = this[key];
 
-			if ( Array.isArray( value ) ) {
-				value.forEach( node => node && node.initialise( transforms ) );
-			} else if ( value && typeof value === 'object' ) {
-				value.initialise( transforms );
+			if (Array.isArray(value)) {
+				value.forEach(node => node && node.initialise(transforms));
+			} else if (value && typeof value === 'object') {
+				value.initialise(transforms);
 			}
 		}
 	}
 
-	toJSON () {
-		return toJSON( this );
+	toJSON() {
+		return toJSON(this);
 	}
 
-	toString () {
-		return this.program.magicString.original.slice( this.start, this.end );
+	toString() {
+		return this.program.magicString.original.slice(this.start, this.end);
 	}
 
-	transpile ( code, transforms ) {
-		for ( const key of this.keys ) {
-			const value = this[ key ];
+	transpile(code, transforms) {
+		for (const key of this.keys) {
+			const value = this[key];
 
-			if ( Array.isArray( value ) ) {
-				value.forEach( node => node && node.transpile( code, transforms ) );
-			} else if ( value && typeof value === 'object' ) {
-				value.transpile( code, transforms );
+			if (Array.isArray(value)) {
+				value.forEach(node => node && node.transpile(code, transforms));
+			} else if (value && typeof value === 'object') {
+				value.transpile(code, transforms);
 			}
 		}
 	}
diff --git a/src/program/Program.js b/src/program/Program.js
index 73c2436..5a6065d 100644
--- a/src/program/Program.js
+++ b/src/program/Program.js
@@ -2,7 +2,7 @@ import MagicString from 'magic-string';
 import BlockStatement from './BlockStatement.js';
 import wrap from './wrap.js';
 
-export default function Program ( source, ast, transforms, options ) {
+export default function Program(source, ast, transforms, options) {
 	this.type = 'Root';
 
 	// options
@@ -10,29 +10,29 @@ export default function Program ( source, ast, transforms, options ) {
 	this.options = options;
 
 	this.source = source;
-	this.magicString = new MagicString( source );
+	this.magicString = new MagicString(source);
 
 	this.ast = ast;
 	this.depth = 0;
 
-	wrap( this.body = ast, this );
+	wrap((this.body = ast), this);
 	this.body.__proto__ = BlockStatement.prototype;
 
 	this.indentExclusionElements = [];
-	this.body.initialise( transforms );
+	this.body.initialise(transforms);
 
-	this.indentExclusions = Object.create( null );
-	for ( const node of this.indentExclusionElements ) {
-		for ( let i = node.start; i < node.end; i += 1 ) {
-			this.indentExclusions[ i ] = true;
+	this.indentExclusions = Object.create(null);
+	for (const node of this.indentExclusionElements) {
+		for (let i = node.start; i < node.end; i += 1) {
+			this.indentExclusions[i] = true;
 		}
 	}
 
-	this.body.transpile( this.magicString, transforms );
+	this.body.transpile(this.magicString, transforms);
 }
 
 Program.prototype = {
-	export ( options = {} ) {
+	export(options = {}) {
 		return {
 			code: this.magicString.toString(),
 			map: this.magicString.generateMap({
@@ -43,11 +43,11 @@ Program.prototype = {
 		};
 	},
 
-	findNearest () {
+	findNearest() {
 		return null;
 	},
 
-	findScope () {
+	findScope() {
 		return null;
 	}
 };
diff --git a/src/program/Scope.js b/src/program/Scope.js
index b844b6b..18f1664 100644
--- a/src/program/Scope.js
+++ b/src/program/Scope.js
@@ -1,100 +1,103 @@
 import extractNames from './extractNames.js';
 import reserved from '../utils/reserved.js';
-import CompileError from '../utils/CompileError.js';
 
-const letConst = /^(?:let|const)$/;
-
-export default function Scope ( options ) {
+export default function Scope(options) {
 	options = options || {};
 
 	this.parent = options.parent;
 	this.isBlockScope = !!options.block;
 
 	let scope = this;
-	while ( scope.isBlockScope ) scope = scope.parent;
+	while (scope.isBlockScope) scope = scope.parent;
 	this.functionScope = scope;
 
 	this.identifiers = [];
-	this.declarations = Object.create( null );
-	this.references = Object.create( null );
-	this.blockScopedDeclarations = this.isBlockScope ? null : Object.create( null );
-	this.aliases = this.isBlockScope ? null : Object.create( null );
+	this.declarations = Object.create(null);
+	this.references = Object.create(null);
+	this.blockScopedDeclarations = this.isBlockScope ? null : Object.create(null);
+	this.aliases = this.isBlockScope ? null : Object.create(null);
 }
 
 Scope.prototype = {
-	addDeclaration ( node, kind ) {
-		for ( const identifier of extractNames( node ) ) {
+	addDeclaration(node, kind) {
+		for (const identifier of extractNames(node)) {
 			const name = identifier.name;
-			const existingDeclaration = this.declarations[ name ];
-			if ( existingDeclaration && ( letConst.test( kind ) || letConst.test( existingDeclaration.kind ) ) ) {
-				// TODO warn about double var declarations?
-				throw new CompileError( identifier, `${name} is already declared` );
-			}
 
 			const declaration = { name, node: identifier, kind, instances: [] };
-			this.declarations[ name ] = declaration;
+			this.declarations[name] = declaration;
 
-			if ( this.isBlockScope ) {
-				if ( !this.functionScope.blockScopedDeclarations[ name ] ) this.functionScope.blockScopedDeclarations[ name ] = [];
-				this.functionScope.blockScopedDeclarations[ name ].push( declaration );
+			if (this.isBlockScope) {
+				if (!this.functionScope.blockScopedDeclarations[name])
+					this.functionScope.blockScopedDeclarations[name] = [];
+				this.functionScope.blockScopedDeclarations[name].push(declaration);
 			}
 		}
 	},
 
-	addReference ( identifier ) {
-		if ( this.consolidated ) {
-			this.consolidateReference( identifier );
+	addReference(identifier) {
+		if (this.consolidated) {
+			this.consolidateReference(identifier);
 		} else {
-			this.identifiers.push( identifier );
+			this.identifiers.push(identifier);
 		}
 	},
 
-	consolidate () {
-		for ( let i = 0; i < this.identifiers.length; i += 1 ) { // we might push to the array during consolidation, so don't cache length
+	consolidate() {
+		for (let i = 0; i < this.identifiers.length; i += 1) {
+			// we might push to the array during consolidation, so don't cache length
 			const identifier = this.identifiers[i];
-			this.consolidateReference( identifier );
+			this.consolidateReference(identifier);
 		}
 
 		this.consolidated = true; // TODO understand why this is necessary... seems bad
 	},
 
-	consolidateReference ( identifier ) {
-		const declaration = this.declarations[ identifier.name ];
-		if ( declaration ) {
-			declaration.instances.push( identifier );
+	consolidateReference(identifier) {
+		const declaration = this.declarations[identifier.name];
+		if (declaration) {
+			declaration.instances.push(identifier);
 		} else {
-			this.references[ identifier.name ] = true;
-			if ( this.parent ) this.parent.addReference( identifier );
+			this.references[identifier.name] = true;
+			if (this.parent) this.parent.addReference(identifier);
 		}
 	},
 
-	contains ( name ) {
-		return this.declarations[ name ] ||
-		       ( this.parent ? this.parent.contains( name ) : false );
+	contains(name) {
+		return (
+			this.declarations[name] ||
+			(this.parent ? this.parent.contains(name) : false)
+		);
 	},
 
-	createIdentifier ( base ) {
-		if ( typeof base === 'number' ) base = base.toString();
+	createIdentifier(base) {
+		if (typeof base === 'number') base = base.toString();
 
 		base = base
-			.replace( /\s/g, '' )
-			.replace( /\[([^\]]+)\]/g, '_$1' )
-			.replace( /[^a-zA-Z0-9_$]/g, '_' )
-			.replace( /_{2,}/, '_' );
+			.replace(/\s/g, '')
+			.replace(/\[([^\]]+)\]/g, '_$1')
+			.replace(/[^a-zA-Z0-9_$]/g, '_')
+			.replace(/_{2,}/, '_');
 
 		let name = base;
 		let counter = 1;
 
-		while ( this.declarations[ name ] || this.references[ name ] || this.aliases[ name ] || name in reserved ) {
+		while (
+			this.declarations[name] ||
+			this.references[name] ||
+			this.aliases[name] ||
+			name in reserved
+		) {
 			name = `${base}$${counter++}`;
 		}
 
-		this.aliases[ name ] = true;
+		this.aliases[name] = true;
 		return name;
 	},
 
-	findDeclaration ( name ) {
-		return this.declarations[ name ] ||
-		       ( this.parent && this.parent.findDeclaration( name ) );
+	findDeclaration(name) {
+		return (
+			this.declarations[name] ||
+			(this.parent && this.parent.findDeclaration(name))
+		);
 	}
 };
diff --git a/src/program/extractNames.js b/src/program/extractNames.js
index 84825e7..e9302d8 100644
--- a/src/program/extractNames.js
+++ b/src/program/extractNames.js
@@ -1,31 +1,35 @@
-export default function extractNames ( node ) {
+export default function extractNames(node) {
 	const names = [];
-	extractors[ node.type ]( names, node );
+	extractors[node.type](names, node);
 	return names;
 }
 
 const extractors = {
-	Identifier ( names, node ) {
-		names.push( node );
+	Identifier(names, node) {
+		names.push(node);
 	},
 
-	ObjectPattern ( names, node ) {
-		for ( const prop of node.properties ) {
-			extractors[ prop.value.type ]( names, prop.value );
+	ObjectPattern(names, node) {
+		for (const prop of node.properties) {
+			extractors[prop.type](names, prop);
 		}
 	},
 
-	ArrayPattern ( names, node ) {
-		for ( const element of node.elements )  {
-			if ( element ) extractors[ element.type ]( names, element );
+	Property(names, node) {
+		extractors[node.value.type](names, node.value);
+	},
+
+	ArrayPattern(names, node) {
+		for (const element of node.elements) {
+			if (element) extractors[element.type](names, element);
 		}
 	},
 
-	RestElement ( names, node ) {
-		extractors[ node.argument.type ]( names, node.argument );
+	RestElement(names, node) {
+		extractors[node.argument.type](names, node.argument);
 	},
 
-	AssignmentPattern ( names, node ) {
-		extractors[ node.left.type ]( names, node.left );
+	AssignmentPattern(names, node) {
+		extractors[node.left.type](names, node.left);
 	}
 };
diff --git a/src/program/keys.js b/src/program/keys.js
index 194ccf7..bb442af 100644
--- a/src/program/keys.js
+++ b/src/program/keys.js
@@ -1,4 +1,4 @@
 export default {
-	Program: [ 'body' ],
+	Program: ['body'],
 	Literal: []
 };
diff --git a/src/program/types/ArrayExpression.js b/src/program/types/ArrayExpression.js
index 15c04dc..c8e6304 100644
--- a/src/program/types/ArrayExpression.js
+++ b/src/program/types/ArrayExpression.js
@@ -2,54 +2,69 @@ import Node from '../Node.js';
 import spread, { isArguments } from '../../utils/spread.js';
 
 export default class ArrayExpression extends Node {
-	initialise ( transforms ) {
-		if ( transforms.spreadRest && this.elements.length ) {
+	initialise(transforms) {
+		if (transforms.spreadRest && this.elements.length) {
 			const lexicalBoundary = this.findLexicalBoundary();
 
 			let i = this.elements.length;
-			while ( i-- ) {
+			while (i--) {
 				const element = this.elements[i];
-				if ( element && element.type === 'SpreadElement' && isArguments( element.argument ) ) {
+				if (
+					element &&
+					element.type === 'SpreadElement' &&
+					isArguments(element.argument)
+				) {
 					this.argumentsArrayAlias = lexicalBoundary.getArgumentsArrayAlias();
 				}
 			}
 		}
 
-		super.initialise( transforms );
+		super.initialise(transforms);
 	}
 
-	transpile ( code, transforms ) {
-		if ( transforms.spreadRest ) {
+	transpile(code, transforms) {
+		if (transforms.spreadRest) {
 			// erase trailing comma after last array element if not an array hole
-			if ( this.elements.length ) {
-				let lastElement = this.elements[ this.elements.length - 1 ];
-				if ( lastElement && /\s*,/.test( code.original.slice( lastElement.end, this.end ) ) ) {
-					code.overwrite( lastElement.end, this.end - 1, ' ' );
+			if (this.elements.length) {
+				let lastElement = this.elements[this.elements.length - 1];
+				if (
+					lastElement &&
+					/\s*,/.test(code.original.slice(lastElement.end, this.end))
+				) {
+					code.overwrite(lastElement.end, this.end - 1, ' ');
 				}
 			}
 
-			if ( this.elements.length === 1 ) {
+			if (this.elements.length === 1) {
 				const element = this.elements[0];
 
-				if ( element && element.type === 'SpreadElement' ) {
+				if (element && element.type === 'SpreadElement') {
 					// special case – [ ...arguments ]
-					if ( isArguments( element.argument ) ) {
-						code.overwrite( this.start, this.end, `[].concat( ${this.argumentsArrayAlias} )` ); // TODO if this is the only use of argsArray, don't bother concating
+					if (isArguments(element.argument)) {
+						code.overwrite(
+							this.start,
+							this.end,
+							`[].concat( ${this.argumentsArrayAlias} )`
+						); // TODO if this is the only use of argsArray, don't bother concating
 					} else {
-						code.overwrite( this.start, element.argument.start, '[].concat( ' );
-						code.overwrite( element.end, this.end, ' )' );
+						code.overwrite(this.start, element.argument.start, '[].concat( ');
+						code.overwrite(element.end, this.end, ' )');
 					}
 				}
-			}
-			else {
-				const hasSpreadElements = spread( code, this.elements, this.start, this.argumentsArrayAlias );
+			} else {
+				const hasSpreadElements = spread(
+					code,
+					this.elements,
+					this.start,
+					this.argumentsArrayAlias
+				);
 
-				if ( hasSpreadElements ) {
-					code.overwrite( this.end - 1, this.end, ')' );
+				if (hasSpreadElements) {
+					code.overwrite(this.end - 1, this.end, ')');
 				}
 			}
 		}
 
-		super.transpile( code, transforms );
+		super.transpile(code, transforms);
 	}
 }
diff --git a/src/program/types/ArrowFunctionExpression.js b/src/program/types/ArrowFunctionExpression.js
index 9951d59..770bb2a 100644
--- a/src/program/types/ArrowFunctionExpression.js
+++ b/src/program/types/ArrowFunctionExpression.js
@@ -1,36 +1,52 @@
 import Node from '../Node.js';
+import removeTrailingComma from '../../utils/removeTrailingComma.js';
 
 export default class ArrowFunctionExpression extends Node {
-	initialise ( transforms ) {
+	initialise(transforms) {
 		this.body.createScope();
-		super.initialise( transforms );
+		super.initialise(transforms);
 	}
 
-	transpile ( code, transforms ) {
-		if ( transforms.arrow ) {
+	transpile(code, transforms) {
+		const naked = this.params.length === 1 && this.start === this.params[0].start;
+
+		if (transforms.arrow || this.needsArguments(transforms)) {
 			// remove arrow
 			let charIndex = this.body.start;
-			while ( code.original[ charIndex ] !== '=' ) {
+			while (code.original[charIndex] !== '=') {
 				charIndex -= 1;
 			}
-			code.remove( charIndex, this.body.start );
+			code.remove(charIndex, this.body.start);
+
+			super.transpile(code, transforms);
 
 			// wrap naked parameter
-			if ( this.params.length === 1 && this.start === this.params[0].start ) {
-				code.insertRight( this.params[0].start, '(' );
-				code.insertLeft( this.params[0].end, ')' );
+			if (naked) {
+				code.prependRight(this.params[0].start, '(');
+				code.appendLeft(this.params[0].end, ')');
 			}
 
 			// add function
-			if ( this.parent && this.parent.type === 'ExpressionStatement' ) {
+			if (this.parent && this.parent.type === 'ExpressionStatement') {
 				// standalone expression statement
-				code.insertRight( this.start, '(function' );
-				code.insertRight( this.end, ')' );
+				code.prependRight(this.start, '!function');
 			} else {
-				code.insertRight( this.start, 'function ' );
+				code.prependRight(this.start, 'function ');
 			}
+		} else {
+			super.transpile(code, transforms);
 		}
 
-		super.transpile( code, transforms );
+		if (transforms.trailingFunctionCommas && this.params.length && !naked) {
+			removeTrailingComma(code, this.params[this.params.length - 1].end);
+		}
+	}
+
+	// Returns whether any transforms that will happen use `arguments`
+	needsArguments(transforms) {
+		return (
+			transforms.spreadRest &&
+			this.params.filter(param => param.type === 'RestElement').length > 0
+		);
 	}
 }
diff --git a/src/program/types/AssignmentExpression.js b/src/program/types/AssignmentExpression.js
index d4969c4..a80dc60 100644
--- a/src/program/types/AssignmentExpression.js
+++ b/src/program/types/AssignmentExpression.js
@@ -2,39 +2,41 @@ import Node from '../Node.js';
 import CompileError from '../../utils/CompileError.js';
 
 export default class AssignmentExpression extends Node {
-	initialise ( transforms ) {
-		if ( this.left.type === 'Identifier' ) {
-			const declaration = this.findScope( false ).findDeclaration( this.left.name );
-			if ( declaration && declaration.kind === 'const' ) {
-				throw new CompileError( this.left, `${this.left.name} is read-only` );
+	initialise(transforms) {
+		if (this.left.type === 'Identifier') {
+			const declaration = this.findScope(false).findDeclaration(this.left.name);
+			if (declaration && declaration.kind === 'const') {
+				throw new CompileError(`${this.left.name} is read-only`, this.left);
 			}
 
 			// special case – https://gitlab.com/Rich-Harris/buble/issues/11
-			const statement = declaration && declaration.node.ancestor( 3 );
-			if ( statement && statement.type === 'ForStatement' && statement.body.contains( this ) ) {
-				statement.reassigned[ this.left.name ] = true;
+			const statement = declaration && declaration.node.ancestor(3);
+			if (
+				statement &&
+				statement.type === 'ForStatement' &&
+				statement.body.contains(this)
+			) {
+				statement.reassigned[this.left.name] = true;
 			}
 		}
 
-		super.initialise( transforms );
+		super.initialise(transforms);
 	}
 
-	transpile ( code, transforms ) {
-		if ( this.operator === '**=' && transforms.exponentiation ) {
-			this.transpileExponentiation( code, transforms );
+	transpile(code, transforms) {
+		if (this.operator === '**=' && transforms.exponentiation) {
+			this.transpileExponentiation(code, transforms);
+		} else if (/Pattern/.test(this.left.type) && transforms.destructuring) {
+			this.transpileDestructuring(code, transforms);
 		}
 
-		else if ( /Pattern/.test( this.left.type ) && transforms.destructuring ) {
-			this.transpileDestructuring( code, transforms );
-		}
-
-		super.transpile( code, transforms );
+		super.transpile(code, transforms);
 	}
 
-	transpileDestructuring ( code ) {
-		const scope = this.findScope( true );
-		const assign = scope.createIdentifier( 'assign' );
-		const temporaries = [ assign ];
+	transpileDestructuring(code) {
+		const scope = this.findScope(true);
+		const assign = scope.createIdentifier('assign');
+		const temporaries = [assign];
 
 		const start = this.start;
 
@@ -43,131 +45,160 @@ export default class AssignmentExpression extends Node {
 		// easily do that while keeping the order of the output
 		// predictable.
 		let text = '';
-		function use ( node ) {
-			code.insertRight( node.start, text );
-			code.move( node.start, node.end, start );
+		function use(node) {
+			code.prependRight(node.start, text);
+			code.move(node.start, node.end, start);
 			text = '';
 		}
-		function write ( string ) {
+		function write(string) {
 			text += string;
 		}
 
-		write( `(${assign} = ` );
-		use( this.right );
+		write(`(${assign} = `);
+		use(this.right);
 
 		// Walk `pattern`, generating code that assigns the value in
 		// `ref` to it. When `mayDuplicate` is false, the function
 		// must take care to only output `ref` once.
-		function destructure ( pattern, ref, mayDuplicate ) {
-			if ( pattern.type === 'Identifier' || pattern.type === 'MemberExpression' ) {
-				write( ', ' );
-				use( pattern );
-				write( ` = ${ref}` );
-			}
+		function destructure(pattern, ref, mayDuplicate) {
+			if (
+				pattern.type === 'Identifier' ||
+				pattern.type === 'MemberExpression'
+			) {
+				write(', ');
+				use(pattern);
+				write(` = ${ref}`);
+			} else if (pattern.type === 'AssignmentPattern') {
+				if (pattern.left.type === 'Identifier') {
+					code.remove(pattern.start, pattern.right.start);
 
-			else if ( pattern.type === 'AssignmentPattern' ) {
-				if ( pattern.left.type === 'Identifier' ) {
 					const target = pattern.left.name;
 					let source = ref;
-					if ( !mayDuplicate ) {
-						write( `, ${target} = ${ref}` );
+					if (!mayDuplicate) {
+						write(`, ${target} = ${ref}`);
 						source = target;
 					}
-					write( `, ${target} = ${source} === void 0 ? ` );
-					use( pattern.right );
-					write( ` : ${source}` );
-				}
-				else {
-					const target = scope.createIdentifier( 'temp' );
+					write(`, ${target} = ${source} === void 0 ? `);
+					use(pattern.right);
+					write(` : ${source}`);
+				} else {
+					code.remove(pattern.left.end, pattern.right.start);
+
+					const target = scope.createIdentifier('temp');
 					let source = ref;
-					temporaries.push( target );
-					if ( !mayDuplicate ) {
-						write( `, ${target} = ${ref}` );
+					temporaries.push(target);
+					if (!mayDuplicate) {
+						write(`, ${target} = ${ref}`);
 						source = target;
 					}
-					write( `, ${target} = ${source} === void 0 ? ` );
-					use( pattern.right );
-					write( ` : ${source}` );
-					destructure( pattern.left, target, true );
+					write(`, ${target} = ${source} === void 0 ? `);
+					use(pattern.right);
+					write(` : ${source}`);
+					destructure(pattern.left, target, true);
 				}
-			}
-
-			else if ( pattern.type === 'ArrayPattern' ) {
+			} else if (pattern.type === 'ArrayPattern') {
 				const elements = pattern.elements;
-				if ( elements.length === 1 ) {
-					destructure( elements[0], `${ref}[0]`, false );
-				}
-				else {
-					if ( !mayDuplicate ) {
-						const temp = scope.createIdentifier( 'array' );
-						temporaries.push( temp );
-						write( `, ${temp} = ${ref}` );
+				if (elements.length === 1) {
+					code.remove(pattern.start, elements[0].start);
+					destructure(elements[0], `${ref}[0]`, false);
+					code.remove(elements[0].end, pattern.end);
+				} else {
+					if (!mayDuplicate) {
+						const temp = scope.createIdentifier('array');
+						temporaries.push(temp);
+						write(`, ${temp} = ${ref}`);
 						ref = temp;
 					}
-					elements.forEach( ( element, i ) => {
-						if ( element ) {
-							if ( element.type === 'RestElement' ) {
-								destructure( element.argument, `${ref}.slice(${i})`, false );
-							} else {
-								destructure( element, `${ref}[${i}]`, false );
-							}
+
+					let c = pattern.start;
+					elements.forEach((element, i) => {
+						if (!element) return;
+
+						code.remove(c, element.start);
+						c = element.end;
+
+						if (element.type === 'RestElement') {
+							code.remove(element.start, element.argument.start);
+							destructure(element.argument, `${ref}.slice(${i})`, false);
+						} else {
+							destructure(element, `${ref}[${i}]`, false);
 						}
-					} );
-				}
-			}
+					});
 
-			else if ( pattern.type === 'ObjectPattern' ) {
+					code.remove(c, pattern.end);
+				}
+			} else if (pattern.type === 'ObjectPattern') {
 				const props = pattern.properties;
-				if ( props.length == 1 ) {
+				if (props.length == 1) {
 					const prop = props[0];
-					const value = prop.computed || prop.key.type !== 'Identifier' ? `${ref}[${code.slice(prop.key.start, prop.key.end)}]` : `${ref}.${prop.key.name}`;
-					destructure( prop.value, value, false );
-				}
-				else {
-					if ( !mayDuplicate ) {
-						const temp = scope.createIdentifier( 'obj' );
-						temporaries.push( temp );
-						write( `, ${temp} = ${ref}` );
+					const value =
+						prop.computed || prop.key.type !== 'Identifier'
+							? `${ref}[${code.slice(prop.key.start, prop.key.end)}]`
+							: `${ref}.${prop.key.name}`;
+
+					code.remove(pattern.start, prop.value.start);
+					destructure(prop.value, value, false);
+					code.remove(prop.end, pattern.end);
+				} else {
+					if (!mayDuplicate) {
+						const temp = scope.createIdentifier('obj');
+						temporaries.push(temp);
+						write(`, ${temp} = ${ref}`);
 						ref = temp;
 					}
-					props.forEach( prop => {
-						const value = prop.computed || prop.key.type !== 'Identifier' ? `${ref}[${code.slice(prop.key.start, prop.key.end)}]` : `${ref}.${prop.key.name}`;
-						destructure( prop.value, value, false );
-					} );
-				}
-			}
 
-			else {
-				throw new Error( `Unexpected node type in destructuring assignment (${pattern.type})` );
+					let c = pattern.start;
+
+					props.forEach(prop => {
+						const value =
+							prop.computed || prop.key.type !== 'Identifier'
+								? `${ref}[${code.slice(prop.key.start, prop.key.end)}]`
+								: `${ref}.${prop.key.name}`;
+
+						code.remove(c, prop.value.start);
+						c = prop.end;
+
+						destructure(prop.value, value, false);
+					});
+
+					code.remove(c, pattern.end);
+				}
+			} else {
+				throw new Error(
+					`Unexpected node type in destructuring assignment (${pattern.type})`
+				);
 			}
 		}
-		destructure( this.left, assign, true );
 
-		if ( this.unparenthesizedParent().type === 'ExpressionStatement' ) {
+		destructure(this.left, assign, true);
+		code.remove(this.left.end, this.right.start);
+
+		if (this.unparenthesizedParent().type === 'ExpressionStatement') {
 			// no rvalue needed for expression statement
-			code.insertRight( start, `${text})` );
+			code.prependRight(start, `${text})`);
 		} else {
 			// destructuring is part of an expression - need an rvalue
-			code.insertRight( start, `${text}, ${assign})` );
+			code.prependRight(start, `${text}, ${assign})`);
 		}
 
-		code.remove( start, this.right.start );
-
-		const statement = this.findNearest( /(?:Statement|Declaration)$/ );
-		code.insertLeft( statement.start, `var ${temporaries.join( ', ' )};\n${statement.getIndentation()}` );
+		const statement = this.findNearest(/(?:Statement|Declaration)$/);
+		code.appendLeft(
+			statement.start,
+			`var ${temporaries.join(', ')};\n${statement.getIndentation()}`
+		);
 	}
 
-	transpileExponentiation ( code ) {
-		const scope = this.findScope( false );
+	transpileExponentiation(code) {
+		const scope = this.findScope(false);
 		const getAlias = name => {
-			const declaration = scope.findDeclaration( name );
+			const declaration = scope.findDeclaration(name);
 			return declaration ? declaration.name : name;
 		};
 
 		// first, the easy part – `**=` -> `=`
 		let charIndex = this.left.end;
-		while ( code.original[ charIndex ] !== '*' ) charIndex += 1;
-		code.remove( charIndex, charIndex + 2 );
+		while (code.original[charIndex] !== '*') charIndex += 1;
+		code.remove(charIndex, charIndex + 2);
 
 		// how we do the next part depends on a number of factors – whether
 		// this is a top-level statement, and whether we're updating a
@@ -176,93 +207,106 @@ export default class AssignmentExpression extends Node {
 
 		const left = this.left.unparenthesize();
 
-		if ( left.type === 'Identifier' ) {
-			base = getAlias( left.name );
-		} else if ( left.type === 'MemberExpression' ) {
+		if (left.type === 'Identifier') {
+			base = getAlias(left.name);
+		} else if (left.type === 'MemberExpression') {
 			let object;
 			let needsObjectVar = false;
 			let property;
 			let needsPropertyVar = false;
 
-			const statement = this.findNearest( /(?:Statement|Declaration)$/ );
+			const statement = this.findNearest(/(?:Statement|Declaration)$/);
 			const i0 = statement.getIndentation();
 
-			if ( left.property.type === 'Identifier' ) {
-				property = left.computed ? getAlias( left.property.name ) : left.property.name;
+			if (left.property.type === 'Identifier') {
+				property = left.computed
+					? getAlias(left.property.name)
+					: left.property.name;
 			} else {
-				property = scope.createIdentifier( 'property' );
+				property = scope.createIdentifier('property');
 				needsPropertyVar = true;
 			}
 
-			if ( left.object.type === 'Identifier' ) {
-				object = getAlias( left.object.name );
+			if (left.object.type === 'Identifier') {
+				object = getAlias(left.object.name);
 			} else {
-				object = scope.createIdentifier( 'object' );
+				object = scope.createIdentifier('object');
 				needsObjectVar = true;
 			}
 
-			if ( left.start === statement.start ) {
-				if ( needsObjectVar && needsPropertyVar ) {
-					code.insertRight( statement.start, `var ${object} = ` );
-					code.overwrite( left.object.end, left.property.start, `;\n${i0}var ${property} = ` );
-					code.overwrite( left.property.end, left.end, `;\n${i0}${object}[${property}]` );
-				}
-
-				else if ( needsObjectVar ) {
-					code.insertRight( statement.start, `var ${object} = ` );
-					code.insertLeft( left.object.end, `;\n${i0}` );
-					code.insertLeft( left.object.end, object );
-				}
-
-				else if ( needsPropertyVar ) {
-					code.insertRight( left.property.start, `var ${property} = ` );
-					code.insertLeft( left.property.end, `;\n${i0}` );
-					code.move( left.property.start, left.property.end, this.start );
-
-					code.insertLeft( left.object.end, `[${property}]` );
-					code.remove( left.object.end, left.property.start );
-					code.remove( left.property.end, left.end );
+			if (left.start === statement.start) {
+				if (needsObjectVar && needsPropertyVar) {
+					code.prependRight(statement.start, `var ${object} = `);
+					code.overwrite(
+						left.object.end,
+						left.property.start,
+						`;\n${i0}var ${property} = `
+					);
+					code.overwrite(
+						left.property.end,
+						left.end,
+						`;\n${i0}${object}[${property}]`
+					);
+				} else if (needsObjectVar) {
+					code.prependRight(statement.start, `var ${object} = `);
+					code.appendLeft(left.object.end, `;\n${i0}`);
+					code.appendLeft(left.object.end, object);
+				} else if (needsPropertyVar) {
+					code.prependRight(left.property.start, `var ${property} = `);
+					code.appendLeft(left.property.end, `;\n${i0}`);
+					code.move(left.property.start, left.property.end, this.start);
+
+					code.appendLeft(left.object.end, `[${property}]`);
+					code.remove(left.object.end, left.property.start);
+					code.remove(left.property.end, left.end);
 				}
-			}
-
-			else {
+			} else {
 				let declarators = [];
-				if ( needsObjectVar ) declarators.push( object );
-				if ( needsPropertyVar ) declarators.push( property );
-
-				if ( declarators.length ) {
-					code.insertRight( statement.start, `var ${declarators.join( ', ' )};\n${i0}` );
+				if (needsObjectVar) declarators.push(object);
+				if (needsPropertyVar) declarators.push(property);
+
+				if (declarators.length) {
+					code.prependRight(
+						statement.start,
+						`var ${declarators.join(', ')};\n${i0}`
+					);
 				}
 
-				if ( needsObjectVar && needsPropertyVar ) {
-					code.insertRight( left.start, `( ${object} = ` );
-					code.overwrite( left.object.end, left.property.start, `, ${property} = ` );
-					code.overwrite( left.property.end, left.end, `, ${object}[${property}]` );
-				}
-
-				else if ( needsObjectVar ) {
-					code.insertRight( left.start, `( ${object} = ` );
-					code.insertLeft( left.object.end, `, ${object}` );
-				}
-
-				else if ( needsPropertyVar ) {
-					code.insertRight( left.property.start, `( ${property} = ` );
-					code.insertLeft( left.property.end, `, ` );
-					code.move( left.property.start, left.property.end, left.start );
-
-					code.overwrite( left.object.end, left.property.start, `[${property}]` );
-					code.remove( left.property.end, left.end );
+				if (needsObjectVar && needsPropertyVar) {
+					code.prependRight(left.start, `( ${object} = `);
+					code.overwrite(
+						left.object.end,
+						left.property.start,
+						`, ${property} = `
+					);
+					code.overwrite(
+						left.property.end,
+						left.end,
+						`, ${object}[${property}]`
+					);
+				} else if (needsObjectVar) {
+					code.prependRight(left.start, `( ${object} = `);
+					code.appendLeft(left.object.end, `, ${object}`);
+				} else if (needsPropertyVar) {
+					code.prependRight(left.property.start, `( ${property} = `);
+					code.appendLeft(left.property.end, `, `);
+					code.move(left.property.start, left.property.end, left.start);
+
+					code.overwrite(left.object.end, left.property.start, `[${property}]`);
+					code.remove(left.property.end, left.end);
 				}
 
-				if ( needsPropertyVar ) {
-					code.insertLeft( this.end, ` )` );
+				if (needsPropertyVar) {
+					code.appendLeft(this.end, ` )`);
 				}
 			}
 
-			base = object + ( left.computed || needsPropertyVar ? `[${property}]` : `.${property}` );
+			base =
+				object +
+				(left.computed || needsPropertyVar ? `[${property}]` : `.${property}`);
 		}
 
-		code.insertRight( this.right.start, `Math.pow( ${base}, ` );
-		code.insertLeft( this.right.end, ` )` );
+		code.prependRight(this.right.start, `Math.pow( ${base}, `);
+		code.appendLeft(this.right.end, ` )`);
 	}
 }
diff --git a/src/program/types/BinaryExpression.js b/src/program/types/BinaryExpression.js
index 8d0bf61..ea778a3 100644
--- a/src/program/types/BinaryExpression.js
+++ b/src/program/types/BinaryExpression.js
@@ -1,12 +1,12 @@
 import Node from '../Node.js';
 
 export default class BinaryExpression extends Node {
-	transpile ( code, transforms ) {
-		if ( this.operator === '**' && transforms.exponentiation ) {
-			code.insertRight( this.start, `Math.pow( ` );
-			code.overwrite( this.left.end, this.right.start, `, ` );
-			code.insertLeft( this.end, ` )` );
+	transpile(code, transforms) {
+		if (this.operator === '**' && transforms.exponentiation) {
+			code.prependRight(this.start, `Math.pow( `);
+			code.overwrite(this.left.end, this.right.start, `, `);
+			code.appendLeft(this.end, ` )`);
 		}
-		super.transpile( code, transforms );
+		super.transpile(code, transforms);
 	}
 }
diff --git a/src/program/types/BreakStatement.js b/src/program/types/BreakStatement.js
index ea24dd9..748fe61 100644
--- a/src/program/types/BreakStatement.js
+++ b/src/program/types/BreakStatement.js
@@ -3,20 +3,24 @@ import CompileError from '../../utils/CompileError.js';
 import { loopStatement } from '../../utils/patterns.js';
 
 export default class BreakStatement extends Node {
-	initialise () {
-		const loop = this.findNearest( loopStatement );
-		const switchCase = this.findNearest( 'SwitchCase' );
+	initialise() {
+		const loop = this.findNearest(loopStatement);
+		const switchCase = this.findNearest('SwitchCase');
 
-		if ( loop && ( !switchCase || loop.depth > switchCase.depth ) ) {
+		if (loop && (!switchCase || loop.depth > switchCase.depth)) {
 			loop.canBreak = true;
 			this.loop = loop;
 		}
 	}
 
-	transpile ( code ) {
-		if ( this.loop && this.loop.shouldRewriteAsFunction ) {
-			if ( this.label ) throw new CompileError( this, 'Labels are not currently supported in a loop with locally-scoped variables' );
-			code.overwrite( this.start, this.start + 5, `return 'break'` );
+	transpile(code) {
+		if (this.loop && this.loop.shouldRewriteAsFunction) {
+			if (this.label)
+				throw new CompileError(
+					'Labels are not currently supported in a loop with locally-scoped variables',
+					this
+				);
+			code.overwrite(this.start, this.start + 5, `return 'break'`);
 		}
 	}
 }
diff --git a/src/program/types/CallExpression.js b/src/program/types/CallExpression.js
index 1e07ede..3f6b2c3 100644
--- a/src/program/types/CallExpression.js
+++ b/src/program/types/CallExpression.js
@@ -1,98 +1,111 @@
 import Node from '../Node.js';
 import spread, { isArguments } from '../../utils/spread.js';
+import removeTrailingComma from '../../utils/removeTrailingComma.js';
 
 export default class CallExpression extends Node {
-	initialise ( transforms ) {
-		if ( transforms.spreadRest && this.arguments.length > 1 ) {
+	initialise(transforms) {
+		if (transforms.spreadRest && this.arguments.length > 1) {
 			const lexicalBoundary = this.findLexicalBoundary();
 
 			let i = this.arguments.length;
-			while ( i-- ) {
+			while (i--) {
 				const arg = this.arguments[i];
-				if ( arg.type === 'SpreadElement' && isArguments( arg.argument ) ) {
+				if (arg.type === 'SpreadElement' && isArguments(arg.argument)) {
 					this.argumentsArrayAlias = lexicalBoundary.getArgumentsArrayAlias();
 				}
 			}
 		}
 
-		super.initialise( transforms );
+		super.initialise(transforms);
 	}
 
-	transpile ( code, transforms ) {
-		if ( transforms.spreadRest && this.arguments.length ) {
+	transpile(code, transforms) {
+		if (transforms.spreadRest && this.arguments.length) {
 			let hasSpreadElements = false;
 			let context;
 
 			const firstArgument = this.arguments[0];
 
-			if ( this.arguments.length === 1 ) {
-				if ( firstArgument.type === 'SpreadElement' ) {
-					code.remove( firstArgument.start, firstArgument.argument.start );
+			if (this.arguments.length === 1) {
+				if (firstArgument.type === 'SpreadElement') {
+					code.remove(firstArgument.start, firstArgument.argument.start);
 					hasSpreadElements = true;
 				}
 			} else {
-				hasSpreadElements = spread( code, this.arguments, firstArgument.start, this.argumentsArrayAlias );
+				hasSpreadElements = spread(
+					code,
+					this.arguments,
+					firstArgument.start,
+					this.argumentsArrayAlias
+				);
 			}
 
-			if ( hasSpreadElements ) {
-
+			if (hasSpreadElements) {
 				// we need to handle super() and super.method() differently
 				// due to its instance
 				let _super = null;
-				if ( this.callee.type === 'Super' ) {
+				if (this.callee.type === 'Super') {
 					_super = this.callee;
-				}
-				else if ( this.callee.type === 'MemberExpression' && this.callee.object.type === 'Super' ) {
+				} else if (
+					this.callee.type === 'MemberExpression' &&
+					this.callee.object.type === 'Super'
+				) {
 					_super = this.callee.object;
 				}
 
-				if ( !_super && this.callee.type === 'MemberExpression' ) {
-					if ( this.callee.object.type === 'Identifier' ) {
+				if (!_super && this.callee.type === 'MemberExpression') {
+					if (this.callee.object.type === 'Identifier') {
 						context = this.callee.object.name;
 					} else {
-						context = this.findScope( true ).createIdentifier( 'ref' );
+						context = this.findScope(true).createIdentifier('ref');
 						const callExpression = this.callee.object;
-						const enclosure = callExpression.findNearest( /Function/ );
-						const block = enclosure ? enclosure.body.body
-							: callExpression.findNearest( /^Program$/ ).body;
-						const lastStatementInBlock = block[ block.length - 1 ];
+						const enclosure = callExpression.findNearest(/Function/);
+						const block = enclosure
+							? enclosure.body.body
+							: callExpression.findNearest(/^Program$/).body;
+						const lastStatementInBlock = block[block.length - 1];
 						const i0 = lastStatementInBlock.getIndentation();
-						code.insertRight( callExpression.start, `(${context} = ` );
-						code.insertLeft( callExpression.end, `)` );
-						code.insertLeft( lastStatementInBlock.end, `\n${i0}var ${context};` );
+						code.prependRight(callExpression.start, `(${context} = `);
+						code.appendLeft(callExpression.end, `)`);
+						code.appendLeft(lastStatementInBlock.end, `\n${i0}var ${context};`);
 					}
 				} else {
 					context = 'void 0';
 				}
 
-				code.insertLeft( this.callee.end, '.apply' );
+				code.appendLeft(this.callee.end, '.apply');
 
-				if ( _super ) {
+				if (_super) {
 					_super.noCall = true; // bit hacky...
 
-					if ( this.arguments.length > 1 ) {
-						if ( firstArgument.type !== 'SpreadElement' ) {
-							code.insertRight( firstArgument.start, `[ ` );
+					if (this.arguments.length > 1) {
+						if (firstArgument.type !== 'SpreadElement') {
+							code.prependRight(firstArgument.start, `[ `);
 						}
 
-						code.insertLeft( this.arguments[ this.arguments.length - 1 ].end, ' )' );
+						code.appendLeft(
+							this.arguments[this.arguments.length - 1].end,
+							' )'
+						);
 					}
-				}
-
-				else if ( this.arguments.length === 1 ) {
-					code.insertRight( firstArgument.start, `${context}, ` );
+				} else if (this.arguments.length === 1) {
+					code.prependRight(firstArgument.start, `${context}, `);
 				} else {
-					if ( firstArgument.type === 'SpreadElement' ) {
-						code.insertLeft( firstArgument.start, `${context}, ` );
+					if (firstArgument.type === 'SpreadElement') {
+						code.appendLeft(firstArgument.start, `${context}, `);
 					} else {
-						code.insertLeft( firstArgument.start, `${context}, [ ` );
+						code.appendLeft(firstArgument.start, `${context}, [ `);
 					}
 
-					code.insertLeft( this.arguments[ this.arguments.length - 1 ].end, ' )' );
+					code.appendLeft(this.arguments[this.arguments.length - 1].end, ' )');
 				}
 			}
 		}
 
-		super.transpile( code, transforms );
+		if (transforms.trailingFunctionCommas && this.arguments.length) {
+			removeTrailingComma(code, this.arguments[this.arguments.length - 1].end);
+		}
+
+		super.transpile(code, transforms);
 	}
 }
diff --git a/src/program/types/ClassBody.js b/src/program/types/ClassBody.js
index 65c00b5..8a91769 100644
--- a/src/program/types/ClassBody.js
+++ b/src/program/types/ClassBody.js
@@ -4,183 +4,234 @@ import reserved from '../../utils/reserved.js';
 
 // TODO this code is pretty wild, tidy it up
 export default class ClassBody extends Node {
-	transpile ( code, transforms, inFunctionExpression, superName ) {
-		if ( transforms.classes ) {
+	transpile(code, transforms, inFunctionExpression, superName) {
+		if (transforms.classes) {
 			const name = this.parent.name;
 
 			const indentStr = code.getIndentString();
-			const i0 = this.getIndentation() + ( inFunctionExpression ? indentStr : '' );
+			const i0 =
+				this.getIndentation() + (inFunctionExpression ? indentStr : '');
 			const i1 = i0 + indentStr;
 
-			const constructorIndex = findIndex( this.body, node => node.kind === 'constructor' );
-			const constructor = this.body[ constructorIndex ];
+			const constructorIndex = findIndex(
+				this.body,
+				node => node.kind === 'constructor'
+			);
+			const constructor = this.body[constructorIndex];
 
 			let introBlock = '';
 			let outroBlock = '';
 
-			if ( this.body.length ) {
-				code.remove( this.start, this.body[0].start );
-				code.remove( this.body[ this.body.length - 1 ].end, this.end );
+			if (this.body.length) {
+				code.remove(this.start, this.body[0].start);
+				code.remove(this.body[this.body.length - 1].end, this.end);
 			} else {
-				code.remove( this.start, this.end );
+				code.remove(this.start, this.end);
 			}
 
-			if ( constructor ) {
+			if (constructor) {
 				constructor.value.body.isConstructorBody = true;
 
-				const previousMethod = this.body[ constructorIndex - 1 ];
-				const nextMethod = this.body[ constructorIndex + 1 ];
+				const previousMethod = this.body[constructorIndex - 1];
+				const nextMethod = this.body[constructorIndex + 1];
 
 				// ensure constructor is first
-				if ( constructorIndex > 0 ) {
-					code.remove( previousMethod.end, constructor.start );
-					code.move( constructor.start, nextMethod ? nextMethod.start : this.end - 1, this.body[0].start );
+				if (constructorIndex > 0) {
+					code.remove(previousMethod.end, constructor.start);
+					code.move(
+						constructor.start,
+						nextMethod ? nextMethod.start : this.end - 1,
+						this.body[0].start
+					);
 				}
 
-				if ( !inFunctionExpression ) code.insertLeft( constructor.end, ';' );
+				if (!inFunctionExpression) code.appendLeft(constructor.end, ';');
 			}
 
-			let namedFunctions = this.program.options.namedFunctionExpressions !== false;
-			let namedConstructor = namedFunctions || this.parent.superClass || this.parent.type !== 'ClassDeclaration';
-			if ( this.parent.superClass ) {
-				let inheritanceBlock = `if ( ${superName} ) ${name}.__proto__ = ${superName};\n${i0}${name}.prototype = Object.create( ${superName} && ${superName}.prototype );\n${i0}${name}.prototype.constructor = ${name};`;
-
-				if ( constructor ) {
+			let namedFunctions =
+				this.program.options.namedFunctionExpressions !== false;
+			let namedConstructor =
+				namedFunctions ||
+				this.parent.superClass ||
+				this.parent.type !== 'ClassDeclaration';
+			if (this.parent.superClass) {
+				let inheritanceBlock = `if ( ${superName} ) ${name}.__proto__ = ${
+					superName
+				};\n${i0}${name}.prototype = Object.create( ${superName} && ${
+					superName
+				}.prototype );\n${i0}${name}.prototype.constructor = ${name};`;
+
+				if (constructor) {
 					introBlock += `\n\n${i0}` + inheritanceBlock;
 				} else {
-					const fn = `function ${name} () {` + ( superName ?
-						`\n${i1}${superName}.apply(this, arguments);\n${i0}}` :
-						`}` ) + ( inFunctionExpression ? '' : ';' ) + ( this.body.length ? `\n\n${i0}` : '' );
+					const fn =
+						`function ${name} () {` +
+						(superName
+							? `\n${i1}${superName}.apply(this, arguments);\n${i0}}`
+							: `}`) +
+						(inFunctionExpression ? '' : ';') +
+						(this.body.length ? `\n\n${i0}` : '');
 
 					inheritanceBlock = fn + inheritanceBlock;
 					introBlock += inheritanceBlock + `\n\n${i0}`;
 				}
-			} else if ( !constructor ) {
+			} else if (!constructor) {
 				let fn = 'function ' + (namedConstructor ? name + ' ' : '') + '() {}';
-				if ( this.parent.type === 'ClassDeclaration' ) fn += ';';
-				if ( this.body.length ) fn += `\n\n${i0}`;
+				if (this.parent.type === 'ClassDeclaration') fn += ';';
+				if (this.body.length) fn += `\n\n${i0}`;
 
 				introBlock += fn;
 			}
 
-			const scope = this.findScope( false );
+			const scope = this.findScope(false);
 
 			let prototypeGettersAndSetters = [];
 			let staticGettersAndSetters = [];
 			let prototypeAccessors;
 			let staticAccessors;
 
-			this.body.forEach( ( method, i ) => {
-				if ( method.kind === 'constructor' ) {
+			this.body.forEach((method, i) => {
+				if (method.kind === 'constructor') {
 					let constructorName = namedConstructor ? ' ' + name : '';
-					code.overwrite( method.key.start, method.key.end, `function${constructorName}` );
+					code.overwrite(
+						method.key.start,
+						method.key.end,
+						`function${constructorName}`
+					);
 					return;
 				}
 
-				if ( method.static ) {
-					const len = code.original[ method.start + 6 ] == ' ' ? 7 : 6;
-					code.remove( method.start, method.start + len );
+				if (method.static) {
+					const len = code.original[method.start + 6] == ' ' ? 7 : 6;
+					code.remove(method.start, method.start + len);
 				}
 
 				const isAccessor = method.kind !== 'method';
 				let lhs;
 
 				let methodName = method.key.name;
-				if ( reserved[ methodName ] || method.value.body.scope.references[methodName] ) {
-					methodName = scope.createIdentifier( methodName );
+				if (
+					reserved[methodName] ||
+					method.value.body.scope.references[methodName]
+				) {
+					methodName = scope.createIdentifier(methodName);
 				}
 
 				// when method name is a string or a number let's pretend it's a computed method
 
 				let fake_computed = false;
-				if ( ! method.computed && method.key.type === 'Literal' ) {
+				if (!method.computed && method.key.type === 'Literal') {
 					fake_computed = true;
 					method.computed = true;
 				}
 
-				if ( isAccessor ) {
-					if ( method.computed ) {
-						throw new Error( 'Computed accessor properties are not currently supported' );
+				if (isAccessor) {
+					if (method.computed) {
+						throw new Error(
+							'Computed accessor properties are not currently supported'
+						);
 					}
 
-					code.remove( method.start, method.key.start );
+					code.remove(method.start, method.key.start);
 
-					if ( method.static ) {
-						if ( !~staticGettersAndSetters.indexOf( method.key.name ) ) staticGettersAndSetters.push( method.key.name );
-						if ( !staticAccessors ) staticAccessors = scope.createIdentifier( 'staticAccessors' );
+					if (method.static) {
+						if (!~staticGettersAndSetters.indexOf(method.key.name))
+							staticGettersAndSetters.push(method.key.name);
+						if (!staticAccessors)
+							staticAccessors = scope.createIdentifier('staticAccessors');
 
 						lhs = `${staticAccessors}`;
 					} else {
-						if ( !~prototypeGettersAndSetters.indexOf( method.key.name ) ) prototypeGettersAndSetters.push( method.key.name );
-						if ( !prototypeAccessors ) prototypeAccessors = scope.createIdentifier( 'prototypeAccessors' );
+						if (!~prototypeGettersAndSetters.indexOf(method.key.name))
+							prototypeGettersAndSetters.push(method.key.name);
+						if (!prototypeAccessors)
+							prototypeAccessors = scope.createIdentifier('prototypeAccessors');
 
 						lhs = `${prototypeAccessors}`;
 					}
 				} else {
-					lhs = method.static ?
-						`${name}` :
-						`${name}.prototype`;
+					lhs = method.static ? `${name}` : `${name}.prototype`;
 				}
 
-				if ( !method.computed ) lhs += '.';
+				if (!method.computed) lhs += '.';
 
-				const insertNewlines = ( constructorIndex > 0 && i === constructorIndex + 1 ) ||
-				                       ( i === 0 && constructorIndex === this.body.length - 1 );
+				const insertNewlines =
+					(constructorIndex > 0 && i === constructorIndex + 1) ||
+					(i === 0 && constructorIndex === this.body.length - 1);
 
-				if ( insertNewlines ) lhs = `\n\n${i0}${lhs}`;
+				if (insertNewlines) lhs = `\n\n${i0}${lhs}`;
 
 				let c = method.key.end;
-				if ( method.computed ) {
-					if ( fake_computed ) {
-						code.insertRight( method.key.start, '[' );
-						code.insertLeft( method.key.end, ']' );
+				if (method.computed) {
+					if (fake_computed) {
+						code.prependRight(method.key.start, '[');
+						code.appendLeft(method.key.end, ']');
 					} else {
-						while ( code.original[c] !== ']' ) c += 1;
+						while (code.original[c] !== ']') c += 1;
 						c += 1;
 					}
 				}
 
-				code.insertRight( method.start, lhs );
-
-				const funcName = method.computed || isAccessor || !namedFunctions ? '' : `${methodName} `;
-				const rhs = ( isAccessor ? `.${method.kind}` : '' ) + ` = function` + ( method.value.generator ? '* ' : ' ' ) + funcName;
-				code.remove( c, method.value.start );
-				code.insertRight( method.value.start, rhs );
-				code.insertLeft( method.end, ';' );
-
-				if ( method.value.generator ) code.remove( method.start, method.key.start );
+				const funcName =
+					method.computed || isAccessor || !namedFunctions
+						? ''
+						: `${methodName} `;
+				const rhs =
+					(isAccessor ? `.${method.kind}` : '') +
+					` = function` +
+					(method.value.generator ? '* ' : ' ') +
+					funcName;
+				code.remove(c, method.value.start);
+				code.prependRight(method.value.start, rhs);
+				code.appendLeft(method.end, ';');
+
+				if (method.value.generator) code.remove(method.start, method.key.start);
+
+				code.prependRight(method.start, lhs);
 			});
 
-			if ( prototypeGettersAndSetters.length || staticGettersAndSetters.length ) {
+			if (prototypeGettersAndSetters.length || staticGettersAndSetters.length) {
 				let intro = [];
 				let outro = [];
 
-				if ( prototypeGettersAndSetters.length ) {
-					intro.push( `var ${prototypeAccessors} = { ${prototypeGettersAndSetters.map( name => `${name}: { configurable: true }` ).join( ',' )} };` );
-					outro.push( `Object.defineProperties( ${name}.prototype, ${prototypeAccessors} );` );
+				if (prototypeGettersAndSetters.length) {
+					intro.push(
+						`var ${prototypeAccessors} = { ${prototypeGettersAndSetters
+							.map(name => `${name}: { configurable: true }`)
+							.join(',')} };`
+					);
+					outro.push(
+						`Object.defineProperties( ${name}.prototype, ${
+							prototypeAccessors
+						} );`
+					);
 				}
 
-				if ( staticGettersAndSetters.length ) {
-					intro.push( `var ${staticAccessors} = { ${staticGettersAndSetters.map( name => `${name}: { configurable: true }` ).join( ',' )} };` );
-					outro.push( `Object.defineProperties( ${name}, ${staticAccessors} );` );
+				if (staticGettersAndSetters.length) {
+					intro.push(
+						`var ${staticAccessors} = { ${staticGettersAndSetters
+							.map(name => `${name}: { configurable: true }`)
+							.join(',')} };`
+					);
+					outro.push(`Object.defineProperties( ${name}, ${staticAccessors} );`);
 				}
 
-				if ( constructor ) introBlock += `\n\n${i0}`;
-				introBlock += intro.join( `\n${i0}` );
-				if ( !constructor ) introBlock += `\n\n${i0}`;
+				if (constructor) introBlock += `\n\n${i0}`;
+				introBlock += intro.join(`\n${i0}`);
+				if (!constructor) introBlock += `\n\n${i0}`;
 
-				outroBlock += `\n\n${i0}` + outro.join( `\n${i0}` );
+				outroBlock += `\n\n${i0}` + outro.join(`\n${i0}`);
 			}
 
-			if ( constructor ) {
-				code.insertLeft( constructor.end, introBlock );
+			if (constructor) {
+				code.appendLeft(constructor.end, introBlock);
 			} else {
-				code.insertRight( this.start, introBlock );
+				code.prependRight(this.start, introBlock);
 			}
 
-			code.insertLeft( this.end, outroBlock );
+			code.appendLeft(this.end, outroBlock);
 		}
 
-		super.transpile( code, transforms );
+		super.transpile(code, transforms);
 	}
 }
diff --git a/src/program/types/ClassDeclaration.js b/src/program/types/ClassDeclaration.js
index 6447737..ddca8e7 100644
--- a/src/program/types/ClassDeclaration.js
+++ b/src/program/types/ClassDeclaration.js
@@ -2,61 +2,78 @@ import Node from '../Node.js';
 import deindent from '../../utils/deindent.js';
 
 export default class ClassDeclaration extends Node {
-	initialise ( transforms ) {
-		this.name = this.id.name;
-		this.findScope( true ).addDeclaration( this.id, 'class' );
+	initialise(transforms) {
+		if (this.id) {
+			this.name = this.id.name;
+			this.findScope(true).addDeclaration(this.id, 'class');
+		} else {
+			this.name = this.findScope(true).createIdentifier("defaultExport");
+		}
 
-		super.initialise( transforms );
+		super.initialise(transforms);
 	}
 
-	transpile ( code, transforms ) {
-		if ( transforms.classes ) {
-			if ( !this.superClass ) deindent( this.body, code );
+	transpile(code, transforms) {
+		if (transforms.classes) {
+			if (!this.superClass) deindent(this.body, code);
 
-			const superName = this.superClass && ( this.superClass.name || 'superclass' );
+			const superName =
+				this.superClass && (this.superClass.name || 'superclass');
 
 			const i0 = this.getIndentation();
 			const i1 = i0 + code.getIndentString();
 
 			// if this is an export default statement, we have to move the export to
 			// after the declaration, because `export default var Foo = ...` is illegal
-			const syntheticDefaultExport = this.parent.type === 'ExportDefaultDeclaration' ?
-				`\n\n${i0}export default ${this.id.name};` :
-				'';
+			const isExportDefaultDeclaration = this.parent.type === 'ExportDefaultDeclaration';
 
-			if ( syntheticDefaultExport ) code.remove( this.parent.start, this.start );
+			if (isExportDefaultDeclaration) {
+				code.remove(this.parent.start, this.start);
+			}
 
-			code.overwrite( this.start, this.id.start, 'var ' );
+			let c = this.start
+			if (this.id) {
+				code.overwrite(c, this.id.start, 'var ');
+				c = this.id.end
+			} else {
+				code.prependLeft(c, `var ${this.name}`);
+			}
 
-			if ( this.superClass ) {
-				if ( this.superClass.end === this.body.start ) {
-					code.remove( this.id.end, this.superClass.start );
-					code.insertLeft( this.id.end, ` = (function (${superName}) {\n${i1}` );
+			if (this.superClass) {
+				if (this.superClass.end === this.body.start) {
+					code.remove(c, this.superClass.start);
+					code.appendLeft(c, ` = (function (${superName}) {\n${i1}`);
 				} else {
-					code.overwrite( this.id.end, this.superClass.start, ' = ' );
-					code.overwrite( this.superClass.end, this.body.start, `(function (${superName}) {\n${i1}` );
+					code.overwrite(c, this.superClass.start, ' = ');
+					code.overwrite(
+						this.superClass.end,
+						this.body.start,
+						`(function (${superName}) {\n${i1}`
+					);
 				}
 			} else {
-				if ( this.id.end === this.body.start ) {
-					code.insertLeft( this.id.end, ' = ' );
+				if (c === this.body.start) {
+					code.appendLeft(c, ' = ');
 				} else {
-					code.overwrite( this.id.end, this.body.start, ' = ' );
+					code.overwrite(c, this.body.start, ' = ');
 				}
 			}
 
-			this.body.transpile( code, transforms, !!this.superClass, superName );
+			this.body.transpile(code, transforms, !!this.superClass, superName);
 
-			if ( this.superClass ) {
-				code.insertLeft( this.end, `\n\n${i1}return ${this.name};\n${i0}}(` );
-				code.move( this.superClass.start, this.superClass.end, this.end );
-				code.insertRight( this.end, `));${syntheticDefaultExport}` );
-			} else if ( syntheticDefaultExport ) {
-				code.insertRight( this.end, syntheticDefaultExport );
+			const syntheticDefaultExport =
+				isExportDefaultDeclaration
+					? `\n\n${i0}export default ${this.name};`
+					: '';
+			if (this.superClass) {
+				code.appendLeft(this.end, `\n\n${i1}return ${this.name};\n${i0}}(`);
+				code.move(this.superClass.start, this.superClass.end, this.end);
+				code.prependRight(this.end, `));${syntheticDefaultExport}`);
+			} else if (syntheticDefaultExport) {
+				code.prependRight(this.end, syntheticDefaultExport);
 			}
-		}
-
-		else {
-			this.body.transpile( code, transforms, false, null );
+		} else {
+			this.body.transpile(code, transforms, false, null);
 		}
 	}
 }
diff --git a/src/program/types/ClassExpression.js b/src/program/types/ClassExpression.js
index e9209ff..5c1f5f5 100644
--- a/src/program/types/ClassExpression.js
+++ b/src/program/types/ClassExpression.js
@@ -1,45 +1,51 @@
 import Node from '../Node.js';
 
 export default class ClassExpression extends Node {
-	initialise ( transforms ) {
-		this.name = this.id ? this.id.name :
-		            this.parent.type === 'VariableDeclarator' ? this.parent.id.name :
-		            this.parent.type === 'AssignmentExpression' ? this.parent.left.name :
-		            this.findScope( true ).createIdentifier( 'anonymous' );
-
-		super.initialise( transforms );
+	initialise(transforms) {
+		this.name = ( this.id
+			? this.id.name
+			: this.parent.type === 'VariableDeclarator'
+				? this.parent.id.name
+				: this.parent.type !== 'AssignmentExpression'
+					? null
+					: this.parent.left.type === 'Identifier'
+						? this.parent.left.name
+						: this.parent.left.type === 'MemberExpression'
+							? this.parent.left.property.name
+							: null ) || this.findScope(true).createIdentifier('anonymous');
+
+		super.initialise(transforms);
 	}
 
-	transpile ( code, transforms ) {
-		if ( transforms.classes ) {
-			const superName = this.superClass && ( this.superClass.name || 'superclass' );
+	transpile(code, transforms) {
+		if (transforms.classes) {
+			const superName =
+				this.superClass && (this.superClass.name || 'superclass');
 
 			const i0 = this.getIndentation();
 			const i1 = i0 + code.getIndentString();
 
-			if ( this.superClass ) {
-				code.remove( this.start, this.superClass.start );
-				code.remove( this.superClass.end, this.body.start );
-				code.insertLeft( this.start, `(function (${superName}) {\n${i1}` );
+			if (this.superClass) {
+				code.remove(this.start, this.superClass.start);
+				code.remove(this.superClass.end, this.body.start);
+				code.appendLeft(this.start, `(function (${superName}) {\n${i1}`);
 			} else {
-				code.overwrite( this.start, this.body.start, `(function () {\n${i1}` );
+				code.overwrite(this.start, this.body.start, `(function () {\n${i1}`);
 			}
 
-			this.body.transpile( code, transforms, true, superName );
+			this.body.transpile(code, transforms, true, superName);
 
 			const outro = `\n\n${i1}return ${this.name};\n${i0}}(`;
 
-			if ( this.superClass ) {
-				code.insertLeft( this.end, outro );
-				code.move( this.superClass.start, this.superClass.end, this.end );
-				code.insertRight( this.end, '))' );
+			if (this.superClass) {
+				code.appendLeft(this.end, outro);
+				code.move(this.superClass.start, this.superClass.end, this.end);
+				code.prependRight(this.end, '))');
 			} else {
-				code.insertLeft( this.end, `\n\n${i1}return ${this.name};\n${i0}}())` );
+				code.appendLeft(this.end, `\n\n${i1}return ${this.name};\n${i0}}())`);
 			}
-		}
-
-		else {
-			this.body.transpile( code, transforms, false );
+		} else {
+			this.body.transpile(code, transforms, false);
 		}
 	}
 }
diff --git a/src/program/types/ContinueStatement.js b/src/program/types/ContinueStatement.js
index a9e50de..efd2ba1 100644
--- a/src/program/types/ContinueStatement.js
+++ b/src/program/types/ContinueStatement.js
@@ -3,11 +3,15 @@ import CompileError from '../../utils/CompileError.js';
 import { loopStatement } from '../../utils/patterns.js';
 
 export default class ContinueStatement extends Node {
-	transpile ( code ) {
-		const loop = this.findNearest( loopStatement );
-		if ( loop.shouldRewriteAsFunction ) {
-			if ( this.label ) throw new CompileError( this, 'Labels are not currently supported in a loop with locally-scoped variables' );
-			code.overwrite( this.start, this.start + 8, 'return' );
+	transpile(code) {
+		const loop = this.findNearest(loopStatement);
+		if (loop.shouldRewriteAsFunction) {
+			if (this.label)
+				throw new CompileError(
+					'Labels are not currently supported in a loop with locally-scoped variables',
+					this
+				);
+			code.overwrite(this.start, this.start + 8, 'return');
 		}
 	}
 }
diff --git a/src/program/types/ExportDefaultDeclaration.js b/src/program/types/ExportDefaultDeclaration.js
index d361a75..883fc5f 100644
--- a/src/program/types/ExportDefaultDeclaration.js
+++ b/src/program/types/ExportDefaultDeclaration.js
@@ -2,8 +2,9 @@ import Node from '../Node.js';
 import CompileError from '../../utils/CompileError.js';
 
 export default class ExportDefaultDeclaration extends Node {
-	initialise ( transforms ) {
-		if ( transforms.moduleExport ) throw new CompileError( this, 'export is not supported' );
-		super.initialise( transforms );
+	initialise(transforms) {
+		if (transforms.moduleExport)
+			throw new CompileError('export is not supported', this);
+		super.initialise(transforms);
 	}
 }
diff --git a/src/program/types/ExportNamedDeclaration.js b/src/program/types/ExportNamedDeclaration.js
index 2db6a76..2435788 100644
--- a/src/program/types/ExportNamedDeclaration.js
+++ b/src/program/types/ExportNamedDeclaration.js
@@ -2,8 +2,9 @@ import Node from '../Node.js';
 import CompileError from '../../utils/CompileError.js';
 
 export default class ExportNamedDeclaration extends Node {
-	initialise ( transforms ) {
-		if ( transforms.moduleExport ) throw new CompileError( this, 'export is not supported' );
-		super.initialise( transforms );
+	initialise(transforms) {
+		if (transforms.moduleExport)
+			throw new CompileError('export is not supported', this);
+		super.initialise(transforms);
 	}
 }
diff --git a/src/program/types/ForInStatement.js b/src/program/types/ForInStatement.js
index 07e63d7..c58a348 100644
--- a/src/program/types/ForInStatement.js
+++ b/src/program/types/ForInStatement.js
@@ -2,21 +2,33 @@ import LoopStatement from './shared/LoopStatement.js';
 import extractNames from '../extractNames.js';
 
 export default class ForInStatement extends LoopStatement {
-	findScope ( functionScope ) {
-		return functionScope || !this.createdScope ? this.parent.findScope( functionScope ) : this.body.scope;
+	findScope(functionScope) {
+		return functionScope || !this.createdScope
+			? this.parent.findScope(functionScope)
+			: this.body.scope;
 	}
 
-	transpile ( code, transforms ) {
-		if ( this.shouldRewriteAsFunction ) {
+	transpile(code, transforms) {
+		if (this.shouldRewriteAsFunction) {
 			// which variables are declared in the init statement?
-			const names = this.left.type === 'VariableDeclaration' ?
-				[].concat.apply( [], this.left.declarations.map( declarator => extractNames( declarator.id ) ) ) :
-				[];
+			const names =
+				this.left.type === 'VariableDeclaration'
+					? [].concat.apply(
+							[],
+							this.left.declarations.map(declarator =>
+								extractNames(declarator.id)
+							)
+						)
+					: [];
 
-			this.args = names.map( name => name in this.aliases ? this.aliases[ name ].outer : name );
-			this.params = names.map( name => name in this.aliases ? this.aliases[ name ].inner : name );
+			this.args = names.map(
+				name => (name in this.aliases ? this.aliases[name].outer : name)
+			);
+			this.params = names.map(
+				name => (name in this.aliases ? this.aliases[name].inner : name)
+			);
 		}
 
-		super.transpile( code, transforms );
+		super.transpile(code, transforms);
 	}
 }
diff --git a/src/program/types/ForOfStatement.js b/src/program/types/ForOfStatement.js
index afe479c..c8229f9 100644
--- a/src/program/types/ForOfStatement.js
+++ b/src/program/types/ForOfStatement.js
@@ -3,73 +3,76 @@ import CompileError from '../../utils/CompileError.js';
 import destructure from '../../utils/destructure.js';
 
 export default class ForOfStatement extends LoopStatement {
-	initialise ( transforms ) {
-		if ( transforms.forOf && !transforms.dangerousForOf ) throw new CompileError( this, 'for...of statements are not supported. Use `transforms: { forOf: false }` to skip transformation and disable this error, or `transforms: { dangerousForOf: true }` if you know what you\'re doing' );
-		super.initialise( transforms );
+	initialise(transforms) {
+		if (transforms.forOf && !transforms.dangerousForOf)
+			throw new CompileError(
+				"for...of statements are not supported. Use `transforms: { forOf: false }` to skip transformation and disable this error, or `transforms: { dangerousForOf: true }` if you know what you're doing",
+				this
+			);
+		super.initialise(transforms);
 	}
 
-	transpile ( code, transforms ) {
-		if ( !transforms.dangerousForOf ) {
-			super.transpile( code, transforms );
-			return;
-		}
+	transpile(code, transforms) {
+		super.transpile(code, transforms);
+		if (!transforms.dangerousForOf) return;
 
 		// edge case (#80)
-		if ( !this.body.body[0] ) {
-			if ( this.left.type === 'VariableDeclaration' && this.left.kind === 'var' ) {
-				code.remove( this.start, this.left.start );
-				code.insertLeft( this.left.end, ';' );
-				code.remove( this.left.end, this.end );
+		if (!this.body.body[0]) {
+			if (
+				this.left.type === 'VariableDeclaration' &&
+				this.left.kind === 'var'
+			) {
+				code.remove(this.start, this.left.start);
+				code.appendLeft(this.left.end, ';');
+				code.remove(this.left.end, this.end);
 			} else {
-				code.remove( this.start, this.end );
+				code.remove(this.start, this.end);
 			}
 
 			return;
 		}
 
-		const scope = this.findScope( true );
+		const scope = this.findScope(true);
 		const i0 = this.getIndentation();
 		const i1 = i0 + code.getIndentString();
 
-		const key = scope.createIdentifier( 'i' );
-		const list = scope.createIdentifier( 'list' );
+		const key = scope.createIdentifier('i');
+		const list = scope.createIdentifier('list');
 
-		if ( this.body.synthetic ) {
-			code.insertRight( this.left.start, `{\n${i1}` );
-			code.insertLeft( this.body.body[0].end, `\n${i0}}` );
+		if (this.body.synthetic) {
+			code.prependRight(this.left.start, `{\n${i1}`);
+			code.appendLeft(this.body.body[0].end, `\n${i0}}`);
 		}
 
 		const bodyStart = this.body.body[0].start;
 
-		code.remove( this.left.end, this.right.start );
-		code.move( this.left.start, this.left.end, bodyStart );
+		code.remove(this.left.end, this.right.start);
+		code.move(this.left.start, this.left.end, bodyStart);
 
-
-		code.insertRight( this.right.start, `var ${key} = 0, ${list} = ` );
-		code.insertLeft( this.right.end, `; ${key} < ${list}.length; ${key} += 1` );
+		code.prependRight(this.right.start, `var ${key} = 0, ${list} = `);
+		code.appendLeft(this.right.end, `; ${key} < ${list}.length; ${key} += 1`);
 
 		// destructuring. TODO non declaration destructuring
-		const declarator = this.left.type === 'VariableDeclaration' && this.left.declarations[0];
-		if ( declarator && declarator.id.type !== 'Identifier' ) {
+		const declarator =
+			this.left.type === 'VariableDeclaration' && this.left.declarations[0];
+		if (declarator && declarator.id.type !== 'Identifier') {
 			let statementGenerators = [];
-			const ref = scope.createIdentifier( 'ref' );
-			destructure( code, scope, declarator.id, ref, false, statementGenerators );
+			const ref = scope.createIdentifier('ref');
+			destructure(code, scope, declarator.id, ref, false, statementGenerators);
 
 			let suffix = `;\n${i1}`;
-			statementGenerators.forEach( ( fn, i ) => {
-				if ( i === statementGenerators.length - 1 ) {
+			statementGenerators.forEach((fn, i) => {
+				if (i === statementGenerators.length - 1) {
 					suffix = `;\n\n${i1}`;
 				}
 
-				fn( bodyStart, '', suffix );
+				fn(bodyStart, '', suffix);
 			});
 
-			code.insertLeft( this.left.start + this.left.kind.length + 1, ref );
-			code.insertLeft( this.left.end, ` = ${list}[${key}];\n${i1}` );
+			code.appendLeft(this.left.start + this.left.kind.length + 1, ref);
+			code.appendLeft(this.left.end, ` = ${list}[${key}];\n${i1}`);
 		} else {
-			code.insertLeft( this.left.end, ` = ${list}[${key}];\n\n${i1}` );
+			code.appendLeft(this.left.end, ` = ${list}[${key}];\n\n${i1}`);
 		}
-
-		super.transpile( code, transforms );
 	}
 }
diff --git a/src/program/types/ForStatement.js b/src/program/types/ForStatement.js
index 8f25b2f..3446ebb 100644
--- a/src/program/types/ForStatement.js
+++ b/src/program/types/ForStatement.js
@@ -2,37 +2,53 @@ import LoopStatement from './shared/LoopStatement.js';
 import extractNames from '../extractNames.js';
 
 export default class ForStatement extends LoopStatement {
-	findScope ( functionScope ) {
-		return functionScope || !this.createdScope ? this.parent.findScope( functionScope ) : this.body.scope;
+	findScope(functionScope) {
+		return functionScope || !this.createdScope
+			? this.parent.findScope(functionScope)
+			: this.body.scope;
 	}
 
-	transpile ( code, transforms ) {
+	transpile(code, transforms) {
 		const i1 = this.getIndentation() + code.getIndentString();
 
-		if ( this.shouldRewriteAsFunction ) {
+		if (this.shouldRewriteAsFunction) {
 			// which variables are declared in the init statement?
-			const names = this.init.type === 'VariableDeclaration' ?
-				[].concat.apply( [], this.init.declarations.map( declarator => extractNames( declarator.id ) ) ) :
-				[];
+			const names =
+				this.init.type === 'VariableDeclaration'
+					? [].concat.apply(
+							[],
+							this.init.declarations.map(declarator =>
+								extractNames(declarator.id)
+							)
+						)
+					: [];
 
 			const aliases = this.aliases;
 
-			this.args = names.map( name => name in this.aliases ? this.aliases[ name ].outer : name );
-			this.params = names.map( name => name in this.aliases ? this.aliases[ name ].inner : name );
+			this.args = names.map(
+				name => (name in this.aliases ? this.aliases[name].outer : name)
+			);
+			this.params = names.map(
+				name => (name in this.aliases ? this.aliases[name].inner : name)
+			);
 
-			const updates = Object.keys( this.reassigned )
-				.map( name => `${aliases[ name ].outer} = ${aliases[ name ].inner};` );
+			const updates = Object.keys(this.reassigned).map(
+				name => `${aliases[name].outer} = ${aliases[name].inner};`
+			);
 
-			if ( updates.length ) {
-				if ( this.body.synthetic ) {
-					code.insertLeft( this.body.body[0].end, `; ${updates.join(` `)}` );
+			if (updates.length) {
+				if (this.body.synthetic) {
+					code.appendLeft(this.body.body[0].end, `; ${updates.join(` `)}`);
 				} else {
-					const lastStatement = this.body.body[ this.body.body.length - 1 ];
-					code.insertLeft( lastStatement.end, `\n\n${i1}${updates.join(`\n${i1}`)}` );
+					const lastStatement = this.body.body[this.body.body.length - 1];
+					code.appendLeft(
+						lastStatement.end,
+						`\n\n${i1}${updates.join(`\n${i1}`)}`
+					);
 				}
 			}
 		}
 
-		super.transpile( code, transforms );
+		super.transpile(code, transforms);
 	}
 }
diff --git a/src/program/types/FunctionDeclaration.js b/src/program/types/FunctionDeclaration.js
index d0fd207..951e90a 100644
--- a/src/program/types/FunctionDeclaration.js
+++ b/src/program/types/FunctionDeclaration.js
@@ -1,15 +1,25 @@
 import Node from '../Node.js';
 import CompileError from '../../utils/CompileError.js';
+import removeTrailingComma from '../../utils/removeTrailingComma.js';
 
 export default class FunctionDeclaration extends Node {
-	initialise ( transforms ) {
-		if ( this.generator && transforms.generator ) {
-			throw new CompileError( this, 'Generators are not supported' );
+	initialise(transforms) {
+		if (this.generator && transforms.generator) {
+			throw new CompileError('Generators are not supported', this);
 		}
 
 		this.body.createScope();
 
-		this.findScope( true ).addDeclaration( this.id, 'function' );
-		super.initialise( transforms );
+		if (this.id) {
+			this.findScope(true).addDeclaration(this.id, 'function');
+		}
+		super.initialise(transforms);
+	}
+
+	transpile(code, transforms) {
+		super.transpile(code, transforms);
+		if (transforms.trailingFunctionCommas && this.params.length) {
+			removeTrailingComma(code, this.params[this.params.length - 1].end);
+		}
 	}
 }
diff --git a/src/program/types/FunctionExpression.js b/src/program/types/FunctionExpression.js
index 86c0e1c..822dd47 100644
--- a/src/program/types/FunctionExpression.js
+++ b/src/program/types/FunctionExpression.js
@@ -1,58 +1,61 @@
 import Node from '../Node.js';
 import CompileError from '../../utils/CompileError.js';
+import removeTrailingComma from '../../utils/removeTrailingComma.js';
 
 export default class FunctionExpression extends Node {
-	initialise ( transforms ) {
-		if ( this.generator && transforms.generator ) {
-			throw new CompileError( this, 'Generators are not supported' );
+	initialise(transforms) {
+		if (this.generator && transforms.generator) {
+			throw new CompileError('Generators are not supported', this);
 		}
 
 		this.body.createScope();
 
-		if ( this.id ) {
+		if (this.id) {
 			// function expression IDs belong to the child scope...
-			this.body.scope.addDeclaration( this.id, 'function' );
+			this.body.scope.addDeclaration(this.id, 'function');
 		}
 
-		super.initialise( transforms );
+		super.initialise(transforms);
 
 		const parent = this.parent;
 		let methodName;
 
-		if ( transforms.conciseMethodProperty
-				&& parent.type === 'Property'
-				&& parent.kind === 'init'
-				&& parent.method
-				&& parent.key.type === 'Identifier' ) {
+		if (
+			transforms.conciseMethodProperty &&
+			parent.type === 'Property' &&
+			parent.kind === 'init' &&
+			parent.method &&
+			parent.key.type === 'Identifier'
+		) {
 			// object literal concise method
 			methodName = parent.key.name;
-		}
-		else if ( transforms.classes
-				&& parent.type === 'MethodDefinition'
-				&& parent.kind === 'method'
-				&& parent.key.type === 'Identifier' ) {
+		} else if (
+			transforms.classes &&
+			parent.type === 'MethodDefinition' &&
+			parent.kind === 'method' &&
+			parent.key.type === 'Identifier'
+		) {
 			// method definition in a class
 			methodName = parent.key.name;
-		}
-		else if ( this.id && this.id.type === 'Identifier' ) {
+		} else if (this.id && this.id.type === 'Identifier') {
 			// naked function expression
 			methodName = this.id.alias || this.id.name;
 		}
 
-		if ( methodName ) {
-			for ( const param of this.params ) {
-				if ( param.type === 'Identifier' && methodName === param.name ) {
+		if (methodName) {
+			for (const param of this.params) {
+				if (param.type === 'Identifier' && methodName === param.name) {
 					// workaround for Safari 9/WebKit bug:
 					// https://gitlab.com/Rich-Harris/buble/issues/154
 					// change parameter name when same as method name
 
 					const scope = this.body.scope;
-					const declaration = scope.declarations[ methodName ];
+					const declaration = scope.declarations[methodName];
 
-					const alias = scope.createIdentifier( methodName );
+					const alias = scope.createIdentifier(methodName);
 					param.alias = alias;
 
-					for ( const identifier of declaration.instances ) {
+					for (const identifier of declaration.instances) {
 						identifier.alias = alias;
 					}
 
@@ -61,4 +64,11 @@ export default class FunctionExpression extends Node {
 			}
 		}
 	}
+
+	transpile(code, transforms) {
+		super.transpile(code, transforms);
+		if (transforms.trailingFunctionCommas && this.params.length) {
+			removeTrailingComma(code, this.params[this.params.length - 1].end);
+		}
+	}
 }
diff --git a/src/program/types/Identifier.js b/src/program/types/Identifier.js
index 8cb6d1c..e71d778 100644
--- a/src/program/types/Identifier.js
+++ b/src/program/types/Identifier.js
@@ -3,41 +3,51 @@ import isReference from '../../utils/isReference.js';
 import { loopStatement } from '../../utils/patterns.js';
 
 export default class Identifier extends Node {
-	findScope ( functionScope ) {
-		if ( this.parent.params && ~this.parent.params.indexOf( this ) ) {
+	findScope(functionScope) {
+		if (this.parent.params && ~this.parent.params.indexOf(this)) {
 			return this.parent.body.scope;
 		}
 
-		if ( this.parent.type === 'FunctionExpression' && this === this.parent.id ) {
+		if (this.parent.type === 'FunctionExpression' && this === this.parent.id) {
 			return this.parent.body.scope;
 		}
 
-		return this.parent.findScope( functionScope	);
+		return this.parent.findScope(functionScope);
 	}
 
-	initialise ( transforms ) {
-		if ( transforms.arrow && isReference( this, this.parent ) ) {
-			if ( this.name === 'arguments' && !this.findScope( false ).contains( this.name ) ) {
+	initialise(transforms) {
+		if (transforms.arrow && isReference(this, this.parent)) {
+			if (
+				this.name === 'arguments' &&
+				!this.findScope(false).contains(this.name)
+			) {
 				const lexicalBoundary = this.findLexicalBoundary();
-				const arrowFunction = this.findNearest( 'ArrowFunctionExpression' );
-				const loop = this.findNearest( loopStatement );
+				const arrowFunction = this.findNearest('ArrowFunctionExpression');
+				const loop = this.findNearest(loopStatement);
 
-				if ( arrowFunction && arrowFunction.depth > lexicalBoundary.depth ) {
+				if (arrowFunction && arrowFunction.depth > lexicalBoundary.depth) {
 					this.alias = lexicalBoundary.getArgumentsAlias();
 				}
 
-				if ( loop && loop.body.contains( this ) && loop.depth > lexicalBoundary.depth ) {
+				if (
+					loop &&
+					loop.body.contains(this) &&
+					loop.depth > lexicalBoundary.depth
+				) {
 					this.alias = lexicalBoundary.getArgumentsAlias();
 				}
 			}
 
-			this.findScope( false ).addReference( this );
+			this.findScope(false).addReference(this);
 		}
 	}
 
-	transpile ( code ) {
-		if ( this.alias ) {
-			code.overwrite( this.start, this.end, this.alias, true );
+	transpile(code) {
+		if (this.alias) {
+			code.overwrite(this.start, this.end, this.alias, {
+				storeName: true,
+				contentOnly: true
+			});
 		}
 	}
 }
diff --git a/src/program/types/IfStatement.js b/src/program/types/IfStatement.js
index 87ad989..bdeebb4 100644
--- a/src/program/types/IfStatement.js
+++ b/src/program/types/IfStatement.js
@@ -1,24 +1,29 @@
 import Node from '../Node.js';
 
 export default class IfStatement extends Node {
-	initialise ( transforms ) {
-		super.initialise( transforms );
+	initialise(transforms) {
+		super.initialise(transforms);
 	}
 
-	transpile ( code, transforms ) {
-		if ( this.consequent.type !== 'BlockStatement'
-				|| this.consequent.type === 'BlockStatement' && this.consequent.synthetic ) {
-			code.insertLeft( this.consequent.start, '{ ' );
-			code.insertRight( this.consequent.end, ' }' );
+	transpile(code, transforms) {
+		if (
+			this.consequent.type !== 'BlockStatement' ||
+			(this.consequent.type === 'BlockStatement' && this.consequent.synthetic)
+		) {
+			code.appendLeft(this.consequent.start, '{ ');
+			code.prependRight(this.consequent.end, ' }');
 		}
 
-		if ( this.alternate && this.alternate.type !== 'IfStatement' && (
-				this.alternate.type !== 'BlockStatement'
-				|| this.alternate.type === 'BlockStatement' && this.alternate.synthetic ) ) {
-			code.insertLeft( this.alternate.start, '{ ' );
-			code.insertRight( this.alternate.end, ' }' );
+		if (
+			this.alternate &&
+			this.alternate.type !== 'IfStatement' &&
+			(this.alternate.type !== 'BlockStatement' ||
+				(this.alternate.type === 'BlockStatement' && this.alternate.synthetic))
+		) {
+			code.appendLeft(this.alternate.start, '{ ');
+			code.prependRight(this.alternate.end, ' }');
 		}
 
-		super.transpile( code, transforms );
+		super.transpile(code, transforms);
 	}
 }
diff --git a/src/program/types/ImportDeclaration.js b/src/program/types/ImportDeclaration.js
index 3c07fc5..b94458d 100644
--- a/src/program/types/ImportDeclaration.js
+++ b/src/program/types/ImportDeclaration.js
@@ -2,8 +2,9 @@ import Node from '../Node.js';
 import CompileError from '../../utils/CompileError.js';
 
 export default class ImportDeclaration extends Node {
-	initialise ( transforms ) {
-		if ( transforms.moduleImport ) throw new CompileError( this, 'import is not supported' );
-		super.initialise( transforms );
+	initialise(transforms) {
+		if (transforms.moduleImport)
+			throw new CompileError('import is not supported', this);
+		super.initialise(transforms);
 	}
 }
diff --git a/src/program/types/ImportDefaultSpecifier.js b/src/program/types/ImportDefaultSpecifier.js
index f1505d0..0f2a9f4 100644
--- a/src/program/types/ImportDefaultSpecifier.js
+++ b/src/program/types/ImportDefaultSpecifier.js
@@ -1,8 +1,8 @@
 import Node from '../Node.js';
 
 export default class ImportDefaultSpecifier extends Node {
-	initialise ( transforms ) {
-		this.findScope( true ).addDeclaration( this.local, 'import' );
-		super.initialise( transforms );
+	initialise(transforms) {
+		this.findScope(true).addDeclaration(this.local, 'import');
+		super.initialise(transforms);
 	}
 }
diff --git a/src/program/types/ImportSpecifier.js b/src/program/types/ImportSpecifier.js
index 1e99f3f..121d630 100644
--- a/src/program/types/ImportSpecifier.js
+++ b/src/program/types/ImportSpecifier.js
@@ -1,8 +1,8 @@
 import Node from '../Node.js';
 
 export default class ImportSpecifier extends Node {
-	initialise ( transforms ) {
-		this.findScope( true ).addDeclaration( this.local, 'import' );
-		super.initialise( transforms );
+	initialise(transforms) {
+		this.findScope(true).addDeclaration(this.local, 'import');
+		super.initialise(transforms);
 	}
 }
diff --git a/src/program/types/JSXAttribute.js b/src/program/types/JSXAttribute.js
index 9e21293..fe52338 100644
--- a/src/program/types/JSXAttribute.js
+++ b/src/program/types/JSXAttribute.js
@@ -2,19 +2,19 @@ import Node from '../Node.js';
 
 const hasDashes = val => /-/.test(val);
 
-const formatKey = key => hasDashes(key) ? `'${key}'` : key;
+const formatKey = key => (hasDashes(key) ? `'${key}'` : key);
 
-const formatVal = val => val ? '' : 'true';
+const formatVal = val => (val ? '' : 'true');
 
 export default class JSXAttribute extends Node {
-	transpile ( code, transforms ) {
-		const { start, name }	= this.name;
+	transpile(code, transforms) {
+		const { start, name } = this.name;
 
 		// Overwrite equals sign if value is present.
 		const end = this.value ? this.value.start : this.name.end;
 
-		code.overwrite( start, end, `${formatKey(name)}: ${formatVal(this.value)}` );
+		code.overwrite(start, end, `${formatKey(name)}: ${formatVal(this.value)}`);
 
-		super.transpile( code, transforms );
+		super.transpile(code, transforms);
 	}
 }
diff --git a/src/program/types/JSXClosingElement.js b/src/program/types/JSXClosingElement.js
index e3e03fe..d2cb44a 100644
--- a/src/program/types/JSXClosingElement.js
+++ b/src/program/types/JSXClosingElement.js
@@ -1,22 +1,27 @@
 import Node from '../Node.js';
 
-function containsNewLine ( node ) {
-	return node.type === 'Literal' && !/\S/.test( node.value ) && /\n/.test( node.value );
+function containsNewLine(node) {
+	return (
+		node.type === 'Literal' && !/\S/.test(node.value) && /\n/.test(node.value)
+	);
 }
 
 export default class JSXClosingElement extends Node {
-	transpile ( code ) {
+	transpile(code) {
 		let spaceBeforeParen = true;
 
-		const lastChild = this.parent.children[ this.parent.children.length - 1 ];
+		const lastChild = this.parent.children[this.parent.children.length - 1];
 
 		// omit space before closing paren if
 		//   a) this is on a separate line, or
 		//   b) there are no children but there are attributes
-		if ( ( lastChild && containsNewLine( lastChild ) ) || ( this.parent.openingElement.attributes.length ) ) {
+		if (
+			(lastChild && containsNewLine(lastChild)) ||
+			this.parent.openingElement.attributes.length
+		) {
 			spaceBeforeParen = false;
 		}
 
-		code.overwrite( this.start, this.end, spaceBeforeParen ? ' )' : ')' );
+		code.overwrite(this.start, this.end, spaceBeforeParen ? ' )' : ')');
 	}
 }
diff --git a/src/program/types/JSXElement.js b/src/program/types/JSXElement.js
index b0f5892..7fa7468 100644
--- a/src/program/types/JSXElement.js
+++ b/src/program/types/JSXElement.js
@@ -1,46 +1,50 @@
 import Node from '../Node.js';
 
-function normalise ( str, removeTrailingWhitespace ) {
-	if ( removeTrailingWhitespace && /\n/.test( str ) ) {
-		str = str.replace( /\s+$/, '' );
+function normalise(str, removeTrailingWhitespace) {
+	if (removeTrailingWhitespace && /\n/.test(str)) {
+		str = str.replace(/\s+$/, '');
 	}
 
 	str = str
-		.replace( /^\n\r?\s+/, '' )       // remove leading newline + space
-		.replace( /\s*\n\r?\s*/gm, ' ' ); // replace newlines with spaces
+		.replace(/^\n\r?\s+/, '') // remove leading newline + space
+		.replace(/\s*\n\r?\s*/gm, ' '); // replace newlines with spaces
 
 	// TODO prefer single quotes?
-	return JSON.stringify( str );
+	return JSON.stringify(str);
 }
 
 export default class JSXElement extends Node {
-	transpile ( code, transforms ) {
-		super.transpile( code, transforms );
+	transpile(code, transforms) {
+		super.transpile(code, transforms);
 
-		const children = this.children.filter( child => {
-			if ( child.type !== 'Literal' ) return true;
+		const children = this.children.filter(child => {
+			if (child.type !== 'Literal') return true;
 
 			// remove whitespace-only literals, unless on a single line
-			return /\S/.test( child.value ) || !/\n/.test( child.value );
+			return /\S/.test(child.raw) || !/\n/.test(child.raw);
 		});
 
-		if ( children.length ) {
+		if (children.length) {
 			let c = this.openingElement.end;
 
 			let i;
-			for ( i = 0; i < children.length; i += 1 ) {
+			for (i = 0; i < children.length; i += 1) {
 				const child = children[i];
 
-				if ( child.type === 'JSXExpressionContainer' && child.expression.type === 'JSXEmptyExpression' ) {
+				if (
+					child.type === 'JSXExpressionContainer' &&
+					child.expression.type === 'JSXEmptyExpression'
+				) {
 					// empty block is a no op
 				} else {
-					const tail = code.original[ c ] === '\n' && child.type !== 'Literal' ? '' : ' ';
-					code.insertLeft( c, `,${tail}` );
+					const tail =
+						code.original[c] === '\n' && child.type !== 'Literal' ? '' : ' ';
+					code.appendLeft(c, `,${tail}`);
 				}
 
-				if ( child.type === 'Literal' ) {
-					const str = normalise( child.value, i === children.length - 1 );
-					code.overwrite( child.start, child.end, str );
+				if (child.type === 'Literal') {
+					const str = normalise(child.raw, i === children.length - 1);
+					code.overwrite(child.start, child.end, str);
 				}
 
 				c = child.end;
diff --git a/src/program/types/JSXExpressionContainer.js b/src/program/types/JSXExpressionContainer.js
index e45e66d..f9f3a81 100644
--- a/src/program/types/JSXExpressionContainer.js
+++ b/src/program/types/JSXExpressionContainer.js
@@ -1,10 +1,10 @@
 import Node from '../Node.js';
 
 export default class JSXExpressionContainer extends Node {
-	transpile ( code, transforms ) {
-		code.remove( this.start, this.expression.start );
-		code.remove( this.expression.end, this.end );
+	transpile(code, transforms) {
+		code.remove(this.start, this.expression.start);
+		code.remove(this.expression.end, this.end);
 
-		super.transpile( code, transforms );
+		super.transpile(code, transforms);
 	}
 }
diff --git a/src/program/types/JSXOpeningElement.js b/src/program/types/JSXOpeningElement.js
index 2c77295..922083e 100644
--- a/src/program/types/JSXOpeningElement.js
+++ b/src/program/types/JSXOpeningElement.js
@@ -2,21 +2,25 @@ import Node from '../Node.js';
 import CompileError from '../../utils/CompileError.js';
 
 export default class JSXOpeningElement extends Node {
-	transpile ( code, transforms ) {
-		code.overwrite( this.start, this.name.start, `${this.program.jsx}( ` );
+	transpile(code, transforms) {
+		super.transpile(code, transforms);
 
-		const html = this.name.type === 'JSXIdentifier' && this.name.name[0] === this.name.name[0].toLowerCase();
-		if ( html ) code.insertRight( this.name.start, `'` );
+		code.overwrite(this.start, this.name.start, `${this.program.jsx}( `);
+
+		const html =
+			this.name.type === 'JSXIdentifier' &&
+			this.name.name[0] === this.name.name[0].toLowerCase();
+		if (html) code.prependRight(this.name.start, `'`);
 
 		const len = this.attributes.length;
 		let c = this.name.end;
 
-		if ( len ) {
+		if (len) {
 			let i;
 
 			let hasSpread = false;
-			for ( i = 0; i < len; i += 1 ) {
-				if ( this.attributes[i].type === 'JSXSpreadAttribute' ) {
+			for (i = 0; i < len; i += 1) {
+				if (this.attributes[i].type === 'JSXSpreadAttribute') {
 					hasSpread = true;
 					break;
 				}
@@ -24,26 +28,24 @@ export default class JSXOpeningElement extends Node {
 
 			c = this.attributes[0].end;
 
-			for ( i = 0; i < len; i += 1 ) {
+			for (i = 0; i < len; i += 1) {
 				const attr = this.attributes[i];
 
-				if ( i > 0 ) {
-					if ( attr.start === c )
-						code.insertRight( c, ', ' );
-					else
-						code.overwrite( c, attr.start, ', ' );
+				if (i > 0) {
+					if (attr.start === c) code.prependRight(c, ', ');
+					else code.overwrite(c, attr.start, ', ');
 				}
 
-				if ( hasSpread && attr.type !== 'JSXSpreadAttribute' ) {
-					const lastAttr = this.attributes[ i - 1 ];
-					const nextAttr = this.attributes[ i + 1 ];
+				if (hasSpread && attr.type !== 'JSXSpreadAttribute') {
+					const lastAttr = this.attributes[i - 1];
+					const nextAttr = this.attributes[i + 1];
 
-					if ( !lastAttr || lastAttr.type === 'JSXSpreadAttribute' ) {
-						code.insertRight( attr.start, '{ ' );
+					if (!lastAttr || lastAttr.type === 'JSXSpreadAttribute') {
+						code.prependRight(attr.start, '{ ');
 					}
 
-					if ( !nextAttr || nextAttr.type === 'JSXSpreadAttribute' ) {
-						code.insertLeft( attr.end, ' }' );
+					if (!nextAttr || nextAttr.type === 'JSXSpreadAttribute') {
+						code.appendLeft(attr.end, ' }');
 					}
 				}
 
@@ -52,14 +54,19 @@ export default class JSXOpeningElement extends Node {
 
 			let after;
 			let before;
-			if ( hasSpread ) {
-				if ( len === 1 ) {
+			if (hasSpread) {
+				if (len === 1) {
 					before = html ? `',` : ',';
 				} else {
 					if (!this.program.options.objectAssign) {
-						throw new CompileError( this, 'Mixed JSX attributes ending in spread requires specified objectAssign option with \'Object.assign\' or polyfill helper.' );
+						throw new CompileError(
+							"Mixed JSX attributes ending in spread requires specified objectAssign option with 'Object.assign' or polyfill helper.",
+							this
+						);
 					}
-					before = html ? `', ${this.program.options.objectAssign}({},` : `, ${this.program.options.objectAssign}({},`;
+					before = html
+						? `', ${this.program.options.objectAssign}({},`
+						: `, ${this.program.options.objectAssign}({},`;
 					after = ')';
 				}
 			} else {
@@ -67,22 +74,20 @@ export default class JSXOpeningElement extends Node {
 				after = ' }';
 			}
 
-			code.insertRight( this.name.end, before );
+			code.prependRight(this.name.end, before);
 
-			if ( after ) {
-				code.insertLeft( this.attributes[ len - 1 ].end, after );
+			if (after) {
+				code.appendLeft(this.attributes[len - 1].end, after);
 			}
 		} else {
-			code.insertLeft( this.name.end, html ? `', null` : `, null` );
+			code.appendLeft(this.name.end, html ? `', null` : `, null`);
 			c = this.name.end;
 		}
 
-		super.transpile( code, transforms );
-
-		if ( this.selfClosing ) {
-			code.overwrite( c, this.end, this.attributes.length ? `)` : ` )` );
+		if (this.selfClosing) {
+			code.overwrite(c, this.end, this.attributes.length ? `)` : ` )`);
 		} else {
-			code.remove( c, this.end );
+			code.remove(c, this.end);
 		}
 	}
 }
diff --git a/src/program/types/JSXSpreadAttribute.js b/src/program/types/JSXSpreadAttribute.js
index 0a68076..cd11a61 100644
--- a/src/program/types/JSXSpreadAttribute.js
+++ b/src/program/types/JSXSpreadAttribute.js
@@ -1,10 +1,10 @@
 import Node from '../Node.js';
 
 export default class JSXSpreadAttribute extends Node {
-	transpile ( code, transforms ) {
-		code.remove( this.start, this.argument.start );
-		code.remove( this.argument.end, this.end );
+	transpile(code, transforms) {
+		code.remove(this.start, this.argument.start);
+		code.remove(this.argument.end, this.end);
 
-		super.transpile( code, transforms );
+		super.transpile(code, transforms);
 	}
 }
diff --git a/src/program/types/Literal.js b/src/program/types/Literal.js
index 3718c56..a2f5692 100644
--- a/src/program/types/Literal.js
+++ b/src/program/types/Literal.js
@@ -3,26 +3,40 @@ import CompileError from '../../utils/CompileError.js';
 import rewritePattern from 'regexpu-core';
 
 export default class Literal extends Node {
-	initialise () {
-		if ( typeof this.value === 'string' ) {
-			this.program.indentExclusionElements.push( this );
+	initialise() {
+		if (typeof this.value === 'string') {
+			this.program.indentExclusionElements.push(this);
 		}
 	}
 
-	transpile ( code, transforms ) {
-		if ( transforms.numericLiteral ) {
-			const leading = this.raw.slice( 0, 2 );
-			if ( leading === '0b' || leading === '0o' ) {
-				code.overwrite( this.start, this.end, String( this.value ), true );
+	transpile(code, transforms) {
+		if (transforms.numericLiteral) {
+			const leading = this.raw.slice(0, 2);
+			if (leading === '0b' || leading === '0o') {
+				code.overwrite(this.start, this.end, String(this.value), {
+					storeName: true,
+					contentOnly: true
+				});
 			}
 		}
 
-		if ( this.regex ) {
+		if (this.regex) {
 			const { pattern, flags } = this.regex;
 
-			if ( transforms.stickyRegExp && /y/.test( flags ) ) throw new CompileError( this, 'Regular expression sticky flag is not supported' );
-			if ( transforms.unicodeRegExp && /u/.test( flags ) ) {
-				code.overwrite( this.start, this.end, `/${rewritePattern( pattern, flags )}/${flags.replace( 'u', '' )}` );
+			if (transforms.stickyRegExp && /y/.test(flags))
+				throw new CompileError(
+					'Regular expression sticky flag is not supported',
+					this
+				);
+			if (transforms.unicodeRegExp && /u/.test(flags)) {
+				code.overwrite(
+					this.start,
+					this.end,
+					`/${rewritePattern(pattern, flags)}/${flags.replace('u', '')}`,
+					{
+						contentOnly: true
+					}
+				);
 			}
 		}
 	}
diff --git a/src/program/types/MemberExpression.js b/src/program/types/MemberExpression.js
index 977a2ff..b254c48 100644
--- a/src/program/types/MemberExpression.js
+++ b/src/program/types/MemberExpression.js
@@ -2,12 +2,12 @@ import Node from '../Node.js';
 import reserved from '../../utils/reserved.js';
 
 export default class MemberExpression extends Node {
-	transpile ( code, transforms ) {
-		if ( transforms.reservedProperties && reserved[ this.property.name ] ) {
-			code.overwrite( this.object.end, this.property.start, `['` );
-			code.insertLeft( this.property.end, `']` );
+	transpile(code, transforms) {
+		if (transforms.reservedProperties && reserved[this.property.name]) {
+			code.overwrite(this.object.end, this.property.start, `['`);
+			code.appendLeft(this.property.end, `']`);
 		}
 
-		super.transpile( code, transforms );
+		super.transpile(code, transforms);
 	}
 }
diff --git a/src/program/types/NewExpression.js b/src/program/types/NewExpression.js
index dce5310..8d33c06 100644
--- a/src/program/types/NewExpression.js
+++ b/src/program/types/NewExpression.js
@@ -2,36 +2,49 @@ import Node from '../Node.js';
 import spread, { isArguments } from '../../utils/spread.js';
 
 export default class NewExpression extends Node {
-	initialise ( transforms ) {
-		if ( transforms.spreadRest && this.arguments.length ) {
+	initialise(transforms) {
+		if (transforms.spreadRest && this.arguments.length) {
 			const lexicalBoundary = this.findLexicalBoundary();
 
 			let i = this.arguments.length;
-			while ( i-- ) {
+			while (i--) {
 				const arg = this.arguments[i];
-				if ( arg.type === 'SpreadElement' && isArguments( arg.argument ) ) {
+				if (arg.type === 'SpreadElement' && isArguments(arg.argument)) {
 					this.argumentsArrayAlias = lexicalBoundary.getArgumentsArrayAlias();
 					break;
 				}
 			}
 		}
 
-		super.initialise( transforms );
+		super.initialise(transforms);
 	}
 
-	transpile ( code, transforms ) {
-		if ( transforms.spreadRest && this.arguments.length ) {
+	transpile(code, transforms) {
+		if (transforms.spreadRest && this.arguments.length) {
 			const firstArgument = this.arguments[0];
 			const isNew = true;
-			let hasSpreadElements = spread( code, this.arguments, firstArgument.start, this.argumentsArrayAlias, isNew );
+			let hasSpreadElements = spread(
+				code,
+				this.arguments,
+				firstArgument.start,
+				this.argumentsArrayAlias,
+				isNew
+			);
 
-			if ( hasSpreadElements ) {
-				code.insertRight( this.start + 'new'.length, ' (Function.prototype.bind.apply(' );
-				code.overwrite( this.callee.end, firstArgument.start, ', [ null ].concat( ' );
-				code.insertLeft( this.end, ' ))' );
+			if (hasSpreadElements) {
+				code.prependRight(
+					this.start + 'new'.length,
+					' (Function.prototype.bind.apply('
+				);
+				code.overwrite(
+					this.callee.end,
+					firstArgument.start,
+					', [ null ].concat( '
+				);
+				code.appendLeft(this.end, ' ))');
 			}
 		}
 
-		super.transpile( code, transforms );
+		super.transpile(code, transforms);
 	}
 }
diff --git a/src/program/types/ObjectExpression.js b/src/program/types/ObjectExpression.js
index 9c3c393..9afff13 100644
--- a/src/program/types/ObjectExpression.js
+++ b/src/program/types/ObjectExpression.js
@@ -2,44 +2,60 @@ import Node from '../Node.js';
 import CompileError from '../../utils/CompileError.js';
 
 export default class ObjectExpression extends Node {
-	transpile ( code, transforms ) {
-		super.transpile( code, transforms );
+	transpile(code, transforms) {
+		super.transpile(code, transforms);
 
 		let firstPropertyStart = this.start + 1;
 		let regularPropertyCount = 0;
 		let spreadPropertyCount = 0;
 		let computedPropertyCount = 0;
+		let firstSpreadProperty = null;
+		let firstComputedProperty = null;
 
-		for ( let prop of this.properties ) {
-			if ( prop.type === 'SpreadProperty' ) {
+		for (let i = 0; i < this.properties.length; ++i) {
+			const prop = this.properties[i];
+			if (prop.type === 'SpreadElement') {
 				spreadPropertyCount += 1;
-			} else if ( prop.computed ) {
+				if (firstSpreadProperty === null) firstSpreadProperty = i;
+			} else if (prop.computed) {
 				computedPropertyCount += 1;
-			} else if ( prop.type === 'Property' ) {
+				if (firstComputedProperty === null) firstComputedProperty = i;
+			} else if (prop.type === 'Property') {
 				regularPropertyCount += 1;
 			}
 		}
 
-		if ( spreadPropertyCount ) {
-			if ( !this.program.options.objectAssign ) {
-				throw new CompileError( this, 'Object spread operator requires specified objectAssign option with \'Object.assign\' or polyfill helper.' );
+		if (spreadPropertyCount) {
+			if (!this.program.options.objectAssign) {
+				throw new CompileError(
+					"Object spread operator requires specified objectAssign option with 'Object.assign' or polyfill helper.",
+					this
+				);
 			}
 			// enclose run of non-spread properties in curlies
 			let i = this.properties.length;
-			if ( regularPropertyCount ) {
-				while ( i-- ) {
+			if (regularPropertyCount && !computedPropertyCount) {
+				while (i--) {
 					const prop = this.properties[i];
 
-					if ( prop.type === 'Property' && !prop.computed ) {
-						const lastProp = this.properties[ i - 1 ];
-						const nextProp = this.properties[ i + 1 ];
+					if (prop.type === 'Property' && !prop.computed) {
+						const lastProp = this.properties[i - 1];
+						const nextProp = this.properties[i + 1];
 
-						if ( !lastProp || lastProp.type !== 'Property' || lastProp.computed ) {
-							code.insertRight( prop.start, '{' );
+						if (
+							!lastProp ||
+							lastProp.type !== 'Property' ||
+							lastProp.computed
+						) {
+							code.prependRight(prop.start, '{');
 						}
 
-						if ( !nextProp || nextProp.type !== 'Property' || nextProp.computed ) {
-							code.insertLeft( prop.end, '}' );
+						if (
+							!nextProp ||
+							nextProp.type !== 'Property' ||
+							nextProp.computed
+						) {
+							code.appendLeft(prop.end, '}');
 						}
 					}
 				}
@@ -47,95 +63,178 @@ export default class ObjectExpression extends Node {
 
 			// wrap the whole thing in Object.assign
 			firstPropertyStart = this.properties[0].start;
-			code.overwrite( this.start, firstPropertyStart, `${this.program.options.objectAssign}({}, `);
-			code.overwrite( this.properties[ this.properties.length - 1 ].end, this.end, ')' );
+			if (!computedPropertyCount) {
+				code.overwrite(
+					this.start,
+					firstPropertyStart,
+					`${this.program.options.objectAssign}({}, `
+				);
+				code.overwrite(
+					this.properties[this.properties.length - 1].end,
+					this.end,
+					')'
+				);
+			} else if (this.properties[0].type === 'SpreadElement') {
+				code.overwrite(
+					this.start,
+					firstPropertyStart,
+					`${this.program.options.objectAssign}({}, `
+				);
+				code.remove(this.end - 1, this.end);
+				code.appendRight(this.end, ')');
+			} else {
+				code.prependLeft(this.start, `${this.program.options.objectAssign}(`);
+				code.appendRight(this.end, ')');
+			}
 		}
 
-		if ( computedPropertyCount && transforms.computedProperty ) {
+		if (computedPropertyCount && transforms.computedProperty) {
 			const i0 = this.getIndentation();
 
 			let isSimpleAssignment;
 			let name;
 
-			if ( this.parent.type === 'VariableDeclarator' && this.parent.parent.declarations.length === 1 ) {
+			if (
+				this.parent.type === 'VariableDeclarator' &&
+				this.parent.parent.declarations.length === 1 &&
+				this.parent.id.type === 'Identifier'
+			) {
 				isSimpleAssignment = true;
 				name = this.parent.id.alias || this.parent.id.name; // TODO is this right?
-			} else if ( this.parent.type === 'AssignmentExpression' && this.parent.parent.type === 'ExpressionStatement' && this.parent.left.type === 'Identifier' ) {
+			} else if (
+				this.parent.type === 'AssignmentExpression' &&
+				this.parent.parent.type === 'ExpressionStatement' &&
+				this.parent.left.type === 'Identifier'
+			) {
 				isSimpleAssignment = true;
 				name = this.parent.left.alias || this.parent.left.name; // TODO is this right?
-			} else if ( this.parent.type === 'AssignmentPattern' && this.parent.left.type === 'Identifier' ) {
+			} else if (
+				this.parent.type === 'AssignmentPattern' &&
+				this.parent.left.type === 'Identifier'
+			) {
 				isSimpleAssignment = true;
 				name = this.parent.left.alias || this.parent.left.name; // TODO is this right?
 			}
 
+			if (spreadPropertyCount) isSimpleAssignment = false;
+
 			// handle block scoping
-			const declaration = this.findScope( false ).findDeclaration( name );
-			if ( declaration ) name = declaration.name;
+			const declaration = this.findScope(false).findDeclaration(name);
+			if (declaration) name = declaration.name;
 
 			const start = firstPropertyStart;
 			const end = this.end;
 
-			if ( isSimpleAssignment ) {
+			if (isSimpleAssignment) {
 				// ???
 			} else {
-				name = this.findScope( true ).createIdentifier( 'obj' );
-
-				const statement = this.findNearest( /(?:Statement|Declaration)$/ );
-				code.insertLeft( statement.end, `\n${i0}var ${name};` );
-
-				code.insertRight( this.start, `( ${name} = ` );
+				if (
+					firstSpreadProperty === null ||
+					firstComputedProperty < firstSpreadProperty
+				) {
+					name = this.findLexicalBoundary().declareIdentifier('obj');
+
+					code.prependRight(this.start, `( ${name} = `);
+				} else name = null; // We don't actually need this variable
 			}
 
 			const len = this.properties.length;
 			let lastComputedProp;
 			let sawNonComputedProperty = false;
+			let isFirst = true;
 
-			for ( let i = 0; i < len; i += 1 ) {
+			for (let i = 0; i < len; i += 1) {
 				const prop = this.properties[i];
+				let moveStart = i > 0 ? this.properties[i - 1].end : start;
 
-				if ( prop.computed ) {
+				if (
+					prop.type === 'Property' &&
+					(prop.computed || (lastComputedProp && !spreadPropertyCount))
+				) {
+					if (i === 0) moveStart = this.start + 1; // Trim leading whitespace
 					lastComputedProp = prop;
-					let moveStart = i > 0 ? this.properties[ i - 1 ].end : start;
 
-					const propId = isSimpleAssignment ? `;\n${i0}${name}` : `, ${name}`;
+					if (!name) {
+						name = this.findLexicalBoundary().declareIdentifier('obj');
 
-					if (moveStart < prop.start) {
-						code.overwrite( moveStart, prop.start, propId );
+						const propId = name + (prop.computed ? '' : '.');
+						code.appendRight(prop.start, `( ${name} = {}, ${propId}`);
 					} else {
-						code.insertRight( prop.start, propId );
+						const propId =
+							(isSimpleAssignment ? `;\n${i0}${name}` : `, ${name}`) +
+							(prop.computed ? '' : '.');
+
+						if (moveStart < prop.start) {
+							code.overwrite(moveStart, prop.start, propId);
+						} else {
+							code.prependRight(prop.start, propId);
+						}
 					}
 
 					let c = prop.key.end;
-					while ( code.original[c] !== ']' ) c += 1;
-					c += 1;
-
-					if ( prop.value.start > c ) code.remove( c, prop.value.start );
-					code.insertLeft( c, ' = ' );
-					code.move( moveStart, prop.end, end );
-
-					if ( i < len - 1 && ! sawNonComputedProperty ) {
-						// remove trailing comma
-						c = prop.end;
-						while ( code.original[c] !== ',' ) c += 1;
+					if (prop.computed) {
+						while (code.original[c] !== ']') c += 1;
+						c += 1;
+					}
+					if (prop.shorthand) {
+						code.overwrite(
+							prop.start,
+							prop.key.end,
+							code.slice(prop.start, prop.key.end).replace(/:/, ' =')
+						);
+					} else {
+						if (prop.value.start > c) code.remove(c, prop.value.start);
+						code.appendLeft(c, ' = ');
+					}
 
-						code.remove( prop.end, c + 1 );
+					if (prop.method && transforms.conciseMethodProperty) {
+						code.prependRight(prop.value.start, 'function ');
 					}
+				} else if (prop.type === 'SpreadElement') {
+					if (name && i > 0) {
+						if (!lastComputedProp) {
+							lastComputedProp = this.properties[i - 1];
+						}
+						code.appendLeft(lastComputedProp.end, `, ${name} )`);
 
-					if ( prop.method && transforms.conciseMethodProperty ) {
-						code.insertRight( prop.value.start, 'function ' );
+						lastComputedProp = null;
+						name = null;
 					}
 				} else {
+					if (!isFirst && spreadPropertyCount) {
+						// We are in an Object.assign context, so we need to wrap regular properties
+						code.prependRight(prop.start, '{');
+						code.appendLeft(prop.end, '}');
+					}
 					sawNonComputedProperty = true;
 				}
+				if (isFirst && (prop.type === 'SpreadElement' || prop.computed)) {
+					let beginEnd = sawNonComputedProperty
+						? this.properties[this.properties.length - 1].end
+						: this.end - 1;
+					// Trim trailing comma because it can easily become a leading comma which is illegal
+					if (code.original[beginEnd] == ',') ++beginEnd;
+					const closing = code.slice(beginEnd, end);
+					code.prependLeft(moveStart, closing);
+					code.remove(beginEnd, end);
+					isFirst = false;
+				}
+
+				// Clean up some extranous whitespace
+				let c = prop.end;
+				if (i < len - 1 && !sawNonComputedProperty) {
+					while (code.original[c] !== ',') c += 1;
+				} else if (i == len - 1) c = this.end;
+				code.remove(prop.end, c);
 			}
 
 			// special case
-			if ( computedPropertyCount === len ) {
-				code.remove( this.properties[ len - 1 ].end, this.end - 1 );
+			if (computedPropertyCount === len) {
+				code.remove(this.properties[len - 1].end, this.end - 1);
 			}
 
-			if ( !isSimpleAssignment ) {
-				code.insertLeft( lastComputedProp.end, `, ${name} )` );
+			if (!isSimpleAssignment && name) {
+				code.appendLeft(lastComputedProp.end, `, ${name} )`);
 			}
 		}
 	}
diff --git a/src/program/types/Property.js b/src/program/types/Property.js
index 520fc3c..6ea9fbc 100644
--- a/src/program/types/Property.js
+++ b/src/program/types/Property.js
@@ -2,39 +2,51 @@ import Node from '../Node.js';
 import reserved from '../../utils/reserved.js';
 
 export default class Property extends Node {
-	transpile ( code, transforms ) {
-		if ( transforms.conciseMethodProperty && !this.computed && this.parent.type !== 'ObjectPattern' ) {
-			if ( this.shorthand ) {
-				code.insertRight( this.start, `${this.key.name}: ` );
-			} else if ( this.method ) {
+	transpile(code, transforms) {
+		super.transpile(code, transforms);
+
+		if (
+			transforms.conciseMethodProperty &&
+			!this.computed &&
+			this.parent.type !== 'ObjectPattern'
+		) {
+			if (this.shorthand) {
+				code.prependRight(this.start, `${this.key.name}: `);
+			} else if (this.method) {
 				let name = '';
-				if ( this.program.options.namedFunctionExpressions !== false ) {
-					if ( this.key.type === 'Literal' && typeof this.key.value === 'number' ) {
-						name = "";
-					} else if ( this.key.type === 'Identifier' ) {
-						if ( reserved[ this.key.name ] ||
-							 ! /^[a-z_$][a-z0-9_$]*$/i.test( this.key.name ) ||
-						     this.value.body.scope.references[this.key.name] ) {
-							name = this.findScope( true ).createIdentifier( this.key.name );
+				if (this.program.options.namedFunctionExpressions !== false) {
+					if (
+						this.key.type === 'Literal' &&
+						typeof this.key.value === 'number'
+					) {
+						name = '';
+					} else if (this.key.type === 'Identifier') {
+						if (
+							reserved[this.key.name] ||
+							!/^[a-z_$][a-z0-9_$]*$/i.test(this.key.name) ||
+							this.value.body.scope.references[this.key.name]
+						) {
+							name = this.findScope(true).createIdentifier(this.key.name);
 						} else {
 							name = this.key.name;
 						}
 					} else {
-						name = this.findScope( true ).createIdentifier( this.key.value );
+						name = this.findScope(true).createIdentifier(this.key.value);
 					}
 					name = ' ' + name;
 				}
 
-				if ( this.value.generator ) code.remove( this.start, this.key.start );
-				code.insertLeft( this.key.end, `: function${this.value.generator ? '*' : ''}${name}` );
+				if (this.value.generator) code.remove(this.start, this.key.start);
+				code.appendLeft(
+					this.key.end,
+					`: function${this.value.generator ? '*' : ''}${name}`
+				);
 			}
 		}
 
-		if ( transforms.reservedProperties && reserved[ this.key.name ] ) {
-			code.insertRight( this.key.start, `'` );
-			code.insertLeft( this.key.end, `'` );
+		if (transforms.reservedProperties && reserved[this.key.name]) {
+			code.prependRight(this.key.start, `'`);
+			code.appendLeft(this.key.end, `'`);
 		}
-
-		super.transpile( code, transforms );
 	}
 }
diff --git a/src/program/types/ReturnStatement.js b/src/program/types/ReturnStatement.js
index edf5618..9620863 100644
--- a/src/program/types/ReturnStatement.js
+++ b/src/program/types/ReturnStatement.js
@@ -2,27 +2,31 @@ import Node from '../Node.js';
 import { loopStatement } from '../../utils/patterns.js';
 
 export default class ReturnStatement extends Node {
-	initialise ( transforms ) {
-		this.loop = this.findNearest( loopStatement );
-		this.nearestFunction = this.findNearest( /Function/ );
+	initialise(transforms) {
+		this.loop = this.findNearest(loopStatement);
+		this.nearestFunction = this.findNearest(/Function/);
 
-		if ( this.loop && ( !this.nearestFunction || this.loop.depth > this.nearestFunction.depth ) ) {
+		if (
+			this.loop &&
+			(!this.nearestFunction || this.loop.depth > this.nearestFunction.depth)
+		) {
 			this.loop.canReturn = true;
 			this.shouldWrap = true;
 		}
 
-		if ( this.argument ) this.argument.initialise( transforms );
+		if (this.argument) this.argument.initialise(transforms);
 	}
 
-	transpile ( code, transforms ) {
-		const shouldWrap = this.shouldWrap && this.loop && this.loop.shouldRewriteAsFunction;
+	transpile(code, transforms) {
+		const shouldWrap =
+			this.shouldWrap && this.loop && this.loop.shouldRewriteAsFunction;
 
-		if ( this.argument ) {
-			if ( shouldWrap ) code.insertRight( this.argument.start, `{ v: ` );
-			this.argument.transpile( code, transforms );
-			if ( shouldWrap ) code.insertLeft( this.argument.end, ` }` );
-		} else if ( shouldWrap ) {
-			code.insertLeft( this.start + 6, ' {}' );
+		if (this.argument) {
+			if (shouldWrap) code.prependRight(this.argument.start, `{ v: `);
+			this.argument.transpile(code, transforms);
+			if (shouldWrap) code.appendLeft(this.argument.end, ` }`);
+		} else if (shouldWrap) {
+			code.appendLeft(this.start + 6, ' {}');
 		}
 	}
 }
diff --git a/src/program/types/SpreadElement.js b/src/program/types/SpreadElement.js
new file mode 100644
index 0000000..891a64e
--- /dev/null
+++ b/src/program/types/SpreadElement.js
@@ -0,0 +1,12 @@
+import Node from '../Node.js';
+
+export default class SpreadElement extends Node {
+	transpile(code, transforms) {
+		if (this.parent.type == 'ObjectExpression') {
+			code.remove(this.start, this.argument.start);
+			code.remove(this.argument.end, this.end);
+		}
+
+		super.transpile(code, transforms);
+	}
+}
diff --git a/src/program/types/SpreadProperty.js b/src/program/types/SpreadProperty.js
deleted file mode 100644
index ed90e25..0000000
--- a/src/program/types/SpreadProperty.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import Node from '../Node.js';
-
-export default class SpreadProperty extends Node {
-	transpile ( code, transforms ) {
-		code.remove( this.start, this.argument.start );
-		code.remove( this.argument.end, this.end );
-
-		super.transpile( code, transforms );
-	}
-}
diff --git a/src/program/types/Super.js b/src/program/types/Super.js
index c112909..d26e873 100644
--- a/src/program/types/Super.js
+++ b/src/program/types/Super.js
@@ -3,65 +3,84 @@ import CompileError from '../../utils/CompileError.js';
 import { loopStatement } from '../../utils/patterns.js';
 
 export default class Super extends Node {
-	initialise ( transforms ) {
-		if ( transforms.classes ) {
-			this.method = this.findNearest( 'MethodDefinition' );
-			if ( !this.method ) throw new CompileError( this, 'use of super outside class method' );
-
-			const parentClass = this.findNearest( 'ClassBody' ).parent;
-			this.superClassName = parentClass.superClass && (parentClass.superClass.name || 'superclass');
-
-			if ( !this.superClassName ) throw new CompileError( this, 'super used in base class' );
-
-			this.isCalled = this.parent.type === 'CallExpression' && this === this.parent.callee;
-
-			if ( this.method.kind !== 'constructor' && this.isCalled ) {
-				throw new CompileError( this, 'super() not allowed outside class constructor' );
+	initialise(transforms) {
+		if (transforms.classes) {
+			this.method = this.findNearest('MethodDefinition');
+			if (!this.method)
+				throw new CompileError(this, 'use of super outside class method');
+
+			const parentClass = this.findNearest('ClassBody').parent;
+			this.superClassName =
+				parentClass.superClass && (parentClass.superClass.name || 'superclass');
+
+			if (!this.superClassName)
+				throw new CompileError('super used in base class', this);
+
+			this.isCalled =
+				this.parent.type === 'CallExpression' && this === this.parent.callee;
+
+			if (this.method.kind !== 'constructor' && this.isCalled) {
+				throw new CompileError(
+					'super() not allowed outside class constructor',
+					this
+				);
 			}
 
 			this.isMember = this.parent.type === 'MemberExpression';
 
-			if ( !this.isCalled && !this.isMember ) {
-				throw new CompileError( this, 'Unexpected use of `super` (expected `super(...)` or `super.*`)' );
+			if (!this.isCalled && !this.isMember) {
+				throw new CompileError(
+					'Unexpected use of `super` (expected `super(...)` or `super.*`)',
+					this
+				);
 			}
 		}
 
-		if ( transforms.arrow ) {
+		if (transforms.arrow) {
 			const lexicalBoundary = this.findLexicalBoundary();
-			const arrowFunction = this.findNearest( 'ArrowFunctionExpression' );
-			const loop = this.findNearest( loopStatement );
+			const arrowFunction = this.findNearest('ArrowFunctionExpression');
+			const loop = this.findNearest(loopStatement);
 
-			if ( arrowFunction && arrowFunction.depth > lexicalBoundary.depth ) {
+			if (arrowFunction && arrowFunction.depth > lexicalBoundary.depth) {
 				this.thisAlias = lexicalBoundary.getThisAlias();
 			}
 
-			if ( loop && loop.body.contains( this ) && loop.depth > lexicalBoundary.depth ) {
+			if (
+				loop &&
+				loop.body.contains(this) &&
+				loop.depth > lexicalBoundary.depth
+			) {
 				this.thisAlias = lexicalBoundary.getThisAlias();
 			}
 		}
 	}
 
-	transpile ( code, transforms ) {
-		if ( transforms.classes ) {
-			const expression = ( this.isCalled || this.method.static ) ?
-				this.superClassName :
-				`${this.superClassName}.prototype`;
+	transpile(code, transforms) {
+		if (transforms.classes) {
+			const expression =
+				this.isCalled || this.method.static
+					? this.superClassName
+					: `${this.superClassName}.prototype`;
 
-			code.overwrite( this.start, this.end, expression, true );
+			code.overwrite(this.start, this.end, expression, {
+				storeName: true,
+				contentOnly: true
+			});
 
 			const callExpression = this.isCalled ? this.parent : this.parent.parent;
 
-			if ( callExpression && callExpression.type === 'CallExpression' ) {
-				if ( !this.noCall ) { // special case – `super( ...args )`
-					code.insertLeft( callExpression.callee.end, '.call' );
+			if (callExpression && callExpression.type === 'CallExpression') {
+				if (!this.noCall) {
+					// special case – `super( ...args )`
+					code.appendLeft(callExpression.callee.end, '.call');
 				}
 
 				const thisAlias = this.thisAlias || 'this';
 
-				if ( callExpression.arguments.length ) {
-					code.insertLeft( callExpression.arguments[0].start, `${thisAlias}, ` );
+				if (callExpression.arguments.length) {
+					code.appendLeft(callExpression.arguments[0].start, `${thisAlias}, `);
 				} else {
-					code.insertLeft( callExpression.end - 1, `${thisAlias}` );
+					code.appendLeft(callExpression.end - 1, `${thisAlias}`);
 				}
 			}
 		}
diff --git a/src/program/types/TaggedTemplateExpression.js b/src/program/types/TaggedTemplateExpression.js
index c70aa01..987aabe 100644
--- a/src/program/types/TaggedTemplateExpression.js
+++ b/src/program/types/TaggedTemplateExpression.js
@@ -2,36 +2,50 @@ import Node from '../Node.js';
 import CompileError from '../../utils/CompileError.js';
 
 export default class TaggedTemplateExpression extends Node {
-	initialise ( transforms ) {
-		if ( transforms.templateString && !transforms.dangerousTaggedTemplateString ) {
-			throw new CompileError( this, 'Tagged template strings are not supported. Use `transforms: { templateString: false }` to skip transformation and disable this error, or `transforms: { dangerousTaggedTemplateString: true }` if you know what you\'re doing' );
+	initialise(transforms) {
+		if (
+			transforms.templateString &&
+			!transforms.dangerousTaggedTemplateString
+		) {
+			throw new CompileError(
+				"Tagged template strings are not supported. Use `transforms: { templateString: false }` to skip transformation and disable this error, or `transforms: { dangerousTaggedTemplateString: true }` if you know what you're doing",
+				this
+			);
 		}
 
-		super.initialise( transforms );
+		super.initialise(transforms);
 	}
 
-	transpile ( code, transforms ) {
-		if ( transforms.templateString && transforms.dangerousTaggedTemplateString ) {
-			const ordered = this.quasi.expressions.concat( this.quasi.quasis ).sort( ( a, b ) => a.start - b.start );
+	transpile(code, transforms) {
+		if (transforms.templateString && transforms.dangerousTaggedTemplateString) {
+			const ordered = this.quasi.expressions
+				.concat(this.quasi.quasis)
+				.sort((a, b) => a.start - b.start);
 
 			// insert strings at start
-			const templateStrings = this.quasi.quasis.map( quasi => JSON.stringify( quasi.value.cooked ) );
-			code.overwrite( this.tag.end, ordered[0].start, `([${templateStrings.join(', ')}]` );
+			const templateStrings = this.quasi.quasis.map(quasi =>
+				JSON.stringify(quasi.value.cooked)
+			);
+			code.overwrite(
+				this.tag.end,
+				ordered[0].start,
+				`([${templateStrings.join(', ')}]`
+			);
 
 			let lastIndex = ordered[0].start;
-			ordered.forEach( node => {
-				if ( node.type === 'TemplateElement' ) {
-					code.remove( lastIndex, node.end );
+			ordered.forEach(node => {
+				if (node.type === 'TemplateElement') {
+					code.remove(lastIndex, node.end);
 				} else {
-					code.overwrite( lastIndex, node.start, ', ' );
+					code.overwrite(lastIndex, node.start, ', ');
 				}
 
 				lastIndex = node.end;
 			});
 
-			code.overwrite( lastIndex, this.end, ')' );
+			code.overwrite(lastIndex, this.end, ')');
 		}
 
-		super.transpile( code, transforms );
+		super.transpile(code, transforms);
 	}
 }
diff --git a/src/program/types/TemplateElement.js b/src/program/types/TemplateElement.js
index b9658b5..e08965d 100644
--- a/src/program/types/TemplateElement.js
+++ b/src/program/types/TemplateElement.js
@@ -1,7 +1,7 @@
 import Node from '../Node.js';
 
 export default class TemplateElement extends Node {
-	initialise () {
-		this.program.indentExclusionElements.push( this );
+	initialise() {
+		this.program.indentExclusionElements.push(this);
 	}
 }
diff --git a/src/program/types/TemplateLiteral.js b/src/program/types/TemplateLiteral.js
index af3b503..b89961f 100644
--- a/src/program/types/TemplateLiteral.js
+++ b/src/program/types/TemplateLiteral.js
@@ -1,16 +1,22 @@
 import Node from '../Node.js';
 
 export default class TemplateLiteral extends Node {
-	transpile ( code, transforms ) {
-		if ( transforms.templateString && this.parent.type !== 'TaggedTemplateExpression' ) {
-			let ordered = this.expressions.concat( this.quasis )
-				.sort( ( a, b ) => a.start - b.start || a.end - b.end )
-				.filter( ( node, i ) => {
+	transpile(code, transforms) {
+		super.transpile(code, transforms);
+
+		if (
+			transforms.templateString &&
+			this.parent.type !== 'TaggedTemplateExpression'
+		) {
+			let ordered = this.expressions
+				.concat(this.quasis)
+				.sort((a, b) => a.start - b.start || a.end - b.end)
+				.filter((node, i) => {
 					// include all expressions
-					if ( node.type !== 'TemplateElement' ) return true;
+					if (node.type !== 'TemplateElement') return true;
 
 					// include all non-empty strings
-					if ( node.value.raw ) return true;
+					if (node.value.raw) return true;
 
 					// exclude all empty strings not at the head
 					return !i;
@@ -20,51 +26,55 @@ export default class TemplateLiteral extends Node {
 			// if it's the empty string, but only if the second and
 			// third elements aren't both expressions (since they maybe
 			// be numeric, and `1 + 2 + '3' === '33'`)
-			if ( ordered.length >= 3 ) {
-				const [ first, , third ] = ordered;
-				if ( first.type === 'TemplateElement' && first.value.raw === '' && third.type === 'TemplateElement' ) {
+			if (ordered.length >= 3) {
+				const [first, , third] = ordered;
+				if (
+					first.type === 'TemplateElement' &&
+					first.value.raw === '' &&
+					third.type === 'TemplateElement'
+				) {
 					ordered.shift();
 				}
 			}
 
-			const parenthesise = ( this.quasis.length !== 1 || this.expressions.length !== 0 ) &&
-			                     this.parent.type !== 'AssignmentExpression' &&
-			                     this.parent.type !== 'AssignmentPattern' &&
-			                     this.parent.type !== 'VariableDeclarator' &&
-			                     ( this.parent.type !== 'BinaryExpression' || this.parent.operator !== '+' );
+			const parenthesise =
+				(this.quasis.length !== 1 || this.expressions.length !== 0) &&
+				this.parent.type !== 'TemplateLiteral' &&
+				this.parent.type !== 'AssignmentExpression' &&
+				this.parent.type !== 'AssignmentPattern' &&
+				this.parent.type !== 'VariableDeclarator' &&
+				(this.parent.type !== 'BinaryExpression' ||
+					this.parent.operator !== '+');
 
-			if ( parenthesise ) code.insertRight( this.start, '(' );
+			if (parenthesise) code.appendRight(this.start, '(');
 
 			let lastIndex = this.start;
 
-			ordered.forEach( ( node, i ) => {
-				if ( node.type === 'TemplateElement' ) {
-					let replacement = '';
-					if ( i ) replacement += ' + ';
-					replacement += JSON.stringify( node.value.cooked );
+			ordered.forEach((node, i) => {
+				let prefix = i === 0 ? (parenthesise ? '(' : '') : ' + ';
 
-					code.overwrite( lastIndex, node.end, replacement );
+				if (node.type === 'TemplateElement') {
+					code.overwrite(
+						lastIndex,
+						node.end,
+						prefix + JSON.stringify(node.value.cooked)
+					);
 				} else {
 					const parenthesise = node.type !== 'Identifier'; // TODO other cases where it's safe
 
-					let replacement = '';
-					if ( i ) replacement += ' + ';
-					if ( parenthesise ) replacement += '(';
+					if (parenthesise) prefix += '(';
 
-					code.overwrite( lastIndex, node.start, replacement );
+					code.remove(lastIndex, node.start);
 
-					if ( parenthesise ) code.insertLeft( node.end, ')' );
+					if (prefix) code.prependRight(node.start, prefix);
+					if (parenthesise) code.appendLeft(node.end, ')');
 				}
 
 				lastIndex = node.end;
 			});
 
-			let close = '';
-			if ( parenthesise ) close += ')';
-
-			code.overwrite( lastIndex, this.end, close );
+			if (parenthesise) code.appendLeft(lastIndex, ')');
+			code.remove(lastIndex, this.end);
 		}
-
-		super.transpile( code, transforms );
 	}
 }
diff --git a/src/program/types/ThisExpression.js b/src/program/types/ThisExpression.js
index 049ebfb..b672b7b 100644
--- a/src/program/types/ThisExpression.js
+++ b/src/program/types/ThisExpression.js
@@ -2,23 +2,30 @@ import Node from '../Node.js';
 import { loopStatement } from '../../utils/patterns.js';
 
 export default class ThisExpression extends Node {
-	initialise ( transforms ) {
-		if ( transforms.arrow ) {
+	initialise(transforms) {
+		if (transforms.arrow) {
 			const lexicalBoundary = this.findLexicalBoundary();
-			const arrowFunction = this.findNearest( 'ArrowFunctionExpression' );
-			const loop = this.findNearest( loopStatement );
+			const arrowFunction = this.findNearest('ArrowFunctionExpression');
+			const loop = this.findNearest(loopStatement);
 
-			if ( ( arrowFunction && arrowFunction.depth > lexicalBoundary.depth )
-			|| ( loop && loop.body.contains( this ) && loop.depth > lexicalBoundary.depth )
-			|| ( loop && loop.right && loop.right.contains( this ) ) ) {
+			if (
+				(arrowFunction && arrowFunction.depth > lexicalBoundary.depth) ||
+				(loop &&
+					loop.body.contains(this) &&
+					loop.depth > lexicalBoundary.depth) ||
+				(loop && loop.right && loop.right.contains(this))
+			) {
 				this.alias = lexicalBoundary.getThisAlias();
 			}
 		}
 	}
 
-	transpile ( code ) {
-		if ( this.alias ) {
-			code.overwrite( this.start, this.end, this.alias, true );
+	transpile(code) {
+		if (this.alias) {
+			code.overwrite(this.start, this.end, this.alias, {
+				storeName: true,
+				contentOnly: true
+			});
 		}
 	}
 }
diff --git a/src/program/types/UpdateExpression.js b/src/program/types/UpdateExpression.js
index 9cd6580..a094db8 100644
--- a/src/program/types/UpdateExpression.js
+++ b/src/program/types/UpdateExpression.js
@@ -2,20 +2,26 @@ import Node from '../Node.js';
 import CompileError from '../../utils/CompileError.js';
 
 export default class UpdateExpression extends Node {
-	initialise ( transforms ) {
-		if ( this.argument.type === 'Identifier' ) {
-			const declaration = this.findScope( false ).findDeclaration( this.argument.name );
-			if ( declaration && declaration.kind === 'const' ) {
-				throw new CompileError( this, `${this.argument.name} is read-only` );
+	initialise(transforms) {
+		if (this.argument.type === 'Identifier') {
+			const declaration = this.findScope(false).findDeclaration(
+				this.argument.name
+			);
+			if (declaration && declaration.kind === 'const') {
+				throw new CompileError(`${this.argument.name} is read-only`, this);
 			}
 
 			// special case – https://gitlab.com/Rich-Harris/buble/issues/150
-			const statement = declaration && declaration.node.ancestor( 3 );
-			if ( statement && statement.type === 'ForStatement' && statement.body.contains( this ) ) {
-				statement.reassigned[ this.argument.name ] = true;
+			const statement = declaration && declaration.node.ancestor(3);
+			if (
+				statement &&
+				statement.type === 'ForStatement' &&
+				statement.body.contains(this)
+			) {
+				statement.reassigned[this.argument.name] = true;
 			}
 		}
 
-		super.initialise( transforms );
+		super.initialise(transforms);
 	}
 }
diff --git a/src/program/types/VariableDeclaration.js b/src/program/types/VariableDeclaration.js
index 3cbd4c9..42134ed 100644
--- a/src/program/types/VariableDeclaration.js
+++ b/src/program/types/VariableDeclaration.js
@@ -3,83 +3,96 @@ import destructure from '../../utils/destructure.js';
 import { loopStatement } from '../../utils/patterns.js';
 
 export default class VariableDeclaration extends Node {
-	initialise ( transforms ) {
-		this.scope = this.findScope( this.kind === 'var' );
-		this.declarations.forEach( declarator => declarator.initialise( transforms ) );
+	initialise(transforms) {
+		this.scope = this.findScope(this.kind === 'var');
+		this.declarations.forEach(declarator => declarator.initialise(transforms));
 	}
 
-	transpile ( code, transforms ) {
+	transpile(code, transforms) {
 		const i0 = this.getIndentation();
 		let kind = this.kind;
 
-		if ( transforms.letConst && kind !== 'var' ) {
+		if (transforms.letConst && kind !== 'var') {
 			kind = 'var';
-			code.overwrite( this.start, this.start + this.kind.length, kind, true );
+			code.overwrite(this.start, this.start + this.kind.length, kind, {
+				storeName: true
+			});
 		}
 
-		if ( transforms.destructuring && this.parent.type !== 'ForOfStatement' ) {
+		if (transforms.destructuring && this.parent.type !== 'ForOfStatement') {
 			let c = this.start;
 			let lastDeclaratorIsPattern;
 
-			this.declarations.forEach( ( declarator, i ) => {
-				if ( declarator.id.type === 'Identifier' ) {
-					if ( i > 0 && this.declarations[ i - 1 ].id.type !== 'Identifier' ) {
-						code.overwrite( c, declarator.id.start, `var ` );
+			this.declarations.forEach((declarator, i) => {
+				declarator.transpile(code, transforms);
+
+				if (declarator.id.type === 'Identifier') {
+					if (i > 0 && this.declarations[i - 1].id.type !== 'Identifier') {
+						code.overwrite(c, declarator.id.start, `var `);
 					}
 				} else {
-					const inline = loopStatement.test( this.parent.type );
+					const inline = loopStatement.test(this.parent.type);
 
-					if ( i === 0 ) {
-						code.remove( c, declarator.id.start );
+					if (i === 0) {
+						code.remove(c, declarator.id.start);
 					} else {
-						code.overwrite( c, declarator.id.start, `;\n${i0}` );
+						code.overwrite(c, declarator.id.start, `;\n${i0}`);
 					}
 
-					const simple = declarator.init.type === 'Identifier' && !declarator.init.rewritten;
+					const simple =
+						declarator.init.type === 'Identifier' && !declarator.init.rewritten;
 
-					const name = simple ? declarator.init.name : declarator.findScope( true ).createIdentifier( 'ref' );
+					const name = simple
+						? declarator.init.name
+						: declarator.findScope(true).createIdentifier('ref');
 
-					let c = declarator.start;
+					c = declarator.start;
 
 					let statementGenerators = [];
 
-					if ( simple ) {
-						code.remove( declarator.id.end, declarator.end );
+					if (simple) {
+						code.remove(declarator.id.end, declarator.end);
 					} else {
-						statementGenerators.push( ( start, prefix, suffix ) => {
-							code.insertRight( declarator.id.end, `var ${name}` );
-							code.insertLeft( declarator.init.end, `${suffix}` );
-							code.move( declarator.id.end, declarator.end, start );
+						statementGenerators.push((start, prefix, suffix) => {
+							code.prependRight(declarator.id.end, `var ${name}`);
+							code.appendLeft(declarator.init.end, `${suffix}`);
+							code.move(declarator.id.end, declarator.end, start);
 						});
 					}
 
-					destructure( code, declarator.findScope( false ), declarator.id, name, inline, statementGenerators );
+					destructure(
+						code,
+						declarator.findScope(false),
+						declarator.id,
+						name,
+						inline,
+						statementGenerators
+					);
 
 					let prefix = inline ? 'var ' : '';
 					let suffix = inline ? `, ` : `;\n${i0}`;
-					statementGenerators.forEach( ( fn, j ) => {
-						if ( i === this.declarations.length - 1 && j === statementGenerators.length - 1 ) {
+					statementGenerators.forEach((fn, j) => {
+						if (
+							i === this.declarations.length - 1 &&
+							j === statementGenerators.length - 1
+						) {
 							suffix = inline ? '' : ';';
 						}
 
-						fn( declarator.start, j === 0 ? prefix : '', suffix );
+						fn(declarator.start, j === 0 ? prefix : '', suffix);
 					});
 				}
 
-				declarator.transpile( code, transforms );
-
 				c = declarator.end;
 				lastDeclaratorIsPattern = declarator.id.type !== 'Identifier';
 			});
 
-			if ( lastDeclaratorIsPattern ) {
-				code.remove( c, this.end );
+			if (lastDeclaratorIsPattern && this.end > c) {
+				code.overwrite(c, this.end, '', { contentOnly: true });
 			}
-		}
-
-		else {
-			this.declarations.forEach( declarator => {
-				declarator.transpile( code, transforms );
+		} else {
+			this.declarations.forEach(declarator => {
+				declarator.transpile(code, transforms);
 			});
 		}
 	}
diff --git a/src/program/types/VariableDeclarator.js b/src/program/types/VariableDeclarator.js
index d3944c0..4f7dc88 100644
--- a/src/program/types/VariableDeclarator.js
+++ b/src/program/types/VariableDeclarator.js
@@ -1,35 +1,43 @@
 import Node from '../Node.js';
 
 export default class VariableDeclarator extends Node {
-	initialise ( transforms ) {
+	initialise(transforms) {
 		let kind = this.parent.kind;
-		if ( kind === 'let' && this.parent.parent.type === 'ForStatement' ) {
+		if (kind === 'let' && this.parent.parent.type === 'ForStatement') {
 			kind = 'for.let'; // special case...
 		}
 
-		this.parent.scope.addDeclaration( this.id, kind );
-		super.initialise( transforms );
+		this.parent.scope.addDeclaration(this.id, kind);
+		super.initialise(transforms);
 	}
 
-	transpile ( code, transforms ) {
-		if ( !this.init && transforms.letConst && this.parent.kind !== 'var' ) {
-			let inLoop = this.findNearest( /Function|^For(In|Of)?Statement|^(?:Do)?WhileStatement/ );
-			if ( inLoop && ! /Function/.test( inLoop.type ) && ! this.isLeftDeclaratorOfLoop() ) {
-				code.insertLeft( this.id.end, ' = (void 0)' );
+	transpile(code, transforms) {
+		if (!this.init && transforms.letConst && this.parent.kind !== 'var') {
+			let inLoop = this.findNearest(
+				/Function|^For(In|Of)?Statement|^(?:Do)?WhileStatement/
+			);
+			if (
+				inLoop &&
+				!/Function/.test(inLoop.type) &&
+				!this.isLeftDeclaratorOfLoop()
+			) {
+				code.appendLeft(this.id.end, ' = (void 0)');
 			}
 		}
 
-		if ( this.id ) this.id.transpile( code, transforms );
-		if ( this.init ) this.init.transpile( code, transforms );
+		if (this.id) this.id.transpile(code, transforms);
+		if (this.init) this.init.transpile(code, transforms);
 	}
 
-	isLeftDeclaratorOfLoop () {
-		return this.parent
-			&& this.parent.type === 'VariableDeclaration'
-			&& this.parent.parent
-			&& (this.parent.parent.type === 'ForInStatement'
-				|| this.parent.parent.type === 'ForOfStatement')
-			&& this.parent.parent.left
-			&& this.parent.parent.left.declarations[0] === this;
+	isLeftDeclaratorOfLoop() {
+		return (
+			this.parent &&
+			this.parent.type === 'VariableDeclaration' &&
+			this.parent.parent &&
+			(this.parent.parent.type === 'ForInStatement' ||
+				this.parent.parent.type === 'ForOfStatement') &&
+			this.parent.parent.left &&
+			this.parent.parent.left.declarations[0] === this
+		);
 	}
 }
diff --git a/src/program/types/index.js b/src/program/types/index.js
index 7591d21..f0d4f65 100644
--- a/src/program/types/index.js
+++ b/src/program/types/index.js
@@ -33,7 +33,7 @@ import NewExpression from './NewExpression.js';
 import ObjectExpression from './ObjectExpression.js';
 import Property from './Property.js';
 import ReturnStatement from './ReturnStatement.js';
-import SpreadProperty from './SpreadProperty.js';
+import SpreadElement from './SpreadElement.js';
 import Super from './Super.js';
 import TaggedTemplateExpression from './TaggedTemplateExpression.js';
 import TemplateElement from './TemplateElement.js';
@@ -79,7 +79,7 @@ export default {
 	ObjectExpression,
 	Property,
 	ReturnStatement,
-	SpreadProperty,
+	SpreadElement,
 	Super,
 	TaggedTemplateExpression,
 	TemplateElement,
diff --git a/src/program/types/shared/LoopStatement.js b/src/program/types/shared/LoopStatement.js
index e53ed29..1e6154f 100644
--- a/src/program/types/shared/LoopStatement.js
+++ b/src/program/types/shared/LoopStatement.js
@@ -1,91 +1,105 @@
 import Node from '../../Node.js';
 
 export default class LoopStatement extends Node {
-	findScope ( functionScope ) {
-		return functionScope || !this.createdScope ? this.parent.findScope( functionScope ) : this.body.scope;
+	findScope(functionScope) {
+		return functionScope || !this.createdScope
+			? this.parent.findScope(functionScope)
+			: this.body.scope;
 	}
 
-	initialise ( transforms ) {
+	initialise(transforms) {
 		this.body.createScope();
 		this.createdScope = true;
 
 		// this is populated as and when reassignments occur
-		this.reassigned = Object.create( null );
-		this.aliases = Object.create( null );
+		this.reassigned = Object.create(null);
+		this.aliases = Object.create(null);
 
-		super.initialise( transforms );
+		super.initialise(transforms);
 
-		if ( transforms.letConst ) {
+		if (transforms.letConst) {
 			// see if any block-scoped declarations are referenced
 			// inside function expressions
-			const names = Object.keys( this.body.scope.declarations );
+			const names = Object.keys(this.body.scope.declarations);
 
 			let i = names.length;
-			while ( i-- ) {
+			while (i--) {
 				const name = names[i];
-				const declaration = this.body.scope.declarations[ name ];
+				const declaration = this.body.scope.declarations[name];
 
 				let j = declaration.instances.length;
-				while ( j-- ) {
+				while (j--) {
 					const instance = declaration.instances[j];
-					const nearestFunctionExpression = instance.findNearest( /Function/ );
+					const nearestFunctionExpression = instance.findNearest(/Function/);
 
-					if ( nearestFunctionExpression && nearestFunctionExpression.depth > this.depth ) {
+					if (
+						nearestFunctionExpression &&
+						nearestFunctionExpression.depth > this.depth
+					) {
 						this.shouldRewriteAsFunction = true;
 						break;
 					}
 				}
 
-				if ( this.shouldRewriteAsFunction ) break;
+				if (this.shouldRewriteAsFunction) break;
 			}
 		}
 	}
 
-	transpile ( code, transforms ) {
-		const needsBlock = this.type != 'ForOfStatement' && (
-			this.body.type !== 'BlockStatement'
-			|| this.body.type === 'BlockStatement' && this.body.synthetic );
+	transpile(code, transforms) {
+		const needsBlock =
+			this.type != 'ForOfStatement' &&
+			(this.body.type !== 'BlockStatement' ||
+				(this.body.type === 'BlockStatement' && this.body.synthetic));
 
-		if ( this.shouldRewriteAsFunction ) {
+		if (this.shouldRewriteAsFunction) {
 			const i0 = this.getIndentation();
 			const i1 = i0 + code.getIndentString();
 
-			const argString = this.args ? ` ${this.args.join( ', ' )} ` : '';
-			const paramString = this.params ? ` ${this.params.join( ', ' )} ` : '';
+			const argString = this.args ? ` ${this.args.join(', ')} ` : '';
+			const paramString = this.params ? ` ${this.params.join(', ')} ` : '';
 
-			const functionScope = this.findScope( true );
-			const loop = functionScope.createIdentifier( 'loop' );
+			const functionScope = this.findScope(true);
+			const loop = functionScope.createIdentifier('loop');
 
-			const before = `var ${loop} = function (${paramString}) ` + ( this.body.synthetic ? `{\n${i0}${code.getIndentString()}` : '' );
-			const after = ( this.body.synthetic ? `\n${i0}}` : '' ) + `;\n\n${i0}`;
+			const before =
+				`var ${loop} = function (${paramString}) ` +
+				(this.body.synthetic ? `{\n${i0}${code.getIndentString()}` : '');
+			const after = (this.body.synthetic ? `\n${i0}}` : '') + `;\n\n${i0}`;
 
-			code.insertRight( this.body.start, before );
-			code.insertLeft( this.body.end, after );
-			code.move( this.start, this.body.start, this.body.end );
+			code.prependRight(this.body.start, before);
+			code.appendLeft(this.body.end, after);
+			code.move(this.start, this.body.start, this.body.end);
 
-			if ( this.canBreak || this.canReturn ) {
-				const returned = functionScope.createIdentifier( 'returned' );
+			if (this.canBreak || this.canReturn) {
+				const returned = functionScope.createIdentifier('returned');
 
 				let insert = `{\n${i1}var ${returned} = ${loop}(${argString});\n`;
-				if ( this.canBreak ) insert += `\n${i1}if ( ${returned} === 'break' ) break;`;
-				if ( this.canReturn ) insert += `\n${i1}if ( ${returned} ) return ${returned}.v;`;
+				if (this.canBreak)
+					insert += `\n${i1}if ( ${returned} === 'break' ) break;`;
+				if (this.canReturn)
+					insert += `\n${i1}if ( ${returned} ) return ${returned}.v;`;
 				insert += `\n${i0}}`;
 
-				code.insertRight( this.body.end, insert );
+				code.prependRight(this.body.end, insert);
 			} else {
 				const callExpression = `${loop}(${argString});`;
 
-				if ( this.type === 'DoWhileStatement' ) {
-					code.overwrite( this.start, this.body.start, `do {\n${i1}${callExpression}\n${i0}}` );
+				if (this.type === 'DoWhileStatement') {
+					code.overwrite(
+						this.start,
+						this.body.start,
+						`do {\n${i1}${callExpression}\n${i0}}`
+					);
 				} else {
-					code.insertRight( this.body.end, callExpression );
+					code.prependRight(this.body.end, callExpression);
 				}
 			}
-		} else if ( needsBlock ) {
-			code.insertLeft( this.body.start, '{ ' );
-			code.insertRight( this.body.end, ' }' );
+		} else if (needsBlock) {
+			code.appendLeft(this.body.start, '{ ');
+			code.prependRight(this.body.end, ' }');
 		}
 
-		super.transpile( code, transforms );
+		super.transpile(code, transforms);
 	}
 }
diff --git a/src/program/types/shared/ModuleDeclaration.js b/src/program/types/shared/ModuleDeclaration.js
index db07ded..0022f9e 100644
--- a/src/program/types/shared/ModuleDeclaration.js
+++ b/src/program/types/shared/ModuleDeclaration.js
@@ -2,8 +2,9 @@ import Node from '../../Node.js';
 import CompileError from '../../../utils/CompileError.js';
 
 export default class ModuleDeclaration extends Node {
-	initialise ( transforms ) {
-		if ( transforms.moduleImport ) throw new CompileError( this, 'Modules are not supported' );
-		super.initialise( transforms );
+	initialise(transforms) {
+		if (transforms.moduleImport)
+			throw new CompileError('Modules are not supported', this);
+		super.initialise(transforms);
 	}
 }
diff --git a/src/program/wrap.js b/src/program/wrap.js
index 1e0a130..c9ea787 100644
--- a/src/program/wrap.js
+++ b/src/program/wrap.js
@@ -13,42 +13,45 @@ const statementsWithBlocks = {
 	ArrowFunctionExpression: 'body'
 };
 
-export default function wrap ( raw, parent ) {
-	if ( !raw ) return;
+export default function wrap(raw, parent) {
+	if (!raw) return;
 
-	if ( 'length' in raw ) {
+	if ('length' in raw) {
 		let i = raw.length;
-		while ( i-- ) wrap( raw[i], parent );
+		while (i--) wrap(raw[i], parent);
 		return;
 	}
 
 	// with e.g. shorthand properties, key and value are
 	// the same node. We don't want to wrap an object twice
-	if ( raw.__wrapped ) return;
+	if (raw.__wrapped) return;
 	raw.__wrapped = true;
 
-	if ( !keys[ raw.type ] ) {
-		keys[ raw.type ] = Object.keys( raw ).filter( key => typeof raw[ key ] === 'object' );
+	if (!keys[raw.type]) {
+		keys[raw.type] = Object.keys(raw).filter(
+			key => typeof raw[key] === 'object'
+		);
 	}
 
 	// special case – body-less if/for/while statements. TODO others?
-	const bodyType = statementsWithBlocks[ raw.type ];
-	if ( bodyType && raw[ bodyType ].type !== 'BlockStatement' ) {
-		const expression = raw[ bodyType ];
+	const bodyType = statementsWithBlocks[raw.type];
+	if (bodyType && raw[bodyType].type !== 'BlockStatement') {
+		const expression = raw[bodyType];
 
 		// create a synthetic block statement, otherwise all hell
 		// breaks loose when it comes to block scoping
-		raw[ bodyType ] = {
+		raw[bodyType] = {
 			start: expression.start,
 			end: expression.end,
 			type: 'BlockStatement',
-			body: [ expression ],
+			body: [expression],
 			synthetic: true
 		};
 	}
 
-	new Node( raw, parent );
+	new Node(raw, parent);
 
-	const type = ( raw.type === 'BlockStatement' ? BlockStatement : types[ raw.type ] ) || Node;
+	const type =
+		(raw.type === 'BlockStatement' ? BlockStatement : types[raw.type]) || Node;
 	raw.__proto__ = type.prototype;
 }
diff --git a/src/support.js b/src/support.js
index d40d4ec..7b48b92 100644
--- a/src/support.js
+++ b/src/support.js
@@ -1,39 +1,39 @@
 export const matrix = {
 	chrome: {
-		    48: 0b1001111011111100111110101111101,
-		    49: 0b1001111111111100111111111111111,
-		    50: 0b1011111111111100111111111111111,
-		    51: 0b1011111111111100111111111111111,
-		    52: 0b1111111111111100111111111111111
+		    48: 0b01001111011111100111110101111101,
+		    49: 0b01001111111111100111111111111111,
+		    50: 0b01011111111111100111111111111111,
+		    51: 0b01011111111111100111111111111111,
+		    52: 0b01111111111111100111111111111111
 	},
 	firefox: {
-		    43: 0b1000111111101100000110111011101,
-		    44: 0b1000111111101100000110111011101,
-		    45: 0b1000111111101100000110111011101,
-		    46: 0b1010111111111100000110111011101,
-		    47: 0b1010111111111100111111111011111,
-		    48: 0b1010111111111100111111111011111
+		    43: 0b01000111111101100000110111011101,
+		    44: 0b01000111111101100000110111011101,
+		    45: 0b01000111111101100000110111011101,
+		    46: 0b01010111111111100000110111011101,
+		    47: 0b01010111111111100111111111011111,
+		    48: 0b01010111111111100111111111011111
 	},
 	safari: {
-		     8: 0b1000000000000000000000000000000,
-		     9: 0b1001111001101100000011101011110
+		     8: 0b01000000000000000000000000000000,
+		     9: 0b01001111001101100000011101011110
 	},
 	ie: {
-		     8: 0b0000000000000000000000000000000,
-		     9: 0b1000000000000000000000000000000,
-		    10: 0b1000000000000000000000000000000,
-		    11: 0b1000000000000000111000001100000
+		     8: 0b00000000000000000000000000000000,
+		     9: 0b01000000000000000000000000000000,
+		    10: 0b01000000000000000000000000000000,
+		    11: 0b01000000000000000111000001100000
 	},
 	edge: {
-		    12: 0b1011110110111100011010001011101,
-		    13: 0b1011111110111100011111001011111
+		    12: 0b01011110110111100011010001011101,
+		    13: 0b01011111110111100011110001011111
 	},
 	node: {
-		'0.10': 0b1000000000101000000000001000000,
-		'0.12': 0b1000001000101000000010001000100,
-		     4: 0b1001111000111100111111001111111,
-		     5: 0b1001111000111100111111001111111,
-		     6: 0b1011111111111100111111111111111
+		'0.10': 0b01000000000101000000000001000000,
+		'0.12': 0b01000001000101000000010001000100,
+		     4: 0b01001111000111100111111001111111,
+		     5: 0b01001111000111100111111001111111,
+		     6: 0b01011111111111100111111111111111
 	}
 };
 
@@ -73,5 +73,7 @@ export const features = [
 
 	// additional transforms, not from
 	// https://featuretests.io
-	'reservedProperties'
+	'reservedProperties',
+
+	'trailingFunctionCommas'
 ];
diff --git a/src/utils/CompileError.js b/src/utils/CompileError.js
index a9027e1..116721d 100644
--- a/src/utils/CompileError.js
+++ b/src/utils/CompileError.js
@@ -2,22 +2,29 @@ import locate from './locate.js';
 import getSnippet from './getSnippet.js';
 
 export default class CompileError extends Error {
-	constructor ( node, message ) {
-		super();
+	constructor(message, node) {
+		super(message);
+
+		this.name = 'CompileError';
+		if (!node) {
+			return;
+		}
 
 		const source = node.program.magicString.original;
-		const loc = locate( source, node.start );
+		const loc = locate(source, node.start);
 
-		this.name = 'CompileError';
 		this.message = message + ` (${loc.line}:${loc.column})`;
 
-		this.stack = new Error().stack.replace( new RegExp( `.+new ${this.name}.+\\n`, 'm' ), '' );
+		this.stack = new Error().stack.replace(
+			new RegExp(`.+new ${this.name}.+\\n`, 'm'),
+			''
+		);
 
 		this.loc = loc;
-		this.snippet = getSnippet( source, loc, node.end - node.start );
+		this.snippet = getSnippet(source, loc, node.end - node.start);
 	}
 
-	toString () {
+	toString() {
 		return `${this.name}: ${this.message}\n${this.snippet}`;
 	}
 }
diff --git a/src/utils/array.js b/src/utils/array.js
index 70dbe6c..d72e4b9 100644
--- a/src/utils/array.js
+++ b/src/utils/array.js
@@ -1,11 +1,11 @@
-export function findIndex ( array, fn ) {
-	for ( let i = 0; i < array.length; i += 1 ) {
-		if ( fn( array[i], i ) ) return i;
+export function findIndex(array, fn) {
+	for (let i = 0; i < array.length; i += 1) {
+		if (fn(array[i], i)) return i;
 	}
 
 	return -1;
 }
 
-export function find ( array, fn ) {
-	return array[ findIndex( array, fn ) ];
+export function find(array, fn) {
+	return array[findIndex(array, fn)];
 }
diff --git a/src/utils/deindent.js b/src/utils/deindent.js
index 6b49f82..8b6c5ee 100644
--- a/src/utils/deindent.js
+++ b/src/utils/deindent.js
@@ -2,7 +2,7 @@
 // not its current edited state.
 // That's not a problem for the way that it's currently used, but it could
 // be in future...
-export default function deindent ( node, code ) {
+export default function deindent(node, code) {
 	const start = node.start;
 	const end = node.end;
 
@@ -10,19 +10,21 @@ export default function deindent ( node, code ) {
 	const indentStrLen = indentStr.length;
 	const indentStart = start - indentStrLen;
 
-	if ( !node.program.indentExclusions[ indentStart ]
-	&& code.original.slice( indentStart, start ) === indentStr ) {
-		code.remove( indentStart, start );
+	if (
+		!node.program.indentExclusions[indentStart] &&
+		code.original.slice(indentStart, start) === indentStr
+	) {
+		code.remove(indentStart, start);
 	}
 
-	const pattern = new RegExp( indentStr + '\\S', 'g' );
-	const slice = code.original.slice( start, end );
+	const pattern = new RegExp(indentStr + '\\S', 'g');
+	const slice = code.original.slice(start, end);
 	let match;
 
-	while ( match = pattern.exec( slice ) ) {
+	while ((match = pattern.exec(slice))) {
 		const removeStart = start + match.index;
-		if ( !node.program.indentExclusions[ removeStart ] ) {
-			code.remove( removeStart, removeStart + indentStrLen );
+		if (!node.program.indentExclusions[removeStart]) {
+			code.remove(removeStart, removeStart + indentStrLen);
 		}
 	}
 }
diff --git a/src/utils/destructure.js b/src/utils/destructure.js
index 28cb023..0f620fd 100644
--- a/src/utils/destructure.js
+++ b/src/utils/destructure.js
@@ -1,3 +1,4 @@
+import CompileError from '../utils/CompileError.js';
 import { findIndex } from './array.js';
 
 const handlers = {
@@ -7,69 +8,168 @@ const handlers = {
 	ObjectPattern: destructureObjectPattern
 };
 
-export default function destructure ( code, scope, node, ref, inline, statementGenerators ) {
-	handlers[ node.type ]( code, scope, node, ref, inline, statementGenerators );
+export default function destructure(
+	code,
+	scope,
+	node,
+	ref,
+	inline,
+	statementGenerators
+) {
+	handlers[node.type](code, scope, node, ref, inline, statementGenerators);
 }
 
-function destructureIdentifier ( code, scope, node, ref, inline, statementGenerators ) {
-	statementGenerators.push( ( start, prefix, suffix ) => {
-		code.insertRight( node.start, inline ? prefix : `${prefix}var ` );
-		code.insertLeft( node.end, ` = ${ref}${suffix}` );
-		code.move( node.start, node.end, start );
+function destructureIdentifier(
+	code,
+	scope,
+	node,
+	ref,
+	inline,
+	statementGenerators
+) {
+	statementGenerators.push((start, prefix, suffix) => {
+		code.prependRight(node.start, inline ? prefix : `${prefix}var `);
+		code.appendLeft(node.end, ` = ${ref}${suffix}`);
+		code.move(node.start, node.end, start);
 	});
 }
 
-function destructureAssignmentPattern ( code, scope, node, ref, inline, statementGenerators ) {
+function destructureAssignmentPattern(
+	code,
+	scope,
+	node,
+	ref,
+	inline,
+	statementGenerators
+) {
 	const isIdentifier = node.left.type === 'Identifier';
 	const name = isIdentifier ? node.left.name : ref;
 
-	if ( !inline ) {
-		statementGenerators.push( ( start, prefix, suffix ) => {
-			code.insertRight( node.left.end, `${prefix}if ( ${name} === void 0 ) ${name}` );
-			code.move( node.left.end, node.right.end, start );
-			code.insertLeft( node.right.end, suffix );
+	if (!inline) {
+		statementGenerators.push((start, prefix, suffix) => {
+			code.prependRight(
+				node.left.end,
+				`${prefix}if ( ${name} === void 0 ) ${name}`
+			);
+			code.move(node.left.end, node.right.end, start);
+			code.appendLeft(node.right.end, suffix);
 		});
 	}
 
-	if ( !isIdentifier ) {
-		destructure( code, scope, node.left, ref, inline, statementGenerators );
+	if (!isIdentifier) {
+		destructure(code, scope, node.left, ref, inline, statementGenerators);
 	}
 }
 
-function destructureArrayPattern ( code, scope, node, ref, inline, statementGenerators ) {
+function destructureArrayPattern(
+	code,
+	scope,
+	node,
+	ref,
+	inline,
+	statementGenerators
+) {
 	let c = node.start;
 
-	node.elements.forEach( ( element, i ) => {
-		if ( !element ) return;
-
-		if ( element.type === 'RestElement' ) {
-			handleProperty( code, scope, c, element.argument, `${ref}.slice(${i})`, inline, statementGenerators );
+	node.elements.forEach((element, i) => {
+		if (!element) return;
+
+		if (element.type === 'RestElement') {
+			handleProperty(
+				code,
+				scope,
+				c,
+				element.argument,
+				`${ref}.slice(${i})`,
+				inline,
+				statementGenerators
+			);
 		} else {
-			handleProperty( code, scope, c, element, `${ref}[${i}]`, inline, statementGenerators );
+			handleProperty(
+				code,
+				scope,
+				c,
+				element,
+				`${ref}[${i}]`,
+				inline,
+				statementGenerators
+			);
 		}
 		c = element.end;
 	});
 
-	code.remove( c, node.end );
+	code.remove(c, node.end);
 }
 
-function destructureObjectPattern ( code, scope, node, ref, inline, statementGenerators ) {
+function destructureObjectPattern(
+	code,
+	scope,
+	node,
+	ref,
+	inline,
+	statementGenerators
+) {
 	let c = node.start;
 
-	node.properties.forEach( prop => {
-		let value = prop.computed || prop.key.type !== 'Identifier' ? `${ref}[${code.slice(prop.key.start, prop.key.end)}]` : `${ref}.${prop.key.name}`;
-		handleProperty( code, scope, c, prop.value, value, inline, statementGenerators );
+	const nonRestKeys = [];
+	node.properties.forEach(prop => {
+		let value;
+		let content;
+		if (prop.type === 'Property') {
+			const isComputedKey = prop.computed || prop.key.type !== 'Identifier';
+			const key = isComputedKey
+				? code.slice(prop.key.start, prop.key.end)
+				: prop.key.name;
+			value = isComputedKey ? `${ref}[${key}]` : `${ref}.${key}`;
+			content = prop.value;
+			nonRestKeys.push(isComputedKey ? key : '"' + key + '"');
+		} else if (prop.type === 'RestElement') {
+			content = prop.argument;
+			value = scope.createIdentifier('rest');
+			const n = scope.createIdentifier('n');
+			statementGenerators.push((start, prefix, suffix) => {
+				code.overwrite(
+					prop.start,
+					(c = prop.argument.start),
+					`${prefix}var ${value} = {}; for (var ${n} in ${ref}) if([${nonRestKeys.join(
+						', '
+					)}].indexOf(${n}) === -1) ${value}[${n}] = ${ref}[${n}]${suffix}`
+				);
+				code.move(prop.start, c, start);
+			});
+		} else {
+			throw new CompileError(
+				this,
+				`Unexpected node of type ${prop.type} in object pattern`
+			);
+		}
+		handleProperty(code, scope, c, content, value, inline, statementGenerators);
 		c = prop.end;
 	});
 
-	code.remove( c, node.end );
+	code.remove(c, node.end);
 }
 
-function handleProperty ( code, scope, c, node, value, inline, statementGenerators ) {
-	switch ( node.type ) {
+function handleProperty(
+	code,
+	scope,
+	c,
+	node,
+	value,
+	inline,
+	statementGenerators
+) {
+	switch (node.type) {
 		case 'Identifier': {
-			code.remove( c, node.start );
-			destructureIdentifier( code, scope, node, value, inline, statementGenerators );
+			code.remove(c, node.start);
+			destructureIdentifier(
+				code,
+				scope,
+				node,
+				value,
+				inline,
+				statementGenerators
+			);
 			break;
 		}
 
@@ -78,110 +178,161 @@ function handleProperty ( code, scope, c, node, value, inline, statementGenerato
 
 			const isIdentifier = node.left.type === 'Identifier';
 
-			if ( isIdentifier ) {
+			if (isIdentifier) {
 				name = node.left.name;
-				const declaration = scope.findDeclaration( name );
-				if ( declaration ) name = declaration.name;
+				const declaration = scope.findDeclaration(name);
+				if (declaration) name = declaration.name;
 			} else {
-				name = scope.createIdentifier( value );
+				name = scope.createIdentifier(value);
 			}
 
-			statementGenerators.push( ( start, prefix, suffix ) => {
-				if ( inline ) {
-					code.insertRight( node.right.start, `${name} = ${value} === undefined ? ` );
-					code.insertLeft( node.right.end, ` : ${value}` );
+			statementGenerators.push((start, prefix, suffix) => {
+				if (inline) {
+					code.prependRight(
+						node.right.start,
+						`${name} = ${value} === undefined ? `
+					);
+					code.appendLeft(node.right.end, ` : ${value}`);
 				} else {
-					code.insertRight( node.right.start, `${prefix}var ${name} = ${value}; if ( ${name} === void 0 ) ${name} = ` );
-					code.insertLeft( node.right.end, suffix );
+					code.prependRight(
+						node.right.start,
+						`${prefix}var ${name} = ${value}; if ( ${name} === void 0 ) ${name} = `
+					);
+					code.appendLeft(node.right.end, suffix);
 				}
 
-				code.move( node.right.start, node.right.end, start );
+				code.move(node.right.start, node.right.end, start);
 			});
 
-			if ( isIdentifier ) {
-				code.remove( c, node.right.start );
+			if (isIdentifier) {
+				code.remove(c, node.right.start);
 			} else {
-				code.remove( c, node.left.start );
-				code.remove( node.left.end, node.right.start );
-				handleProperty( code, scope, c, node.left, name, inline, statementGenerators );
+				code.remove(c, node.left.start);
+				code.remove(node.left.end, node.right.start);
+				handleProperty(
+					code,
+					scope,
+					c,
+					node.left,
+					name,
+					inline,
+					statementGenerators
+				);
 			}
 
 			break;
 		}
 
 		case 'ObjectPattern': {
-			code.remove( c, c = node.start );
+			code.remove(c, (c = node.start));
 
-			if ( node.properties.length > 1 ) {
-				const ref = scope.createIdentifier( value );
+			let ref = value;
+			if (node.properties.length > 1) {
+				ref = scope.createIdentifier(value);
 
-				statementGenerators.push( ( start, prefix, suffix ) => {
+				statementGenerators.push((start, prefix, suffix) => {
 					// this feels a tiny bit hacky, but we can't do a
-					// straightforward insertLeft and keep correct order...
-					code.insertRight( node.start, `${prefix}var ${ref} = ` );
-					code.overwrite( node.start, c = node.start + 1, value );
-					code.insertLeft( c, suffix );
-
-					code.move( node.start, c, start );
+					// straightforward appendLeft and keep correct order...
+					code.prependRight(node.start, `${prefix}var ${ref} = `);
+					code.overwrite(node.start, (c = node.start + 1), value);
+					code.appendLeft(c, suffix);
+
+					code.overwrite(
+						node.start,
+						(c = node.start + 1),
+						`${prefix}var ${ref} = ${value}${suffix}`
+					);
+					code.move(node.start, c, start);
 				});
-
-				node.properties.forEach( prop => {
-					const value = prop.computed || prop.key.type !== 'Identifier' ? `${ref}[${code.slice(prop.key.start, prop.key.end)}]` : `${ref}.${prop.key.name}`;
-					handleProperty( code, scope, c, prop.value, value, inline, statementGenerators );
-					c = prop.end;
-				});
-			} else {
-				const prop = node.properties[0];
-				const value_suffix = prop.computed || prop.key.type !== 'Identifier' ? `[${code.slice(prop.key.start, prop.key.end)}]` : `.${prop.key.name}`;
-				handleProperty( code, scope, c, prop.value, `${value}${value_suffix}`, inline, statementGenerators );
-				c = prop.end;
 			}
 
-			code.remove( c, node.end );
+			destructureObjectPattern(
+				code,
+				scope,
+				node,
+				ref,
+				inline,
+				statementGenerators
+			);
+
 			break;
 		}
 
 		case 'ArrayPattern': {
-			code.remove( c, c = node.start );
+			code.remove(c, (c = node.start));
 
-			if ( node.elements.filter( Boolean ).length > 1 ) {
-				const ref = scope.createIdentifier( value );
+			if (node.elements.filter(Boolean).length > 1) {
+				const ref = scope.createIdentifier(value);
 
-				statementGenerators.push( ( start, prefix, suffix ) => {
-					code.insertRight( node.start, `${prefix}var ${ref} = ` );
-					code.overwrite( node.start, c = node.start + 1, value );
-					code.insertLeft( c, suffix );
+				statementGenerators.push((start, prefix, suffix) => {
+					code.prependRight(node.start, `${prefix}var ${ref} = `);
+					code.overwrite(node.start, (c = node.start + 1), value, {
+						contentOnly: true
+					});
+					code.appendLeft(c, suffix);
 
-					code.move( node.start, c, start );
+					code.move(node.start, c, start);
 				});
 
-				node.elements.forEach( ( element, i ) => {
-					if ( !element ) return;
-
-					if ( element.type === 'RestElement' ) {
-						handleProperty( code, scope, c, element.argument, `${ref}.slice(${i})`, inline, statementGenerators );
+				node.elements.forEach((element, i) => {
+					if (!element) return;
+
+					if (element.type === 'RestElement') {
+						handleProperty(
+							code,
+							scope,
+							c,
+							element.argument,
+							`${ref}.slice(${i})`,
+							inline,
+							statementGenerators
+						);
 					} else {
-						handleProperty( code, scope, c, element, `${ref}[${i}]`, inline, statementGenerators );
+						handleProperty(
+							code,
+							scope,
+							c,
+							element,
+							`${ref}[${i}]`,
+							inline,
+							statementGenerators
+						);
 					}
 					c = element.end;
 				});
 			} else {
-				const index = findIndex( node.elements, Boolean );
-				const element = node.elements[ index ];
-				if ( element.type === 'RestElement' ) {
-					handleProperty( code, scope, c, element.argument, `${value}.slice(${index})`, inline, statementGenerators );
+				const index = findIndex(node.elements, Boolean);
+				const element = node.elements[index];
+				if (element.type === 'RestElement') {
+					handleProperty(
+						code,
+						scope,
+						c,
+						element.argument,
+						`${value}.slice(${index})`,
+						inline,
+						statementGenerators
+					);
 				} else {
-					handleProperty( code, scope, c, element, `${value}[${index}]`, inline, statementGenerators );
+					handleProperty(
+						code,
+						scope,
+						c,
+						element,
+						`${value}[${index}]`,
+						inline,
+						statementGenerators
+					);
 				}
 				c = element.end;
 			}
 
-			code.remove( c, node.end );
+			code.remove(c, node.end);
 			break;
 		}
 
 		default: {
-			throw new Error( `Unexpected node type in destructuring (${node.type})` );
+			throw new Error(`Unexpected node type in destructuring (${node.type})`);
 		}
 	}
 }
diff --git a/src/utils/getSnippet.js b/src/utils/getSnippet.js
index 950a515..a114c46 100644
--- a/src/utils/getSnippet.js
+++ b/src/utils/getSnippet.js
@@ -1,30 +1,30 @@
-function pad ( num, len ) {
-	let result = String( num );
-	return result + repeat( ' ', len - result.length );
+function pad(num, len) {
+	let result = String(num);
+	return result + repeat(' ', len - result.length);
 }
 
-function repeat ( str, times ) {
+function repeat(str, times) {
 	let result = '';
-	while ( times-- ) result += str;
+	while (times--) result += str;
 	return result;
 }
 
-export default function getSnippet ( source, loc, length = 1 ) {
-	const first = Math.max( loc.line - 5, 0 );
+export default function getSnippet(source, loc, length = 1) {
+	const first = Math.max(loc.line - 5, 0);
 	const last = loc.line;
 
-	const numDigits = String( last ).length;
+	const numDigits = String(last).length;
 
-	const lines = source.split( '\n' ).slice( first, last );
+	const lines = source.split('\n').slice(first, last);
 
-	const lastLine = lines[ lines.length - 1 ];
-	const offset = lastLine.slice( 0, loc.column ).replace( /\t/g, '  ' ).length;
+	const lastLine = lines[lines.length - 1];
+	const offset = lastLine.slice(0, loc.column).replace(/\t/g, '  ').length;
 
 	let snippet = lines
-		.map( ( line, i ) => `${pad( i + first + 1, numDigits )} : ${line.replace( /\t/g, '  ')}` )
-		.join( '\n' );
+		.map((line, i) => `${pad(i + first + 1, numDigits)} : ${line.replace(/\t/g, '  ')}`)
+		.join('\n');
 
-	snippet += '\n' + repeat( ' ', numDigits + 3 + offset ) + repeat( '^', length );
+	snippet += '\n' + repeat(' ', numDigits + 3 + offset) + repeat('^', length);
 
 	return snippet;
 }
diff --git a/src/utils/isReference.js b/src/utils/isReference.js
index 2084681..f0cbcb5 100644
--- a/src/utils/isReference.js
+++ b/src/utils/isReference.js
@@ -1,36 +1,40 @@
-export default function isReference ( node, parent ) {
-	if ( node.type === 'MemberExpression' ) {
-		return !node.computed && isReference( node.object, node );
+export default function isReference(node, parent) {
+	if (node.type === 'MemberExpression') {
+		return !node.computed && isReference(node.object, node);
 	}
 
-	if ( node.type === 'Identifier' ) {
+	if (node.type === 'Identifier') {
 		// the only time we could have an identifier node without a parent is
 		// if it's the entire body of a function without a block statement –
 		// i.e. an arrow function expression like `a => a`
-		if ( !parent ) return true;
+		if (!parent) return true;
 
-		if ( /(Function|Class)Expression/.test( parent.type ) ) return false;
+		if (/(Function|Class)Expression/.test(parent.type)) return false;
 
-		if ( parent.type === 'VariableDeclarator' ) return node === parent.init;
+		if (parent.type === 'VariableDeclarator') return node === parent.init;
 
 		// TODO is this right?
-		if ( parent.type === 'MemberExpression' || parent.type === 'MethodDefinition' ) {
+		if (
+			parent.type === 'MemberExpression' ||
+			parent.type === 'MethodDefinition'
+		) {
 			return parent.computed || node === parent.object;
 		}
 
-		if ( parent.type === 'ArrayPattern' ) return false;
+		if (parent.type === 'ArrayPattern') return false;
 
 		// disregard the `bar` in `{ bar: foo }`, but keep it in `{ [bar]: foo }`
-		if ( parent.type === 'Property' ) {
-			if ( parent.parent.type === 'ObjectPattern' ) return false;
+		if (parent.type === 'Property') {
+			if (parent.parent.type === 'ObjectPattern') return false;
 			return parent.computed || node === parent.value;
 		}
 
 		// disregard the `bar` in `class Foo { bar () {...} }`
-		if ( parent.type === 'MethodDefinition' ) return false;
+		if (parent.type === 'MethodDefinition') return false;
 
 		// disregard the `bar` in `export { foo as bar }`
-		if ( parent.type === 'ExportSpecifier' && node !== parent.local ) return false;
+		if (parent.type === 'ExportSpecifier' && node !== parent.local)
+			return false;
 
 		return true;
 	}
diff --git a/src/utils/locate.js b/src/utils/locate.js
index 03ebc42..bd10b00 100644
--- a/src/utils/locate.js
+++ b/src/utils/locate.js
@@ -1,20 +1,20 @@
-export default function locate ( source, index ) {
-	var lines = source.split( '\n' );
+export default function locate(source, index) {
+	var lines = source.split('\n');
 	var len = lines.length;
 
 	var lineStart = 0;
 	var i;
 
-	for ( i = 0; i < len; i += 1 ) {
+	for (i = 0; i < len; i += 1) {
 		var line = lines[i];
-		var lineEnd =  lineStart + line.length + 1; // +1 for newline
+		var lineEnd = lineStart + line.length + 1; // +1 for newline
 
-		if ( lineEnd > index ) {
+		if (lineEnd > index) {
 			return { line: i + 1, column: index - lineStart, char: i };
 		}
 
 		lineStart = lineEnd;
 	}
 
-	throw new Error( 'Could not determine location of character' );
+	throw new Error('Could not determine location of character');
 }
diff --git a/src/utils/removeTrailingComma.js b/src/utils/removeTrailingComma.js
new file mode 100644
index 0000000..50b610c
--- /dev/null
+++ b/src/utils/removeTrailingComma.js
@@ -0,0 +1,10 @@
+export default function removeTrailingComma(code, c) {
+	while (code.original[c] !== ')') {
+		if (code.original[c] === ',') {
+			code.remove(c, c + 1);
+			return;
+		}
+
+		c += 1;
+	}
+}
\ No newline at end of file
diff --git a/src/utils/reserved.js b/src/utils/reserved.js
index fa3e838..7268cbe 100644
--- a/src/utils/reserved.js
+++ b/src/utils/reserved.js
@@ -1,5 +1,6 @@
-let reserved = Object.create( null );
-'do if in for let new try var case else enum eval null this true void with await break catch class const false super throw while yield delete export import public return static switch typeof default extends finally package private continue debugger function arguments interface protected implements instanceof'.split( ' ' )
-	.forEach( word => reserved[ word ] = true );
+let reserved = Object.create(null);
+'do if in for let new try var case else enum eval null this true void with await break catch class const false super throw while yield delete export import public return static switch typeof default extends finally package private continue debugger function arguments interface protected implements instanceof'
+	.split(' ')
+	.forEach(word => (reserved[word] = true));
 
 export default reserved;
diff --git a/src/utils/spread.js b/src/utils/spread.js
index 73e4235..2026a92 100644
--- a/src/utils/spread.js
+++ b/src/utils/spread.js
@@ -1,57 +1,67 @@
-export function isArguments ( node ) {
+export function isArguments(node) {
 	return node.type === 'Identifier' && node.name === 'arguments';
 }
 
-export default function spread ( code, elements, start, argumentsArrayAlias, isNew ) {
+export default function spread(
+	code,
+	elements,
+	start,
+	argumentsArrayAlias,
+	isNew
+) {
 	let i = elements.length;
 	let firstSpreadIndex = -1;
 
-	while ( i-- ) {
+	while (i--) {
 		const element = elements[i];
-		if ( element && element.type === 'SpreadElement' ) {
-			if ( isArguments( element.argument ) ) {
-				code.overwrite( element.argument.start, element.argument.end, argumentsArrayAlias );
+		if (element && element.type === 'SpreadElement') {
+			if (isArguments(element.argument)) {
+				code.overwrite(
+					element.argument.start,
+					element.argument.end,
+					argumentsArrayAlias
+				);
 			}
 
 			firstSpreadIndex = i;
 		}
 	}
 
-	if ( firstSpreadIndex === -1 ) return false; // false indicates no spread elements
+	if (firstSpreadIndex === -1) return false; // false indicates no spread elements
 
 	if (isNew) {
-		for ( i = 0; i < elements.length; i += 1 ) {
+		for (i = 0; i < elements.length; i += 1) {
 			let element = elements[i];
-			if ( element.type === 'SpreadElement' ) {
-				code.remove( element.start, element.argument.start );
+			if (element.type === 'SpreadElement') {
+				code.remove(element.start, element.argument.start);
 			} else {
-				code.insertRight( element.start, '[' );
-				code.insertRight( element.end, ']' );
+				code.prependRight(element.start, '[');
+				code.prependRight(element.end, ']');
 			}
 		}
 
 		return true; // true indicates some spread elements
 	}
 
-	let element = elements[ firstSpreadIndex ];
-	const previousElement = elements[ firstSpreadIndex - 1 ];
+	let element = elements[firstSpreadIndex];
+	const previousElement = elements[firstSpreadIndex - 1];
 
-	if ( !previousElement ) {
-		code.remove( start, element.start );
-		code.overwrite( element.end, elements[1].start, '.concat( ' );
+	if (!previousElement) {
+		code.remove(start, element.start);
+		code.overwrite(element.end, elements[1].start, '.concat( ');
 	} else {
-		code.overwrite( previousElement.end, element.start, ' ].concat( ' );
+		code.overwrite(previousElement.end, element.start, ' ].concat( ');
 	}
 
-	for ( i = firstSpreadIndex; i < elements.length; i += 1 ) {
+	for (i = firstSpreadIndex; i < elements.length; i += 1) {
 		element = elements[i];
 
-		if ( element ) {
-			if ( element.type === 'SpreadElement' ) {
-				code.remove( element.start, element.argument.start );
+		if (element) {
+			if (element.type === 'SpreadElement') {
+				code.remove(element.start, element.argument.start);
 			} else {
-				code.insertLeft( element.start, '[' );
-				code.insertLeft( element.end, ']' );
+				code.appendLeft(element.start, '[');
+				code.appendLeft(element.end, ']');
 			}
 		}
 	}
diff --git a/test/cli/compiles-directory/expected/bar.js b/test/cli/compiles-directory/expected/bar.js
index a6d1507..02838e6 100644
--- a/test/cli/compiles-directory/expected/bar.js
+++ b/test/cli/compiles-directory/expected/bar.js
@@ -1,2 +1,2 @@
 console.log( 'bar' );
-//# sourceMappingURL=bar.js.map
+//# sourceMappingURL=bar.js.map
\ No newline at end of file
diff --git a/test/cli/compiles-directory/expected/foo.js b/test/cli/compiles-directory/expected/foo.js
index 380852d..27d0cb8 100644
--- a/test/cli/compiles-directory/expected/foo.js
+++ b/test/cli/compiles-directory/expected/foo.js
@@ -1,2 +1,3 @@
-console.log( 'foo' );
-//# sourceMappingURL=foo.js.map
+console.log('foo');
+
+//# sourceMappingURL=foo.js.map
\ No newline at end of file
diff --git a/test/cli/compiles-directory/expected/foo.js.map b/test/cli/compiles-directory/expected/foo.js.map
index 71906e7..412da24 100644
--- a/test/cli/compiles-directory/expected/foo.js.map
+++ b/test/cli/compiles-directory/expected/foo.js.map
@@ -1 +1 @@
-{"version":3,"file":"foo.js","sources":["../src/foo.js"],"sourcesContent":["console.log( 'foo' );"],"names":[],"mappings":"AAAA,OAAO,CAAC,GAAG,EAAE,KAAK,EAAE"}
+{"version":3,"file":"foo.js","sources":["../src/foo.js"],"sourcesContent":["console.log('foo');\n"],"names":[],"mappings":"AAAA,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;"}
\ No newline at end of file
diff --git a/test/cli/compiles-directory/src/foo.js b/test/cli/compiles-directory/src/foo.js
index db1b707..81afa31 100644
--- a/test/cli/compiles-directory/src/foo.js
+++ b/test/cli/compiles-directory/src/foo.js
@@ -1 +1 @@
-console.log( 'foo' );
\ No newline at end of file
+console.log('foo');
diff --git a/test/cli/creates-inline-sourcemap/expected/output.js b/test/cli/creates-inline-sourcemap/expected/output.js
index 31e80fc..ae45cb9 100644
--- a/test/cli/creates-inline-sourcemap/expected/output.js
+++ b/test/cli/creates-inline-sourcemap/expected/output.js
@@ -1,2 +1,3 @@
 var answer = function () { return 42; };
-//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3V0cHV0LmpzIiwic291cmNlcyI6WyIuLi9pbnB1dC5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJjb25zdCBhbnN3ZXIgPSAoKSA9PiA0MjsiXSwibmFtZXMiOlsiY29uc3QiXSwibWFwcGluZ3MiOiJBQUFBQSxHQUFLLENBQUMsTUFBTSxHQUFHLFNBQUEsR0FBRyxBQUFHLFNBQUEsRUFBRSxHQUFBIn0=
\ No newline at end of file
+
+//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3V0cHV0LmpzIiwic291cmNlcyI6WyIuLi9pbnB1dC5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJjb25zdCBhbnN3ZXIgPSAoKSA9PiA0MjtcbiJdLCJuYW1lcyI6WyJjb25zdCJdLCJtYXBwaW5ncyI6IkFBQUFBLEdBQUssQ0FBQyxNQUFNLFlBQUcsR0FBRyxTQUFHLEtBQUUsQ0FBQzsifQ==
\ No newline at end of file
diff --git a/test/cli/creates-inline-sourcemap/input.js b/test/cli/creates-inline-sourcemap/input.js
index 1a3ed38..e2ce27d 100644
--- a/test/cli/creates-inline-sourcemap/input.js
+++ b/test/cli/creates-inline-sourcemap/input.js
@@ -1 +1 @@
-const answer = () => 42;
\ No newline at end of file
+const answer = () => 42;
diff --git a/test/cli/creates-sourcemap/expected/output.js b/test/cli/creates-sourcemap/expected/output.js
index 7e74c47..2badd3f 100644
--- a/test/cli/creates-sourcemap/expected/output.js
+++ b/test/cli/creates-sourcemap/expected/output.js
@@ -1,2 +1,3 @@
 var answer = function () { return 42; };
-//# sourceMappingURL=output.js.map
+
+//# sourceMappingURL=output.js.map
\ No newline at end of file
diff --git a/test/cli/creates-sourcemap/expected/output.js.map b/test/cli/creates-sourcemap/expected/output.js.map
index 8b4f877..b549e15 100644
--- a/test/cli/creates-sourcemap/expected/output.js.map
+++ b/test/cli/creates-sourcemap/expected/output.js.map
@@ -1 +1 @@
-{"version":3,"file":"output.js","sources":["../input.js"],"sourcesContent":["const answer = () => 42;"],"names":["const"],"mappings":"AAAAA,GAAK,CAAC,MAAM,GAAG,SAAA,GAAG,AAAG,SAAA,EAAE,GAAA"}
+{"version":3,"file":"output.js","sources":["../input.js"],"sourcesContent":["const answer = () => 42;\n"],"names":["const"],"mappings":"AAAAA,GAAK,CAAC,MAAM,YAAG,GAAG,SAAG,KAAE,CAAC;"}
\ No newline at end of file
diff --git a/test/cli/creates-sourcemap/input.js b/test/cli/creates-sourcemap/input.js
index 1a3ed38..e2ce27d 100644
--- a/test/cli/creates-sourcemap/input.js
+++ b/test/cli/creates-sourcemap/input.js
@@ -1 +1 @@
-const answer = () => 42;
\ No newline at end of file
+const answer = () => 42;
diff --git a/test/cli/supports-jsx-pragma-comment/expected/output.js b/test/cli/supports-jsx-pragma-comment/expected/output.js
index db30e6f..71766d1 100644
--- a/test/cli/supports-jsx-pragma-comment/expected/output.js
+++ b/test/cli/supports-jsx-pragma-comment/expected/output.js
@@ -1,2 +1,2 @@
 /* @jsx customPragma */
-var div = customPragma( 'div', null, "Hello" )
+var div = customPragma( 'div', null, "Hello" );
diff --git a/test/cli/supports-jsx-pragma-comment/input.js b/test/cli/supports-jsx-pragma-comment/input.js
index 356d281..d3b3936 100644
--- a/test/cli/supports-jsx-pragma-comment/input.js
+++ b/test/cli/supports-jsx-pragma-comment/input.js
@@ -1,2 +1,2 @@
 /* @jsx customPragma */
-var div = <div>Hello</div>
+var div = <div>Hello</div>;
diff --git a/test/cli/supports-jsx-pragma/expected/output.js b/test/cli/supports-jsx-pragma/expected/output.js
index 1175b68..2b4e202 100644
--- a/test/cli/supports-jsx-pragma/expected/output.js
+++ b/test/cli/supports-jsx-pragma/expected/output.js
@@ -1 +1 @@
-var img = NotReact.createElement( 'img', { src: 'foo.gif' });
+var img = NotReact.createElement( 'img', { src: "foo.gif" });
diff --git a/test/cli/supports-jsx-pragma/input.js b/test/cli/supports-jsx-pragma/input.js
index fe2d823..645a506 100644
--- a/test/cli/supports-jsx-pragma/input.js
+++ b/test/cli/supports-jsx-pragma/input.js
@@ -1 +1 @@
-var img = <img src='foo.gif'/>;
+var img = <img src="foo.gif" />;
diff --git a/test/cli/writes-to-stdout/expected/output.js b/test/cli/writes-to-stdout/expected/output.js
index ed31167..4a6c09d 100644
--- a/test/cli/writes-to-stdout/expected/output.js
+++ b/test/cli/writes-to-stdout/expected/output.js
@@ -1 +1,2 @@
 var answer = function () { return 42; };
+
diff --git a/test/samples/arrow-functions.js b/test/samples/arrow-functions.js
index d76da1a..45ae20d 100644
--- a/test/samples/arrow-functions.js
+++ b/test/samples/arrow-functions.js
@@ -171,11 +171,12 @@ module.exports = [
 			() => console.log( 'not printed' );`,
 
 		output: `
-			(function() { return console.log( 'not printed' ); });`
+			!function() { return console.log( 'not printed' ); };`
 	},
 
 	{
-		description: 'handles standalone arrow function expression statement within a function',
+		description:
+			'handles standalone arrow function expression statement within a function',
 
 		input: `
 			function no_op () {
@@ -184,7 +185,25 @@ module.exports = [
 
 		output: `
 			function no_op () {
-				(function() { return console.log( 'not printed' ); });
+				!function() { return console.log( 'not printed' ); };
 			}`
+	},
+
+	{
+		description:
+			'are transformed even if disabled if they have a transpiled spread parameter',
+
+		options: { transforms: { arrow: false, spreadRest: true } },
+
+		input: `
+				(...args) => console.log( args );`,
+
+		output: `
+				!function() {
+					var args = [], len = arguments.length;
+					while ( len-- ) args[ len ] = arguments[ len ];
+
+					return console.log( args );
+				};`
 	}
 ];
diff --git a/test/samples/async.js b/test/samples/async.js
new file mode 100644
index 0000000..c98bb52
--- /dev/null
+++ b/test/samples/async.js
@@ -0,0 +1,11 @@
+module.exports = [
+	{
+		description: 'supports async as property name',
+
+		input: `
+			({async, foo})`,
+
+		output: `
+			({async: async, foo: foo})`
+	}
+];
diff --git a/test/samples/block-scoping.js b/test/samples/block-scoping.js
index c486493..bb2f802 100644
--- a/test/samples/block-scoping.js
+++ b/test/samples/block-scoping.js
@@ -73,7 +73,7 @@ module.exports = [
 			let x = 1;
 			let x = 2;
 		`,
-		error: /x is already declared/
+		error: /Identifier 'x' has already been declared/
 	},
 
 	{
diff --git a/test/samples/classes-no-named-function-expressions.js b/test/samples/classes-no-named-function-expressions.js
index 20543fb..b3be24c 100644
--- a/test/samples/classes-no-named-function-expressions.js
+++ b/test/samples/classes-no-named-function-expressions.js
@@ -42,7 +42,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles a class declaration without a constructor function',
+		description:
+			'transpiles a class declaration without a constructor function',
 		options: { namedFunctionExpressions: false },
 
 		input: `
@@ -236,7 +237,10 @@ module.exports = [
 
 	{
 		description: 'transpiles export default class',
-		options: { transforms: { moduleExport: false }, namedFunctionExpressions: false },
+		options: {
+			transforms: { moduleExport: false },
+			namedFunctionExpressions: false
+		},
 
 		input: `
 			export default class Foo {
@@ -253,7 +257,10 @@ module.exports = [
 
 	{
 		description: 'transpiles export default subclass',
-		options: { transforms: { moduleExport: false }, namedFunctionExpressions: false },
+		options: {
+			transforms: { moduleExport: false },
+			namedFunctionExpressions: false
+		},
 
 		input: `
 			export default class Foo extends Bar {
@@ -280,7 +287,10 @@ module.exports = [
 
 	{
 		description: 'transpiles export default subclass with subsequent statement',
-		options: { transforms: { moduleExport: false }, namedFunctionExpressions: false },
+		options: {
+			transforms: { moduleExport: false },
+			namedFunctionExpressions: false
+		},
 
 		input: `
 			export default class Foo extends Bar {
@@ -354,7 +364,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles an anonymous class expression with a non-constructor method',
+		description:
+			'transpiles an anonymous class expression with a non-constructor method',
 		options: { namedFunctionExpressions: false },
 
 		input: `
@@ -540,7 +551,10 @@ module.exports = [
 
 	{
 		description: 'can be disabled with `transforms.classes: false`',
-		options: { namedFunctionExpressions: false, transforms: { classes: false } },
+		options: {
+			namedFunctionExpressions: false,
+			transforms: { classes: false }
+		},
 
 		input: `
 			class Foo extends Bar {
@@ -676,7 +690,8 @@ module.exports = [
 	},
 
 	{
-		description: 'verify deindent() does not corrupt string literals in class methods (#159)',
+		description:
+			'verify deindent() does not corrupt string literals in class methods (#159)',
 		options: { namedFunctionExpressions: false },
 
 		input: `
@@ -836,7 +851,8 @@ module.exports = [
 	},
 
 	{
-		description: 'uses correct indentation for inserted statements in constructor (#39)',
+		description:
+			'uses correct indentation for inserted statements in constructor (#39)',
 		options: { namedFunctionExpressions: false },
 
 		input: `
@@ -880,7 +896,8 @@ module.exports = [
 	},
 
 	{
-		description: 'uses correct indentation for inserted statements in subclass constructor (#39)',
+		description:
+			'uses correct indentation for inserted statements in subclass constructor (#39)',
 		options: { namedFunctionExpressions: false },
 
 		input: `
@@ -1071,7 +1088,7 @@ module.exports = [
 
 			Foo.prototype.catch = function () {
 				// code goes here
-			};`,
+			};`
 	},
 
 	{
@@ -1149,7 +1166,8 @@ module.exports = [
 	},
 
 	{
-		description: 'static methods with computed names with varied spacing (#139)',
+		description:
+			'static methods with computed names with varied spacing (#139)',
 		options: { namedFunctionExpressions: false },
 
 		input: `
@@ -1201,7 +1219,8 @@ module.exports = [
 	},
 
 	{
-		description: 'static methods with numeric or string names with varied spacing (#139)',
+		description:
+			'static methods with numeric or string names with varied spacing (#139)',
 		options: { namedFunctionExpressions: false },
 
 		input: `
@@ -1218,7 +1237,7 @@ module.exports = [
 			D["Static Method"] = function (){};
 			D["foo"] = function (){};
 		`
-	},
+	}
 
 	// TODO more tests. e.g. getters and setters.
 	// 'super.*' is not allowed before super()
diff --git a/test/samples/classes.js b/test/samples/classes.js
index 4d5ff09..704de9b 100644
--- a/test/samples/classes.js
+++ b/test/samples/classes.js
@@ -40,7 +40,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles a class declaration without a constructor function',
+		description:
+			'transpiles a class declaration without a constructor function',
 
 		input: `
 			class Foo {
@@ -343,7 +344,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles an anonymous class expression with a non-constructor method',
+		description:
+			'transpiles an anonymous class expression with a non-constructor method',
 
 		input: `
 			var Foo = class {
@@ -365,6 +367,29 @@ module.exports = [
 	},
 
 	{
+		description:
+			'transpiles an anonymous class expression that is assigned to a property',
+
+		input: `
+			const q = {};
+
+			q.a = class {
+				c () {}
+			};`,
+
+		output: `
+			var q = {};
+
+			q.a = (function () {
+				function a () {}
+
+				a.prototype.c = function c () {};
+
+				return a;
+			}());`
+	},
+
+	{
 		description: 'allows constructor to be in middle of body',
 
 		input: `
@@ -656,7 +681,8 @@ module.exports = [
 	},
 
 	{
-		description: 'verify deindent() does not corrupt string literals in class methods (#159)',
+		description:
+			'verify deindent() does not corrupt string literals in class methods (#159)',
 
 		input: `
 			class Foo {
@@ -811,7 +837,8 @@ module.exports = [
 	},
 
 	{
-		description: 'uses correct indentation for inserted statements in constructor (#39)',
+		description:
+			'uses correct indentation for inserted statements in constructor (#39)',
 
 		input: `
 			class Foo {
@@ -854,7 +881,8 @@ module.exports = [
 	},
 
 	{
-		description: 'uses correct indentation for inserted statements in subclass constructor (#39)',
+		description:
+			'uses correct indentation for inserted statements in subclass constructor (#39)',
 
 		input: `
 			class Foo extends Bar {
@@ -1038,7 +1066,7 @@ module.exports = [
 
 			Foo.prototype.catch = function catch$1 () {
 				// code goes here
-			};`,
+			};`
 	},
 
 	{
@@ -1113,7 +1141,8 @@ module.exports = [
 	},
 
 	{
-		description: 'static methods with computed names with varied spacing (#139)',
+		description:
+			'static methods with computed names with varied spacing (#139)',
 
 		input: `
 			class B {
@@ -1163,7 +1192,8 @@ module.exports = [
 	},
 
 	{
-		description: 'static methods with numeric or string names with varied spacing (#139)',
+		description:
+			'static methods with numeric or string names with varied spacing (#139)',
 
 		input: `
 			class D {
@@ -1196,7 +1226,7 @@ module.exports = [
 			X.prototype.foo = function foo$1 () { return foo };
 			X.prototype.bar = function bar () {};
 		`
-	},
+	}
 
 	// TODO more tests. e.g. getters and setters.
 	// 'super.*' is not allowed before super()
diff --git a/test/samples/computed-properties.js b/test/samples/computed-properties.js
index 48f648f..d68001d 100644
--- a/test/samples/computed-properties.js
+++ b/test/samples/computed-properties.js
@@ -35,10 +35,9 @@ module.exports = [
 			};`,
 
 		output: `
-			var obj = {
-				b: 2
-			};
-			obj[a] = 1;`
+			var obj = {};
+			obj[a] = 1;
+			obj.b = 2;`
 	},
 
 	{
@@ -69,10 +68,10 @@ module.exports = [
 
 		output: `
 			var obj = {
-				a: 1,
-				c: 3
+				a: 1
 			};
-			obj[b] = 2;`
+			obj[b] = 2;
+			obj.c = 3;`
 	},
 
 	{
@@ -89,13 +88,12 @@ module.exports = [
 			};`,
 
 		output: `
-			var obj = {
-				b: 2,
-				e: 5
-			};
+			var obj = {};
 			obj[a] = 1;
+			obj.b = 2;
 			obj[c] = 3;
 			obj[d] = 4;
+			obj.e = 5;
 			obj[f] = 6;`
 	},
 
@@ -106,8 +104,9 @@ module.exports = [
 			var a = 'foo', obj = { [a]: 'bar', x: 42 }, bar = obj.foo;`,
 
 		output: `
-			var a = 'foo', obj = ( obj$1 = { x: 42 }, obj$1[a] = 'bar', obj$1 ), bar = obj.foo;
-			var obj$1;`
+			var obj$1;
+
+			var a = 'foo', obj = ( obj$1 = {}, obj$1[a] = 'bar', obj$1.x = 42, obj$1 ), bar = obj.foo;`
 	},
 
 	{
@@ -138,8 +137,9 @@ module.exports = [
 			call({ [a]: 5 });`,
 
 		output: `
-			call(( obj = {}, obj[a] = 5, obj ));
-			var obj;`
+			var obj;
+
+			call(( obj = {}, obj[a] = 5, obj ));`
 	},
 
 	{
@@ -160,7 +160,8 @@ module.exports = [
 	},
 
 	{
-		description: 'creates a computed method with a non-identifier expression (#78)',
+		description:
+			'creates a computed method with a non-identifier expression (#78)',
 
 		input: `
 			var obj = {
@@ -177,7 +178,8 @@ module.exports = [
 	},
 
 	{
-		description: 'does not require space before parens of computed method (#82)',
+		description:
+			'does not require space before parens of computed method (#82)',
 
 		input: `
 			var obj = {
@@ -194,7 +196,8 @@ module.exports = [
 	},
 
 	{
-		description: 'supports computed shorthand function with object spread in body (#135)',
+		description:
+			'supports computed shorthand function with object spread in body (#135)',
 
 		options: {
 			objectAssign: 'Object.assign'
@@ -217,15 +220,29 @@ module.exports = [
 	},
 
 	{
-		description: 'object literal with computed property within arrow expression (#126)',
+		description:
+			'object literal with computed property within arrow expression (#126)',
 
 		input: `
 			foo => bar({[x - y]: obj});
 		`,
 		output: `
-			(function(foo) { return bar(( obj$1 = {}, obj$1[x - y] = obj, obj$1 ))
-				var obj$1;; });
+			var obj$1;
+
+			!function(foo) { return bar(( obj$1 = {}, obj$1[x - y] = obj, obj$1 )); };
 		`
 	},
 
+	{
+		description: 'Supports nested computed properties (#51)',
+
+		input: `
+			(function () { return { [key]: { [key]: val } } })
+		`,
+		output: `
+			(function () {
+			var obj, obj$1;
+ return ( obj$1 = {}, obj$1[key] = ( obj = {}, obj[key] = val, obj ), obj$1 ) })
+		`
+	}
 ];
diff --git a/test/samples/destructuring.js b/test/samples/destructuring.js
index 97bf918..8ea25cb 100644
--- a/test/samples/destructuring.js
+++ b/test/samples/destructuring.js
@@ -87,14 +87,16 @@ module.exports = [
 	},
 
 	{
-		description: 'can be disabled in declarations with `transforms.destructuring === false`',
+		description:
+			'can be disabled in declarations with `transforms.destructuring === false`',
 		options: { transforms: { destructuring: false } },
 		input: `var { x, y } = point;`,
 		output: `var { x, y } = point;`
 	},
 
 	{
-		description: 'can be disabled in function parameters with `transforms.parameterDestructuring === false`',
+		description:
+			'can be disabled in function parameters with `transforms.parameterDestructuring === false`',
 		options: { transforms: { parameterDestructuring: false } },
 		input: `function foo ({ x, y }) {}`,
 		output: `function foo ({ x, y }) {}`
@@ -140,7 +142,8 @@ module.exports = [
 	},
 
 	{
-		description: 'does not destructure variable declarations intelligently (#53)',
+		description:
+			'does not destructure variable declarations intelligently (#53)',
 
 		input: `
 			var { foo: bar, baz } = obj;
@@ -204,7 +207,8 @@ module.exports = [
 	},
 
 	{
-		description: 'default values in destructured object parameter with a default value (#37)',
+		description:
+			'default values in destructured object parameter with a default value (#37)',
 
 		input: `
 			function foo ({ arg1 = 123, arg2 = 456 } = {}) {
@@ -511,7 +515,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles destructuring assignment of an array with a default value',
+		description:
+			'transpiles destructuring assignment of an array with a default value',
 		input: `
 			[x = 4, y] = [1, 2];`,
 		output: `
@@ -529,7 +534,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles nested destructuring assignment of an array without evaluating a memberexpr twice',
+		description:
+			'transpiles nested destructuring assignment of an array without evaluating a memberexpr twice',
 		input: `
 			[[x, z], y] = [1, 2];`,
 		output: `
@@ -538,7 +544,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles nested destructuring assignment of an array with a default',
+		description:
+			'transpiles nested destructuring assignment of an array with a default',
 		input: `
 			[[x] = [], y] = [1, 2];`,
 		output: `
@@ -574,7 +581,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles destructuring assignment of an object where key and pattern names differ',
+		description:
+			'transpiles destructuring assignment of an object where key and pattern names differ',
 		input: `
 			({x, y: z} = {x: 1});`,
 		output: `
@@ -592,7 +600,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles destructuring assignment of an object with a default value',
+		description:
+			'transpiles destructuring assignment of an object with a default value',
 		input: `
 			({x, y = 4} = {x: 1});`,
 		output: `
@@ -610,7 +619,7 @@ module.exports = [
 	},
 
 	{
-		description: 'doesn\'t create an object temporary unless necessary',
+		description: "doesn't create an object temporary unless necessary",
 		input: `
 			({x, y: {z}} = {x: 1});`,
 		output: `
@@ -675,11 +684,12 @@ module.exports = [
 			] = [ "ok" ];
 		`,
 		output: `
+			var obj;
+
 			var ref = [ "ok" ];
 			var a = ref[0]; if ( a === void 0 ) a = "A" + (baz() - 4);
 			var c = ref[2]; if ( c === void 0 ) c = (function (x) { return -x; });
 			var d = ref[3]; if ( d === void 0 ) d = (( obj = { r: 5 }, obj[h()] = i, obj ));
-			var obj;
 		`
 	},
 
@@ -695,11 +705,12 @@ module.exports = [
 			} = { b: 3 };
 		`,
 		output: `
+			var obj;
+
 			var ref = { b: 3 };
 			var a = ref.a; if ( a === void 0 ) a = "A" + (baz() - 4);
 			var c = ref.c; if ( c === void 0 ) c = (function (x) { return -x; });
 			var d = ref.d; if ( d === void 0 ) d = (( obj = { r: 5 }, obj[1 + 1] = 2, obj[h()] = i, obj ));
-			var obj;
 		`
 	},
 
@@ -757,4 +768,19 @@ module.exports = [
 		`
 	},
 
+	{
+		description: 'destructures with computed property',
+
+		input: `
+			const { a, b } = { ['a']: 1 };
+		`,
+
+		output: `
+			var obj;
+
+			var ref = ( obj = {}, obj['a'] = 1, obj );
+			var a = ref.a;
+			var b = ref.b;
+		`
+	}
 ];
diff --git a/test/samples/exponentiation-operator.js b/test/samples/exponentiation-operator.js
index 6679cea..813ca0d 100644
--- a/test/samples/exponentiation-operator.js
+++ b/test/samples/exponentiation-operator.js
@@ -6,19 +6,22 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles an exponentiation assignment to a simple reference',
+		description:
+			'transpiles an exponentiation assignment to a simple reference',
 		input: `x **= y`,
 		output: `x = Math.pow( x, y )`
 	},
 
 	{
-		description: 'transpiles an exponentiation assignment to a simple parenthesized reference',
+		description:
+			'transpiles an exponentiation assignment to a simple parenthesized reference',
 		input: `( x ) **= y`,
 		output: `( x ) = Math.pow( x, y )`
 	},
 
 	{
-		description: 'transpiles an exponentiation assignment to a rewritten simple reference',
+		description:
+			'transpiles an exponentiation assignment to a rewritten simple reference',
 
 		input: `
 			let x = 1;
@@ -38,7 +41,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles an exponentiation assignment to a simple member expression',
+		description:
+			'transpiles an exponentiation assignment to a simple member expression',
 
 		input: `
 			foo.bar **= y;`,
@@ -48,7 +52,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles an exponentiation assignment to a simple deep member expression',
+		description:
+			'transpiles an exponentiation assignment to a simple deep member expression',
 
 		input: `
 			foo.bar.baz **= y;`,
@@ -59,7 +64,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles an exponentiation assignment to a simple computed member expression',
+		description:
+			'transpiles an exponentiation assignment to a simple computed member expression',
 
 		input: `
 			foo[ bar ] **= y;`,
@@ -69,7 +75,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles an exponentiation assignment to a complex reference',
+		description:
+			'transpiles an exponentiation assignment to a complex reference',
 
 		input: `
 			foo[ bar() ] **= y;`,
@@ -80,7 +87,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles an exponentiation assignment to a contrivedly complex reference',
+		description:
+			'transpiles an exponentiation assignment to a contrivedly complex reference',
 
 		input: `
 			foo[ bar() ][ baz() ] **= y;`,
@@ -92,7 +100,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles an exponentiation assignment to a contrivedly complex reference (that is not a top-level statement)',
+		description:
+			'transpiles an exponentiation assignment to a contrivedly complex reference (that is not a top-level statement)',
 
 		input: `
 			var baz = 1, lolwut = foo[ bar() ][ baz * 2 ] **= y;`,
@@ -103,7 +112,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles an exponentiation assignment to a contrivedly complex reference with simple object (that is not a top-level statement)',
+		description:
+			'transpiles an exponentiation assignment to a contrivedly complex reference with simple object (that is not a top-level statement)',
 
 		input: `
 			var baz = 1, lolwut = foo[ bar() ] **= y;`,
@@ -153,7 +163,8 @@ module.exports = [
 	},
 
 	{
-		description: 'handles assignment of exponentiation assignment to property with side effect',
+		description:
+			'handles assignment of exponentiation assignment to property with side effect',
 
 		input: `
 			x=a[bar()]**=2;
@@ -162,7 +173,7 @@ module.exports = [
 			var property;
 			x=( property = bar(), a[property]=Math.pow( a[property], 2 ) );
 		`
-	},
+	}
 
 	/* TODO: Test currently errors out with: TypeError: Cannot read property 'property' of null
 	{
diff --git a/test/samples/for-of.js b/test/samples/for-of.js
index 6bf4bf2..ec9b643 100644
--- a/test/samples/for-of.js
+++ b/test/samples/for-of.js
@@ -13,7 +13,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles for-of with array assumption with `transforms.dangerousForOf`',
+		description:
+			'transpiles for-of with array assumption with `transforms.dangerousForOf`',
 		options: { transforms: { dangerousForOf: true } },
 
 		input: `
@@ -118,7 +119,6 @@ module.exports = [
 
 				if ( item.foo ) { continue; }
 			}`
-
 	},
 
 	{
diff --git a/test/samples/generators.js b/test/samples/generators.js
index b0a90c3..b58149c 100644
--- a/test/samples/generators.js
+++ b/test/samples/generators.js
@@ -48,28 +48,32 @@ module.exports = [
 	},
 
 	{
-		description: 'ignores generator function declarations with `transforms.generator: false`',
+		description:
+			'ignores generator function declarations with `transforms.generator: false`',
 		options: { transforms: { generator: false } },
 		input: `function* foo () {}`,
 		output: `function* foo () {}`
 	},
 
 	{
-		description: 'ignores generator function expressions with `transforms.generator: false`',
+		description:
+			'ignores generator function expressions with `transforms.generator: false`',
 		options: { transforms: { generator: false } },
 		input: `var foo = function* foo () {}`,
 		output: `var foo = function* foo () {}`
 	},
 
 	{
-		description: 'ignores generator function methods with `transforms.generator: false`',
+		description:
+			'ignores generator function methods with `transforms.generator: false`',
 		options: { transforms: { generator: false } },
 		input: `var obj = { *foo () {} }`,
 		output: `var obj = { foo: function* foo () {} }`
 	},
 
 	{
-		description: 'ignores generator function class methods with `transforms.generator: false`',
+		description:
+			'ignores generator function class methods with `transforms.generator: false`',
 		options: { transforms: { generator: false } },
 		input: `
 			class Foo {
diff --git a/test/samples/jsx.js b/test/samples/jsx.js
index fe1b2fa..da5b146 100644
--- a/test/samples/jsx.js
+++ b/test/samples/jsx.js
@@ -136,7 +136,7 @@ module.exports = [
 		input: `
 			<h1>
 				Hello {name}
-				!${"      "}
+				!${'      '}
 				It's  nice to meet you
 			</h1>`,
 		output: `
@@ -177,7 +177,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles mixed JSX spread attributes ending in spread with custom Object.assign',
+		description:
+			'transpiles mixed JSX spread attributes ending in spread with custom Object.assign',
 		options: {
 			objectAssign: 'angular.extend'
 		},
@@ -186,7 +187,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles mixed JSX spread attributes ending in other values',
+		description:
+			'transpiles mixed JSX spread attributes ending in other values',
 		options: {
 			objectAssign: 'Object.assign'
 		},
@@ -270,5 +272,20 @@ module.exports = [
 		output: `
 			React.createElement( Thing, { 'data-foo': true })
 		`
+	},
+
+	{
+		description: 'handles non-breaking white-space entities',
+
+		input: `
+			<div>
+				<a>1</a> 
+				 
+			</div>
+		`,
+		output: `
+			React.createElement( 'div', null,
+				React.createElement( 'a', null, "1" ), "   ")
+		`
 	}
 ];
diff --git a/test/samples/loops.js b/test/samples/loops.js
index dcc8aef..7f96945 100644
--- a/test/samples/loops.js
+++ b/test/samples/loops.js
@@ -30,7 +30,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles block scoping inside while loops with function bodies',
+		description:
+			'transpiles block scoping inside while loops with function bodies',
 
 		input: `
 			function log ( square ) {
@@ -60,7 +61,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles block scoping inside do-while loops with function bodies',
+		description:
+			'transpiles block scoping inside do-while loops with function bodies',
 
 		input: `
 			function log ( square ) {
@@ -92,7 +94,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles block-less for loops with block-scoped declarations inside function body',
+		description:
+			'transpiles block-less for loops with block-scoped declarations inside function body',
 
 		input: `
 			for ( let i = 0; i < 10; i += 1 ) setTimeout( () => console.log( i ), i * 100 );`,
@@ -106,7 +109,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles block scoping inside loops without function bodies',
+		description:
+			'transpiles block scoping inside loops without function bodies',
 
 		input: `
 			for ( let i = 0; i < 10; i += 1 ) {
@@ -122,7 +126,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles block-less for loops without block-scoped declarations inside function body',
+		description:
+			'transpiles block-less for loops without block-scoped declarations inside function body',
 
 		input: `
 			for ( let i = 0; i < 10; i += 1 ) console.log( i );`,
@@ -132,7 +137,8 @@ module.exports = [
 	},
 
 	{
-		description: 'preserves correct `this` and `arguments` inside block scoped loop (#10)',
+		description:
+			'preserves correct `this` and `arguments` inside block scoped loop (#10)',
 
 		input: `
 			for ( let i = 0; i < 10; i += 1 ) {
@@ -157,7 +163,8 @@ module.exports = [
 	},
 
 	{
-		description: 'maintains value of for loop variables between iterations (#11)',
+		description:
+			'maintains value of for loop variables between iterations (#11)',
 
 		input: `
 			var fns = [];
@@ -181,7 +188,8 @@ module.exports = [
 	},
 
 	{
-		description: 'maintains value of for loop variables between iterations, with conflict (#11)',
+		description:
+			'maintains value of for loop variables between iterations, with conflict (#11)',
 
 		input: `
 			var i = 'conflicting';
@@ -207,7 +215,8 @@ module.exports = [
 	},
 
 	{
-		description: 'loop variables with UpdateExpresssions between iterations (#150)',
+		description:
+			'loop variables with UpdateExpresssions between iterations (#150)',
 
 		input: `
 			var fns = [];
@@ -234,7 +243,8 @@ module.exports = [
 	},
 
 	{
-		description: 'loop variables with UpdateExpresssions between iterations, with conflict (#150)',
+		description:
+			'loop variables with UpdateExpresssions between iterations, with conflict (#150)',
 
 		input: `
 			var i = 'conflicting';
@@ -349,7 +359,8 @@ module.exports = [
 	},
 
 	{
-		description: 'does not incorrectly rename variables declared in for loop head',
+		description:
+			'does not incorrectly rename variables declared in for loop head',
 
 		input: `
 			for ( let foo = 0; foo < 10; foo += 1 ) {
@@ -365,7 +376,8 @@ module.exports = [
 	},
 
 	{
-		description: 'does not rewrite as function if `transforms.letConst === false`',
+		description:
+			'does not rewrite as function if `transforms.letConst === false`',
 		options: { transforms: { letConst: false } },
 
 		input: `
@@ -514,7 +526,8 @@ module.exports = [
 	},
 
 	{
-		description: 'complex destructuring in variable declaration in for loop head',
+		description:
+			'complex destructuring in variable declaration in for loop head',
 
 		input: `
 			var range = function () {
@@ -536,7 +549,8 @@ module.exports = [
 	},
 
 	{
-		description: 'arrow functions in block-less for loops in a block-less if/else chain (#110)',
+		description:
+			'arrow functions in block-less for loops in a block-less if/else chain (#110)',
 
 		input: `
 			if (x)
@@ -595,7 +609,8 @@ module.exports = [
 	},
 
 	{
-		description: 'always initialises block-scoped variable in for-of loop (#125)',
+		description:
+			'always initialises block-scoped variable in for-of loop (#125)',
 
 		options: { transforms: { dangerousForOf: true } },
 
@@ -618,11 +633,12 @@ module.exports = [
 					f(b, j, k, x, y)
 				}
 			}
-		`,
+		`
 	},
 
 	{
-		description: 'always initialises block-scoped variable in simple for-of loop (#125)',
+		description:
+			'always initialises block-scoped variable in simple for-of loop (#125)',
 
 		options: { transforms: { dangerousForOf: true } },
 
@@ -639,7 +655,7 @@ module.exports = [
 				var x = (void 0), y = 2, z = (void 0);
 				f(b, x++, y++, z++)
 			}
-		`,
+		`
 	},
 
 	{
@@ -658,11 +674,12 @@ module.exports = [
 				var x = (void 0), y = 2, z = (void 0);
 				f(k, r++, s++, t++, x++, y++, z++)
 			}
-		`,
+		`
 	},
 
 	{
-		description: 'use alias for this in right side of nested for-in loop declaration (#142)',
+		description:
+			'use alias for this in right side of nested for-in loop declaration (#142)',
 
 		input: `
 			let arr = [];
@@ -701,11 +718,12 @@ module.exports = [
 				console.log( arr.join( ' ' ) );
 			};
 			new Foo().do();
-		`,
+		`
 	},
 
 	{
-		description: 'use alias for this in right side of nested for-of loop declaration (#142)',
+		description:
+			'use alias for this in right side of nested for-of loop declaration (#142)',
 
 		options: { transforms: { dangerousForOf: true } },
 
@@ -750,7 +768,6 @@ module.exports = [
 				console.log( arr.join( ' ' ) );
 			};
 			new Foo().do();
-		`,
-	},
-
+		`
+	}
 ];
diff --git a/test/samples/misc.js b/test/samples/misc.js
index 0fc9ac2..5900ffd 100644
--- a/test/samples/misc.js
+++ b/test/samples/misc.js
@@ -60,7 +60,7 @@ module.exports = [
 				let x = 2;
 			}`,
 
-		error: /x is already declared/
+		error: /Identifier 'x' has already been declared/
 	},
 
 	{
@@ -72,7 +72,7 @@ module.exports = [
 				var x = 2;
 			}`,
 
-		error: /x is already declared/
+		error: /Identifier 'x' has already been declared/
 	},
 
 	{
@@ -96,7 +96,8 @@ module.exports = [
 	},
 
 	{
-		description: 'Safari/WebKit bug workaround: parameter shadowing function expression name (#154)',
+		description:
+			'Safari/WebKit bug workaround: parameter shadowing function expression name (#154)',
 
 		input: `
 			"use strict"; // necessary to trigger WebKit bug
@@ -184,6 +185,5 @@ module.exports = [
 				return bar;
 			}
 		`
-	},
-
+	}
 ];
diff --git a/test/samples/modules.js b/test/samples/modules.js
index 6e443cb..ab63e16 100644
--- a/test/samples/modules.js
+++ b/test/samples/modules.js
@@ -26,9 +26,46 @@ module.exports = [
 	},
 
 	{
-		description: 'imports and exports are ignored with `transforms.modules === false`',
+		description:
+			'imports and exports are ignored with `transforms.modules === false`',
 		options: { transforms: { modules: false } },
 		input: `import 'foo'; export { foo };`,
 		output: `import 'foo'; export { foo };`
+	},
+
+	{
+		description:
+			'Supports anonymous functions as default export',
+		options: { transforms: { modules: false } },
+		input: `export default function () {}`,
+		output: `export default function () {}`
+  },
+  
+  {
+		description:
+			'Supports anonymous classes as default export',
+		options: { transforms: { modules: false } },
+		input: `
+			export default class {
+				constructor() {
+					foo()
+				}
+
+				a() {
+					bar()
+				}
+			}
+		`,
+		output: `
+			var defaultExport = function defaultExport() {
+				foo()
+			};
+
+			defaultExport.prototype.a = function a () {
+				bar()
+			};
+
+			export default defaultExport;
+		`
 	}
 ];
diff --git a/test/samples/object-properties-no-named-function-expressions.js b/test/samples/object-properties-no-named-function-expressions.js
index d3a9910..0fb4931 100644
--- a/test/samples/object-properties-no-named-function-expressions.js
+++ b/test/samples/object-properties-no-named-function-expressions.js
@@ -52,7 +52,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles shorthand methods with numeric or string names (#139)',
+		description:
+			'transpiles shorthand methods with numeric or string names (#139)',
 		options: { namedFunctionExpressions: false },
 
 		input: `
@@ -83,15 +84,23 @@ module.exports = [
 	},
 
 	{
-		description: 'shorthand properties can be disabled with `transforms.conciseMethodProperty === false`',
-		options: { namedFunctionExpressions: false, transforms: { conciseMethodProperty: false } },
+		description:
+			'shorthand properties can be disabled with `transforms.conciseMethodProperty === false`',
+		options: {
+			namedFunctionExpressions: false,
+			transforms: { conciseMethodProperty: false }
+		},
 		input: `var obj = { x, y, z () {} }`,
 		output: `var obj = { x, y, z () {} }`
 	},
 
 	{
-		description: 'computed properties can be disabled with `transforms.computedProperty === false`',
-		options: { namedFunctionExpressions: false, transforms: { computedProperty: false } },
+		description:
+			'computed properties can be disabled with `transforms.computedProperty === false`',
+		options: {
+			namedFunctionExpressions: false,
+			transforms: { computedProperty: false }
+		},
 		input: `var obj = { [x]: 'x' }`,
 		output: `var obj = { [x]: 'x' }`
 	},
@@ -107,12 +116,12 @@ module.exports = [
 				console.log(JSON.stringify({['bar']:3}));
 		`,
 		output: `
+			var obj, obj$1;
+
 			if (1)
-				{ console.log(JSON.stringify(( obj = {}, obj['com'+'puted'] = 1, obj['foo'] = 2, obj )));
-					var obj; }
+				{ console.log(JSON.stringify(( obj = {}, obj['com'+'puted'] = 1, obj['foo'] = 2, obj ))); }
 			else
-				{ console.log(JSON.stringify(( obj$1 = {}, obj$1['bar'] = 3, obj$1 )));
-			var obj$1; }
+				{ console.log(JSON.stringify(( obj$1 = {}, obj$1['bar'] = 3, obj$1 ))); }
 		`
-	},
+	}
 ];
diff --git a/test/samples/object-properties.js b/test/samples/object-properties.js
index 4f28c9e..c43427f 100644
--- a/test/samples/object-properties.js
+++ b/test/samples/object-properties.js
@@ -48,7 +48,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles shorthand methods with numeric or string names (#139)',
+		description:
+			'transpiles shorthand methods with numeric or string names (#139)',
 
 		input: `
 			obj = {
@@ -78,14 +79,16 @@ module.exports = [
 	},
 
 	{
-		description: 'shorthand properties can be disabled with `transforms.conciseMethodProperty === false`',
+		description:
+			'shorthand properties can be disabled with `transforms.conciseMethodProperty === false`',
 		options: { transforms: { conciseMethodProperty: false } },
 		input: `var obj = { x, y, z () {} }`,
 		output: `var obj = { x, y, z () {} }`
 	},
 
 	{
-		description: 'computed properties can be disabled with `transforms.computedProperty === false`',
+		description:
+			'computed properties can be disabled with `transforms.computedProperty === false`',
 		options: { transforms: { computedProperty: false } },
 		input: `var obj = { [x]: 'x' }`,
 		output: `var obj = { [x]: 'x' }`
@@ -101,12 +104,12 @@ module.exports = [
 				console.log(JSON.stringify({['bar']:3}));
 		`,
 		output: `
+			var obj, obj$1;
+
 			if (1)
-				{ console.log(JSON.stringify(( obj = {}, obj['com'+'puted'] = 1, obj['foo'] = 2, obj )));
-					var obj; }
+				{ console.log(JSON.stringify(( obj = {}, obj['com'+'puted'] = 1, obj['foo'] = 2, obj ))); }
 			else
-				{ console.log(JSON.stringify(( obj$1 = {}, obj$1['bar'] = 3, obj$1 )));
-			var obj$1; }
+				{ console.log(JSON.stringify(( obj$1 = {}, obj$1['bar'] = 3, obj$1 ))); }
 		`
 	},
 
@@ -125,5 +128,5 @@ module.exports = [
 				bar: function bar() {}
 			}
 		`
-	},
+	}
 ];
diff --git a/test/samples/object-rest-spread.js b/test/samples/object-rest-spread.js
index 4c01ce0..603a1f9 100644
--- a/test/samples/object-rest-spread.js
+++ b/test/samples/object-rest-spread.js
@@ -24,7 +24,7 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles object rest spread with regular keys in between',
+		description: 'transpiles object spread with regular keys in between',
 		options: {
 			objectAssign: 'Object.assign'
 		},
@@ -33,7 +33,7 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles object rest spread mixed',
+		description: 'transpiles object spread mixed',
 		options: {
 			objectAssign: 'Object.assign'
 		},
@@ -42,14 +42,14 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles objects with rest spread with computed property (#144)',
+		description: 'transpiles objects with spread with computed property (#144)',
 		options: {
 			objectAssign: 'Object.assign'
 		},
 		input: `
 			var a0 = { [ x ] : true , ... y };
 			var a1 = { [ w ] : 0 , [ x ] : true , ... y };
-			var a2 = { v, [ w ] : 0, [ x ] : true , ... y };
+			var a2 = { v, [ w ] : 0, [ x ] : true, ... y };
 			var a3 = { [ w ] : 0, [ x ] : true };
 			var a4 = { [ w ] : 0 , [ x ] : true , y };
 			var a5 = { k : 9 , [ x ] : true, ... y };
@@ -62,51 +62,44 @@ module.exports = [
 			var a12 = { ...b, [c]:3, d:4 };
 		`,
 		output: `
-			var a0 = Object.assign({},  y);
-			a0[ x ] = true;
-			var a1 = Object.assign({},  y);
-			a1[ w ] = 0;
-			a1[ x ] = true;
-			var a2 = Object.assign({}, {v: v} , y);
-			a2[ w ] = 0;
-			a2[ x ] = true;
+			var obj, obj$1, obj$2, obj$3, obj$4, obj$5, obj$6, obj$7, obj$8;
+
+			var a0 = Object.assign(( obj = {}, obj[ x ] = true, obj ), y);
+			var a1 = Object.assign(( obj$1 = {}, obj$1[ w ] = 0, obj$1[ x ] = true, obj$1 ), y);
+			var a2 = Object.assign(( obj$2 = { v: v }, obj$2[ w ] = 0, obj$2[ x ] = true, obj$2 ), y);
 			var a3 = {};
 			a3[ w ] = 0;
 			a3[ x ] = true;
-			var a4 = { y: y };
+			var a4 = {};
 			a4[ w ] = 0;
 			a4[ x ] = true;
-			var a5 = Object.assign({}, {k : 9}, y);
-			a5[ x ] = true;
-			var a6 = Object.assign({}, y);
-			a6[ x ] = true;
-			var a7 = Object.assign({}, y);
-			a7[ w ] = 0;
-			a7[ x ] = true;
-			var a8 = Object.assign({}, {k : 9}, y);
-			a8[ x ] = true;
+			a4.y = y;
+			var a5 = Object.assign(( obj$3 = { k : 9 }, obj$3[ x ] = true, obj$3 ), y);
+			var a6 = Object.assign({}, y, ( obj$4 = {}, obj$4[ x ] = true, obj$4 ));
+			var a7 = Object.assign({}, y, ( obj$5 = {}, obj$5[ w ] = 0, obj$5[ x ] = true, obj$5 ));
+			var a8 = Object.assign({ k : 9 }, y, ( obj$6 = {}, obj$6[ x ] = true, obj$6 ));
 			var a9 = {};
 			a9[ x ] = true;
 			a9[ y ] = false;
 			a9[ z ] = 9;
-			var a10 = Object.assign({},  y, {p: p}, q);
-			a10[ x ] = true;
-			var a11 = { x: x , y: y };
+			var a10 = Object.assign(( obj$7 = {}, obj$7[ x ] = true, obj$7 ), y, {p: p}, q);
+			var a11 = { x: x };
 			a11[c] = 9;
-			var a12 = Object.assign({}, b, {d:4});
-			a12[c] = 3;
+			a11.y = y;
+			var a12 = Object.assign({}, b, ( obj$8 = {}, obj$8[c] = 3, obj$8 ), {d:4});
 		`
 	},
 
 	{
-		description: 'transpiles inline objects with rest spread with computed property (#144)',
+		description:
+			'transpiles inline objects with spread with computed property (#144)',
 		options: {
 			objectAssign: 'Object.assign'
 		},
 		input: `
 			f0( { [ x ] : true , ... y } );
 			f1( { [ w ] : 0 , [ x ] : true , ... y } );
-			f2( { v, [ w ] : 0, [ x ] : true , ... y } );
+			f2( { v, [ w ] : 0, [ x ] : true, ... y } );
 			f3( { [ w ] : 0, [ x ] : true } );
 			f4( { [ w ] : 0 , [ x ] : true , y } );
 			f5( { k : 9 , [ x ] : true, ... y } );
@@ -119,37 +112,26 @@ module.exports = [
 			f12({ ...b, [c]:3, d:4 });
 		`,
 		output: `
-			f0( ( obj = Object.assign({},  y), obj[ x ] = true, obj ) );
-			var obj;
-			f1( ( obj$1 = Object.assign({},  y), obj$1[ w ] = 0, obj$1[ x ] = true, obj$1 ) );
-			var obj$1;
-			f2( ( obj$2 = Object.assign({}, {v: v} , y), obj$2[ w ] = 0, obj$2[ x ] = true, obj$2 ) );
-			var obj$2;
+			var obj, obj$1, obj$2, obj$3, obj$4, obj$5, obj$6, obj$7, obj$8, obj$9, obj$10, obj$11, obj$12;
+
+			f0( Object.assign(( obj = {}, obj[ x ] = true, obj ), y) );
+			f1( Object.assign(( obj$1 = {}, obj$1[ w ] = 0, obj$1[ x ] = true, obj$1 ), y) );
+			f2( Object.assign(( obj$2 = { v: v }, obj$2[ w ] = 0, obj$2[ x ] = true, obj$2 ), y) );
 			f3( ( obj$3 = {}, obj$3[ w ] = 0, obj$3[ x ] = true, obj$3 ) );
-			var obj$3;
-			f4( ( obj$4 = { y: y }, obj$4[ w ] = 0, obj$4[ x ] = true, obj$4 ) );
-			var obj$4;
-			f5( ( obj$5 = Object.assign({}, {k : 9}, y), obj$5[ x ] = true, obj$5 ) );
-			var obj$5;
-			f6( ( obj$6 = Object.assign({}, y), obj$6[ x ] = true, obj$6 ) );
-			var obj$6;
-			f7( ( obj$7 = Object.assign({}, y), obj$7[ w ] = 0, obj$7[ x ] = true, obj$7 ) );
-			var obj$7;
-			f8( ( obj$8 = Object.assign({}, {k : 9}, y), obj$8[ x ] = true, obj$8 ) );
-			var obj$8;
+			f4( ( obj$4 = {}, obj$4[ w ] = 0, obj$4[ x ] = true, obj$4.y = y, obj$4 ) );
+			f5( Object.assign(( obj$5 = { k : 9 }, obj$5[ x ] = true, obj$5 ), y) );
+			f6( Object.assign({}, y, ( obj$6 = {}, obj$6[ x ] = true, obj$6 )) );
+			f7( Object.assign({}, y, ( obj$7 = {}, obj$7[ w ] = 0, obj$7[ x ] = true, obj$7 )) );
+			f8( Object.assign({ k : 9 }, y, ( obj$8 = {}, obj$8[ x ] = true, obj$8 )) );
 			f9( ( obj$9 = {}, obj$9[ x ] = true, obj$9[ y ] = false, obj$9[ z ] = 9, obj$9 ) );
-			var obj$9;
-			f10( ( obj$10 = Object.assign({},  y, {p: p}, q), obj$10[ x ] = true, obj$10 ) );
-			var obj$10;
-			f11( ( obj$11 = { x: x , y: y }, obj$11[c] = 9, obj$11 ) );
-			var obj$11;
-			f12(( obj$12 = Object.assign({}, b, {d:4}), obj$12[c] = 3, obj$12 ));
-			var obj$12;
+			f10( Object.assign(( obj$10 = {}, obj$10[ x ] = true, obj$10 ), y, {p: p}, q) );
+			f11( ( obj$11 = { x: x }, obj$11[c] = 9, obj$11.y = y, obj$11 ) );
+			f12(Object.assign({}, b, ( obj$12 = {}, obj$12[c] = 3, obj$12 ), {d:4}));
 		`
 	},
 
 	{
-		description: 'transpiles object rest spread nested',
+		description: 'transpiles object spread nested',
 		options: {
 			objectAssign: 'Object.assign'
 		},
@@ -158,7 +140,7 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles object rest spread deeply nested',
+		description: 'transpiles object spread deeply nested',
 		options: {
 			objectAssign: 'Object.assign'
 		},
@@ -167,11 +149,39 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles object reset spread with custom Object.assign',
+		description: 'transpiles object spread with custom Object.assign',
 		options: {
 			objectAssign: 'angular.extend'
 		},
 		input: `var obj = { ...a, b: 1, dd: {...d, f: 1}, e};`,
 		output: `var obj = angular.extend({}, a, {b: 1, dd: angular.extend({}, d, {f: 1}), e: e});`
+	},
+
+	{
+		description: 'transpiles rest properties',
+		input: `var {a, ...b} = c`,
+		output: `var a = c.a;
+var rest = {}; for (var n in c) if(["a"].indexOf(n) === -1) rest[n] = c[n];
+var b = rest;`
+	},
+
+	{
+		description: 'transpiles rest properties in arguments',
+		input: `(function({x, ...y}) {})`,
+		output: `(function(ref) {
+	var x = ref.x;
+	var rest = {}; for (var n in ref) if(["x"].indexOf(n) === -1) rest[n] = ref[n];
+	var y = rest;
+})`
+	},
+
+	{
+		description: 'transpiles rest properties in arrow function arguments',
+		input: `(({x, ...y}) => {})`,
+		output: `(function (ref) {
+	var x = ref.x;
+	var rest = {}; for (var n in ref) if(["x"].indexOf(n) === -1) rest[n] = ref[n];
+	var y = rest;
+})`
 	}
 ];
diff --git a/test/samples/spread-operator.js b/test/samples/spread-operator.js
index fe78410..d831eea 100644
--- a/test/samples/spread-operator.js
+++ b/test/samples/spread-operator.js
@@ -24,13 +24,15 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles a spread operator in a method call with other arguments',
+		description:
+			'transpiles a spread operator in a method call with other arguments',
 		input: `var max = Math.max( 0, ...values );`,
 		output: `var max = Math.max.apply( Math, [ 0 ].concat( values ) );`
 	},
 
 	{
-		description: 'transpiles a spread operator in a method call of an expression',
+		description:
+			'transpiles a spread operator in a method call of an expression',
 
 		input: `
 			( foo || bar ).baz( ...values );`,
@@ -55,7 +57,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles a spread operator in a call in an arrow function using this (#115)',
+		description:
+			'transpiles a spread operator in a call in an arrow function using this (#115)',
 
 		input: `
 			function foo(...args) {
@@ -106,7 +109,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles a spread operator in a new call in an arrow function using this',
+		description:
+			'transpiles a spread operator in a new call in an arrow function using this',
 
 		input: `
 			function foo(...args) {
@@ -156,7 +160,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles a spread operator in an expression method call within an if',
+		description:
+			'transpiles a spread operator in an expression method call within an if',
 
 		input: `
 			var result;
@@ -173,7 +178,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles spread operators in expression method calls within a function',
+		description:
+			'transpiles spread operators in expression method calls within a function',
 
 		input: `
 			function foo() {
@@ -245,9 +251,11 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles spread operators with template literals (issue #99)',
+		description:
+			'transpiles spread operators with template literals (issue #99)',
 		input: 'console.log( `%s ${label}:`, `${color}`, ...args );',
-		output: 'console.log.apply( console, [ ("%s " + label + ":"), ("" + color) ].concat( args ) );'
+		output:
+			'console.log.apply( console, [ ("%s " + label + ":"), ("" + color) ].concat( args ) );'
 	},
 
 	{
@@ -257,7 +265,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles a spread operator in a function call with other arguments',
+		description:
+			'transpiles a spread operator in a function call with other arguments',
 		input: `sprintf( str, ...values );`,
 		output: `sprintf.apply( void 0, [ str ].concat( values ) );`
 	},
@@ -269,14 +278,16 @@ module.exports = [
 	},
 
 	{
-		description: 'can be disabled in array expressions `transforms.spreadRest: false`',
+		description:
+			'can be disabled in array expressions `transforms.spreadRest: false`',
 		options: { transforms: { spreadRest: false } },
 		input: `var chars = [ ...string ]`,
 		output: `var chars = [ ...string ]`
 	},
 
 	{
-		description: 'can be disabled in call expressions with `transforms.spreadRest: false`',
+		description:
+			'can be disabled in call expressions with `transforms.spreadRest: false`',
 		options: { transforms: { spreadRest: false } },
 		input: `var max = Math.max( ...values );`,
 		output: `var max = Math.max( ...values );`
@@ -289,7 +300,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles multiple spread operators in an array with trailing comma',
+		description:
+			'transpiles multiple spread operators in an array with trailing comma',
 		input: `var arr = [ ...a, ...b, ...c, ];`,
 		output: `var arr = a.concat( b, c );`
 	},
@@ -301,7 +313,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles mixture of spread and non-spread elements in array with trailing comma',
+		description:
+			'transpiles mixture of spread and non-spread elements in array with trailing comma',
 		input: `var arr = [ ...a, b, ...c, d, ];`,
 		output: `var arr = a.concat( [b], c, [d] );`
 	},
@@ -370,7 +383,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles mixture of spread and non-spread operators in function call',
+		description:
+			'transpiles mixture of spread and non-spread operators in function call',
 		input: `var max = Math.max( ...a, b, ...c, d );`,
 		output: `var max = Math.max.apply( Math, a.concat( [b], c, [d] ) );`
 	},
@@ -559,6 +573,5 @@ module.exports = [
 						var ref$1;; }; }
 			}
 		`
-	},
-
+	}
 ];
diff --git a/test/samples/template-strings.js b/test/samples/template-strings.js
index 85ef6e8..6b69b50 100644
--- a/test/samples/template-strings.js
+++ b/test/samples/template-strings.js
@@ -12,7 +12,8 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles an untagged template literal containing complex expressions',
+		description:
+			'transpiles an untagged template literal containing complex expressions',
 		input: 'var str = `foo${bar + baz}qux`;',
 		output: `var str = "foo" + (bar + baz) + "qux";`
 	},
@@ -36,14 +37,16 @@ module.exports = [
 	},
 
 	{
-		description: 'transpiles tagged template literals with `transforms.dangerousTaggedTemplateString = true`',
+		description:
+			'transpiles tagged template literals with `transforms.dangerousTaggedTemplateString = true`',
 		options: { transforms: { dangerousTaggedTemplateString: true } },
 		input: 'var str = x`y${(() => 42)()}`;',
 		output: `var str = x(["y", ""], (function () { return 42; })());`
 	},
 
 	{
-		description: 'transpiles tagged template literals with `transforms.dangerousTaggedTemplateString = true`',
+		description:
+			'transpiles tagged template literals with `transforms.dangerousTaggedTemplateString = true`',
 		options: { transforms: { dangerousTaggedTemplateString: true } },
 		input: 'var str = x`${(() => 42)()}y`;',
 		output: `var str = x(["", "y"], (function () { return 42; })());`
@@ -62,7 +65,8 @@ module.exports = [
 	},
 
 	{
-		description: 'does not parenthesise template strings in arithmetic expressions',
+		description:
+			'does not parenthesise template strings in arithmetic expressions',
 		input: 'var str = `x${y}` + z; var str2 = `x${y}` * z;',
 		output: 'var str = "x" + y + z; var str2 = ("x" + y) * z;'
 	},
@@ -102,5 +106,11 @@ module.exports = [
 		description: 'concats expression with variable',
 		input: 'var str = `${a + b}${c}`;',
 		output: 'var str = "" + (a + b) + c;'
+	},
+
+	{
+		description: 'interpolations inside interpolations',
+		input: 'var string = `foo${`${bar}`}`',
+		output: `var string = "foo" + ("" + bar)`
 	}
 ];
diff --git a/test/samples/trailing-function-commas.js b/test/samples/trailing-function-commas.js
new file mode 100644
index 0000000..67cc793
--- /dev/null
+++ b/test/samples/trailing-function-commas.js
@@ -0,0 +1,68 @@
+module.exports = [
+	{
+		description: 'strips trailing commas in call arguments',
+
+		input: `
+			f(a,)`,
+
+		output: `
+			f(a)`
+	},
+
+	{
+		description: 'strips trailing commas in function expression arguments',
+
+		input: `
+			let f = function (a,) {}`,
+
+		output: `
+			var f = function (a) {}`
+	},
+
+	{
+		description: 'strips trailing commas in normal function declaration arguments',
+
+		input: `
+			function f(a,) {}`,
+
+		output: `
+			function f(a) {}`
+	},
+
+	{
+		description: 'strips trailing commas in method arguments',
+
+		input: `
+			class A {
+				f(a,) {}
+			}`,
+
+		output: `
+			var A = function A () {};
+
+			A.prototype.f = function f (a) {};`
+	},
+
+	{
+		description: 'strips trailing commas in arrow function declaration arguments',
+
+		input: `
+			((a,) => {})`,
+
+		output: `
+			(function (a) {})`
+	},
+
+	{
+		description: 'strips trailing commas after destructured argument in arrow function declaration arguments',
+
+		input: `
+			((a,[b],{c},) => {})`,
+
+		output: `
+			(function (a,ref,ref$1) {
+				var b = ref[0];
+				var c = ref$1.c;
+})`
+	}
+];
diff --git a/test/test.js b/test/test.js
index db70966..0c91a14 100644
--- a/test/test.js
+++ b/test/test.js
@@ -1,238 +1,266 @@
-var path = require( 'path' );
-var fs = require( 'fs' );
-var rimraf = require( 'rimraf' );
-var child_process = require( 'child_process' );
-var assert = require( 'assert' );
-var glob = require( 'glob' );
-var SourceMapConsumer = require( 'source-map' ).SourceMapConsumer;
-var getLocation = require( './utils/getLocation.js' );
-var buble = require( '../dist/buble.umd.js' );
-
-require( 'source-map-support' ).install();
-require( 'console-group' ).install();
-
-function equal ( a, b ) {
-	assert.equal( showInvisibles( a ), showInvisibles( b ) );
+var path = require('path');
+var fs = require('fs');
+var rimraf = require('rimraf');
+var child_process = require('child_process');
+var assert = require('assert');
+var glob = require('glob');
+var SourceMapConsumer = require('source-map').SourceMapConsumer;
+var getLocation = require('./utils/getLocation.js');
+var buble = require('../dist/buble.umd.js');
+
+require('source-map-support').install();
+require('console-group').install();
+
+function equal(a, b) {
+	assert.equal(showInvisibles(a), showInvisibles(b));
 }
 
-function showInvisibles ( str ) {
+function showInvisibles(str) {
 	return str
-		.replace( /^ +/gm, spaces => repeat( '•', spaces.length ) )
-		.replace( / +$/gm, spaces => repeat( '•', spaces.length ) )
-		.replace( /^\t+/gm, tabs => repeat( '›   ', tabs.length ) )
-		.replace( /\t+$/gm, tabs => repeat( '›   ', tabs.length ) );
+		.replace(/^ +/gm, spaces => repeat('•', spaces.length))
+		.replace(/ +$/gm, spaces => repeat('•', spaces.length))
+		.replace(/^\t+/gm, tabs => repeat('›   ', tabs.length))
+		.replace(/\t+$/gm, tabs => repeat('›   ', tabs.length));
 }
 
-function repeat ( str, times ) {
+function repeat(str, times) {
 	var result = '';
-	while ( times-- ) result += str;
+	while (times--) result += str;
 	return result;
 }
 
-const subsetIndex = process.argv.indexOf( '--subset' );
-const subset = ~subsetIndex ? process.argv[ subsetIndex + 1 ].split( ',' ).map( file => `${file}.js` ) : null;
-const subsetFilter = subset ? file => ~subset.indexOf( file ) : () => true;
-
-describe( 'buble', () => {
-	fs.readdirSync( 'test/samples' ).filter( subsetFilter ).forEach( file => {
-		if ( !/\.js$/.test( file ) ) return; // avoid vim .js.swp files
-		var samples = require( './samples/' + file );
-
-		describe( path.basename( file ), () => {
-			samples.forEach( sample => {
-				( sample.solo ? it.only : sample.skip ? it.skip : it )( sample.description, () => {
-					if ( sample.error ) {
-						assert.throws( () => {
-							buble.transform( sample.input, sample.options );
-						}, sample.error );
-					}
-
-					else {
-						equal( buble.transform( sample.input, sample.options  ).code, sample.output );
-					}
+const subsetIndex = process.argv.indexOf('--subset');
+const subset = ~subsetIndex
+	? process.argv[subsetIndex + 1].split(',').map(file => `${file}.js`)
+	: null;
+const subsetFilter = subset ? file => ~subset.indexOf(file) : () => true;
+
+describe('buble', () => {
+	fs
+		.readdirSync('test/samples')
+		.filter(subsetFilter)
+		.forEach(file => {
+			if (!/\.js$/.test(file)) return; // avoid vim .js.swp files
+			var samples = require('./samples/' + file);
+
+			describe(path.basename(file), () => {
+				samples.forEach(sample => {
+					(sample.solo ? it.only : sample.skip ? it.skip : it)(
+						sample.description,
+						() => {
+							if (sample.error) {
+								assert.throws(() => {
+									buble.transform(sample.input, sample.options);
+								}, sample.error);
+							} else {
+								equal(
+									buble.transform(sample.input, sample.options).code,
+									sample.output
+								);
+							}
+						}
+					);
 				});
 			});
 		});
-	});
 
-	if ( subset ) return;
+	if (subset) return;
 
-	describe( 'cli', () => {
-		fs.readdirSync( 'test/cli' ).forEach( dir => {
-			if ( dir[0] === '.' ) return; // .DS_Store
+	describe('cli', () => {
+		fs.readdirSync('test/cli').forEach(dir => {
+			if (dir[0] === '.') return; // .DS_Store
 
-			it( dir, done => {
-				dir = path.resolve( 'test/cli', dir );
-				rimraf.sync( path.resolve( dir, 'actual' ) );
-				fs.mkdirSync( path.resolve( dir, 'actual' ) );
+			it(dir, done => {
+				dir = path.resolve('test/cli', dir);
+				rimraf.sync(path.resolve(dir, 'actual'));
+				fs.mkdirSync(path.resolve(dir, 'actual'));
 
 				var binFile = path.resolve(__dirname, '../bin/buble');
-				var commandFile = path.resolve( dir, 'command.sh' );
-
-				var command = fs.readFileSync( commandFile, 'utf-8' )
-					.replace( 'buble', 'node "' + binFile + '"' );
-				child_process.exec( command, {
-					cwd: dir
-				}, ( err, stdout, stderr ) => {
-					if ( err ) return done( err );
-
-					if ( stdout ) console.log( stdout );
-					if ( stderr ) console.error( stderr );
-
-					function catalogue ( subdir ) {
-						subdir = path.resolve( dir, subdir );
-
-						return glob.sync( '**/*.js?(.map)', { cwd: subdir })
-							.sort()
-							.map( name => {
-								var contents = fs.readFileSync( path.resolve( subdir, name ), 'utf-8' )
-									.replace(/\r\n/g, '\n')
-									.trim();
-
-								if ( path.extname( name ) === '.map' ) {
-									contents = JSON.parse( contents );
-								}
-
-								return { name, contents };
-							});
+				var commandFile = path.resolve(dir, 'command.sh');
+
+				var command = fs
+					.readFileSync(commandFile, 'utf-8')
+					.replace('buble', 'node "' + binFile + '"');
+				child_process.exec(
+					command,
+					{
+						cwd: dir
+					},
+					(err, stdout, stderr) => {
+						if (err) return done(err);
+
+						if (stdout) console.log(stdout);
+						if (stderr) console.error(stderr);
+
+						function catalogue(subdir) {
+							subdir = path.resolve(dir, subdir);
+
+							return glob
+								.sync('**/*.js?(.map)', { cwd: subdir })
+								.sort()
+								.map(name => {
+									var contents = fs
+										.readFileSync(path.resolve(subdir, name), 'utf-8')
+										.replace(/\r\n/g, '\n')
+										.trim();
+
+									if (path.extname(name) === '.map') {
+										contents = JSON.parse(contents);
+									}
+
+									return { name, contents };
+								});
+						}
+
+						var expected = catalogue('expected');
+						var actual = catalogue('actual');
+
+						try {
+							assert.deepEqual(actual, expected);
+							done();
+						} catch (err) {
+							done(err);
+						}
 					}
-
-					var expected = catalogue( 'expected' );
-					var actual = catalogue( 'actual' );
-
-					try {
-						assert.deepEqual( actual, expected );
-						done();
-					} catch ( err ) {
-						done( err );
-					}
-				});
+				);
 			});
 		});
 	});
 
-	describe( 'errors', () => {
-		it( 'reports the location of a syntax error', () => {
+	describe('errors', () => {
+		it('reports the location of a syntax error', () => {
 			var source = `var 42 = nope;`;
 
 			try {
-				buble.transform( source );
-			} catch ( err ) {
-				assert.equal( err.name, 'SyntaxError' );
-				assert.deepEqual( err.loc, { line: 1, column: 4 });
-				assert.equal( err.message, 'Unexpected token (1:4)' );
-				assert.equal( err.snippet, `1 : var 42 = nope;\n        ^` );
-				assert.equal( err.toString(), `SyntaxError: Unexpected token (1:4)\n1 : var 42 = nope;\n        ^` );
+				buble.transform(source);
+			} catch (err) {
+				assert.equal(err.name, 'SyntaxError');
+				assert.deepEqual(err.loc, { line: 1, column: 4 });
+				assert.equal(err.message, 'Unexpected token (1:4)');
+				assert.equal(err.snippet, `1 : var 42 = nope;\n        ^`);
+				assert.equal(
+					err.toString(),
+					`SyntaxError: Unexpected token (1:4)\n1 : var 42 = nope;\n        ^`
+				);
 			}
 		});
 
-		it( 'reports the location of a compile error', () => {
+		it('reports the location of a compile error', () => {
 			var source = `const x = 1; x++;`;
 
 			try {
-				buble.transform( source );
-			} catch ( err ) {
-				assert.equal( err.name, 'CompileError' );
-				assert.equal( err.loc.line, 1 );
-				assert.equal( err.loc.column, 13 );
-				assert.equal( err.message, 'x is read-only (1:13)' );
-				assert.equal( err.snippet, `1 : const x = 1; x++;\n                 ^^^` );
-				assert.equal( err.toString(), `CompileError: x is read-only (1:13)\n1 : const x = 1; x++;\n                 ^^^` );
+				buble.transform(source);
+			} catch (err) {
+				assert.equal(err.name, 'CompileError');
+				assert.equal(err.loc.line, 1);
+				assert.equal(err.loc.column, 13);
+				assert.equal(err.message, 'x is read-only (1:13)');
+				assert.equal(
+					err.snippet,
+					`1 : const x = 1; x++;\n                 ^^^`
+				);
+				assert.equal(
+					err.toString(),
+					`CompileError: x is read-only (1:13)\n1 : const x = 1; x++;\n                 ^^^`
+				);
 			}
 		});
 	});
 
-	describe( 'target', () => {
-		it( 'determines necessary transforms for a target environment', () => {
+	describe('target', () => {
+		it('determines necessary transforms for a target environment', () => {
 			var transforms = buble.target({ chrome: 49 });
 
-			assert.ok( transforms.moduleImport );
-			assert.ok( !transforms.arrow );
+			assert.ok(transforms.moduleImport);
+			assert.ok(!transforms.arrow);
 		});
 
-		it( 'returns lowest common denominator support info', () => {
+		it('returns lowest common denominator support info', () => {
 			var transforms = buble.target({ chrome: 49, node: 5 });
 
-			assert.ok( transforms.defaultParameter );
-			assert.ok( !transforms.arrow );
+			assert.ok(transforms.defaultParameter);
+			assert.ok(!transforms.arrow);
 		});
 
-		it( 'only applies necessary transforms', () => {
+		it('only applies necessary transforms', () => {
 			var source = `
 				const power = ( base, exponent = 2 ) => Math.pow( base, exponent );`;
 
-			var result = buble.transform( source, {
+			var result = buble.transform(source, {
 				target: { chrome: 49, node: 5 }
 			}).code;
 
-			assert.equal( result, `
+			assert.equal(
+				result,
+				`
 				const power = ( base, exponent ) => {
 					if ( exponent === void 0 ) exponent = 2;
 
 					return Math.pow( base, exponent );
-				};` );
+				};`
+			);
 		});
 	});
 
-	describe( 'sourcemaps', () => {
-		it( 'generates a valid sourcemap', () => {
-			var map = buble.transform( '' ).map;
-			assert.equal( map.version, 3 );
+	describe('sourcemaps', () => {
+		it('generates a valid sourcemap', () => {
+			var map = buble.transform('').map;
+			assert.equal(map.version, 3);
 		});
 
-		it( 'uses provided file and source', () => {
-			var map = buble.transform( '', {
+		it('uses provided file and source', () => {
+			var map = buble.transform('', {
 				file: 'output.js',
 				source: 'input.js'
 			}).map;
 
-			assert.equal( map.file, 'output.js' );
-			assert.deepEqual( map.sources, [ 'input.js' ] );
+			assert.equal(map.file, 'output.js');
+			assert.deepEqual(map.sources, ['input.js']);
 		});
 
-		it( 'includes content by default', () => {
+		it('includes content by default', () => {
 			var source = `let { x, y } = foo();`;
-			var map = buble.transform( source ).map;
+			var map = buble.transform(source).map;
 
-			assert.deepEqual( map.sourcesContent, [ source ] );
+			assert.deepEqual(map.sourcesContent, [source]);
 		});
 
-		it( 'excludes content if requested', () => {
+		it('excludes content if requested', () => {
 			var source = `let { x, y } = foo();`;
-			var map = buble.transform( source, {
+			var map = buble.transform(source, {
 				includeContent: false
 			}).map;
 
-			assert.deepEqual( map.sourcesContent, [ null ] );
+			assert.deepEqual(map.sourcesContent, [null]);
 		});
 
-		it( 'locates original content', () => {
+		it('locates original content', () => {
 			var source = `const add = ( a, b ) => a + b;`;
-			var result = buble.transform( source, {
+			var result = buble.transform(source, {
 				file: 'output.js',
 				source: 'input.js'
 			});
 
-			var smc = new SourceMapConsumer( result.map );
+			var smc = new SourceMapConsumer(result.map);
 
-			var location = getLocation( result.code, 'add' );
-			var expected = getLocation( source, 'add' );
+			var location = getLocation(result.code, 'add');
+			var expected = getLocation(source, 'add');
 
-			var actual = smc.originalPositionFor( location );
+			var actual = smc.originalPositionFor(location);
 
-			assert.deepEqual( actual, {
+			assert.deepEqual(actual, {
 				line: expected.line,
 				column: expected.column,
 				source: 'input.js',
 				name: null
 			});
 
-			location = getLocation( result.code, 'a +' );
-			expected = getLocation( source, 'a +' );
+			location = getLocation(result.code, 'a +');
+			expected = getLocation(source, 'a +');
 
-			actual = smc.originalPositionFor( location );
+			actual = smc.originalPositionFor(location);
 
-			assert.deepEqual( actual, {
+			assert.deepEqual(actual, {
 				line: expected.line,
 				column: expected.column,
 				source: 'input.js',
@@ -240,36 +268,36 @@ describe( 'buble', () => {
 			});
 		});
 
-		it( 'recovers names', () => {
+		it('recovers names', () => {
 			var source = `
 				const foo = 1;
 				if ( x ) {
 					const foo = 2;
 				}`;
 
-			var result = buble.transform( source, {
+			var result = buble.transform(source, {
 				file: 'output.js',
 				source: 'input.js'
 			});
-			var smc = new SourceMapConsumer( result.map );
+			var smc = new SourceMapConsumer(result.map);
 
-			var location = getLocation( result.code, 'var' );
-			var actual = smc.originalPositionFor( location );
+			var location = getLocation(result.code, 'var');
+			var actual = smc.originalPositionFor(location);
 
-			assert.equal( actual.name, 'const' );
+			assert.equal(actual.name, 'const');
 
-			location = getLocation( result.code, 'var', location.char + 1 );
-			actual = smc.originalPositionFor( location );
+			location = getLocation(result.code, 'var', location.char + 1);
+			actual = smc.originalPositionFor(location);
 
-			assert.equal( actual.name, 'const' );
+			assert.equal(actual.name, 'const');
 
-			location = getLocation( result.code, 'foo$1', location.char + 1 );
-			actual = smc.originalPositionFor( location );
+			location = getLocation(result.code, 'foo$1', location.char + 1);
+			actual = smc.originalPositionFor(location);
 
-			assert.equal( actual.name, 'foo' );
+			assert.equal(actual.name, 'foo');
 		});
 
-		it( 'handles moved content', () => {
+		it('handles moved content', () => {
 			var source = `
 				for ( let i = 0; i < 10; i += 1 ) {
 					const square = i * i;
@@ -278,30 +306,30 @@ describe( 'buble', () => {
 					}, i * 100 );
 				}`;
 
-			var result = buble.transform( source, {
+			var result = buble.transform(source, {
 				file: 'output.js',
 				source: 'input.js'
 			});
-			var smc = new SourceMapConsumer( result.map );
+			var smc = new SourceMapConsumer(result.map);
 
-			var location = getLocation( result.code, 'i < 10' );
-			var expected = getLocation( source, 'i < 10' );
+			var location = getLocation(result.code, 'i < 10');
+			var expected = getLocation(source, 'i < 10');
 
-			var actual = smc.originalPositionFor( location );
+			var actual = smc.originalPositionFor(location);
 
-			assert.deepEqual( actual, {
+			assert.deepEqual(actual, {
 				line: expected.line,
 				column: expected.column,
 				source: 'input.js',
 				name: null
 			});
 
-			location = getLocation( result.code, 'setTimeout' );
-			expected = getLocation( source, 'setTimeout' );
+			location = getLocation(result.code, 'setTimeout');
+			expected = getLocation(source, 'setTimeout');
 
-			actual = smc.originalPositionFor( location );
+			actual = smc.originalPositionFor(location);
 
-			assert.deepEqual( actual, {
+			assert.deepEqual(actual, {
 				line: expected.line,
 				column: expected.column,
 				source: 'input.js',
diff --git a/test/utils/getLocation.js b/test/utils/getLocation.js
index 78742cc..bbf6f89 100644
--- a/test/utils/getLocation.js
+++ b/test/utils/getLocation.js
@@ -1,24 +1,24 @@
-module.exports = function getLocation ( source, search, start ) {
-	if ( typeof search === 'string' ) {
-		search = source.indexOf( search, start );
+module.exports = function getLocation(source, search, start) {
+	if (typeof search === 'string') {
+		search = source.indexOf(search, start);
 	}
 
-	var lines = source.split( '\n' );
+	var lines = source.split('\n');
 	var len = lines.length;
 
 	var lineStart = 0;
 	var i;
 
-	for ( i = 0; i < len; i += 1 ) {
+	for (i = 0; i < len; i += 1) {
 		var line = lines[i];
-		var lineEnd =  lineStart + line.length + 1; // +1 for newline
+		var lineEnd = lineStart + line.length + 1; // +1 for newline
 
-		if ( lineEnd > search ) {
+		if (lineEnd > search) {
 			return { line: i + 1, column: search - lineStart, char: i };
 		}
 
 		lineStart = lineEnd;
 	}
 
-	throw new Error( 'Could not determine location of character' );
-}
+	throw new Error('Could not determine location of character');
+};

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



More information about the Pkg-javascript-commits mailing list