jstransform
Advanced tools
Comparing version 7.0.0 to 8.0.0
var jstransform = require('./src/jstransform'); | ||
var arrowFuncVisitors = require('./visitors/es6-arrow-function-visitors'); | ||
var restParamVisitors = require('./visitors/es6-rest-param-visitors'); | ||
var es7SpreadPropertyVisitors = require('./visitors/es7-spread-property-visitors'); | ||
@@ -8,5 +9,6 @@ exports.process = function(sourceText, sourcePath) { | ||
arrowFuncVisitors.visitorList | ||
.concat(restParamVisitors.visitorList), | ||
.concat(restParamVisitors.visitorList) | ||
.concat(es7SpreadPropertyVisitors.visitorList), | ||
sourceText | ||
).code; | ||
}; |
{ | ||
"name": "jstransform", | ||
"version": "7.0.0", | ||
"version": "8.0.0", | ||
"description": "A simple AST visitor-based JS transformer", | ||
@@ -24,3 +24,3 @@ "contributors": [ | ||
"base62": "0.1.1", | ||
"esprima-fb": "~7001.0001.0000-dev-harmony-fb", | ||
"esprima-fb": "8001.1.0-dev-harmony-fb", | ||
"source-map": "0.1.31" | ||
@@ -41,3 +41,4 @@ }, | ||
"jest": { | ||
"scriptPreprocessor": "<rootDir>/jestPreprocessor.js" | ||
"scriptPreprocessor": "<rootDir>/jestPreprocessor.js", | ||
"setupEnvScriptFile": "<rootDir>/jestEnvironment.js" | ||
}, | ||
@@ -44,0 +45,0 @@ "scripts": { |
@@ -451,4 +451,4 @@ /** | ||
boundaryNode: metaData.boundaryNode, | ||
path: metaData.path, | ||
node: metaData.node, | ||
path: metaData.bindingPath, | ||
node: metaData.bindingNode, | ||
state: Object.create(state) | ||
@@ -546,5 +546,11 @@ }; | ||
function containsChildOfType(node, type) { | ||
return containsChildMatching(node, function(node) { | ||
return node.type === type; | ||
}); | ||
} | ||
function containsChildMatching(node, matcher) { | ||
var foundMatchingChild = false; | ||
function nodeTypeAnalyzer(node) { | ||
if (node.type === type) { | ||
if (matcher(node) === true) { | ||
foundMatchingChild = true; | ||
@@ -556,3 +562,3 @@ return false; | ||
if (!foundMatchingChild) { | ||
foundMatchingChild = containsChildOfType(child, type); | ||
foundMatchingChild = containsChildMatching(child, matcher); | ||
} | ||
@@ -586,2 +592,10 @@ } | ||
function getTempVar(tempVarIndex) { | ||
return '$__' + tempVarIndex; | ||
} | ||
function getTempVarWithValue(tempVarIndex, tempVarValue) { | ||
return getTempVar(tempVarIndex) + '=' + tempVarValue; | ||
} | ||
exports.append = append; | ||
@@ -592,2 +606,3 @@ exports.catchup = catchup; | ||
exports.catchupNewlines = catchupNewlines; | ||
exports.containsChildMatching = containsChildMatching; | ||
exports.containsChildOfType = containsChildOfType; | ||
@@ -610,1 +625,3 @@ exports.createState = createState; | ||
exports.getNodeSourceText = getNodeSourceText; | ||
exports.getTempVar = getTempVar; | ||
exports.getTempVarWithValue = getTempVarWithValue; |
@@ -44,3 +44,2 @@ /** | ||
it('should capture correct this value at different levels', function() { | ||
var code = transform([ | ||
@@ -75,2 +74,12 @@ 'var foo = {', | ||
it('binds if any `super` keyword is referenced', function() { | ||
var code = transform( | ||
'var fn=x=>super;' | ||
); | ||
// We have to do a source text comparison here because `super` is a reserved | ||
// keyword (so we can't eval it). | ||
expect(code).toEqual('var fn=function(x){return super;}.bind(this);'); | ||
}); | ||
it('should filter an array using arrow with two params', function() { | ||
@@ -108,3 +117,2 @@ this.factor = 0; | ||
it('should correctly transform arrows', function() { | ||
// 0 params, expression. | ||
@@ -231,5 +239,4 @@ expectTransform( | ||
); | ||
}); | ||
}); | ||
@@ -378,3 +378,4 @@ /** | ||
it('handles extension from an expression', function() { | ||
// ES6 draft section 14.5 | ||
it('handles extension from a left hand expression', function() { | ||
var code = transform([ | ||
@@ -387,3 +388,3 @@ 'function Parent1() {}', | ||
'class Child extends true ? Parent1 : Parent2 {}' | ||
'class Child extends (true ? Parent1 : Parent2) {}' | ||
].join('\n')); | ||
@@ -1443,2 +1444,24 @@ | ||
}); | ||
describe('preserve newlines', function() { | ||
it('preserve newlines', function() { | ||
expect(transform([ | ||
'class Foo {', | ||
' A', | ||
' (', | ||
' x, y) {', | ||
' bar();', | ||
' }', | ||
'}' | ||
].join('\n'))).toBe([ | ||
'function Foo(){"use strict";}', | ||
' Foo.prototype.A=function(', | ||
'', | ||
'x, y) {"use strict";', | ||
' bar();', | ||
' };', | ||
'' | ||
].join('\n')) | ||
}); | ||
}); | ||
}); |
@@ -61,2 +61,11 @@ /** | ||
it('should handle literal property names', function() { | ||
var code = transform([ | ||
'var {x, "y y": yy, 0: z} = {x: 10, "y y": 20, 0: 30};', | ||
'([x, yy, z]);' | ||
].join('\n')); | ||
expect(eval(code)).toEqual([10, 20, 30]); | ||
}); | ||
it('should handle array pattern assignment expression', function() { | ||
@@ -72,2 +81,14 @@ var code = transform([ | ||
it('should should not redeclare vars with assignment expression', function() { | ||
var code = transform([ | ||
'var x = 10, y = 20;', | ||
'(function() {', | ||
' [x, y] = [y, x];', | ||
'})();', | ||
'([x, y]);' | ||
].join('\n')); | ||
expect(eval(code)).toEqual([20, 10]); | ||
}); | ||
it('should handle object pattern assignment expression', function() { | ||
@@ -74,0 +95,0 @@ var code = transform([ |
@@ -263,3 +263,5 @@ /** | ||
'function foo(x, y, ...args) { return x + y + args[0]; }', | ||
'function foo(x, y ) {var args=Array.prototype.slice.call(arguments,2); return x + y + args[0]; }' | ||
'function foo(x, y ) {for (var args=[],$__0=2,$__1=arguments.length;' + | ||
'$__0<$__1;$__0++) args.push(arguments[$__0]); return x + y + ' + | ||
'args[0]; }' | ||
); | ||
@@ -271,3 +273,4 @@ }) | ||
'(function(x, ...args) { return args;});', | ||
'(function(x ) {var args=Array.prototype.slice.call(arguments,1); return args;});' | ||
'(function(x ) {for (var args=[],$__0=1,$__1=arguments.length;' + | ||
'$__0<$__1;$__0++) args.push(arguments[$__0]); return args;});' | ||
); | ||
@@ -279,3 +282,5 @@ }); | ||
'map(function(...args) { return args.map(log); });', | ||
'map(function() {var args=Array.prototype.slice.call(arguments,0); return args.map(log); });' | ||
'map(function() {for (var args=[],$__0=0,$__1=arguments.length;' + | ||
'$__0<$__1;$__0++) args.push(arguments[$__0]); ' + | ||
'return args.map(log); });' | ||
); | ||
@@ -306,3 +311,4 @@ }); | ||
'', | ||
' {var args=Array.prototype.slice.call(arguments,1);', | ||
' {for (var args=[],$__0=1,$__1=arguments.length;$__0<$__1;' + | ||
'$__0++) args.push(arguments[$__0]);', | ||
' return args;', | ||
@@ -317,3 +323,4 @@ '}' | ||
'function foo(/*string*/foo, /*bool*/bar ) {' + | ||
'var args=Array.prototype.slice.call(arguments,2); ' + | ||
'for (var args=[],$__0=2,$__1=arguments.length;$__0<$__1;$__0++) ' + | ||
'args.push(arguments[$__0]); ' + | ||
'return args; ' + | ||
@@ -320,0 +327,0 @@ '}' |
@@ -11,6 +11,3 @@ /** | ||
var transformFn; | ||
var shortObjectsVisitors; | ||
var spreadPropertyVisitors; | ||
var originalAssign = Object.assign; | ||
var visitors; | ||
@@ -28,8 +25,3 @@ | ||
shortObjectsVisitors = require('../es6-object-short-notation-visitors').visitorList; | ||
spreadPropertyVisitors = require('../es7-spread-property-visitors').visitorList; | ||
visitors = spreadPropertyVisitors.concat( | ||
shortObjectsVisitors | ||
); | ||
visitors = require('../es7-spread-property-visitors').visitorList; | ||
}); | ||
@@ -52,2 +44,13 @@ | ||
afterEach(function() { | ||
Object.assign = originalAssign; | ||
}); | ||
// Polyfill sanity checks | ||
it('has access to a working Object.assign implementation', function() { | ||
expect(typeof Object.assign).toBe('function'); | ||
expect(Object.assign({ b: 2 }, null, { a: 1 })).toEqual({ a: 1, b: 2 }); | ||
}); | ||
// Semantic tests. | ||
@@ -63,3 +66,3 @@ | ||
expectObjectAssign( | ||
'var xyz = { ...x, y: 2, z }' | ||
'var xyz = { ...x, y: 2, z: z }' | ||
).toBeCalledWith({}, x, { y: 2, z: z }); | ||
@@ -109,2 +112,16 @@ | ||
it('should remove trailing commas after properties', function() { | ||
expectTransform( | ||
'let xyz = { ...x, y: 1, }', | ||
'let xyz = Object.assign({}, x, {y: 1 })' | ||
); | ||
}); | ||
it('should remove trailing commas after spread', function() { | ||
expectTransform( | ||
'let xyz = { x: 1, ...y, }', | ||
'let xyz = Object.assign({ x: 1}, y )' | ||
); | ||
}); | ||
// Don't transform | ||
@@ -136,2 +153,8 @@ | ||
it('should silently ignore falsy values', function() { | ||
var x = null; | ||
var y = { y: 'y' }; | ||
var obj = { ...x, ...y, ...{ ...false, z: 'z', ...y } }; | ||
expect(obj).toEqual({ y: 'y', z: 'z' }); | ||
}); | ||
}); |
@@ -43,2 +43,5 @@ /** | ||
expect(transform(code)).toEqual('foo["delete"];'); | ||
code = '(foo++).delete;'; | ||
expect(transform(code)).toEqual('(foo++)["delete"];'); | ||
}); | ||
@@ -45,0 +48,0 @@ |
@@ -75,3 +75,10 @@ /** | ||
// inside it or inside any sub-expression. | ||
if (utils.containsChildOfType(node.body, Syntax.ThisExpression)) { | ||
var containsBindingSyntax = | ||
utils.containsChildMatching(node.body, function(node) { | ||
return node.type === Syntax.ThisExpression | ||
|| (node.type === Syntax.Identifier | ||
&& node.name === "super"); | ||
}); | ||
if (containsBindingSyntax) { | ||
utils.append('.bind(this)', state); | ||
@@ -117,3 +124,3 @@ } | ||
utils.append( | ||
restParamVisitors.renderRestParamSetup(node), | ||
restParamVisitors.renderRestParamSetup(node, state), | ||
state | ||
@@ -120,0 +127,0 @@ ); |
@@ -231,3 +231,3 @@ /** | ||
if (params.length > 0) { | ||
utils.move(params[0].range[0], state); | ||
utils.catchupNewlines(params[0].range[0], state); | ||
for (var i = 0; i < params.length; i++) { | ||
@@ -234,0 +234,0 @@ utils.catchup(node.params[i].range[0], state); |
@@ -56,3 +56,3 @@ /** | ||
// Allocate new temp for the pattern. | ||
utils.append(getTmpVar(state.localScope.tempVarIndex) + '=', state); | ||
utils.append(utils.getTempVar(state.localScope.tempVarIndex) + '=', state); | ||
// Skip the pattern and assign the init to the temp. | ||
@@ -97,3 +97,3 @@ utils.catchupWhiteSpace(node.init.range[0], state); | ||
'=Array.prototype.slice.call(' + | ||
getTmpVar(tmpIndex) + ',' + idx + ')' | ||
utils.getTempVar(tmpIndex) + ',' + idx + ')' | ||
); | ||
@@ -105,3 +105,3 @@ continue; | ||
var restExpression = restPropertyHelpers.renderRestExpression( | ||
getTmpVar(tmpIndex), | ||
utils.getTempVar(tmpIndex), | ||
patternItems | ||
@@ -125,4 +125,4 @@ ); | ||
components.push( | ||
getInitialValue(++state.localScope.tempVarIndex, accessor) + ',' + | ||
getDestructuredComponents(value, state) | ||
utils.getTempVarWithValue(++state.localScope.tempVarIndex, accessor) + | ||
',' + getDestructuredComponents(value, state) | ||
); | ||
@@ -140,10 +140,12 @@ } | ||
function getPatternItemAccessor(node, patternItem, tmpIndex, idx) { | ||
var tmpName = getTmpVar(tmpIndex); | ||
var tmpName = utils.getTempVar(tmpIndex); | ||
if (node.type === Syntax.ObjectPattern) { | ||
if (reservedWordsHelper.isReservedWord(patternItem.key.name)) { | ||
return tmpName + '["' + patternItem.key.name + '"]'; | ||
} else { | ||
} else if (patternItem.key.type === Syntax.Literal) { | ||
return tmpName + '[' + JSON.stringify(patternItem.key.value) + ']'; | ||
} else if (patternItem.key.type === Syntax.Identifier) { | ||
return tmpName + '.' + patternItem.key.name; | ||
} | ||
} else { | ||
} else if (node.type === Syntax.ArrayPattern) { | ||
return tmpName + '[' + idx + ']'; | ||
@@ -159,10 +161,2 @@ } | ||
function getInitialValue(index, value) { | ||
return getTmpVar(index) + '=' + value; | ||
} | ||
function getTmpVar(index) { | ||
return '$__' + index; | ||
} | ||
// ------------------------------------------------------- | ||
@@ -177,3 +171,3 @@ // 2. Assignment expression. | ||
var exprNode = node.expression; | ||
utils.append('var ' + getTmpVar(state.localScope.tempVarIndex) + '=', state); | ||
utils.append('var ' + utils.getTempVar(state.localScope.tempVarIndex) + '=', state); | ||
@@ -185,3 +179,3 @@ utils.catchupWhiteSpace(exprNode.right.range[0], state); | ||
utils.append( | ||
',' + getDestructuredComponents(exprNode.left, state) + ';', | ||
';' + getDestructuredComponents(exprNode.left, state) + ';', | ||
state | ||
@@ -211,3 +205,3 @@ ); | ||
function visitStructuredParameter(traverse, node, path, state) { | ||
utils.append(getTmpVar(getParamIndex(node, path)), state); | ||
utils.append(utils.getTempVar(getParamIndex(node, path)), state); | ||
utils.catchupWhiteSpace(node.range[1], state); | ||
@@ -257,3 +251,3 @@ return true; | ||
utils.append( | ||
restParamVisitors.renderRestParamSetup(funcNode), | ||
restParamVisitors.renderRestParamSetup(funcNode, state), | ||
state | ||
@@ -260,0 +254,0 @@ ); |
@@ -27,3 +27,3 @@ /** | ||
* | ||
* // Destrucruting. | ||
* // Destructuring. | ||
* function init({port, ip, coords: {x, y}}) { ... } | ||
@@ -30,0 +30,0 @@ * |
@@ -20,12 +20,16 @@ /** | ||
/** | ||
* Desugars ES6 rest parameters into ES3 arguments slicing. | ||
* Desugars ES6 rest parameters into an ES3 arguments array. | ||
* | ||
* function printf(template, ...args) { | ||
* args.forEach(...); | ||
* }; | ||
* } | ||
* | ||
* We could use `Array.prototype.slice.call`, but that usage of arguments causes | ||
* functions to be deoptimized in V8, so instead we use a for-loop. | ||
* | ||
* function printf(template) { | ||
* var args = [].slice.call(arguments, 1); | ||
* for (var args = [], $__0 = 1, $__1 = arguments.length; $__0 < $__1; $__0++) | ||
* args.push(arguments[$__0]); | ||
* args.forEach(...); | ||
* }; | ||
* } | ||
* | ||
@@ -75,7 +79,12 @@ */ | ||
function renderRestParamSetup(functionNode) { | ||
return 'var ' + functionNode.rest.name + '=Array.prototype.slice.call(' + | ||
'arguments,' + | ||
functionNode.params.length + | ||
');'; | ||
function renderRestParamSetup(functionNode, state) { | ||
var idx = state.localScope.tempVarIndex++; | ||
var len = state.localScope.tempVarIndex++; | ||
return 'for (var ' + functionNode.rest.name + '=[],' + | ||
utils.getTempVarWithValue(idx, functionNode.params.length) + ',' + | ||
utils.getTempVarWithValue(len, 'arguments.length') + ';' + | ||
utils.getTempVar(idx) + '<' + utils.getTempVar(len) + ';' + | ||
utils.getTempVar(idx) + '++) ' + | ||
functionNode.rest.name + '.push(arguments[' + utils.getTempVar(idx) + ']);'; | ||
} | ||
@@ -86,3 +95,3 @@ | ||
var parentNode = path[0]; | ||
utils.append(renderRestParamSetup(parentNode), state); | ||
utils.append(renderRestParamSetup(parentNode, state), state); | ||
return true; | ||
@@ -89,0 +98,0 @@ } |
@@ -72,3 +72,7 @@ /** | ||
utils.catchup(node.range[1] - 1, state); | ||
// Strip any non-whitespace between the last item and the end. | ||
// We only catch up on whitespace so that we ignore any trailing commas which | ||
// are stripped out for IE8 support. Unfortunately, this also strips out any | ||
// trailing comments. | ||
utils.catchupWhiteSpace(node.range[1] - 1, state); | ||
@@ -75,0 +79,0 @@ // Skip the trailing } |
@@ -42,3 +42,3 @@ /** | ||
var reservedWordsMap = {}; | ||
var reservedWordsMap = Object.create(null); | ||
RESERVED_WORDS.forEach(function(k) { | ||
@@ -45,0 +45,0 @@ reservedWordsMap[k] = true; |
@@ -50,3 +50,3 @@ /** | ||
traverse(node.object, path, state); | ||
utils.catchup(node.object.range[1], state); | ||
utils.catchup(node.property.range[0] - 1, state); | ||
utils.append('[', state); | ||
@@ -53,0 +53,0 @@ utils.catchupWhiteSpace(node.property.range[0], state); |
@@ -13,2 +13,3 @@ var esprima = require('esprima-fb'); | ||
function visitClassProperty(traverse, node, path, state) { | ||
utils.catchup(node.range[0], state); | ||
utils.catchupWhiteOut(node.range[1], state); | ||
@@ -21,3 +22,20 @@ return false; | ||
function visitTypeAlias(traverse, node, path, state) { | ||
utils.catchupWhiteOut(node.range[1], state); | ||
return false; | ||
} | ||
visitTypeAlias.test = function(node, path, state) { | ||
return node.type === Syntax.TypeAlias; | ||
}; | ||
function visitInterfaceDeclaration(traverse, node, path, state) { | ||
utils.catchupWhiteOut(node.range[1], state); | ||
return false; | ||
} | ||
visitInterfaceDeclaration.test = function(node, path, state) { | ||
return node.type === Syntax.InterfaceDeclaration; | ||
}; | ||
function visitFunctionParametricAnnotation(traverse, node, path, state) { | ||
utils.catchup(node.range[0], state); | ||
utils.catchupWhiteOut(node.range[1], state); | ||
@@ -27,10 +45,11 @@ return false; | ||
visitFunctionParametricAnnotation.test = function(node, path, state) { | ||
return node.type === Syntax.ParametricTypeAnnotation | ||
return node.type === Syntax.TypeParameterDeclaration | ||
&& path[0] | ||
&& _isFunctionNode(path[0]) | ||
&& node === path[0].parametricType; | ||
&& node === path[0].typeParameters; | ||
}; | ||
function visitFunctionReturnAnnotation(traverse, node, path, state) { | ||
utils.catchupWhiteOut(node.range[1], state); | ||
utils.catchup(node.range[0], state); | ||
utils.move(node.range[1] + 1, state); | ||
return false; | ||
@@ -43,11 +62,9 @@ } | ||
function visitOptionalFunctionParameterAnnotation(traverse, node, path, state) { | ||
path.unshift(node); | ||
traverse(node.id, path, state); | ||
path.shift(); | ||
utils.catchup(node.id.range[1], state); | ||
utils.catchupWhiteOut(node.range[1], state); | ||
utils.catchup(node.range[0] + node.name.length, state); | ||
utils.move(node.range[1], state); | ||
return false; | ||
} | ||
visitOptionalFunctionParameterAnnotation.test = function(node, path, state) { | ||
return node.type === Syntax.OptionalParameter | ||
return node.type === Syntax.Identifier | ||
&& node.optional | ||
&& path[0] | ||
@@ -58,17 +75,63 @@ && _isFunctionNode(path[0]); | ||
function visitTypeAnnotatedIdentifier(traverse, node, path, state) { | ||
traverse(node.id, path, state); | ||
utils.catchup(node.id.range[1], state); | ||
utils.catchupWhiteOut(node.range[1], state); | ||
utils.catchup(node.typeAnnotation.range[0], state); | ||
utils.move(node.typeAnnotation.range[1], state); | ||
return false; | ||
} | ||
visitTypeAnnotatedIdentifier.test = function(node, path, state) { | ||
return node.type === Syntax.TypeAnnotatedIdentifier; | ||
return node.type === Syntax.Identifier && node.typeAnnotation; | ||
}; | ||
function visitTypeAnnotatedObjectOrArrayPattern(traverse, node, path, state) { | ||
utils.catchup(node.typeAnnotation.range[0], state); | ||
utils.catchupWhiteOut(node.typeAnnotation.range[1], state); | ||
return false; | ||
} | ||
visitTypeAnnotatedObjectOrArrayPattern.test = function(node, path, state) { | ||
var rightType = node.type === Syntax.ObjectPattern | ||
|| node.type === Syntax.ArrayPattern; | ||
return rightType && node.typeAnnotation; | ||
}; | ||
/** | ||
* Methods cause trouble, since esprima parses them as a key/value pair, where | ||
* the location of the value starts at the method body. For example | ||
* { bar(x:number,...y:Array<number>):number {} } | ||
* is parsed as | ||
* { bar: function(x: number, ...y:Array<number>): number {} } | ||
* except that the location of the FunctionExpression value is 40-something, | ||
* which is the location of the function body. This means that by the time we | ||
* visit the params, rest param, and return type organically, we've already | ||
* catchup()'d passed them. | ||
*/ | ||
function visitMethod(traverse, node, path, state) { | ||
path.unshift(node); | ||
traverse(node.key, path, state); | ||
path.unshift(node.value); | ||
traverse(node.value.params, path, state); | ||
node.value.rest && traverse(node.value.rest, path, state); | ||
node.value.returnType && traverse(node.value.returnType, path, state); | ||
traverse(node.value.body, path, state); | ||
path.shift(); | ||
path.shift(); | ||
return false; | ||
} | ||
visitMethod.test = function(node, path, state) { | ||
return (node.type === "Property" && node.method) | ||
|| (node.type === "MethodDefinition"); | ||
}; | ||
exports.visitorList = [ | ||
visitClassProperty, | ||
visitInterfaceDeclaration, | ||
visitFunctionParametricAnnotation, | ||
visitFunctionReturnAnnotation, | ||
visitMethod, | ||
visitOptionalFunctionParameterAnnotation, | ||
visitTypeAnnotatedIdentifier | ||
visitTypeAlias, | ||
visitTypeAnnotatedIdentifier, | ||
visitTypeAnnotatedObjectOrArrayPattern | ||
]; |
230573
43
6598
153
+ Addedesprima-fb@8001.1.0-dev-harmony-fb(transitive)
- Removedesprima-fb@7001.1.0-dev-harmony-fb(transitive)