This commit is contained in:
chrosey
2017-09-13 07:52:34 +02:00
parent a1f16c37f4
commit 2340b0226b
24621 changed files with 2912161 additions and 149 deletions
+381
View File
@@ -0,0 +1,381 @@
# buble changelog
## 0.12.5
* Prevent reserved words being used as identifiers ([#86](https://gitlab.com/Rich-Harris/buble/issues/86))
* Use correct `this` when transpiling `super` inside arrow function ([#89](https://gitlab.com/Rich-Harris/buble/issues/89))
* Handle body-less `for-of` loops ([#80](https://gitlab.com/Rich-Harris/buble/issues/80))
## 0.12.4
* Allow references to precede declaration (inside function) in block scoping ([#87](https://gitlab.com/Rich-Harris/buble/issues/87))
## 0.12.3
* Argh, npm
## 0.12.2
* Files missing in 0.12.1 (???)
## 0.12.1
* Don't require space before parens of shorthand computed method ([#82](https://gitlab.com/Rich-Harris/buble/issues/82))
* Allow string keys for shorthand methods ([#82](https://gitlab.com/Rich-Harris/buble/issues/82))
## 0.12.0
* Support `u` flag in regular expression literals ([!62](https://gitlab.com/Rich-Harris/buble/merge_requests/62))
* Save `buble/register` transformations to `$HOME/.buble-cache` ([!63](https://gitlab.com/Rich-Harris/buble/merge_requests/63))
## 0.11.6
* Allow shorthand methods with computed names ([#78](https://gitlab.com/Rich-Harris/buble/issues/78))
* Include code snippet in `error.toString()` ([#79](https://gitlab.com/Rich-Harris/buble/issues/79))
## 0.11.5
* Preserve whitespace between JSX tags on single line ([#65](https://gitlab.com/Rich-Harris/buble/issues/65))
## 0.11.4
* Allow computed class methods, except accessors ([!56](https://gitlab.com/Rich-Harris/buble/merge_requests/56))
* Compound destructuring ([!58](https://gitlab.com/Rich-Harris/buble/merge_requests/58))
## 0.11.3
* Ensure inserted statements follow use strict pragma ([#72](https://gitlab.com/Rich-Harris/buble/issues/72))
## 0.11.2
* Ensure closing parenthesis is in correct place when transpiling inline computed property object expressions ([#73](https://gitlab.com/Rich-Harris/buble/issues/73))
## 0.11.1
* Fix computed property followed by non-computed property in inline expression
## 0.11.0
* Computed properties ([#67](https://gitlab.com/Rich-Harris/buble/issues/67))
* Allow `super(...)` to use rest arguments ([#69](https://gitlab.com/Rich-Harris/buble/issues/69))
## 0.10.7
* Allow customisation of `Object.assign` (used in object spread) ([!51](https://gitlab.com/Rich-Harris/buble/merge_requests/51))
## 0.10.6
* Handle sparse arrays ([#62](https://gitlab.com/Rich-Harris/buble/issues/62))
* Handle spread expressions in JSX ([#64](https://gitlab.com/Rich-Harris/buble/issues/64))
## 0.10.5
* Create intermediate directories when transforming via CLI ([#63](https://gitlab.com/Rich-Harris/buble/issues/63))
* Update README ([#57](https://gitlab.com/Rich-Harris/buble/issues/57))
## 0.10.4
* Support spread operator in object literals ([!45](https://gitlab.com/Rich-Harris/buble/merge_requests/45)) and JSX elements ([!46](https://gitlab.com/Rich-Harris/buble/merge_requests/46))
## 0.10.3
* Disable intelligent destructuring, temporarily ([#53](https://gitlab.com/Rich-Harris/buble/issues/53))
* Fix whitespace in JSX literals ([!39](https://gitlab.com/Rich-Harris/buble/merge_requests/39))
* Add `: true` to value-less JSX attributes ([!40](https://gitlab.com/Rich-Harris/buble/merge_requests/40))
* Quote invalid attribute names ([!41](https://gitlab.com/Rich-Harris/buble/merge_requests/41))
## 0.10.2
* Don't add closing quote to JSX component without attributes ([#58](https://gitlab.com/Rich-Harris/buble/issues/58))
## 0.10.1
* Fix handling of literals inside JSX
## 0.10.0
* Basic JSX support
## 0.9.3
* Better spread operator support, including with `arguments` ([#40](https://gitlab.com/Rich-Harris/buble/issues/40))
* Fix indentation of inserted statements in class constructors ([#39](https://gitlab.com/Rich-Harris/buble/issues/39))
## 0.9.2
* Allow class to have accessors and no constructor ([#48](https://gitlab.com/Rich-Harris/buble/issues/48))
* Fix help message in CLI
## 0.9.1
* Prevent confusion over `Literal` node keys
## 0.9.0
* More complete and robust destructuring support ([#37](https://gitlab.com/Rich-Harris/buble/issues/37), [#43](https://gitlab.com/Rich-Harris/buble/issues/43))
* Correct `this`/`arguments` references inside for-of loop
## 0.8.5
* Allow destructured parameter to have default ([#43](https://gitlab.com/Rich-Harris/buble/issues/43))
* Allow `continue`/`break` statements inside a for-of loop
## 0.8.4
* Allow class body to follow ID/superclass without whitespace ([#46](https://gitlab.com/Rich-Harris/buble/issues/46))
## 0.8.3
* Performance enhancements ([!23](https://gitlab.com/Rich-Harris/buble/merge_requests/23))
## 0.8.2
* More robust version of ([!22](https://gitlab.com/Rich-Harris/buble/merge_requests/22))
## 0.8.1
* Fix `export default class A extends B` (broken in 0.8.0) ([!22](https://gitlab.com/Rich-Harris/buble/merge_requests/22))
## 0.8.0
* Subclasses inherit static methods ([#33](https://gitlab.com/Rich-Harris/buble/issues/33))
* Performance enhancements ([!21](https://gitlab.com/Rich-Harris/buble/merge_requests/21))
## 0.7.1
* Prevent omission of closing paren in template string ([#42](https://gitlab.com/Rich-Harris/buble/issues/42))
* Separate variable declarations for each name in destructured declaration ([#18](https://gitlab.com/Rich-Harris/buble/merge_requests/18))
## 0.7.0
* Allow arrow functions to be used as default parameter values ([#36](https://gitlab.com/Rich-Harris/buble/issues/36))
## 0.6.7
* Support `static get` and `set` in classes ([#34](https://gitlab.com/Rich-Harris/buble/issues/34))
* Support spread operator in expression method call ([!14](https://gitlab.com/Rich-Harris/buble/merge_requests/14))
* Fix `for-of` loops with no space after opening paren ([#35](https://gitlab.com/Rich-Harris/buble/issues/35))
## 0.6.6
* Fix another subclass `super()` bug ([#32](https://gitlab.com/Rich-Harris/buble/issues/32))
## 0.6.5
* Fix `super()` call in subclass expression ([#32](https://gitlab.com/Rich-Harris/buble/issues/32))
* Less defensive template string parenthesising ([!9](https://gitlab.com/Rich-Harris/buble/merge_requests/9))
## 0.6.4
* Add Node 6 to support matrix
## 0.6.3
* Handle empty template strings ([#28](https://gitlab.com/Rich-Harris/buble/issues/28))
## 0.6.2
* Handle body-less do-while blocks ([#27](https://gitlab.com/Rich-Harris/buble/issues/27))
## 0.6.1
* Always remember to close parens in template strings
## 0.6.0
* Strip unnecessary empty strings from template literals
* Intelligent destructuring for object patterns in parameters ([#17](https://gitlab.com/Rich-Harris/buble/issues/17))
## 0.5.8
* Fix exponentiation assignment operator edge case
## 0.5.7
* Exponentiation operator support ([#24](https://gitlab.com/Rich-Harris/buble/issues/24))
* More informative error messages for for-of and tagged template strings
## 0.5.6
* Add `dangerousTaggedTemplateString` ([!2](https://gitlab.com/Rich-Harris/buble/merge_requests/2)) and `dangerousForOf` ([!3](https://gitlab.com/Rich-Harris/buble/merge_requests/3)) transforms
* Prevent deindentation causing errors with removed whitespace in class methods
* Use correct identifier with default destructured function parameters ([#23](https://gitlab.com/Rich-Harris/buble/issues/23))
## 0.5.5
* Ensure `return` is in correct place when creating bodies for arrow functions ([#21](https://gitlab.com/Rich-Harris/buble/issues/21))
* Prevent deindentation of class methods causing breakage with destructuring statements ([#22](https://gitlab.com/Rich-Harris/buble/issues/22))
## 0.5.4
* Install missing `chalk` dependency
* Informative error messages when `buble/register` fails
## 0.5.3
* Add `register.js` to package. Yes I'm an idiot
## 0.5.2
* Add `buble/register` for use with e.g. Mocha
## 0.5.1
* Remove unused dependency
## 0.5.0
* Support `--target`, `--yes` and `--no` in CLI
* Compile entire directory of files via CLI
* Sourcemap support in CLI
* All transforms can be disabled (or errors suppressed) with the `transforms` option (or `--yes` and `--no`, in the CLI)
* `import` and `export` will throw an error unless `--no modules` transform is specified
* Fix bug with destructuring
* Fix bug with block scoping and loops
## 0.4.24
* Throw if `let`/`const` is redeclared, or `var` is redeclared with a `let`/`const` (0.4.22 regression)
## 0.4.23
* Add `buble.VERSION`
* Tidy up build process (don't bundle Acorn incorrectly)
## 0.4.22
* Allow double `var` declarations (only throw if `let` or `const` is redeclared)
## 0.4.21
* Add `find` and `findIndex` helpers for 0.12 support
## 0.4.20
* Bump to resolve unknown npm issue
## 0.4.19
* Fix block scoping bug with for loops that don't need to be rewritten as functions
## 0.4.18
* Fix break-inside-switch bug
## 0.4.17
* Support `for...in` loops and block scoping
## 0.4.16
* Add `ie` and `edge` to support matrix
## 0.4.15
* Rewrite reserved properties if specified ([#9](https://gitlab.com/Rich-Harris/buble/issues/9))
## 0.4.14
* Allow classes to extend expressions ([#15](https://gitlab.com/Rich-Harris/buble/issues/15))
* Experimental (partially implemented) API for disabling transforms based on target environment or custom requirements
## 0.4.13
* Fix return statement bug
## 0.4.12
* More complete and robust transpilation of loops that need to be rewritten as functions to simulate block scoping ([#11](https://gitlab.com/Rich-Harris/buble/issues/11), [#12](https://gitlab.com/Rich-Harris/buble/issues/12), [#13](https://gitlab.com/Rich-Harris/buble/issues/13))
## 0.4.11
* Remove source-map-support from CLI (only useful during development)
## 0.4.10
* Basic support for spread operator
## 0.4.9
* Support getters and setters on subclasses
* Disallow unsupported features e.g. generators
## 0.4.8
* Support getters and setters on classes
* Allow identifiers to be renamed in block-scoped destructuring ([#8](https://gitlab.com/Rich-Harris/buble/issues/8))
* Transpile body-less arrow functions correctly ([#9](https://gitlab.com/Rich-Harris/buble/issues/4))
## 0.4.7
* Add browser version
## 0.4.6
* Report locations of parse/compile errors ([#4](https://gitlab.com/Rich-Harris/buble/issues/4))
## 0.4.5
* Sourcemap support
## 0.4.4
* Support for class expressions
* More robust deconflicting
* Various bugfixes
## 0.4.3
* Handle arbitrary whitespace inside template literals
## 0.4.2
* Fix bug-inducing typo
## 0.4.1
* Rest parameters
## 0.4.0
* Self-hosting!
## 0.3.4
* Class inheritance
## 0.3.3
* Handle quote marks in template literals
## 0.3.2
* Handle empty `class` declarations
## 0.3.1
* Add `bin` to package
## 0.3.0
* (Very) basic CLI
* Handle `export default class ...`
## 0.2.2
* Initialise children of Property nodes
* Prevent false positives with reference detection
## 0.2.1
* Add missing files
## 0.2.0
* Support for a bunch more ES2015 features
## 0.1.0
* First (experimental) release
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env node
var minimist = require( 'minimist' );
var command = minimist( process.argv.slice( 2 ), {
alias: {
// Short options
h: 'help',
i: 'input',
m: 'sourcemap',
o: 'output',
v: 'version',
t: 'target',
y: 'yes',
n: 'no'
}
});
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 );
}
+46
View File
@@ -0,0 +1,46 @@
var chalk = require( 'chalk' );
var handlers = {
MISSING_INPUT_OPTION: function () {
console.error( chalk.red( '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_FILE: function () {
console.error( chalk.red( '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' ) );
},
DUPLICATE_IMPORT_OPTIONS: function ( err ) {
console.error( chalk.red( 'use --input, or pass input path as argument not both' ) );
},
BAD_TARGET: function ( err ) {
console.error( chalk.red( 'illegal --target option' ) );
}
};
module.exports = function handleError ( err ) {
var handler;
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.stack ) {
console.error( chalk.grey( err.stack ) );
}
}
console.error( 'Type ' + chalk.cyan( 'buble --help' ) + ' for help, or visit https://buble.surge.sh/guide/' );
process.exit( 1 );
};
+42
View File
@@ -0,0 +1,42 @@
Bublé version <%= version %>
=====================================
Usage: buble [options] <entry file>
Basic options:
-v, --version Show version number
-h, --help Show this help message
-i, --input Input (alternative to <entry file>)
-o, --output <output> Output (if absent, prints to stdout)
-m, --sourcemap Generate sourcemap (`-m inline` for inline map)
-t, --target Select compilation targets
-y, --yes Transforms to always apply (overrides --target)
-n, --no Transforms to always skip (overrides --target)
--jsx Custom JSX pragma
Examples:
# Compile input.js to output.js
buble input.js > output.js
# Compile input.js to output.js, write sourcemap to output.js.map
buble input.js -o output.js -m
# Compile input.js to output.js with inline sourcemap
buble input.js -o output.js -m inline
# Only use transforms necessary for output.js to run in FF43 and Node 5
buble input.js -o output.js -t firefox:43,node:5
# As above, but use arrow function and destructuring transforms
buble input.js -o output.js -t firefox:43,node:5 -y arrow,destructuring
# Compile all the files in src/ to dest/
buble src -o dest
Notes:
* When piping to stdout, only inline sourcemaps are permitted
For more information visit http://buble.surge.sh/guide
+136
View File
@@ -0,0 +1,136 @@
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 ) {
try {
var stats = fs.statSync( from );
if ( stats.isDirectory() ) {
compileDir( from, to, command, options );
} else {
compileFile( from, to, command, options );
}
} catch ( err ) {
handleError( err );
}
}
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.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 );
if ( ext !== '.js' && ext !== '.jsm' && ext !== '.es6' ) return;
if ( to ) to = to.slice( 0, -ext.length ) + '.js';
var source = fs.readFileSync( from, 'utf-8' );
var result = buble.transform( source, {
target: options.target,
transforms: options.transforms,
source: from,
file: to,
jsx: options.jsx
});
write( result, to, command );
}
function write ( result, to, command ) {
if ( command.sourcemap === 'inline' ) {
result.code += EOL + '//# sourceMappingURL=' + result.map.toUrl();
} 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() );
}
if ( to ) {
fs.writeFileSync( to, result.code );
} else {
console.log( result.code ); // eslint-disable-line no-console
}
}
module.exports = function ( command ) {
if ( command._.length > 1 ) {
handleError({ code: 'ONE_AT_A_TIME' });
}
if ( command._.length === 1 ) {
if ( command.input ) {
handleError({ code: 'DUPLICATE_IMPORT_OPTIONS' });
}
command.input = command._[0];
}
var options = {
target: {},
transforms: {},
jsx: command.jsx
};
if ( command.target ) {
if ( !/^(?:(\w+):([\d\.]+),)*(\w+):([\d\.]+)$/.test( command.target ) ) {
handleError({ code: 'BAD_TARGET' });
}
command.target.split( ',' )
.map( function ( target ) {
return target.split( ':' );
})
.forEach( function ( pair ) {
options.target[ pair[0] ] = pair[1];
});
}
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.input ) {
compile( command.input, command.output, command, options );
}
else {
process.stdin.resume();
process.stdin.setEncoding( 'utf8' );
var source = '';
process.stdin.on( 'data', function ( chunk ) {
source += chunk;
});
process.stdin.on( 'end', function () {
var result = buble.transform( source, options );
write( result, null, command );
});
}
};
+13
View File
@@ -0,0 +1,13 @@
var fs = require( 'fs' );
var path = require( 'path' );
module.exports = function () {
fs.readFile( path.join( __dirname, 'help.md' ), function ( err, result ) {
var help;
if ( err ) throw err;
help = result.toString().replace( '<%= version %>', require( '../package.json' ).version );
console.log( '\n' + help + '\n' );
});
};
+14464
View File
File diff suppressed because one or more lines are too long
+1
View File
File diff suppressed because one or more lines are too long
+9865
View File
File diff suppressed because one or more lines are too long
+1
View File
File diff suppressed because one or more lines are too long
+9877
View File
File diff suppressed because one or more lines are too long
+1
View File
File diff suppressed because one or more lines are too long
+8
View File
@@ -0,0 +1,8 @@
language: node_js
node_js:
- "0.8"
- "0.10"
- "0.12"
- "iojs"
before_install:
- npm install -g npm@~1.4.6
+18
View File
@@ -0,0 +1,18 @@
This software is released under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+2
View File
@@ -0,0 +1,2 @@
var argv = require('../')(process.argv.slice(2));
console.dir(argv);
+236
View File
@@ -0,0 +1,236 @@
module.exports = function (args, opts) {
if (!opts) opts = {};
var flags = { bools : {}, strings : {}, unknownFn: null };
if (typeof opts['unknown'] === 'function') {
flags.unknownFn = opts['unknown'];
}
if (typeof opts['boolean'] === 'boolean' && opts['boolean']) {
flags.allBools = true;
} else {
[].concat(opts['boolean']).filter(Boolean).forEach(function (key) {
flags.bools[key] = true;
});
}
var aliases = {};
Object.keys(opts.alias || {}).forEach(function (key) {
aliases[key] = [].concat(opts.alias[key]);
aliases[key].forEach(function (x) {
aliases[x] = [key].concat(aliases[key].filter(function (y) {
return x !== y;
}));
});
});
[].concat(opts.string).filter(Boolean).forEach(function (key) {
flags.strings[key] = true;
if (aliases[key]) {
flags.strings[aliases[key]] = true;
}
});
var defaults = opts['default'] || {};
var argv = { _ : [] };
Object.keys(flags.bools).forEach(function (key) {
setArg(key, defaults[key] === undefined ? false : defaults[key]);
});
var notFlags = [];
if (args.indexOf('--') !== -1) {
notFlags = args.slice(args.indexOf('--')+1);
args = args.slice(0, args.indexOf('--'));
}
function argDefined(key, arg) {
return (flags.allBools && /^--[^=]+$/.test(arg)) ||
flags.strings[key] || flags.bools[key] || aliases[key];
}
function setArg (key, val, arg) {
if (arg && flags.unknownFn && !argDefined(key, arg)) {
if (flags.unknownFn(arg) === false) return;
}
var value = !flags.strings[key] && isNumber(val)
? Number(val) : val
;
setKey(argv, key.split('.'), value);
(aliases[key] || []).forEach(function (x) {
setKey(argv, x.split('.'), value);
});
}
function setKey (obj, keys, value) {
var o = obj;
keys.slice(0,-1).forEach(function (key) {
if (o[key] === undefined) o[key] = {};
o = o[key];
});
var key = keys[keys.length - 1];
if (o[key] === undefined || flags.bools[key] || typeof o[key] === 'boolean') {
o[key] = value;
}
else if (Array.isArray(o[key])) {
o[key].push(value);
}
else {
o[key] = [ o[key], value ];
}
}
function aliasIsBoolean(key) {
return aliases[key].some(function (x) {
return flags.bools[x];
});
}
for (var i = 0; i < args.length; i++) {
var arg = args[i];
if (/^--.+=/.test(arg)) {
// Using [\s\S] instead of . because js doesn't support the
// 'dotall' regex modifier. See:
// http://stackoverflow.com/a/1068308/13216
var m = arg.match(/^--([^=]+)=([\s\S]*)$/);
var key = m[1];
var value = m[2];
if (flags.bools[key]) {
value = value !== 'false';
}
setArg(key, value, arg);
}
else if (/^--no-.+/.test(arg)) {
var key = arg.match(/^--no-(.+)/)[1];
setArg(key, false, arg);
}
else if (/^--.+/.test(arg)) {
var key = arg.match(/^--(.+)/)[1];
var next = args[i + 1];
if (next !== undefined && !/^-/.test(next)
&& !flags.bools[key]
&& !flags.allBools
&& (aliases[key] ? !aliasIsBoolean(key) : true)) {
setArg(key, next, arg);
i++;
}
else if (/^(true|false)$/.test(next)) {
setArg(key, next === 'true', arg);
i++;
}
else {
setArg(key, flags.strings[key] ? '' : true, arg);
}
}
else if (/^-[^-]+/.test(arg)) {
var letters = arg.slice(1,-1).split('');
var broken = false;
for (var j = 0; j < letters.length; j++) {
var next = arg.slice(j+2);
if (next === '-') {
setArg(letters[j], next, arg)
continue;
}
if (/[A-Za-z]/.test(letters[j]) && /=/.test(next)) {
setArg(letters[j], next.split('=')[1], arg);
broken = true;
break;
}
if (/[A-Za-z]/.test(letters[j])
&& /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) {
setArg(letters[j], next, arg);
broken = true;
break;
}
if (letters[j+1] && letters[j+1].match(/\W/)) {
setArg(letters[j], arg.slice(j+2), arg);
broken = true;
break;
}
else {
setArg(letters[j], flags.strings[letters[j]] ? '' : true, arg);
}
}
var key = arg.slice(-1)[0];
if (!broken && key !== '-') {
if (args[i+1] && !/^(-|--)[^-]/.test(args[i+1])
&& !flags.bools[key]
&& (aliases[key] ? !aliasIsBoolean(key) : true)) {
setArg(key, args[i+1], arg);
i++;
}
else if (args[i+1] && /true|false/.test(args[i+1])) {
setArg(key, args[i+1] === 'true', arg);
i++;
}
else {
setArg(key, flags.strings[key] ? '' : true, arg);
}
}
}
else {
if (!flags.unknownFn || flags.unknownFn(arg) !== false) {
argv._.push(
flags.strings['_'] || !isNumber(arg) ? arg : Number(arg)
);
}
if (opts.stopEarly) {
argv._.push.apply(argv._, args.slice(i + 1));
break;
}
}
}
Object.keys(defaults).forEach(function (key) {
if (!hasKey(argv, key.split('.'))) {
setKey(argv, key.split('.'), defaults[key]);
(aliases[key] || []).forEach(function (x) {
setKey(argv, x.split('.'), defaults[key]);
});
}
});
if (opts['--']) {
argv['--'] = new Array();
notFlags.forEach(function(key) {
argv['--'].push(key);
});
}
else {
notFlags.forEach(function(key) {
argv._.push(key);
});
}
return argv;
};
function hasKey (obj, keys) {
var o = obj;
keys.slice(0,-1).forEach(function (key) {
o = (o[key] || {});
});
var key = keys[keys.length - 1];
return key in o;
}
function isNumber (x) {
if (typeof x === 'number') return true;
if (/^0x[0-9a-f]+$/i.test(x)) return true;
return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x);
}
+105
View File
@@ -0,0 +1,105 @@
{
"_args": [
[
{
"raw": "minimist@^1.2.0",
"scope": null,
"escapedName": "minimist",
"name": "minimist",
"rawSpec": "^1.2.0",
"spec": ">=1.2.0 <2.0.0",
"type": "range"
},
"c:\\xampp\\htdocs\\laravel\\node_modules\\buble"
]
],
"_from": "minimist@>=1.2.0 <2.0.0",
"_id": "minimist@1.2.0",
"_inCache": true,
"_location": "/buble/minimist",
"_nodeVersion": "2.4.0",
"_npmUser": {
"name": "substack",
"email": "substack@gmail.com"
},
"_npmVersion": "3.2.2",
"_phantomChildren": {},
"_requested": {
"raw": "minimist@^1.2.0",
"scope": null,
"escapedName": "minimist",
"name": "minimist",
"rawSpec": "^1.2.0",
"spec": ">=1.2.0 <2.0.0",
"type": "range"
},
"_requiredBy": [
"/buble"
],
"_resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"_shasum": "a35008b20f41383eec1fb914f4cd5df79a264284",
"_shrinkwrap": null,
"_spec": "minimist@^1.2.0",
"_where": "c:\\xampp\\htdocs\\laravel\\node_modules\\buble",
"author": {
"name": "James Halliday",
"email": "mail@substack.net",
"url": "http://substack.net"
},
"bugs": {
"url": "https://github.com/substack/minimist/issues"
},
"dependencies": {},
"description": "parse argument options",
"devDependencies": {
"covert": "^1.0.0",
"tap": "~0.4.0",
"tape": "^3.5.0"
},
"directories": {},
"dist": {
"shasum": "a35008b20f41383eec1fb914f4cd5df79a264284",
"tarball": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz"
},
"gitHead": "dc624482fcfec5bc669c68cdb861f00573ed4e64",
"homepage": "https://github.com/substack/minimist",
"keywords": [
"argv",
"getopt",
"parser",
"optimist"
],
"license": "MIT",
"main": "index.js",
"maintainers": [
{
"name": "substack",
"email": "mail@substack.net"
}
],
"name": "minimist",
"optionalDependencies": {},
"readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git://github.com/substack/minimist.git"
},
"scripts": {
"coverage": "covert test/*.js",
"test": "tap test/*.js"
},
"testling": {
"files": "test/*.js",
"browsers": [
"ie/6..latest",
"ff/5",
"firefox/latest",
"chrome/10",
"chrome/latest",
"safari/5.1",
"safari/latest",
"opera/12"
]
},
"version": "1.2.0"
}
+91
View File
@@ -0,0 +1,91 @@
# minimist
parse argument options
This module is the guts of optimist's argument parser without all the
fanciful decoration.
[![browser support](https://ci.testling.com/substack/minimist.png)](http://ci.testling.com/substack/minimist)
[![build status](https://secure.travis-ci.org/substack/minimist.png)](http://travis-ci.org/substack/minimist)
# example
``` js
var argv = require('minimist')(process.argv.slice(2));
console.dir(argv);
```
```
$ node example/parse.js -a beep -b boop
{ _: [], a: 'beep', b: 'boop' }
```
```
$ node example/parse.js -x 3 -y 4 -n5 -abc --beep=boop foo bar baz
{ _: [ 'foo', 'bar', 'baz' ],
x: 3,
y: 4,
n: 5,
a: true,
b: true,
c: true,
beep: 'boop' }
```
# methods
``` js
var parseArgs = require('minimist')
```
## var argv = parseArgs(args, opts={})
Return an argument object `argv` populated with the array arguments from `args`.
`argv._` contains all the arguments that didn't have an option associated with
them.
Numeric-looking arguments will be returned as numbers unless `opts.string` or
`opts.boolean` is set for that argument name.
Any arguments after `'--'` will not be parsed and will end up in `argv._`.
options can be:
* `opts.string` - a string or array of strings argument names to always treat as
strings
* `opts.boolean` - a boolean, string or array of strings to always treat as
booleans. if `true` will treat all double hyphenated arguments without equal signs
as boolean (e.g. affects `--foo`, not `-f` or `--foo=bar`)
* `opts.alias` - an object mapping string names to strings or arrays of string
argument names to use as aliases
* `opts.default` - an object mapping string argument names to default values
* `opts.stopEarly` - when true, populate `argv._` with everything after the
first non-option
* `opts['--']` - when true, populate `argv._` with everything before the `--`
and `argv['--']` with everything after the `--`. Here's an example:
* `opts.unknown` - a function which is invoked with a command line parameter not
defined in the `opts` configuration object. If the function returns `false`, the
unknown option is not added to `argv`.
```
> require('./')('one two three -- four five --six'.split(' '), { '--': true })
{ _: [ 'one', 'two', 'three' ],
'--': [ 'four', 'five', '--six' ] }
```
Note that with `opts['--']` set, parsing for arguments still stops after the
`--`.
# install
With [npm](https://npmjs.org) do:
```
npm install minimist
```
# license
MIT
+32
View File
@@ -0,0 +1,32 @@
var parse = require('../');
var test = require('tape');
test('flag boolean true (default all --args to boolean)', function (t) {
var argv = parse(['moo', '--honk', 'cow'], {
boolean: true
});
t.deepEqual(argv, {
honk: true,
_: ['moo', 'cow']
});
t.deepEqual(typeof argv.honk, 'boolean');
t.end();
});
test('flag boolean true only affects double hyphen arguments without equals signs', function (t) {
var argv = parse(['moo', '--honk', 'cow', '-p', '55', '--tacos=good'], {
boolean: true
});
t.deepEqual(argv, {
honk: true,
tacos: 'good',
p: 55,
_: ['moo', 'cow']
});
t.deepEqual(typeof argv.honk, 'boolean');
t.end();
});
+166
View File
@@ -0,0 +1,166 @@
var parse = require('../');
var test = require('tape');
test('flag boolean default false', function (t) {
var argv = parse(['moo'], {
boolean: ['t', 'verbose'],
default: { verbose: false, t: false }
});
t.deepEqual(argv, {
verbose: false,
t: false,
_: ['moo']
});
t.deepEqual(typeof argv.verbose, 'boolean');
t.deepEqual(typeof argv.t, 'boolean');
t.end();
});
test('boolean groups', function (t) {
var argv = parse([ '-x', '-z', 'one', 'two', 'three' ], {
boolean: ['x','y','z']
});
t.deepEqual(argv, {
x : true,
y : false,
z : true,
_ : [ 'one', 'two', 'three' ]
});
t.deepEqual(typeof argv.x, 'boolean');
t.deepEqual(typeof argv.y, 'boolean');
t.deepEqual(typeof argv.z, 'boolean');
t.end();
});
test('boolean and alias with chainable api', function (t) {
var aliased = [ '-h', 'derp' ];
var regular = [ '--herp', 'derp' ];
var opts = {
herp: { alias: 'h', boolean: true }
};
var aliasedArgv = parse(aliased, {
boolean: 'herp',
alias: { h: 'herp' }
});
var propertyArgv = parse(regular, {
boolean: 'herp',
alias: { h: 'herp' }
});
var expected = {
herp: true,
h: true,
'_': [ 'derp' ]
};
t.same(aliasedArgv, expected);
t.same(propertyArgv, expected);
t.end();
});
test('boolean and alias with options hash', function (t) {
var aliased = [ '-h', 'derp' ];
var regular = [ '--herp', 'derp' ];
var opts = {
alias: { 'h': 'herp' },
boolean: 'herp'
};
var aliasedArgv = parse(aliased, opts);
var propertyArgv = parse(regular, opts);
var expected = {
herp: true,
h: true,
'_': [ 'derp' ]
};
t.same(aliasedArgv, expected);
t.same(propertyArgv, expected);
t.end();
});
test('boolean and alias array with options hash', function (t) {
var aliased = [ '-h', 'derp' ];
var regular = [ '--herp', 'derp' ];
var alt = [ '--harp', 'derp' ];
var opts = {
alias: { 'h': ['herp', 'harp'] },
boolean: 'h'
};
var aliasedArgv = parse(aliased, opts);
var propertyArgv = parse(regular, opts);
var altPropertyArgv = parse(alt, opts);
var expected = {
harp: true,
herp: true,
h: true,
'_': [ 'derp' ]
};
t.same(aliasedArgv, expected);
t.same(propertyArgv, expected);
t.same(altPropertyArgv, expected);
t.end();
});
test('boolean and alias using explicit true', function (t) {
var aliased = [ '-h', 'true' ];
var regular = [ '--herp', 'true' ];
var opts = {
alias: { h: 'herp' },
boolean: 'h'
};
var aliasedArgv = parse(aliased, opts);
var propertyArgv = parse(regular, opts);
var expected = {
herp: true,
h: true,
'_': [ ]
};
t.same(aliasedArgv, expected);
t.same(propertyArgv, expected);
t.end();
});
// regression, see https://github.com/substack/node-optimist/issues/71
test('boolean and --x=true', function(t) {
var parsed = parse(['--boool', '--other=true'], {
boolean: 'boool'
});
t.same(parsed.boool, true);
t.same(parsed.other, 'true');
parsed = parse(['--boool', '--other=false'], {
boolean: 'boool'
});
t.same(parsed.boool, true);
t.same(parsed.other, 'false');
t.end();
});
test('boolean --boool=true', function (t) {
var parsed = parse(['--boool=true'], {
default: {
boool: false
},
boolean: ['boool']
});
t.same(parsed.boool, true);
t.end();
});
test('boolean --boool=false', function (t) {
var parsed = parse(['--boool=false'], {
default: {
boool: true
},
boolean: ['boool']
});
t.same(parsed.boool, false);
t.end();
});
+31
View File
@@ -0,0 +1,31 @@
var parse = require('../');
var test = require('tape');
test('-', function (t) {
t.plan(5);
t.deepEqual(parse([ '-n', '-' ]), { n: '-', _: [] });
t.deepEqual(parse([ '-' ]), { _: [ '-' ] });
t.deepEqual(parse([ '-f-' ]), { f: '-', _: [] });
t.deepEqual(
parse([ '-b', '-' ], { boolean: 'b' }),
{ b: true, _: [ '-' ] }
);
t.deepEqual(
parse([ '-s', '-' ], { string: 's' }),
{ s: '-', _: [] }
);
});
test('-a -- b', function (t) {
t.plan(3);
t.deepEqual(parse([ '-a', '--', 'b' ]), { a: true, _: [ 'b' ] });
t.deepEqual(parse([ '--a', '--', 'b' ]), { a: true, _: [ 'b' ] });
t.deepEqual(parse([ '--a', '--', 'b' ]), { a: true, _: [ 'b' ] });
});
test('move arguments after the -- into their own `--` array', function(t) {
t.plan(1);
t.deepEqual(
parse([ '--name', 'John', 'before', '--', 'after' ], { '--': true }),
{ name: 'John', _: [ 'before' ], '--': [ 'after' ] });
});
+35
View File
@@ -0,0 +1,35 @@
var test = require('tape');
var parse = require('../');
test('boolean default true', function (t) {
var argv = parse([], {
boolean: 'sometrue',
default: { sometrue: true }
});
t.equal(argv.sometrue, true);
t.end();
});
test('boolean default false', function (t) {
var argv = parse([], {
boolean: 'somefalse',
default: { somefalse: false }
});
t.equal(argv.somefalse, false);
t.end();
});
test('boolean default to null', function (t) {
var argv = parse([], {
boolean: 'maybe',
default: { maybe: null }
});
t.equal(argv.maybe, null);
var argv = parse(['--maybe'], {
boolean: 'maybe',
default: { maybe: null }
});
t.equal(argv.maybe, true);
t.end();
})
+22
View File
@@ -0,0 +1,22 @@
var parse = require('../');
var test = require('tape');
test('dotted alias', function (t) {
var argv = parse(['--a.b', '22'], {default: {'a.b': 11}, alias: {'a.b': 'aa.bb'}});
t.equal(argv.a.b, 22);
t.equal(argv.aa.bb, 22);
t.end();
});
test('dotted default', function (t) {
var argv = parse('', {default: {'a.b': 11}, alias: {'a.b': 'aa.bb'}});
t.equal(argv.a.b, 11);
t.equal(argv.aa.bb, 11);
t.end();
});
test('dotted default with no alias', function (t) {
var argv = parse('', {default: {'a.b': 11}});
t.equal(argv.a.b, 11);
t.end();
});
+16
View File
@@ -0,0 +1,16 @@
var parse = require('../');
var test = require('tape');
test('short -k=v' , function (t) {
t.plan(1);
var argv = parse([ '-b=123' ]);
t.deepEqual(argv, { b: 123, _: [] });
});
test('multi short -k=v' , function (t) {
t.plan(1);
var argv = parse([ '-a=whatever', '-b=robots' ]);
t.deepEqual(argv, { a: 'whatever', b: 'robots', _: [] });
});
+31
View File
@@ -0,0 +1,31 @@
var test = require('tape');
var parse = require('../');
test('long opts', function (t) {
t.deepEqual(
parse([ '--bool' ]),
{ bool : true, _ : [] },
'long boolean'
);
t.deepEqual(
parse([ '--pow', 'xixxle' ]),
{ pow : 'xixxle', _ : [] },
'long capture sp'
);
t.deepEqual(
parse([ '--pow=xixxle' ]),
{ pow : 'xixxle', _ : [] },
'long capture eq'
);
t.deepEqual(
parse([ '--host', 'localhost', '--port', '555' ]),
{ host : 'localhost', port : 555, _ : [] },
'long captures sp'
);
t.deepEqual(
parse([ '--host=localhost', '--port=555' ]),
{ host : 'localhost', port : 555, _ : [] },
'long captures eq'
);
t.end();
});
+36
View File
@@ -0,0 +1,36 @@
var parse = require('../');
var test = require('tape');
test('nums', function (t) {
var argv = parse([
'-x', '1234',
'-y', '5.67',
'-z', '1e7',
'-w', '10f',
'--hex', '0xdeadbeef',
'789'
]);
t.deepEqual(argv, {
x : 1234,
y : 5.67,
z : 1e7,
w : '10f',
hex : 0xdeadbeef,
_ : [ 789 ]
});
t.deepEqual(typeof argv.x, 'number');
t.deepEqual(typeof argv.y, 'number');
t.deepEqual(typeof argv.z, 'number');
t.deepEqual(typeof argv.w, 'string');
t.deepEqual(typeof argv.hex, 'number');
t.deepEqual(typeof argv._[0], 'number');
t.end();
});
test('already a number', function (t) {
var argv = parse([ '-x', 1234, 789 ]);
t.deepEqual(argv, { x : 1234, _ : [ 789 ] });
t.deepEqual(typeof argv.x, 'number');
t.deepEqual(typeof argv._[0], 'number');
t.end();
});
+197
View File
@@ -0,0 +1,197 @@
var parse = require('../');
var test = require('tape');
test('parse args', function (t) {
t.deepEqual(
parse([ '--no-moo' ]),
{ moo : false, _ : [] },
'no'
);
t.deepEqual(
parse([ '-v', 'a', '-v', 'b', '-v', 'c' ]),
{ v : ['a','b','c'], _ : [] },
'multi'
);
t.end();
});
test('comprehensive', function (t) {
t.deepEqual(
parse([
'--name=meowmers', 'bare', '-cats', 'woo',
'-h', 'awesome', '--multi=quux',
'--key', 'value',
'-b', '--bool', '--no-meep', '--multi=baz',
'--', '--not-a-flag', 'eek'
]),
{
c : true,
a : true,
t : true,
s : 'woo',
h : 'awesome',
b : true,
bool : true,
key : 'value',
multi : [ 'quux', 'baz' ],
meep : false,
name : 'meowmers',
_ : [ 'bare', '--not-a-flag', 'eek' ]
}
);
t.end();
});
test('flag boolean', function (t) {
var argv = parse([ '-t', 'moo' ], { boolean: 't' });
t.deepEqual(argv, { t : true, _ : [ 'moo' ] });
t.deepEqual(typeof argv.t, 'boolean');
t.end();
});
test('flag boolean value', function (t) {
var argv = parse(['--verbose', 'false', 'moo', '-t', 'true'], {
boolean: [ 't', 'verbose' ],
default: { verbose: true }
});
t.deepEqual(argv, {
verbose: false,
t: true,
_: ['moo']
});
t.deepEqual(typeof argv.verbose, 'boolean');
t.deepEqual(typeof argv.t, 'boolean');
t.end();
});
test('newlines in params' , function (t) {
var args = parse([ '-s', "X\nX" ])
t.deepEqual(args, { _ : [], s : "X\nX" });
// reproduce in bash:
// VALUE="new
// line"
// node program.js --s="$VALUE"
args = parse([ "--s=X\nX" ])
t.deepEqual(args, { _ : [], s : "X\nX" });
t.end();
});
test('strings' , function (t) {
var s = parse([ '-s', '0001234' ], { string: 's' }).s;
t.equal(s, '0001234');
t.equal(typeof s, 'string');
var x = parse([ '-x', '56' ], { string: 'x' }).x;
t.equal(x, '56');
t.equal(typeof x, 'string');
t.end();
});
test('stringArgs', function (t) {
var s = parse([ ' ', ' ' ], { string: '_' })._;
t.same(s.length, 2);
t.same(typeof s[0], 'string');
t.same(s[0], ' ');
t.same(typeof s[1], 'string');
t.same(s[1], ' ');
t.end();
});
test('empty strings', function(t) {
var s = parse([ '-s' ], { string: 's' }).s;
t.equal(s, '');
t.equal(typeof s, 'string');
var str = parse([ '--str' ], { string: 'str' }).str;
t.equal(str, '');
t.equal(typeof str, 'string');
var letters = parse([ '-art' ], {
string: [ 'a', 't' ]
});
t.equal(letters.a, '');
t.equal(letters.r, true);
t.equal(letters.t, '');
t.end();
});
test('string and alias', function(t) {
var x = parse([ '--str', '000123' ], {
string: 's',
alias: { s: 'str' }
});
t.equal(x.str, '000123');
t.equal(typeof x.str, 'string');
t.equal(x.s, '000123');
t.equal(typeof x.s, 'string');
var y = parse([ '-s', '000123' ], {
string: 'str',
alias: { str: 's' }
});
t.equal(y.str, '000123');
t.equal(typeof y.str, 'string');
t.equal(y.s, '000123');
t.equal(typeof y.s, 'string');
t.end();
});
test('slashBreak', function (t) {
t.same(
parse([ '-I/foo/bar/baz' ]),
{ I : '/foo/bar/baz', _ : [] }
);
t.same(
parse([ '-xyz/foo/bar/baz' ]),
{ x : true, y : true, z : '/foo/bar/baz', _ : [] }
);
t.end();
});
test('alias', function (t) {
var argv = parse([ '-f', '11', '--zoom', '55' ], {
alias: { z: 'zoom' }
});
t.equal(argv.zoom, 55);
t.equal(argv.z, argv.zoom);
t.equal(argv.f, 11);
t.end();
});
test('multiAlias', function (t) {
var argv = parse([ '-f', '11', '--zoom', '55' ], {
alias: { z: [ 'zm', 'zoom' ] }
});
t.equal(argv.zoom, 55);
t.equal(argv.z, argv.zoom);
t.equal(argv.z, argv.zm);
t.equal(argv.f, 11);
t.end();
});
test('nested dotted objects', function (t) {
var argv = parse([
'--foo.bar', '3', '--foo.baz', '4',
'--foo.quux.quibble', '5', '--foo.quux.o_O',
'--beep.boop'
]);
t.same(argv.foo, {
bar : 3,
baz : 4,
quux : {
quibble : 5,
o_O : true
}
});
t.same(argv.beep, { boop : true });
t.end();
});
+9
View File
@@ -0,0 +1,9 @@
var parse = require('../');
var test = require('tape');
test('parse with modifier functions' , function (t) {
t.plan(1);
var argv = parse([ '-b', '123' ], { boolean: 'b' });
t.deepEqual(argv, { b: true, _: [123] });
});
+67
View File
@@ -0,0 +1,67 @@
var parse = require('../');
var test = require('tape');
test('numeric short args', function (t) {
t.plan(2);
t.deepEqual(parse([ '-n123' ]), { n: 123, _: [] });
t.deepEqual(
parse([ '-123', '456' ]),
{ 1: true, 2: true, 3: 456, _: [] }
);
});
test('short', function (t) {
t.deepEqual(
parse([ '-b' ]),
{ b : true, _ : [] },
'short boolean'
);
t.deepEqual(
parse([ 'foo', 'bar', 'baz' ]),
{ _ : [ 'foo', 'bar', 'baz' ] },
'bare'
);
t.deepEqual(
parse([ '-cats' ]),
{ c : true, a : true, t : true, s : true, _ : [] },
'group'
);
t.deepEqual(
parse([ '-cats', 'meow' ]),
{ c : true, a : true, t : true, s : 'meow', _ : [] },
'short group next'
);
t.deepEqual(
parse([ '-h', 'localhost' ]),
{ h : 'localhost', _ : [] },
'short capture'
);
t.deepEqual(
parse([ '-h', 'localhost', '-p', '555' ]),
{ h : 'localhost', p : 555, _ : [] },
'short captures'
);
t.end();
});
test('mixed short bool and capture', function (t) {
t.same(
parse([ '-h', 'localhost', '-fp', '555', 'script.js' ]),
{
f : true, p : 555, h : 'localhost',
_ : [ 'script.js' ]
}
);
t.end();
});
test('short and long', function (t) {
t.deepEqual(
parse([ '-h', 'localhost', '-fp', '555', 'script.js' ]),
{
f : true, p : 555, h : 'localhost',
_ : [ 'script.js' ]
}
);
t.end();
});
+15
View File
@@ -0,0 +1,15 @@
var parse = require('../');
var test = require('tape');
test('stops parsing on the first non-option when stopEarly is set', function (t) {
var argv = parse(['--aaa', 'bbb', 'ccc', '--ddd'], {
stopEarly: true
});
t.deepEqual(argv, {
aaa: 'bbb',
_: ['ccc', '--ddd']
});
t.end();
});
+102
View File
@@ -0,0 +1,102 @@
var parse = require('../');
var test = require('tape');
test('boolean and alias is not unknown', function (t) {
var unknown = [];
function unknownFn(arg) {
unknown.push(arg);
return false;
}
var aliased = [ '-h', 'true', '--derp', 'true' ];
var regular = [ '--herp', 'true', '-d', 'true' ];
var opts = {
alias: { h: 'herp' },
boolean: 'h',
unknown: unknownFn
};
var aliasedArgv = parse(aliased, opts);
var propertyArgv = parse(regular, opts);
t.same(unknown, ['--derp', '-d']);
t.end();
});
test('flag boolean true any double hyphen argument is not unknown', function (t) {
var unknown = [];
function unknownFn(arg) {
unknown.push(arg);
return false;
}
var argv = parse(['--honk', '--tacos=good', 'cow', '-p', '55'], {
boolean: true,
unknown: unknownFn
});
t.same(unknown, ['--tacos=good', 'cow', '-p']);
t.same(argv, {
honk: true,
_: []
});
t.end();
});
test('string and alias is not unknown', function (t) {
var unknown = [];
function unknownFn(arg) {
unknown.push(arg);
return false;
}
var aliased = [ '-h', 'hello', '--derp', 'goodbye' ];
var regular = [ '--herp', 'hello', '-d', 'moon' ];
var opts = {
alias: { h: 'herp' },
string: 'h',
unknown: unknownFn
};
var aliasedArgv = parse(aliased, opts);
var propertyArgv = parse(regular, opts);
t.same(unknown, ['--derp', '-d']);
t.end();
});
test('default and alias is not unknown', function (t) {
var unknown = [];
function unknownFn(arg) {
unknown.push(arg);
return false;
}
var aliased = [ '-h', 'hello' ];
var regular = [ '--herp', 'hello' ];
var opts = {
default: { 'h': 'bar' },
alias: { 'h': 'herp' },
unknown: unknownFn
};
var aliasedArgv = parse(aliased, opts);
var propertyArgv = parse(regular, opts);
t.same(unknown, []);
t.end();
unknownFn(); // exercise fn for 100% coverage
});
test('value following -- is not unknown', function (t) {
var unknown = [];
function unknownFn(arg) {
unknown.push(arg);
return false;
}
var aliased = [ '--bad', '--', 'good', 'arg' ];
var opts = {
'--': true,
unknown: unknownFn
};
var argv = parse(aliased, opts);
t.same(unknown, ['--bad']);
t.same(argv, {
'--': ['good', 'arg'],
'_': []
})
t.end();
});
+8
View File
@@ -0,0 +1,8 @@
var parse = require('../');
var test = require('tape');
test('whitespace should be whitespace' , function (t) {
t.plan(1);
var x = parse([ '-x', '\t' ]).x;
t.equal(x, '\t');
});
+135
View File
@@ -0,0 +1,135 @@
{
"_args": [
[
{
"raw": "buble@^0.12.0",
"scope": null,
"escapedName": "buble",
"name": "buble",
"rawSpec": "^0.12.0",
"spec": ">=0.12.0 <0.13.0",
"type": "range"
},
"c:\\xampp\\htdocs\\laravel\\node_modules\\bubleify"
]
],
"_from": "buble@>=0.12.0 <0.13.0",
"_id": "buble@0.12.5",
"_inCache": true,
"_location": "/buble",
"_nodeVersion": "6.2.0",
"_npmOperationalInternal": {
"host": "packages-12-west.internal.npmjs.com",
"tmp": "tmp/buble-0.12.5.tgz_1468122059663_0.31926094298250973"
},
"_npmUser": {
"name": "rich_harris",
"email": "richard.a.harris@gmail.com"
},
"_npmVersion": "3.8.9",
"_phantomChildren": {},
"_requested": {
"raw": "buble@^0.12.0",
"scope": null,
"escapedName": "buble",
"name": "buble",
"rawSpec": "^0.12.0",
"spec": ">=0.12.0 <0.13.0",
"type": "range"
},
"_requiredBy": [
"/bubleify"
],
"_resolved": "https://registry.npmjs.org/buble/-/buble-0.12.5.tgz",
"_shasum": "c66ffe92f9f4a3c65d3256079b711e2bd0bc5013",
"_shrinkwrap": null,
"_spec": "buble@^0.12.0",
"_where": "c:\\xampp\\htdocs\\laravel\\node_modules\\bubleify",
"author": {
"name": "Rich Harris"
},
"bin": {
"buble": "./bin/buble"
},
"bugs": {
"url": "https://gitlab.com/Rich-Harris/buble/issues"
},
"dependencies": {
"acorn": "^3.1.0",
"acorn-jsx": "^3.0.1",
"acorn-object-spread": "^1.0.0",
"chalk": "^1.1.3",
"magic-string": "^0.14.0",
"minimist": "^1.2.0",
"os-homedir": "^1.0.1"
},
"description": "The blazing fast, batteries-included ES2015 compiler",
"devDependencies": {
"buble": "0.8.2",
"console-group": "^0.2.1",
"eslint": "^2.10.1",
"glob": "^7.0.3",
"mocha": "^2.4.5",
"regexpu-core": "^2.0.0",
"rimraf": "^2.5.2",
"rollup": "^0.26.3",
"rollup-plugin-buble": "^0.8.0",
"rollup-plugin-commonjs": "^2.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"
},
"directories": {},
"dist": {
"shasum": "c66ffe92f9f4a3c65d3256079b711e2bd0bc5013",
"tarball": "https://registry.npmjs.org/buble/-/buble-0.12.5.tgz"
},
"files": [
"bin",
"src",
"dist",
"register.js",
"README.md"
],
"gitHead": "048673f53927c2c086b13877e04e79c921db9440",
"homepage": "https://gitlab.com/Rich-Harris/buble#README",
"jsnext:main": "dist/buble.es.js",
"keywords": [
"javascript",
"transpilation",
"compilation",
"esnext",
"es2015",
"es2017",
"es6",
"es7"
],
"license": "MIT",
"main": "dist/buble.umd.js",
"maintainers": [
{
"name": "rich_harris",
"email": "richard.a.harris@gmail.com"
}
],
"name": "buble",
"optionalDependencies": {},
"readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git+https://gitlab.com/Rich-Harris/buble.git"
},
"scripts": {
"build": "npm run build:umd && npm run build:es && npm run build:browser",
"build:browser": "rollup -c --environment DEPS -f umd -o dist/buble.deps.js",
"build:es": "rollup -c -f es6 -o dist/buble.es.js",
"build:umd": "rollup -c -f umd -o dist/buble.umd.js",
"prepublish": "npm test && npm run build:es",
"pretest": "npm run build:umd && npm run build:browser",
"pretest:node": "npm run build:umd",
"test": "mocha test/test.js --compilers js:buble/register",
"test:node": "mocha test/test.js --compilers js:buble/register"
},
"version": "0.12.5"
}
+86
View File
@@ -0,0 +1,86 @@
var fs = require( 'fs' );
var path = require( 'path' );
var crypto = require( 'crypto' );
var homedir = require( 'os-homedir' );
var buble = require( './' );
var original = require.extensions[ '.js' ];
var nodeModulesPattern = path.sep === '/' ? /\/node_modules\// : /\\node_modules\\/;
var nodeVersion = /(?:0\.)?\d+/.exec( process.version )[0];
var versions = [ '0.10', '0.12', '4', '5', '6' ];
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' );
}
}
var options = {
target: {
node: nodeVersion
}
};
function mkdirp ( dir ) {
var parent = path.dirname( dir );
if ( dir === parent ) return;
mkdirp( parent );
try {
fs.mkdirSync( dir );
} catch ( err ) {
if ( err.code !== 'EEXIST' ) throw err;
}
}
var home = homedir();
if ( home ) {
var cachedir = path.join( home, '.buble-cache', nodeVersion );
mkdirp( cachedir );
fs.writeFileSync( path.join( home, '.buble-cache/README.txt' ), 'These files enable a faster startup when using buble/register. You can safely delete this folder at any time. See https://buble.surge.sh/guide/ for more information.' );
}
require.extensions[ '.js' ] = function ( m, filename ) {
if ( nodeModulesPattern.test( filename ) ) return original( m, filename );
var source = fs.readFileSync( filename, 'utf-8' );
var hash = crypto.createHash( 'sha256' );
hash.update( source );
var key = hash.digest( 'hex' ) + '.json';
var cachepath = path.join( cachedir, key );
var compiled;
if ( cachedir ) {
try {
compiled = JSON.parse( fs.readFileSync( cachepath, 'utf-8' ) );
} catch ( err ) {
// noop
}
}
if ( !compiled ) {
try {
compiled = buble.transform( source, options );
if ( cachedir ) {
fs.writeFileSync( cachepath, JSON.stringify( compiled ) );
}
} catch ( err ) {
if ( err.snippet ) {
console.log( 'Error compiling ' + filename + ':\n---' );
console.log( err.snippet );
console.log( err.message );
console.log( '' )
process.exit( 1 );
}
throw err;
}
}
m._compile( '"use strict";\n' + compiled.code, filename );
};
+81
View File
@@ -0,0 +1,81 @@
import acorn from 'acorn';
import acornJsx from 'acorn-jsx/inject';
import acornObjectSpread from 'acorn-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 dangerousTransforms = [
'dangerousTaggedTemplateString',
'dangerousForOf'
];
export function target ( target ) {
const targets = Object.keys( target );
let bitmask = targets.length ?
0b1111111111111111111111111111111 :
0b1000000000000000000000000000000;
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` );
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 ];
bitmask &= support;
});
let transforms = Object.create( null );
features.forEach( ( name, i ) => {
transforms[ name ] = !( bitmask & 1 << i );
});
dangerousTransforms.forEach( name => {
transforms[ name ] = false;
});
return transforms;
}
export function transform ( source, options = {} ) {
let ast;
try {
ast = parse( source, {
ecmaVersion: 7,
preserveParens: true,
sourceType: 'module',
plugins: {
jsx: true,
objectSpread: true
}
});
} 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;
return;
}
if ( !( name in transforms ) ) throw new Error( `Unknown transform '${name}'` );
transforms[ name ] = options.transforms[ name ];
});
return new Program( source, ast, transforms, options ).export( options );
}
export { version as VERSION } from '../package.json';
+305
View File
@@ -0,0 +1,305 @@
import wrap from './wrap.js';
import Node from './Node.js';
import Scope from './Scope.js';
import CompileError from '../utils/CompileError.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;
return node.expression.value === 'use strict';
}
export default class BlockStatement extends Node {
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 )
});
if ( this.parentIsFunction ) {
this.parent.params.forEach( node => {
this.scope.addDeclaration( node, 'param' );
});
}
}
initialise ( transforms ) {
this.thisAlias = null;
this.argumentsAlias = null;
this.defaultParameters = [];
// 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();
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;
return this.parent.findLexicalBoundary();
}
findScope ( functionScope ) {
if ( functionScope && !this.isFunctionBlock ) return this.parent.findScope( functionScope );
return this.scope;
}
getArgumentsAlias () {
if ( !this.argumentsAlias ) {
this.argumentsAlias = this.scope.createIdentifier( 'arguments' );
}
return this.argumentsAlias;
}
getArgumentsArrayAlias () {
if ( !this.argumentsArrayAlias ) {
this.argumentsArrayAlias = this.scope.createIdentifier( 'argsArray' );
}
return this.argumentsArrayAlias;
}
getThisAlias () {
if ( !this.thisAlias ) {
this.thisAlias = this.scope.createIdentifier( 'this' );
}
return this.thisAlias;
}
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;
this.indentation = '';
while ( true ) {
c += 1;
const char = source[c];
if ( char !== ' ' && char !== '\t' ) break;
this.indentation += char;
}
const indentString = this.program.magicString.getIndentString();
// 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, '' );
}
parent = parent.parent;
}
if ( useOuter ) this.indentation += indentString;
}
return this.indentation;
}
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.thisAlias ) {
introStatementGenerators.push( ( start, prefix, suffix ) => {
const assignment = `${prefix}var ${this.thisAlias} = this;${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.insertLeft( start, assignment );
});
}
if ( /Function/.test( this.parent.type ) ) {
this.transpileParameters( code, transforms, indentation, introStatementGenerators );
}
if ( transforms.letConst && this.isFunctionBlock ) {
this.transpileBlockScopedIdentifiers( code );
}
super.transpile( code, transforms );
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()}}` );
code.insertRight( expr.start, `\n${indentation}return ` );
code.insertLeft( expr.end, `;\n` );
} else if ( transforms.arrow ) {
code.insertRight( expr.start, `{ return ` );
code.insertLeft( expr.end, `; }` );
}
}
else if ( introStatementGenerators.length ) {
code.insertLeft( this.start, `{` ).insertRight( this.end, `}` );
}
}
let start;
if ( isUseStrict( this.body[0] ) ) {
start = this.body[0].end;
} else if ( this.synthetic || this.parent.type === 'Root' ) {
start = this.start;
} else {
start = this.start + 1;
}
let prefix = `\n${indentation}`;
let suffix = '';
introStatementGenerators.forEach( ( fn, i ) => {
if ( i === introStatementGenerators.length - 1 ) suffix = `\n`;
fn( start, prefix, suffix );
});
}
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}`;
code
.insertRight( param.left.end, `${lhs}` )
.move( param.left.end, param.right.end, start )
.insertLeft( 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 {
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;
code.remove( start, end );
}
const name = param.argument.name;
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}` );
} else {
code.insertLeft( 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, introStatementGenerators );
code.insertLeft( param.start, ref );
}
}
});
}
transpileBlockScopedIdentifiers ( code ) {
Object.keys( this.scope.blockScopedDeclarations ).forEach( name => {
const declarations = this.scope.blockScopedDeclarations[ name ];
for ( let declaration of declarations ) {
let cont = false; // TODO implement proper continue...
if ( declaration.kind === 'for.let' ) {
// special case
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;
declaration.name = outerAlias;
code.overwrite( declaration.node.start, declaration.node.end, outerAlias, true );
forStatement.aliases[ name ] = {
outer: outerAlias,
inner: innerAlias
};
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 );
}
}
cont = true;
}
}
if ( !cont ) {
const alias = this.scope.createIdentifier( name );
if ( name !== alias ) {
declaration.name = alias;
code.overwrite( declaration.node.start, declaration.node.end, alias, true );
for ( const identifier of declaration.instances ) {
identifier.rewritten = true;
code.overwrite( identifier.start, identifier.end, alias, true );
}
}
}
}
});
}
}
+108
View File
@@ -0,0 +1,108 @@
import wrap from './wrap.js';
import keys from './keys.js';
// used for debugging, without the noise created by
// circular references
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();
} else {
obj[ key ] = node[ key ];
}
});
return obj;
}
export default class Node {
constructor ( raw, parent ) {
raw.parent = parent;
raw.program = parent.program || parent;
raw.depth = parent.depth + 1;
raw.keys = keys[ raw.type ];
raw.indentation = undefined;
for ( const key of keys[ raw.type ] ) {
wrap( raw[ key ], raw );
}
raw.program.magicString.addSourcemapLocation( raw.start );
raw.program.magicString.addSourcemapLocation( raw.end );
}
ancestor ( level ) {
let node = this;
while ( level-- ) {
node = node.parent;
if ( !node ) return null;
}
return node;
}
contains ( node ) {
while ( node ) {
if ( node === this ) return true;
node = node.parent;
}
return false;
}
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 );
}
findScope ( functionScope ) {
return this.parent.findScope( functionScope );
}
getIndentation () {
return this.parent.getIndentation();
}
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 );
}
}
}
toJSON () {
return toJSON( this );
}
toString () {
return this.program.magicString.original.slice( this.start, this.end );
}
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 );
}
}
}
}
+53
View File
@@ -0,0 +1,53 @@
import MagicString from 'magic-string';
import BlockStatement from './BlockStatement.js';
import wrap from './wrap.js';
export default function Program ( source, ast, transforms, options ) {
this.type = 'Root';
// options
this.jsx = options.jsx || 'React.createElement';
this.objectAssign = options.objectAssign || 'Object.assign';
this.source = source;
this.magicString = new MagicString( source );
this.ast = ast;
this.depth = 0;
wrap( this.body = ast, this );
this.body.__proto__ = BlockStatement.prototype;
this.templateElements = [];
this.body.initialise( transforms );
this.indentExclusions = {};
for ( const node of this.templateElements ) {
for ( let i = node.start; i < node.end; i += 1 ) {
this.indentExclusions[ node.start + i ] = true;
}
}
this.body.transpile( this.magicString, transforms );
}
Program.prototype = {
export ( options = {} ) {
return {
code: this.magicString.toString(),
map: this.magicString.generateMap({
file: options.file,
source: options.source,
includeContent: options.includeContent !== false
})
};
},
findNearest () {
return null;
},
findScope () {
return null;
}
};
+98
View File
@@ -0,0 +1,98 @@
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 ) {
options = options || {};
this.parent = options.parent;
this.isBlockScope = !!options.block;
let scope = this;
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 );
}
Scope.prototype = {
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;
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 );
} else {
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
const identifier = this.identifiers[i];
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 );
} else {
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 );
},
createIdentifier ( base ) {
base = base
.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 ) {
name = `${base}$${counter++}`;
}
this.aliases[ name ] = true;
return name;
},
findDeclaration ( name ) {
return this.declarations[ name ] ||
( this.parent && this.parent.findDeclaration( name ) );
}
};
+31
View File
@@ -0,0 +1,31 @@
export default function extractNames ( node ) {
const names = [];
extractors[ node.type ]( names, node );
return names;
}
const extractors = {
Identifier ( names, node ) {
names.push( node );
},
ObjectPattern ( names, node ) {
for ( const prop of node.properties ) {
extractors[ prop.value.type ]( names, prop.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 );
},
AssignmentPattern ( names, node ) {
extractors[ node.left.type ]( names, node.left );
}
};
+4
View File
@@ -0,0 +1,4 @@
export default {
Program: [ 'body' ],
Literal: []
};
+48
View File
@@ -0,0 +1,48 @@
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 ) {
const lexicalBoundary = this.findLexicalBoundary();
let i = this.elements.length;
while ( i-- ) {
const element = this.elements[i];
if ( element && element.type === 'SpreadElement' && isArguments( element.argument ) ) {
this.argumentsArrayAlias = lexicalBoundary.getArgumentsArrayAlias();
}
}
}
super.initialise( transforms );
}
transpile ( code, transforms ) {
if ( transforms.spreadRest ) {
if ( this.elements.length === 1 ) {
const element = this.elements[0];
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
} else {
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 );
if ( hasSpreadElements ) {
code.overwrite( this.end - 1, this.end, ')' );
}
}
}
super.transpile( code, transforms );
}
}
+30
View File
@@ -0,0 +1,30 @@
import Node from '../Node.js';
export default class ArrowFunctionExpression extends Node {
initialise ( transforms ) {
this.body.createScope();
super.initialise( transforms );
}
transpile ( code, transforms ) {
if ( transforms.arrow ) {
// remove arrow
let charIndex = this.body.start;
while ( code.original[ charIndex ] !== '=' ) {
charIndex -= 1;
}
code.remove( charIndex, this.body.start );
// 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, ')' );
}
// add function
code.insertRight( this.start, 'function ' );
}
super.transpile( code, transforms );
}
}
+134
View File
@@ -0,0 +1,134 @@
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` );
}
// 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;
}
}
if ( /Pattern/.test( this.left.type ) ) {
throw new CompileError( this.left, 'Destructuring assignments are not currently supported. Coming soon!' );
}
super.initialise( transforms );
}
transpile ( code, transforms ) {
if ( this.operator === '**=' && transforms.exponentiation ) {
const scope = this.findScope( false );
const getAlias = 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 );
// 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
// simple or complex reference
let base;
let left = this.left;
while ( left.type === 'ParenthesizedExpression' ) left = left.expression;
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 i0 = statement.getIndentation();
if ( left.property.type === 'Identifier' ) {
property = left.computed ? getAlias( left.property.name ) : left.property.name;
} else {
property = scope.createIdentifier( 'property' );
needsPropertyVar = true;
}
if ( left.object.type === 'Identifier' ) {
object = getAlias( left.object.name );
} else {
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 );
}
}
else {
let declarators = [];
if ( needsObjectVar ) declarators.push( object );
if ( needsPropertyVar ) declarators.push( property );
code.insertRight( 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 );
}
code.insertLeft( this.end, ` )` );
}
base = object + ( left.computed || needsPropertyVar ? `[${property}]` : `.${property}` );
}
code.insertRight( this.right.start, `Math.pow( ${base}, ` );
code.insertLeft( this.right.end, ` )` );
}
super.transpile( code, transforms );
}
}
+12
View File
@@ -0,0 +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, ` )` );
}
super.transpile( code, transforms );
}
}
+21
View File
@@ -0,0 +1,21 @@
import Node from '../Node.js';
import CompileError from '../../utils/CompileError.js';
export default class BreakStatement extends Node {
initialise ( transforms ) {
const loop = this.findNearest( /(?:For(?:In)?|While)Statement/ );
const switchCase = this.findNearest( 'SwitchCase' );
if ( loop && ( !switchCase || loop.depth > switchCase.depth ) ) {
loop.canBreak = true;
this.loop = loop;
}
}
transpile ( code, transforms ) {
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'` );
}
}
}
+86
View File
@@ -0,0 +1,86 @@
import Node from '../Node.js';
import spread, { isArguments } from '../../utils/spread.js';
export default class CallExpression extends Node {
initialise ( transforms ) {
if ( transforms.spreadRest && this.arguments.length > 1 ) {
const lexicalBoundary = this.findLexicalBoundary();
let i = this.arguments.length;
while ( i-- ) {
const arg = this.arguments[i];
if ( arg.type === 'SpreadElement' && isArguments( arg.argument ) ) {
this.argumentsArrayAlias = lexicalBoundary.getArgumentsArrayAlias();
}
}
}
super.initialise( transforms );
}
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 );
hasSpreadElements = true;
}
} else {
hasSpreadElements = spread( code, this.arguments, firstArgument.start, this.argumentsArrayAlias );
}
if ( hasSpreadElements ) {
if ( this.callee.type === 'MemberExpression' ) {
if ( this.callee.object.type === 'Identifier' ) {
context = this.callee.object.name;
} else {
const statement = this.callee.object;
const i0 = statement.getIndentation();
context = this.findScope( true ).createIdentifier( 'ref' );
code.insertRight( statement.start, `var ${context} = ` );
code.insertLeft( statement.end, `;\n${i0}${context}` );
}
} else {
context = 'void 0';
}
code.insertLeft( this.callee.end, '.apply' );
// we need to handle `super()` different, because `SuperClass.call.apply`
// isn't very helpful
const isSuper = this.callee.type === 'Super';
if ( isSuper ) {
this.callee.noCall = true; // bit hacky...
if ( this.arguments.length > 1 ) {
if ( firstArgument.type !== 'SpreadElement' ) {
code.insertRight( firstArgument.start, `[ ` );
}
code.insertLeft( this.arguments[ this.arguments.length - 1 ].end, ' )' );
}
}
else if ( this.arguments.length === 1 ) {
code.insertRight( firstArgument.start, `${context}, ` );
} else {
if ( firstArgument.type === 'SpreadElement' ) {
code.insertRight( firstArgument.start, `${context}, ` );
} else {
code.insertRight( firstArgument.start, `${context}, [ ` );
}
code.insertLeft( this.arguments[ this.arguments.length - 1 ].end, ' )' );
}
}
}
super.transpile( code, transforms );
}
}
+164
View File
@@ -0,0 +1,164 @@
import Node from '../Node.js';
import { findIndex } from '../../utils/array.js';
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 ) {
const name = this.parent.name;
const indentStr = code.getIndentString();
const i0 = this.getIndentation() + ( inFunctionExpression ? indentStr : '' );
const i1 = i0 + indentStr;
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 );
} else {
code.remove( this.start, this.end );
}
if ( constructor ) {
constructor.value.body.isConstructorBody = true;
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 ( !inFunctionExpression ) code.insertLeft( constructor.end, ';' );
}
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}` : '' );
inheritanceBlock = fn + inheritanceBlock;
introBlock += inheritanceBlock + `\n\n${i0}`;
}
} else if ( !constructor ) {
let fn = `function ${name} () {}`;
if ( this.parent.type === 'ClassDeclaration' ) fn += ';';
if ( this.body.length ) fn += `\n\n${i0}`;
introBlock += fn;
}
const scope = this.findScope( false );
let prototypeGettersAndSetters = [];
let staticGettersAndSetters = [];
let prototypeAccessors;
let staticAccessors;
this.body.forEach( ( method, i ) => {
if ( method.kind === 'constructor' ) {
code.overwrite( method.key.start, method.key.end, `function ${name}` );
return;
}
if ( method.static ) code.remove( method.start, method.start + 7 );
const isAccessor = method.kind !== 'method';
let lhs;
let methodName = method.key.name;
if ( scope.contains( methodName ) || reserved[ methodName ] ) methodName = scope.createIdentifier( methodName );
if ( isAccessor ) {
if ( method.computed ) {
throw new Error( 'Computed accessor properties are not currently supported' );
}
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' );
lhs = `${staticAccessors}`;
} else {
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`;
}
if ( !method.computed ) lhs += '.';
const insertNewlines = ( constructorIndex > 0 && i === constructorIndex + 1 ) ||
( i === 0 && constructorIndex === this.body.length - 1 );
if ( insertNewlines ) lhs = `\n\n${i0}${lhs}`;
let c = method.key.end;
if ( method.computed ) {
while ( code.original[c] !== ']' ) c += 1;
c += 1;
}
code.insertRight( method.start, lhs );
const rhs = ( isAccessor ? `.${method.kind}` : '' ) + ` = function` + ( method.value.generator ? '* ' : ' ' ) + ( method.computed || isAccessor ? '' : `${methodName} ` );
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 );
});
if ( prototypeGettersAndSetters.length || staticGettersAndSetters.length ) {
let intro = [];
let outro = [];
if ( prototypeGettersAndSetters.length ) {
intro.push( `var ${prototypeAccessors} = { ${prototypeGettersAndSetters.map( name => `${name}: {}` ).join( ',' )} };` );
outro.push( `Object.defineProperties( ${name}.prototype, ${prototypeAccessors} );` );
}
if ( staticGettersAndSetters.length ) {
intro.push( `var ${staticAccessors} = { ${staticGettersAndSetters.map( name => `${name}: {}` ).join( ',' )} };` );
outro.push( `Object.defineProperties( ${name}, ${staticAccessors} );` );
}
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}` );
}
if ( constructor ) {
code.insertLeft( constructor.end, introBlock );
} else {
code.insertRight( this.start, introBlock );
}
code.insertLeft( this.end, outroBlock );
}
super.transpile( code, transforms );
}
}
+62
View File
@@ -0,0 +1,62 @@
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' );
super.initialise( transforms );
}
transpile ( code, transforms ) {
if ( transforms.classes ) {
if ( !this.superClass ) deindent( this.body, code );
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};` :
'';
if ( syntheticDefaultExport ) code.remove( this.parent.start, this.start );
code.overwrite( this.start, this.id.start, 'var ' );
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}` );
} else {
code.overwrite( this.id.end, 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, ' = ' );
} else {
code.overwrite( this.id.end, this.body.start, ' = ' );
}
}
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 );
}
}
else {
this.body.transpile( code, transforms, false, null );
}
}
}
+45
View File
@@ -0,0 +1,45 @@
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 );
}
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}` );
} else {
code.overwrite( this.start, this.body.start, `(function () {\n${i1}` );
}
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, '))' );
} else {
code.insertLeft( this.end, `\n\n${i1}return ${this.name};\n${i0}}())` );
}
}
else {
this.body.transpile( code, transforms, false );
}
}
}
+12
View File
@@ -0,0 +1,12 @@
import Node from '../Node.js';
import CompileError from '../../utils/CompileError.js';
export default class ContinueStatement extends Node {
transpile ( code, transforms ) {
const loop = this.findNearest( /(?:For(?:In|Of)?|While)Statement/ );
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' );
}
}
}
+9
View File
@@ -0,0 +1,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 );
}
}
+9
View File
@@ -0,0 +1,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 );
}
}
+22
View File
@@ -0,0 +1,22 @@
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;
}
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 ) ) ) :
[];
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 );
}
}
+52
View File
@@ -0,0 +1,52 @@
import LoopStatement from './shared/LoopStatement.js';
import CompileError from '../../utils/CompileError.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 );
}
transpile ( code, transforms ) {
if ( !transforms.dangerousForOf ) {
super.transpile( code, transforms );
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 );
} else {
code.remove( this.start, this.end );
}
return;
}
const scope = this.findScope( true );
const i0 = this.getIndentation();
const i1 = i0 + code.getIndentString();
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}}` );
}
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.insertLeft( this.left.end, ` = ${list}[${key}];\n\n${i1}` );
code.insertRight( this.right.start, `var ${key} = 0, ${list} = ` );
code.insertLeft( this.right.end, `; ${key} < ${list}.length; ${key} += 1` );
super.transpile( code, transforms );
}
}
+38
View File
@@ -0,0 +1,38 @@
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;
}
transpile ( code, transforms ) {
const i1 = this.getIndentation() + code.getIndentString();
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 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 );
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(` `)}` );
} else {
const lastStatement = this.body.body[ this.body.body.length - 1 ];
code.insertLeft( lastStatement.end, `\n\n${i1}${updates.join(`\n${i1}`)}` );
}
}
}
super.transpile( code, transforms );
}
}
+15
View File
@@ -0,0 +1,15 @@
import Node from '../Node.js';
import CompileError from '../../utils/CompileError.js';
export default class FunctionDeclaration extends Node {
initialise ( transforms ) {
if ( this.generator && transforms.generator ) {
throw new CompileError( this, 'Generators are not supported' );
}
this.body.createScope();
this.findScope( true ).addDeclaration( this.id, 'function' );
super.initialise( transforms );
}
}
+19
View File
@@ -0,0 +1,19 @@
import Node from '../Node.js';
import CompileError from '../../utils/CompileError.js';
export default class FunctionExpression extends Node {
initialise ( transforms ) {
if ( this.generator && transforms.generator ) {
throw new CompileError( this, 'Generators are not supported' );
}
this.body.createScope();
if ( this.id ) {
// function expression IDs belong to the child scope...
this.body.scope.addDeclaration( this.id, 'function' );
}
super.initialise( transforms );
}
}
+42
View File
@@ -0,0 +1,42 @@
import Node from '../Node.js';
import isReference from '../../utils/isReference.js';
export default class Identifier extends Node {
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 ) {
return this.parent.body.scope;
}
return this.parent.findScope( functionScope );
}
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( /(?:For(?:In|Of)?|While)Statement/ );
if ( arrowFunction && arrowFunction.depth > lexicalBoundary.depth ) {
this.alias = lexicalBoundary.getArgumentsAlias();
}
if ( loop && loop.body.contains( this ) && loop.depth > lexicalBoundary.depth ) {
this.alias = lexicalBoundary.getArgumentsAlias();
}
}
this.findScope( false ).addReference( this );
}
}
transpile ( code, transforms ) {
if ( this.alias ) {
code.overwrite( this.start, this.end, this.alias, true );
}
}
}
+9
View File
@@ -0,0 +1,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 );
}
}
+8
View File
@@ -0,0 +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 );
}
}
+8
View File
@@ -0,0 +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 );
}
}
+20
View File
@@ -0,0 +1,20 @@
import Node from '../Node.js';
const IS_DATA_ATTRIBUTE = /-/;
export default class JSXAttribute extends Node {
transpile ( code, transforms ) {
if ( this.value ) {
code.overwrite( this.name.end, this.value.start, ': ' );
} else {
// tag without value
code.overwrite( this.name.start, this.name.end, `${this.name.name}: true`)
}
if ( IS_DATA_ATTRIBUTE.test( this.name.name ) ) {
code.overwrite( this.name.start, this.name.end, `'${this.name.name}'` );
}
super.transpile( code, transforms );
}
}
+22
View File
@@ -0,0 +1,22 @@
import Node from '../Node.js';
function containsNewLine ( node ) {
return node.type === 'Literal' && !/\S/.test( node.value ) && /\n/.test( node.value );
}
export default class JSXClosingElement extends Node {
transpile ( code, transforms ) {
let spaceBeforeParen = true;
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 ) ) {
spaceBeforeParen = false;
}
code.overwrite( this.start, this.end, spaceBeforeParen ? ' )' : ')' );
}
}
+46
View File
@@ -0,0 +1,46 @@
import Node from '../Node.js';
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
// TODO prefer single quotes?
return JSON.stringify( str );
}
export default class JSXElement extends Node {
transpile ( code, transforms ) {
super.transpile( code, transforms );
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 );
});
if ( children.length ) {
let c = this.openingElement.end;
let i;
for ( i = 0; i < children.length; i += 1 ) {
const child = children[i];
const tail = code.original[ c ] === '\n' && child.type !== 'Literal' ? '' : ' ';
code.insertLeft( c, `,${tail}` );
if ( child.type === 'Literal' ) {
const str = normalise( child.value, i === children.length - 1 );
code.overwrite( child.start, child.end, str );
}
c = child.end;
}
}
}
}
+10
View File
@@ -0,0 +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 );
super.transpile( code, transforms );
}
}
+81
View File
@@ -0,0 +1,81 @@
import Node from '../Node.js';
export default class JSXOpeningElement extends Node {
transpile ( code, transforms ) {
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.insertRight( this.name.start, `'` );
const len = this.attributes.length;
let c = this.name.end;
if ( len ) {
let i;
let hasSpread = false;
for ( i = 0; i < len; i += 1 ) {
if ( this.attributes[i].type === 'JSXSpreadAttribute' ) {
hasSpread = true;
break;
}
}
c = this.attributes[0].end;
for ( i = 0; i < len; i += 1 ) {
const attr = this.attributes[i];
if ( i > 0 ) {
code.overwrite( c, attr.start, ', ' );
}
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 ( !nextAttr || nextAttr.type === 'JSXSpreadAttribute' ) {
code.insertLeft( attr.end, ' }' );
}
}
c = attr.end;
}
let after;
let before;
if ( hasSpread ) {
if ( len === 1 ) {
before = html ? `',` : ',';
} else {
before = html ? `', ${this.program.objectAssign}({},` : `, ${this.program.objectAssign}({},`;
after = ')';
}
} else {
before = html ? `', {` : ', {';
after = ' }';
}
code.insertRight( this.name.end, before );
if ( after ) {
code.insertLeft( this.attributes[ len - 1 ].end, after );
}
} else {
code.insertLeft( 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 ? `)` : ` )` );
} else {
code.remove( c, this.end );
}
}
}
+10
View File
@@ -0,0 +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 );
super.transpile( code, transforms );
}
}
+23
View File
@@ -0,0 +1,23 @@
import Node from '../Node.js';
import CompileError from '../../utils/CompileError.js';
import rewritePattern from 'regexpu-core';
export default class Literal extends Node {
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 );
}
}
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', '' )}` );
}
}
}
}
+13
View File
@@ -0,0 +1,13 @@
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, `']` );
}
super.transpile( code, transforms );
}
}
+121
View File
@@ -0,0 +1,121 @@
import Node from '../Node.js';
import deindent from '../../utils/deindent.js';
export default class ObjectExpression extends Node {
transpile ( code, transforms ) {
super.transpile( code, transforms );
let spreadPropertyCount = 0;
let computedPropertyCount = 0;
for ( let prop of this.properties ) {
if ( prop.type === 'SpreadProperty' ) spreadPropertyCount += 1;
if ( prop.computed ) computedPropertyCount += 1;
}
if ( spreadPropertyCount ) {
// enclose run of non-spread properties in curlies
let i = this.properties.length;
while ( i-- ) {
const prop = this.properties[i];
if ( prop.type === 'Property' ) {
const lastProp = this.properties[ i - 1 ];
const nextProp = this.properties[ i + 1 ];
if ( !lastProp || lastProp.type !== 'Property' ) {
code.insertRight( prop.start, '{' );
}
if ( !nextProp || nextProp.type !== 'Property' ) {
code.insertLeft( prop.end, '}' );
}
}
}
// wrap the whole thing in Object.assign
code.overwrite( this.start, this.properties[0].start, `${this.program.objectAssign}({}, `);
code.overwrite( this.properties[ this.properties.length - 1 ].end, this.end, ')' );
}
if ( computedPropertyCount && transforms.computedProperty ) {
const i0 = this.getIndentation();
let isSimpleAssignment;
let name;
let start;
let end;
if ( this.parent.type === 'VariableDeclarator' && this.parent.parent.declarations.length === 1 ) {
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' ) {
isSimpleAssignment = true;
name = this.parent.left.alias || this.parent.left.name; // TODO is this right?
}
// handle block scoping
const declaration = this.findScope( false ).findDeclaration( name );
if ( declaration ) name = declaration.name;
start = this.start + 1;
end = this.end;
if ( isSimpleAssignment ) {
// ???
} else {
name = this.findScope( true ).createIdentifier( 'obj' );
const statement = this.findNearest( /(?:Statement|Declaration)$/ );
code.insertRight( statement.start, `var ${name};\n${i0}` );
code.insertRight( this.start, `( ${name} = ` );
}
const len = this.properties.length;
let lastComputedProp;
for ( let i = 0; i < len; i += 1 ) {
const prop = this.properties[i];
if ( prop.computed ) {
lastComputedProp = prop;
let moveStart = i > 0 ? this.properties[ i - 1 ].end : start;
code.overwrite( moveStart, prop.start, isSimpleAssignment ? `;\n${i0}${name}` : `, ${name}` );
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 === 0 && len > 1 ) {
// remove trailing comma
c = prop.end;
while ( code.original[c] !== ',' ) c += 1;
code.remove( prop.end, c + 1 );
}
if ( prop.method && transforms.conciseMethodProperty ) {
code.insertRight( prop.value.start, 'function ' );
}
deindent( prop.value, code );
}
}
// special case
if ( computedPropertyCount === len ) {
code.remove( this.properties[ len - 1 ].end, this.end - 1 );
}
if ( !isSimpleAssignment ) {
code.insertLeft( lastComputedProp.end, `, ${name} )` );
}
}
}
}
+23
View File
@@ -0,0 +1,23 @@
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 ) {
const name = this.findScope( true ).createIdentifier( this.key.type === 'Identifier' ? this.key.name : this.key.value );
if ( this.value.generator ) code.remove( this.start, this.key.start );
code.insertLeft( 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, `'` );
}
super.transpile( code, transforms );
}
}
+27
View File
@@ -0,0 +1,27 @@
import Node from '../Node.js';
export default class ReturnStatement extends Node {
initialise ( transforms ) {
this.loop = this.findNearest( /(?:For(?:In)?|While)Statement/ );
this.nearestFunction = this.findNearest( /Function/ );
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 );
}
transpile ( code, transforms ) {
if ( this.argument ) {
const shouldWrap = this.shouldWrap && this.loop && this.loop.shouldRewriteAsFunction;
if ( shouldWrap ) code.insertRight( this.argument.start, `{ v: ` );
if ( this.argument ) this.argument.transpile( code, transforms );
if ( shouldWrap ) code.insertLeft( this.argument.end, ` }` );
}
}
}
+11
View File
@@ -0,0 +1,11 @@
import Node from '../Node.js';
import reserved from '../../utils/reserved.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 );
}
}
+68
View File
@@ -0,0 +1,68 @@
import Node from '../Node.js';
import CompileError from '../../utils/CompileError.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' );
}
this.isMember = this.parent.type === 'MemberExpression';
if ( !this.isCalled && !this.isMember ) {
throw new CompileError( this, 'Unexpected use of `super` (expected `super(...)` or `super.*`)' );
}
}
if ( transforms.arrow ) {
const lexicalBoundary = this.findLexicalBoundary();
const arrowFunction = this.findNearest( 'ArrowFunctionExpression' );
const loop = this.findNearest( /(?:For(?:In|Of)?|While)Statement/ );
if ( arrowFunction && arrowFunction.depth > lexicalBoundary.depth ) {
this.thisAlias = lexicalBoundary.getThisAlias();
}
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`;
code.overwrite( this.start, this.end, expression, 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' );
}
const thisAlias = this.thisAlias || 'this';
if ( callExpression.arguments.length ) {
code.insertLeft( callExpression.arguments[0].start, `${thisAlias}, ` );
} else {
code.insertLeft( callExpression.end - 1, `${thisAlias}` );
}
}
}
}
}
+37
View File
@@ -0,0 +1,37 @@
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' );
}
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 );
// 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(', ')}]` );
let lastIndex = ordered[0].start;
ordered.forEach( node => {
if ( node.type === 'TemplateElement' ) {
code.remove( lastIndex, node.end );
} else {
code.overwrite( lastIndex, node.start, ', ' );
}
lastIndex = node.end;
});
code.overwrite( lastIndex, this.end, ')' );
}
super.transpile( code, transforms );
}
}
+7
View File
@@ -0,0 +1,7 @@
import Node from '../Node.js';
export default class TemplateElement extends Node {
initialise ( transforms ) {
this.program.templateElements.push( this );
}
}
+69
View File
@@ -0,0 +1,69 @@
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 ) => {
// include all expressions
if ( node.type !== 'TemplateElement' ) return true;
// include all non-empty strings
if ( node.value.raw ) return true;
// exclude all empty strings not at the head
return !i;
});
// special case we may be able to skip the first element,
// 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' ) {
ordered.shift();
}
}
const parenthesise = ( this.quasis.length !== 1 || this.expressions.length !== 0 ) &&
this.parent.type !== 'AssignmentExpression' &&
this.parent.type !== 'VariableDeclarator' &&
( this.parent.type !== 'BinaryExpression' || this.parent.operator !== '+' );
if ( parenthesise ) code.insertRight( 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 );
code.overwrite( lastIndex, node.end, replacement );
} else {
const parenthesise = node.type !== 'Identifier'; // TODO other cases where it's safe
let replacement = '';
if ( i ) replacement += ' + ';
if ( parenthesise ) replacement += '(';
code.overwrite( lastIndex, node.start, replacement );
if ( parenthesise ) code.insertLeft( node.end, ')' );
}
lastIndex = node.end;
});
let close = '';
if ( parenthesise ) close += ')';
code.overwrite( lastIndex, this.end, close );
}
super.transpile( code, transforms );
}
}
+25
View File
@@ -0,0 +1,25 @@
import Node from '../Node.js';
export default class ThisExpression extends Node {
initialise ( transforms ) {
if ( transforms.arrow ) {
const lexicalBoundary = this.findLexicalBoundary();
const arrowFunction = this.findNearest( 'ArrowFunctionExpression' );
const loop = this.findNearest( /(?:For(?:In|Of)?|While)Statement/ );
if ( arrowFunction && arrowFunction.depth > lexicalBoundary.depth ) {
this.alias = lexicalBoundary.getThisAlias();
}
if ( loop && loop.body.contains( this ) && loop.depth > lexicalBoundary.depth ) {
this.alias = lexicalBoundary.getThisAlias();
}
}
}
transpile ( code, transforms ) {
if ( this.alias ) {
code.overwrite( this.start, this.end, this.alias, true );
}
}
}
+15
View File
@@ -0,0 +1,15 @@
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` );
}
}
super.initialise( transforms );
}
}
+85
View File
@@ -0,0 +1,85 @@
import Node from '../Node.js';
import destructure from '../../utils/destructure.js';
export default class VariableDeclaration extends Node {
initialise ( transforms ) {
this.scope = this.findScope( this.kind === 'var' );
this.declarations.forEach( declarator => declarator.initialise( transforms ) );
}
transpile ( code, transforms ) {
const i0 = this.getIndentation();
let kind = this.kind;
if ( transforms.letConst && kind !== 'var' ) {
kind = 'var';
code.overwrite( this.start, this.start + this.kind.length, kind, true );
}
if ( transforms.destructuring ) {
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 ` );
}
} else {
if ( i === 0 ) {
code.remove( c, declarator.id.start );
} else {
code.overwrite( c, declarator.id.start, `;\n${i0}` );
}
const simple = declarator.init.type === 'Identifier' && !declarator.init.rewritten;
const name = simple ? declarator.init.name : declarator.findScope( true ).createIdentifier( 'ref' );
let c = declarator.start;
let statementGenerators = [];
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 );
});
}
destructure( code, declarator.findScope( false ), declarator.id, name, statementGenerators );
let suffix = `\n${i0}`;
statementGenerators.forEach( ( fn, j ) => {
if ( i === this.declarations.length - 1 && j === statementGenerators.length - 1 ) {
suffix = '';
}
fn( declarator.start, '', suffix );
});
}
if ( declarator.init ) {
declarator.init.transpile( code, transforms );
}
c = declarator.end;
lastDeclaratorIsPattern = declarator.id.type !== 'Identifier';
});
if ( lastDeclaratorIsPattern ) {
code.remove( c, this.end );
}
}
else {
this.declarations.forEach( declarator => {
if ( declarator.init ) declarator.init.transpile( code, transforms );
});
}
}
}
+14
View File
@@ -0,0 +1,14 @@
import Node from '../Node.js';
import CompileError from '../../utils/CompileError.js';
export default class VariableDeclarator extends Node {
initialise ( transforms ) {
let kind = this.parent.kind;
if ( kind === 'let' && this.parent.parent.type === 'ForStatement' ) {
kind = 'for.let'; // special case...
}
this.parent.scope.addDeclaration( this.id, kind );
super.initialise( transforms );
}
}
+88
View File
@@ -0,0 +1,88 @@
import ArrayExpression from './ArrayExpression.js';
import ArrowFunctionExpression from './ArrowFunctionExpression.js';
import AssignmentExpression from './AssignmentExpression.js';
import BinaryExpression from './BinaryExpression.js';
import BreakStatement from './BreakStatement.js';
import CallExpression from './CallExpression.js';
import ClassBody from './ClassBody.js';
import ClassDeclaration from './ClassDeclaration.js';
import ClassExpression from './ClassExpression.js';
import ContinueStatement from './ContinueStatement.js';
import ExportDefaultDeclaration from './ExportDefaultDeclaration.js';
import ExportNamedDeclaration from './ExportNamedDeclaration.js';
import ForStatement from './ForStatement.js';
import ForInStatement from './ForInStatement.js';
import ForOfStatement from './ForOfStatement.js';
import FunctionDeclaration from './FunctionDeclaration.js';
import FunctionExpression from './FunctionExpression.js';
import Identifier from './Identifier.js';
import ImportDeclaration from './ImportDeclaration.js';
import ImportDefaultSpecifier from './ImportDefaultSpecifier.js';
import ImportSpecifier from './ImportSpecifier.js';
import JSXAttribute from './JSXAttribute.js';
import JSXClosingElement from './JSXClosingElement.js';
import JSXElement from './JSXElement.js';
import JSXExpressionContainer from './JSXExpressionContainer.js';
import JSXOpeningElement from './JSXOpeningElement.js';
import JSXSpreadAttribute from './JSXSpreadAttribute.js';
import Literal from './Literal.js';
import LoopStatement from './shared/LoopStatement.js';
import MemberExpression from './MemberExpression.js';
import ObjectExpression from './ObjectExpression.js';
import Property from './Property.js';
import ReturnStatement from './ReturnStatement.js';
import SpreadProperty from './SpreadProperty.js';
import Super from './Super.js';
import TaggedTemplateExpression from './TaggedTemplateExpression.js';
import TemplateElement from './TemplateElement.js';
import TemplateLiteral from './TemplateLiteral.js';
import ThisExpression from './ThisExpression.js';
import UpdateExpression from './UpdateExpression.js';
import VariableDeclaration from './VariableDeclaration.js';
import VariableDeclarator from './VariableDeclarator.js';
export default {
ArrayExpression,
ArrowFunctionExpression,
AssignmentExpression,
BinaryExpression,
BreakStatement,
CallExpression,
ClassBody,
ClassDeclaration,
ClassExpression,
ContinueStatement,
DoWhileStatement: LoopStatement,
ExportNamedDeclaration,
ExportDefaultDeclaration,
ForStatement,
ForInStatement,
ForOfStatement,
FunctionDeclaration,
FunctionExpression,
Identifier,
ImportDeclaration,
ImportDefaultSpecifier,
ImportSpecifier,
JSXAttribute,
JSXClosingElement,
JSXElement,
JSXExpressionContainer,
JSXOpeningElement,
JSXSpreadAttribute,
Literal,
MemberExpression,
ObjectExpression,
Property,
ReturnStatement,
SpreadProperty,
Super,
TaggedTemplateExpression,
TemplateElement,
TemplateLiteral,
ThisExpression,
UpdateExpression,
VariableDeclaration,
VariableDeclarator,
WhileStatement: LoopStatement
};
+85
View File
@@ -0,0 +1,85 @@
import Node from '../../Node.js';
import extractNames from '../../extractNames.js';
export default class LoopStatement extends Node {
findScope ( functionScope ) {
return functionScope || !this.createdScope ? this.parent.findScope( functionScope ) : this.body.scope;
}
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 );
super.initialise( transforms );
if ( transforms.letConst ) {
// see if any block-scoped declarations are referenced
// inside function expressions
const names = Object.keys( this.body.scope.declarations );
let i = names.length;
while ( i-- ) {
const name = names[i];
const declaration = this.body.scope.declarations[ name ];
let j = declaration.instances.length;
while ( j-- ) {
const instance = declaration.instances[j];
const nearestFunctionExpression = instance.findNearest( /Function/ );
if ( nearestFunctionExpression && nearestFunctionExpression.depth > this.depth ) {
this.shouldRewriteAsFunction = true;
break;
}
}
if ( this.shouldRewriteAsFunction ) break;
}
}
}
transpile ( code, transforms ) {
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 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}`;
code.insertRight( this.body.start, before );
code.insertLeft( this.body.end, after );
code.move( this.start, this.body.start, this.body.end );
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;`;
insert += `\n${i0}}`;
code.insertRight( 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}}` );
} else {
code.insertRight( this.body.end, callExpression );
}
}
}
super.transpile( code, transforms );
}
}
+9
View File
@@ -0,0 +1,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 );
}
}
+54
View File
@@ -0,0 +1,54 @@
import types from './types/index.js';
import BlockStatement from './BlockStatement.js';
import Node from './Node.js';
import keys from './keys.js';
const statementsWithBlocks = {
IfStatement: 'consequent',
ForStatement: 'body',
ForInStatement: 'body',
ForOfStatement: 'body',
WhileStatement: 'body',
DoWhileStatement: 'body',
ArrowFunctionExpression: 'body'
};
export default function wrap ( raw, parent ) {
if ( !raw ) return;
if ( 'length' in raw ) {
let i = raw.length;
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;
raw.__wrapped = true;
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 ];
// create a synthetic block statement, otherwise all hell
// breaks loose when it comes to block scoping
raw[ bodyType ] = {
start: expression.start,
end: expression.end,
type: 'BlockStatement',
body: [ expression ],
synthetic: true
};
}
Node( raw, parent );
const type = ( raw.type === 'BlockStatement' ? BlockStatement : types[ raw.type ] ) || Node;
raw.__proto__ = type.prototype;
}
+72
View File
@@ -0,0 +1,72 @@
export const matrix = {
chrome: {
48: 0b1001111011111100111110101111101,
49: 0b1001111111111100111111111111111,
50: 0b1011111111111100111111111111111
},
firefox: {
43: 0b1000111111101100000110111011101,
44: 0b1000111111101100000110111011101,
45: 0b1000111111101100000110111011101
},
safari: {
8: 0b1000000000000000000000000000000,
9: 0b1001111001101100000011101011110
},
ie: {
8: 0b0000000000000000000000000000000,
9: 0b1000000000000000000000000000000,
10: 0b1000000000000000000000000000000,
11: 0b1000000000000000111000001100000
},
edge: {
12: 0b1011110110111100011010001011101,
13: 0b1011111110111100011111001011111
},
node: {
'0.10': 0b1000000000101000000000001000000,
'0.12': 0b1000001000101000000010001000100,
4: 0b1001111000111100111111001111111,
5: 0b1001111000111100111111001111111,
6: 0b1011111111111100111111111111111
}
};
export const features = [
'arrow',
'classes',
'collections',
'computedProperty',
'conciseMethodProperty',
'constLoop',
'constRedef',
'defaultParameter',
'destructuring',
'extendNatives',
'forOf',
'generator',
'letConst',
'letLoop',
'letLoopScope',
'moduleExport',
'moduleImport',
'numericLiteral',
'objectProto',
'objectSuper',
'oldOctalLiteral',
'parameterDestructuring',
'spreadRest',
'stickyRegExp',
'symbol',
'templateString',
'unicodeEscape',
'unicodeIdentifier',
'unicodeRegExp',
// ES2016
'exponentiation',
// additional transforms, not from
// https://featuretests.io
'reservedProperties'
];
+23
View File
@@ -0,0 +1,23 @@
import locate from './locate.js';
import getSnippet from './getSnippet.js';
export default class CompileError extends Error {
constructor ( node, message ) {
super();
const source = node.program.magicString.original;
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.loc = loc;
this.snippet = getSnippet( source, loc, node.end - node.start );
}
toString () {
return `${this.name}: ${this.message}\n${this.snippet}`;
}
}
+11
View File
@@ -0,0 +1,11 @@
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 ) ];
}
+21
View File
@@ -0,0 +1,21 @@
// TODO this function is slightly flawed it works on the original string,
// 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 ) {
const start = node.start;
const end = node.end;
const indentStr = code.getIndentString();
const pattern = new RegExp( indentStr + '\\S', 'g' );
if ( code.original.slice( start - indentStr.length, start ) === indentStr ) {
code.remove( start - indentStr.length, start );
}
const slice = code.original.slice( start, end );
let match;
while ( match = pattern.exec( slice ) ) {
if ( !node.program.indentExclusions[ match.index ] ) code.remove( start + match.index, start + match.index + indentStr.length );
}
}
+170
View File
@@ -0,0 +1,170 @@
import { findIndex } from './array.js';
const handlers = {
ArrayPattern: destructureArrayPattern,
ObjectPattern: destructureObjectPattern,
AssignmentPattern: destructureAssignmentPattern,
Identifier: destructureIdentifier
};
export default function destructure ( code, scope, node, ref, statementGenerators ) {
_destructure( code, scope, node, ref, ref, statementGenerators );
}
function _destructure ( code, scope, node, ref, expr, statementGenerators ) {
const handler = handlers[ node.type ];
if ( !handler ) throw new Error( `not implemented: ${node.type}` );
handler( code, scope, node, ref, expr, statementGenerators );
}
function destructureIdentifier ( code, scope, node, ref, expr, statementGenerators ) {
statementGenerators.push( ( start, prefix, suffix ) => {
code.insertRight( node.start, `${prefix}var ` );
code.insertLeft( node.end, ` = ${expr};${suffix}` );
code.move( node.start, node.end, start );
});
}
function handleProperty ( code, scope, c, node, value, statementGenerators ) {
switch ( node.type ) {
case 'Identifier':
code.remove( c, node.start );
statementGenerators.push( ( start, prefix, suffix ) => {
code.insertRight( node.start, `${prefix}var ` );
code.insertLeft( node.end, ` = ${value};${suffix}` );
code.move( node.start, node.end, start );
});
break;
case 'AssignmentPattern':
let name;
const isIdentifier = node.left.type === 'Identifier';
if ( isIdentifier ) {
name = node.left.name;
const declaration = scope.findDeclaration( name );
if ( declaration ) name = declaration.name;
} else {
name = scope.createIdentifier( value );
}
statementGenerators.push( ( start, prefix, suffix ) => {
code.insertRight( node.right.start, `${prefix}var ${name} = ${value}; if ( ${name} === void 0 ) ${name} = ` );
code.move( node.right.start, node.right.end, start );
code.insertLeft( node.right.end, `;${suffix}` );
});
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, statementGenerators );
}
break;
case 'ObjectPattern':
code.remove( c, c = node.start );
if ( node.properties.length > 1 ) {
const ref = scope.createIdentifier( value );
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 );
});
node.properties.forEach( prop => {
handleProperty( code, scope, c, prop.value, `${ref}.${prop.key.name}`, statementGenerators );
c = prop.end;
});
} else {
const prop = node.properties[0];
handleProperty( code, scope, c, prop.value, `${value}.${prop.key.name}`, statementGenerators );
c = prop.end;
}
code.remove( c, node.end );
break;
case 'ArrayPattern':
code.remove( c, c = node.start );
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}` );
code.move( node.start, c, start );
});
node.elements.forEach( ( element, i ) => {
if ( !element ) return;
handleProperty( code, scope, c, element, `${ref}[${i}]`, statementGenerators );
c = element.end;
});
} else {
const index = findIndex( node.elements, Boolean );
const element = node.elements[ index ];
handleProperty( code, scope, c, element, `${value}[${index}]`, statementGenerators );
c = element.end;
}
code.remove( c, node.end );
break;
default:
throw new Error( `Unexpected node type in destructuring (${node.type})` );
}
}
function destructureArrayPattern ( code, scope, node, ref, expr, statementGenerators ) {
let c = node.start;
node.elements.forEach( ( element, i ) => {
if ( !element ) return;
handleProperty( code, scope, c, element, `${ref}[${i}]`, statementGenerators );
c = element.end;
});
code.remove( c, node.end );
}
function destructureObjectPattern ( code, scope, node, ref, expr, statementGenerators ) {
let c = node.start;
node.properties.forEach( prop => {
handleProperty( code, scope, c, prop.value, `${ref}.${prop.key.name}`, statementGenerators );
c = prop.end;
});
code.remove( c, node.end );
}
function destructureAssignmentPattern ( code, scope, node, ref, expr, statementGenerators ) {
const isIdentifier = node.left.type === 'Identifier';
const name = isIdentifier ? node.left.name : ref;
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 ( !isIdentifier ) {
_destructure( code, scope, node.left, ref, expr, statementGenerators );
}
}
+30
View File
@@ -0,0 +1,30 @@
function pad ( num, len ) {
let result = String( num );
return result + repeat( ' ', len - result.length );
}
function repeat ( str, times ) {
let result = '';
while ( times-- ) result += str;
return result;
}
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 lines = source.split( '\n' ).slice( first, last );
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' );
snippet += '\n' + repeat( ' ', numDigits + 3 + offset ) + repeat( '^', length );
return snippet;
}
+37
View File
@@ -0,0 +1,37 @@
export default function isReference ( node, parent ) {
if ( node.type === 'MemberExpression' ) {
return !node.computed && isReference( node.object, node );
}
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 ( /(Function|Class)Expression/.test( parent.type ) ) return false;
if ( parent.type === 'VariableDeclarator' ) return node === parent.init;
// TODO is this right?
if ( parent.type === 'MemberExpression' || parent.type === 'MethodDefinition' ) {
return parent.computed || node === parent.object;
}
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;
return parent.computed || node === parent.value;
}
// disregard the `bar` in `class Foo { bar () {...} }`
if ( parent.type === 'MethodDefinition' ) return false;
// disregard the `bar` in `export { foo as bar }`
if ( parent.type === 'ExportSpecifier' && node !== parent.local ) return false;
return true;
}
}
+20
View File
@@ -0,0 +1,20 @@
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 ) {
var line = lines[i];
var lineEnd = lineStart + line.length + 1; // +1 for newline
if ( lineEnd > index ) {
return { line: i + 1, column: index - lineStart, char: i };
}
lineStart = lineEnd;
}
throw new Error( 'Could not determine location of character' );
}
+5
View File
@@ -0,0 +1,5 @@
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;
+46
View File
@@ -0,0 +1,46 @@
export function isArguments ( node ) {
return node.type === 'Identifier' && node.name === 'arguments';
}
export default function spread ( code, elements, start, argumentsArrayAlias ) {
let i = elements.length;
let firstSpreadIndex = -1;
while ( i-- ) {
const element = elements[i];
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
let element = elements[ firstSpreadIndex ];
const previousElement = elements[ firstSpreadIndex - 1 ];
if ( !previousElement ) {
code.remove( start, element.start );
code.overwrite( element.end, elements[1].start, '.concat( ' );
} else {
code.overwrite( previousElement.end, element.start, ' ].concat( ' );
}
for ( i = firstSpreadIndex; i < elements.length; i += 1 ) {
element = elements[i];
if ( element ) {
if ( element.type === 'SpreadElement' ) {
code.remove( element.start, element.argument.start );
} else {
code.insertRight( element.start, '[' );
code.insertLeft( element.end, ']' );
}
}
}
return true; // true indicates some spread elements
}