Comparing version 0.8.5 to 0.9.0
# buble changelog | ||
## 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 | ||
@@ -4,0 +9,0 @@ |
{ | ||
"name": "buble", | ||
"version": "0.8.5", | ||
"version": "0.9.0", | ||
"description": "The blazing fast, batteries-included ES2015 compiler", | ||
@@ -5,0 +5,0 @@ "main": "dist/buble.umd.js", |
@@ -5,2 +5,3 @@ import wrap from './wrap.js'; | ||
import CompileError from '../utils/CompileError.js'; | ||
import destructure from '../utils/destructure.js'; | ||
@@ -89,17 +90,3 @@ export default class BlockStatement extends Node { | ||
if ( /Function/.test( this.parent.type ) ) { | ||
// object pattern | ||
if ( transforms.parameterDestructuring ) { | ||
this.transpileObjectPattern( code, introStatementGenerators ); | ||
this.transpileArrayPattern( code, introStatementGenerators ); | ||
} | ||
// default parameters | ||
if ( transforms.defaultParameter ) { | ||
this.transpileDefaultParameters( code, introStatementGenerators ); | ||
} | ||
// rest parameter | ||
if ( transforms.spreadRest ) { | ||
this.transpileRestElement( code, introStatementGenerators, indentation ); | ||
} | ||
this.transpileParameters( code, transforms, indentation, introStatementGenerators ); | ||
} | ||
@@ -141,2 +128,58 @@ | ||
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 ) { | ||
@@ -161,2 +204,3 @@ Object.keys( this.scope.blockScopedDeclarations ).forEach( name => { | ||
declaration.name = outerAlias; | ||
code.overwrite( declaration.node.start, declaration.node.end, outerAlias, true ); | ||
@@ -187,2 +231,3 @@ forStatement.aliases[ name ] = { | ||
declaration.name = alias; | ||
code.overwrite( declaration.node.start, declaration.node.end, alias, true ); | ||
@@ -198,142 +243,2 @@ for ( const identifier of declaration.instances ) { | ||
} | ||
transpileDefaultParameters ( code, introStatementGenerators ) { | ||
this.parent.params.filter( param => param.type === 'AssignmentPattern' ).forEach( param => { | ||
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}` ); | ||
}); | ||
}); | ||
} | ||
transpileObjectPattern ( code, introStatementGenerators ) { | ||
const params = this.parent.params; | ||
const objectPatterns = params.filter( param => param.type === 'ObjectPattern' ); | ||
const assignmentPatterns = params.filter( param => param.type === 'AssignmentPattern' ) | ||
.map( exp => exp.left ).filter( param => param.type === 'ObjectPattern' ); | ||
[].concat(objectPatterns, assignmentPatterns).forEach( param => { | ||
const ref = this.scope.createIdentifier( 'ref' ); | ||
param.name = ref; | ||
code.insertRight( param.start, ref ); | ||
let c = param.start; | ||
param.properties.forEach( prop => { | ||
code.remove( c, prop.value.start ); | ||
const key = prop.key.name; | ||
if ( prop.value.type === 'Identifier' ) { | ||
code.remove( prop.value.start, prop.value.end ); | ||
const value = prop.value.name; | ||
const declaration = this.scope.findDeclaration( value ); | ||
if ( declaration.instances.length === 1 ) { | ||
const instance = declaration.instances[0]; | ||
code.overwrite( instance.start, instance.end, `${ref}.${key}` ); | ||
} else { | ||
introStatementGenerators.push( ( start, prefix, suffix ) => { | ||
code.insertLeft( start, `${prefix}var ${value} = ${ref}.${key};${suffix}` ); | ||
}); | ||
} | ||
} | ||
else if ( prop.value.type === 'AssignmentPattern' ) { | ||
introStatementGenerators.push( ( start, prefix, suffix ) => { | ||
code.remove( prop.value.start, prop.value.right.start ); | ||
const value = prop.value.left.name; | ||
code | ||
.insertRight( prop.value.right.start, `${prefix}var ${ref}_${key} = ${ref}.${key}, ${value} = ${ref}_${key} === void 0 ? ` ) | ||
.insertLeft( prop.value.right.end, ` : ${ref}_${key};${suffix}` ) | ||
.move( prop.value.right.start, prop.value.right.end, start ); | ||
}); | ||
} | ||
else { | ||
throw new CompileError( prop, `Compound destructuring is not supported` ); | ||
} | ||
c = prop.value.end; | ||
}); | ||
code.remove( c, param.end ); | ||
}); | ||
} | ||
transpileArrayPattern ( code, introStatementGenerators ) { | ||
// array pattern. TODO dry this out | ||
this.parent.params.filter( param => param.type === 'ArrayPattern' ).forEach( param => { | ||
const ref = this.scope.createIdentifier( 'ref' ); | ||
code.insertRight( param.start, ref ); | ||
let c = param.start; | ||
param.elements.forEach( ( element, i ) => { | ||
code.remove( c, element.start ); | ||
if ( element.type === 'Identifier' ) { | ||
code.remove( element.start, element.end ); | ||
introStatementGenerators.push( ( start, prefix, suffix ) => { | ||
code.insertLeft( start, `${prefix}var ${element.name} = ${ref}[${i}];${suffix}` ); | ||
}); | ||
} else if ( element.type === 'AssignmentPattern' ) { | ||
introStatementGenerators.push( ( start, prefix, suffix ) => { | ||
code.remove( element.start, element.right.start ); | ||
const name = element.left.name; | ||
code | ||
.insertRight( element.right.start, `${prefix}var ${ref}_${i} = ref[${i}], ${name} = ref_${i} === void 0 ? ` ) | ||
.insertLeft( element.right.end, ` : ref_${i};${suffix}` ) | ||
.move( element.right.start, element.right.end, start ); | ||
}); | ||
} | ||
else { | ||
throw new CompileError( element, `Compound destructuring is not supported` ); | ||
} | ||
c = element.end; | ||
}); | ||
code.remove( c, param.end ); | ||
}); | ||
} | ||
transpileRestElement ( code, introStatementGenerators, indentation ) { | ||
const params = this.parent.params; | ||
const lastParam = params[ params.length - 1 ]; | ||
if ( lastParam && lastParam.type === 'RestElement' ) { | ||
introStatementGenerators.push( ( start, prefix, suffix ) => { | ||
const penultimateParam = params[ params.length - 2 ]; | ||
if ( penultimateParam ) { | ||
code.remove( penultimateParam ? penultimateParam.end : lastParam.start, lastParam.end ); | ||
} else { | ||
let start = lastParam.start, end = lastParam.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 = lastParam.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}` ); | ||
} | ||
}); | ||
} | ||
} | ||
} |
@@ -8,8 +8,8 @@ export default function extractNames ( node ) { | ||
const extractors = { | ||
Identifier ( names, param ) { | ||
names.push( param.name ); | ||
Identifier ( names, node ) { | ||
names.push( node ); | ||
}, | ||
ObjectPattern ( names, param ) { | ||
for ( const prop of param.properties ) { | ||
ObjectPattern ( names, node ) { | ||
for ( const prop of node.properties ) { | ||
extractors[ prop.value.type ]( names, prop.value ); | ||
@@ -19,4 +19,4 @@ } | ||
ArrayPattern ( names, param ) { | ||
for ( const element of param.elements ) { | ||
ArrayPattern ( names, node ) { | ||
for ( const element of node.elements ) { | ||
if ( element ) extractors[ element.type ]( names, element ); | ||
@@ -26,9 +26,9 @@ } | ||
RestElement ( names, param ) { | ||
extractors[ param.argument.type ]( names, param.argument ); | ||
RestElement ( names, node ) { | ||
extractors[ node.argument.type ]( names, node.argument ); | ||
}, | ||
AssignmentPattern ( names, param ) { | ||
extractors[ param.left.type ]( names, param.left ); | ||
AssignmentPattern ( names, node ) { | ||
extractors[ node.left.type ]( names, node.left ); | ||
} | ||
}; |
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 { | ||
@@ -86,4 +106,8 @@ constructor ( raw, parent ) { | ||
toJSON () { | ||
return toJSON( this ); | ||
} | ||
toString () { | ||
return this.program.magicString.slice( this.start, this.end ); | ||
return this.program.magicString.original.slice( this.start, this.end ); | ||
} | ||
@@ -90,0 +114,0 @@ |
@@ -25,10 +25,11 @@ import extractNames from './extractNames.js'; | ||
addDeclaration ( node, kind ) { | ||
for ( const name of extractNames( node ) ) { | ||
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( node, `${name} is already declared` ); | ||
throw new CompileError( identifier, `${name} is already declared` ); | ||
} | ||
const declaration = { name, node, kind, instances: [] }; | ||
const declaration = { name, node: identifier, kind, instances: [] }; | ||
this.declarations[ name ] = declaration; | ||
@@ -35,0 +36,0 @@ |
@@ -22,3 +22,3 @@ import Node from '../Node.js'; | ||
const arrowFunction = this.findNearest( 'ArrowFunctionExpression' ); | ||
const loop = this.findNearest( /(?:For|While)Statement/ ); | ||
const loop = this.findNearest( /(?:For(?:In|Of)?|While)Statement/ ); | ||
@@ -29,3 +29,3 @@ if ( arrowFunction && arrowFunction.depth > lexicalBoundary.depth ) { | ||
if ( loop && loop.depth > lexicalBoundary.depth ) { | ||
if ( loop && loop.body.contains( this ) && loop.depth > lexicalBoundary.depth ) { | ||
this.alias = lexicalBoundary.getArgumentsAlias(); | ||
@@ -32,0 +32,0 @@ } |
@@ -8,3 +8,3 @@ import Node from '../Node.js'; | ||
const arrowFunction = this.findNearest( 'ArrowFunctionExpression' ); | ||
const loop = this.findNearest( /(?:For|While)Statement/ ); | ||
const loop = this.findNearest( /(?:For(?:In|Of)?|While)Statement/ ); | ||
@@ -15,3 +15,3 @@ if ( arrowFunction && arrowFunction.depth > lexicalBoundary.depth ) { | ||
if ( loop && loop.depth > lexicalBoundary.depth ) { | ||
if ( loop && loop.body.contains( this ) && loop.depth > lexicalBoundary.depth ) { | ||
this.alias = lexicalBoundary.getThisAlias(); | ||
@@ -18,0 +18,0 @@ } |
import Node from '../Node.js'; | ||
import destructure from '../../utils/destructure.js'; | ||
@@ -10,8 +11,77 @@ export default class VariableDeclaration extends Node { | ||
transpile ( code, transforms ) { | ||
if ( transforms.letConst && this.kind !== 'var' ) { | ||
code.overwrite( this.start, this.start + this.kind.length, 'var', true ); | ||
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 ); | ||
} | ||
this.declarations.forEach( declarator => declarator.transpile( code, transforms ) ); | ||
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 i0 = declarator.getIndentation(); | ||
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( true ), 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 ); | ||
}); | ||
} | ||
} | ||
} |
@@ -6,13 +6,2 @@ import Node from '../Node.js'; | ||
initialise ( transforms ) { | ||
this.isObjectPattern = this.id.type === 'ObjectPattern'; | ||
// disallow compound destructuring, for now at least | ||
if ( /Pattern/.test( this.id.type ) ) { | ||
this.id[ this.isObjectPattern ? 'properties' : 'elements' ].forEach( node => { | ||
if ( node && /Pattern/.test( this.isObjectPattern ? node.value.type : node.type ) ) { | ||
throw new CompileError( node.value, 'Compound destructuring is not supported' ); | ||
} | ||
}); | ||
} | ||
let kind = this.parent.kind; | ||
@@ -26,45 +15,2 @@ if ( kind === 'let' && this.parent.parent.type === 'ForStatement' ) { | ||
} | ||
transpile ( code, transforms ) { | ||
if ( transforms.destructuring && this.id.type !== 'Identifier' ) { | ||
const simple = this.init.type === 'Identifier' && !this.init.rewritten; | ||
const props = this.isObjectPattern ? this.id.properties : this.id.elements; | ||
const i0 = this.getIndentation(); | ||
const name = simple ? this.init.name : this.findScope( true ).createIdentifier( 'ref' ); | ||
let c = this.start; | ||
let first = simple; | ||
if ( simple ) { | ||
code.remove( this.id.end, this.end ); | ||
} else { | ||
code.insertRight( this.id.end, `${name}` ); | ||
code.move( this.id.end, this.end, c ); | ||
} | ||
props.forEach( ( property, i ) => { | ||
if ( property ) { | ||
const id = this.isObjectPattern ? property.value : property; | ||
const rhs = this.isObjectPattern ? `${name}.${property.key.name}` : `${name}[${i}]`; | ||
let start = first ? '' : 'var '; | ||
if ( !first ) start = `;\n${i0}${start}`; | ||
code.insertRight( id.start, start ); | ||
code.move( id.start, id.end, this.start ); | ||
code.insertLeft( id.end, ` = ${rhs}` ); | ||
code.remove( c, id.start ); | ||
c = property.end; | ||
first = false; | ||
} | ||
}); | ||
code.remove( c, this.id.end ); | ||
} | ||
super.transpile( code, transforms ); | ||
} | ||
} |
@@ -14,2 +14,4 @@ export default function isReference ( node, parent ) { | ||
if ( parent.type === 'VariableDeclarator' ) return node === parent.init; | ||
// TODO is this right? | ||
@@ -20,4 +22,9 @@ if ( parent.type === 'MemberExpression' || parent.type === 'MethodDefinition' ) { | ||
if ( parent.type === 'ArrayPattern' ) return false; | ||
// disregard the `bar` in `{ bar: foo }`, but keep it in `{ [bar]: foo }` | ||
if ( parent.type === 'Property' ) return parent.computed || node === parent.value; | ||
if ( parent.type === 'Property' ) { | ||
if ( parent.parent.type === 'ObjectPattern' ) return false; | ||
return parent.computed || node === parent.value; | ||
} | ||
@@ -24,0 +31,0 @@ // disregard the `bar` in `class Foo { bar () {...} }` |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
67
1249119
11432