initial commit

This commit is contained in:
2026-03-22 03:21:45 +02:00
commit 897fea9f4e
15431 changed files with 2548840 additions and 0 deletions

View File

@@ -0,0 +1,236 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails dmitrys@fb.com
*/
/*jshint evil:true*/
/*jshint -W117*/
require('mock-modules').autoMockOff();
describe('es6ArrowFunctionsTransform', function() {
var transformFn;
var visitors;
beforeEach(function() {
require('mock-modules').dumpCache();
visitors = require('../es6-arrow-function-visitors').visitorList;
transformFn = require('../../src/jstransform').transform;
});
function transform(code) {
return transformFn(visitors, code).code;
}
function expectTransform(code, result) {
expect(transform(code)).toEqual(result);
}
it('should capture correct this value at different levels', function() {
var code = transform([
'var foo = {',
' createFooGetter: function() {',
' return (x) => [x, this];', // captures foo
' },',
' getParentThis: () => this', // captures parent this
'};'
].join('\n'));
eval(code);
expect(typeof foo.createFooGetter).toBe('function');
expect(typeof foo.createFooGetter()).toBe('function');
expect(typeof foo.getParentThis).toBe('function');
expect(foo.getParentThis()).toEqual(this);
expect(foo.createFooGetter()(10)).toEqual([10, foo]);
});
it('should map an array using arrow capturing this value', function() {
this.factor = 10;
var code = transform(
'[1, 2, 3].map(x => x * x * this.factor);'
);
expect(eval(code)).toEqual([10, 40, 90]);
});
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() {
this.factor = 0;
var code = transform([
'[1, 2, 3].filter((v, idx) => {',
' if (idx > 1 && this.factor > 0) {',
' return true;',
' }',
' this.factor++;',
' return false;',
'});'
].join('\n'));
expect(eval(code)).toEqual([3]);
});
it('should fetch this value data from nested arrow', function() {
var code = transform([
'({',
' bird: 22,',
' run: function() {',
' return () => () => this.bird;',
' }',
'}).run()()();'
].join('\n'));
expect(eval(code)).toEqual(22);
});
// Syntax tests.
it('should correctly transform arrows', function() {
// 0 params, expression.
expectTransform(
'() => this.value;',
'(function() {return this.value;}.bind(this));'
);
// 0 params, expression wrapped in parens
expectTransform(
'() => (this.value);',
'(function() {return this.value;}.bind(this));'
);
// 1 param, no-parens, expression, no this.
expectTransform(
'x => x * x;',
'(function(x) {return x * x;});'
);
// 1 param, parens, expression, as argument, no this.
expectTransform(
'map((x) => x * x);',
'map(function(x) {return x * x;});'
);
// 2 params, block, as argument, nested.
expectTransform(
'makeRequest((response, error) => {'.concat(
' return this.update(data => this.onData(data), response);',
'});'),
'makeRequest(function(response, error) {'.concat(
' return this.update(function(data) {return this.onData(data);}.bind(this), response);',
'}.bind(this));')
);
// Assignment to a var, simple, 1 param.
expectTransform(
'var action = (value) => this.performAction(value);',
'var action = function(value) {return this.performAction(value);}.bind(this);'
);
// Preserve lines transforming ugly code.
expectTransform([
'(',
'',
'',
' x,',
' y',
'',
')',
'',
' =>',
'',
' {',
' return x + y;',
'};'
].join('\n'), [
'(function(',
'',
'',
' x,',
' y)',
'',
'',
'',
' ',
'',
' {',
' return x + y;',
'});'
].join('\n'));
// Preserve line numbers with single parens-free param ugly code.
expectTransform([
'x',
'',
' =>',
' x;'
].join('\n'), [
'(function(x)',
'',
' ',
' {return x;});'
].join('\n'));
// Preserve line numbers with single parens param ugly code.
expectTransform([
'(',
'',
' x',
'',
')',
'',
' =>',
' x;'
].join('\n'), [
'(function(',
'',
' x)',
'',
'',
'',
' ',
' {return x;});'
].join('\n'));
// Preserve line numbers with parens around expression.
expectTransform([
'(x) => (',
' x',
');'
].join('\n'), [
'(function(x) ',
' {return x;}',
');'
].join('\n'));
// Preserve typechecker annotation.
expectTransform(
'(/*string*/foo, /*bool*/bar) => foo;',
'(function(/*string*/foo, /*bool*/bar) {return foo;});'
);
// Binds this for arrows containing <this />
expectTransform(
'(() => <this />)',
'((function() {return <this />;}.bind(this)))'
);
});
});

View File

@@ -0,0 +1,103 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails dmitrys@fb.com
*/
/*jshint evil:true*/
require('mock-modules').autoMockOff();
describe('es6-call-spread-visitors', function() {
var transformFn;
var visitors;
beforeEach(function() {
require('mock-modules').dumpCache();
transformFn = require('../../src/jstransform').transform;
visitors = require('../es6-call-spread-visitors').visitorList;
});
function transform(code, options) {
return transformFn(visitors, code, options).code;
}
it('should spread given data with context', function() {
expect(transform('Math.max(1,\t[2], 3, ...[4, 5, 6])'))
.toEqual('var $__0;($__0 = Math).max.apply($__0, [1,\t[2], 3].concat([4, 5, 6]))');
});
it('should avoid unnecessary concat call', function() {
expect(transform('window.Math.max(...list)'))
.toEqual('var $__0;($__0 = window.Math).max.apply($__0, list)');
});
it('should default to undefined context', function() {
expect(transform('max(1, 2, ...list)'))
.toEqual('max.apply(undefined, [1, 2].concat(list))');
});
it('should handle computed method names', function() {
expect(transform('Math["m" + (0 ? "in" : "ax")](1, 2, ...list)'))
.toEqual('var $__0;($__0 = Math)["m" + (0 ? "in" : "ax")].apply($__0, [1, 2].concat(list))');
});
it('should handle immediately invoked function expressions', function() {
expect(transform('(function(a, b, c) { return a+b+c; })(1, 2, ...more)'))
.toEqual('(function(a, b, c) { return a+b+c; }).apply(undefined, [1, 2].concat(more))');
});
it('should spread while creating new instances', function() {
expect(transform('new Set(1, 2, ...list)'))
.toEqual('new (Function.prototype.bind.apply(Set, [null, 1, 2].concat(list)))');
});
it('should create temporary variables when necessary in program scope', function() {
expect(transform('foo().bar(arg1, arg2, ...more)'))
.toEqual('var $__0;($__0 = foo()).bar.apply($__0, [arg1, arg2].concat(more))');
});
it('should create temporary variables when necessary in function scope', function() {
expect(transform('function fn(){ return foo().bar(arg1, arg2, ...more); }'))
.toEqual('function fn(){var $__0; return ($__0 = foo()).bar.apply($__0, [arg1, arg2].concat(more)); }');
});
it('should not evaluate context more than once', function() {
var code = transform([
'var obj = {',
' calls: 0,',
' get context() {',
' this.calls++;',
' return {',
' add: function(a, b) { return a + b; }',
' };',
' }',
'};',
'var nums = [1, 2];',
'obj.context.add(...nums);',
'obj.calls === 1;',
].join('\n'));
expect(eval(code)).toEqual(true);
});
it('should transform nested spread expressions', function() {
var code = transform([
'function getBase() {',
' return {',
' getParams: function(a, b) {',
' return [a, b];',
' }',
' };',
'}',
'[].concat(...getBase().getParams(...[1, 2, 3])).join(" ");',
].join('\n'));
expect(eval(code)).toEqual("1 2");
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,300 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails dmitrys@fb.com
*/
/*jshint evil:true*/
/*jshint -W117*/
require('mock-modules').autoMockOff();
describe('es6-destructuring-visitors', function() {
var transformFn;
var destructuringVisitors;
var conciseMethodVisitors;
var shortObjectsVisitors;
var reservedWordsVisitors;
var restParamVisitors;
var classVisitorsVisitors;
var arrowFunctionVisitors;
var visitors;
beforeEach(function() {
require('mock-modules').dumpCache();
transformFn = require('../../src/jstransform').transform;
destructuringVisitors = require('../es6-destructuring-visitors').visitorList;
conciseMethodVisitors = require('../es6-object-concise-method-visitors').visitorList;
shortObjectsVisitors = require('../es6-object-short-notation-visitors').visitorList;
reservedWordsVisitors = require('../reserved-words-visitors').visitorList;
restParamVisitors = require('../es6-rest-param-visitors').visitorList;
classVisitorsVisitors = require('../es6-class-visitors').visitorList;
arrowFunctionVisitors = require('../es6-arrow-function-visitors').visitorList;
visitors = destructuringVisitors.concat(
conciseMethodVisitors,
shortObjectsVisitors,
restParamVisitors,
classVisitorsVisitors,
arrowFunctionVisitors,
reservedWordsVisitors
);
});
function transform(code) {
return transformFn(visitors, code).code;
}
function expectTransform(code, result) {
expect(transform(code)).toEqual(result);
}
it('should handle simple object pattern', function() {
var code = transform([
'var {x, y} = {x: 10, y: 20};',
'(x + y);'
].join('\n'));
expect(eval(code)).toEqual(30);
});
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() {
var code = transform([
'var x = 10, y = 20;',
'[x, y] = [y, x];',
'([x, y]);'
].join('\n'));
expect(eval(code)).toEqual([20, 10]);
});
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() {
var code = transform([
'var x = 10, y = 20;',
'({x, y} = {y, x});',
'({x, y});'
].join('\n'));
expect(eval(code)).toEqual({x: 10, y: 20});
});
it('should destructure result of a function', function() {
var code = transform([
'var [x, y] = (function({x, y}) { return [x, y]; })({x: 1, y: 2});',
'([x, y]);'
].join('\n'));
expect(eval(code)).toEqual([1, 2]);
});
it('should handle skipped array elements', function() {
var code = transform([
'var [x, , y] = [1, 2, 3];',
'([x, y]);'
].join('\n'));
expect(eval(code)).toEqual([1, 3]);
});
it('should handle rest elements of an array', function() {
var code = transform([
'var [x, ...xs] = [1, 2, 3];'
].join('\n'));
eval(code);
expect(x).toEqual(1);
expect(xs).toEqual([2, 3]);
});
it('should swap two variables w/o third using pattern', function() {
var code = transform([
'var x = 10, y = 20;',
'var [x, y] = [y, x];',
'([x, y]);'
].join('\n'));
expect(eval(code)).toEqual([20, 10]);
});
it('should transform complex pattern argument', function() {
var code = transform([
'function init(user, {ip, coords: [x, y], port}) {',
' return [user, ip, x, y, port].join(", ");',
'}'
].join('\n'));
eval(code);
expect(init(
'John Doe', {
ip: '127.0.0.1',
coords: [1, 2],
port: 8080
}
)).toBe('John Doe, 127.0.0.1, 1, 2, 8080');
});
it('should work with rest params', function() {
var code = transform([
'function foo({bar, baz}, ...rest) {',
' return {bar, baz, qux: rest[0]};',
'}'
].join('\n'));
eval(code);
expect(foo({bar: 10, baz: 20}, 30))
.toEqual({bar: 10, baz: 20, qux: 30});
});
it('should work with class methods', function() {
var code = transform([
'class Point {',
' constructor({x, y}) {',
' this._x = x;',
' this._y = y;',
' }',
'',
' getData([deltaX, deltaY]) {',
' return this._x + deltaX + this._y + deltaY',
' }',
'}'
].join('\n'));
eval(code);
var x = 10, y = 20;
var foo = new Point({x: x, y: y});
var data = foo.getData([30, 40]);
expect(data).toBe(100);
});
it('should work with object concise methods', function() {
var code = transform([
'var foo = {',
' bar({x, y}) {',
' return {x, y};',
' }',
'}'
].join('\n'));
eval(code);
expect(foo.bar({x: 10, y: 20}))
.toEqual({x: 10, y: 20});
});
it('should work with arrows', function() {
var code = transform([
'var foo = ({x, y}, z) => x + y + z;'
].join('\n'));
eval(code);
expect(foo({x: 10, y: 20}, 30))
.toEqual(60);
});
it('should work with arrows without other arguments', function() {
var code = transform([
'var foo = ({x, y}) => x + y;',
'var bar = ([x, y]) => x + y;'
].join('\n'));
eval(code);
expect(foo({x: 10, y: 20}))
.toEqual(30);
expect(bar([10, 20]))
.toEqual(30);
});
// Auto-generated temp vars test.
it('should allocate correct temp index', function() {
var code = transform([
'function foo(x, {y}, {z}) {',
' var {q} = {q: 30};',
' return [$__0, $__1, $__2];',
'}'
].join('\n'));
eval(code);
expect(foo(1, {y: 10}, {z: 20}))
.toEqual([{y: 10}, {z: 20}, {q: 30}]);
});
it('should allocate correct temp nested index', function() {
var code = transform([
'var foo = function(x, {y}, {z}) {',
' var {q, m: {v}} = {q: 30, m: {v: 40}};',
' var {a} = (function({a}) { return $__0; })({a: 50});',
' return [$__0, $__1, $__2, $__3, a];',
'}'
].join('\n'));
eval(code);
expect(foo(1, {y: 10}, {z: 20}))
.toEqual([{y: 10}, {z: 20}, {q: 30, m: {v: 40}}, {v: 40}, 50]);
});
// Syntax tests.
it('should correctly transform structured patterns', function() {
// Variable declaration.
expectTransform(
'var a, {x, data: [y, , z]} = {x: 10, data: [1, 2, 3]};',
'var a, $__0= {x: 10, data: [1, 2, 3]},x=$__0.x,$__1=$__0.data,y=$__1[0],z=$__1[2];'
);
// Function parameters.
expectTransform(
'function f(a, {x, data: [y, z]}, b) {}',
'function f(a, $__0 , b) {var x=$__0.x,$__1=$__0.data,y=$__1[0],z=$__1[1];}'
);
});
it('should handle reserved words', function() {
expectTransform(
'var {delete: x} = {delete: 1};',
'var $__0= {"delete": 1},x=$__0["delete"];'
);
});
});

View File

@@ -0,0 +1,69 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails sema@fb.com javascript@lists.facebook.com
*/
/*jshint evil:true*/
require('mock-modules').autoMockOff();
describe('es6-es7-object-integration-test', function() {
var transformFn;
var visitors;
// These are placeholder variables in scope that we can use to assert that a
// specific variable reference was passed, rather than an object clone of it.
var x = 123456;
var z = 345678;
beforeEach(function() {
require('mock-modules').dumpCache();
transformFn = require('../../src/jstransform').transform;
var conciseMethodVisitors = require('../es6-object-concise-method-visitors').visitorList;
var shortObjectsVisitors = require('../es6-object-short-notation-visitors').visitorList;
var spreadPropertyVisitors = require('../es7-spread-property-visitors').visitorList;
visitors = spreadPropertyVisitors.concat(
shortObjectsVisitors,
conciseMethodVisitors
);
});
function transform(code) {
return transformFn(visitors, code).code;
}
it('handles spread with concise methods and short notation', function() {
var code = 'var xyz = { ...x, y() { return 42; }, z }';
var objectAssignMock = jest.genMockFunction();
Object.assign = objectAssignMock;
eval(transform(code));
var assignCalls = objectAssignMock.mock.calls;
expect(assignCalls.length).toBe(1);
expect(assignCalls[0].length).toBe(3);
expect(assignCalls[0][0]).toEqual({});
expect(assignCalls[0][1]).toEqual(x);
var trailingObject = assignCalls[0][2];
expect(trailingObject.y()).toEqual(42);
expect(trailingObject.z).toEqual(z);
});
it('does not call assign when there are no spread properties', function() {
var code = 'var xyz = { x, y() { return 42 }, z }';
var objectAssignMock = jest.genMockFunction();
Object.assign = objectAssignMock;
eval(transform(code));
expect(objectAssignMock).not.toBeCalled();
});
});

View File

@@ -0,0 +1,113 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails dmitrys@fb.com
*/
/*jshint evil:true*/
require('mock-modules').autoMockOff();
describe('es6-call-spread-visitors', function() {
var transformFn;
var visitors;
var results;
beforeEach(function() {
require('mock-modules').dumpCache();
transformFn = require('../../src/jstransform').transform;
visitors = require('../es6-for-of-visitors').visitorList;
results = undefined;
});
function transform(code, options) {
return transformFn(visitors, code, options).code;
}
it('should correctly transform and run the simple case', function() {
eval(transform([
'var results = [];',
'var y = [10, 20, 30];',
'for (var x of y) {',
' results.push(x);',
'}',
].join('\n')));
expect(results).toEqual([10, 20, 30]);
});
it('should correctly run a simple for of without block', function() {
eval(transform([
'var results = [];',
'var y = [10, 20, 30];',
'for (var x of y)',
' results.push(x);',
].join('\n')));
expect(results).toEqual([10, 20, 30]);
});
it('should correctly run a simple for of without a var', function() {
var x;
eval(transform([
'var results = [];',
'var x;',
'var y = [10, 20, 30];',
'for (x of y) {',
' results.push(x);',
'}',
].join('\n')));
expect(results).toEqual([10, 20, 30]);
expect(x).toEqual(30);
});
it('should correctly run a for of with expr on rhs', function() {
eval(transform([
'var results = [];',
'for (var x of [10, 20, 30]) {',
' results.push(x);',
'}',
].join('\n')));
expect(results).toEqual([10, 20, 30]);
});
it('should correctly work with an interable', function() {
// Custom iterable object.
var foo = {
_data: [10, 20, 30],
_index: 0,
'@@iterator': function() {
this._index = 0;
return {
next: () => {
return {
value: this._data[this._index++],
done: this._index > this._data.length
};
}
};
}
};
// dummy statement to appease lint.
foo;
eval(transform([
'var results = [];',
'for (var x of foo) {',
' results.push(x);',
'}',
].join('\n')));
expect(results).toEqual([10, 20, 30]);
});
});

View File

@@ -0,0 +1,108 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails dmitrys@fb.com
*/
/*jshint evil:true*/
require('mock-modules').autoMockOff();
describe('es6-computed-property-visitors', function() {
var transformFn;
var visitors;
beforeEach(function() {
require('mock-modules').dumpCache();
transformFn = require('../../src/jstransform').transform;
visitors = require('../es6-object-computed-property-visitors').visitorList
.concat(require('../es6-object-concise-method-visitors').visitorList)
.concat(require('../es6-object-short-notation-visitors').visitorList)
.concat(require('../es7-spread-property-visitors').visitorList);
});
function transform(code, options) {
return transformFn(visitors, code, options).code;
}
it('should transforms simple computed properties', function() {
var result;
eval(transform([
'var x = "foo", q = "bar";',
'var result = {[x]: 1, y: 2, "z": 3, [x + q]: 4};',
].join('\n')));
expect(result).toEqual({foo: 1, y: 2, z: 3, foobar: 4});
});
it('should transform computed property', function() {
expect(transform('var x = {alpha: 12, \'beta\': 34, [\'gam\' + \'ma\']: 56};'))
.toEqual('var $__0;var x = ($__0={},$__0.alpha= 12,$__0[ \'beta\']= 34,$__0[ \'gam\' + \'ma\']= 56,$__0);');
});
it('should handle spread operator', function() {
expect(transform('({[\'a\'+\'b\']: \'ab\', ...cde})'))
.toEqual('var $__0;(($__0={},$__0[\'a\'+\'b\']= \'ab\',Object.assign($__0 ,cde),$__0))');
});
it('should handle consecutive spread operators', function() {
expect(transform('({...a, [1+1]: 2, ...c, ...d})'))
.toEqual('var $__0;(($__0={},Object.assign($__0,a),$__0[ 1+1]= 2,Object.assign($__0 ,c, d),$__0))');
});
it('should handle everything individually while preserving whitespace', function() {
var original = [
'\n...\na\n', // spread property
'\tb\t', // short object
'\nc\n(\nx\n)\n{ return x * x; }\n', // concise method
'\t[\td? "e": "f"\t]\t:\tfunction(y)\t{ return y * y; }\t', // computed property
'\ng\n:\n"h"\n' // normal property
].join(',');
var transformed = [
'Object.assign($__0\n,\na\n',
'\t{b:b\t',
'\nc:function\n(\nx\n)\n{ return x * x; }})',
'$__0[\n\t\td? "e": "f"]=\t\t\tfunction(y)\t{ return y * y; }',
'$__0.g=\t\n\n\n"h"\n'
].join(',');
expect(transform('({' + original + '})'))
.toEqual('var $__0;(($__0={},' + transformed + ',$__0))');
});
it('should fail for short notation with computed names', function() {
expect(transform.bind(null, '({["a" + "b"]})')).toThrow();
});
it('should handle concise generator methods with computed names', function() {
expect(transform('({*["a" + b ](c) { return c * c; },\t*d(e) { return e * e; }})'))
.toEqual('var $__0;(($__0={},$__0["a" + b]= function*(c) { return c * c; },$__0.d=\tfunction*(e) { return e * e; },$__0))');
});
it('should evaluate simple object with computed properties', function() {
var result = eval(transform('({["alp"+\'ha\']:1, [1>2?"beta":"gamma"]:3})'));
expect(result).toEqual({alpha: 1, gamma: 3});
});
it('should evaluate complex objects with computed properties', function() {
var complex;
eval(transform([
'var spread = {identifier: 1, "literal": 2, ["comp"+"uted"]: "overwritten"};',
'var short = "value";',
'var complex = {short, ...spread, ["con"+"cise"]() { return "method"; }, [String.fromCharCode(100-1)+"omputed"]: "actual"};',
].join('\n')));
expect(complex).toEqual({
short: 'value',
identifier: 1,
literal: 2,
concise: complex.concise,
computed: 'actual',
});
});
});

View File

@@ -0,0 +1,118 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails dmitrys@fb.com
*/
/*jshint evil:true*/
require('mock-modules').autoMockOff();
describe('es6-object-concise-method-visitors', function() {
var transformFn;
var conciseMethodVisitors;
var restParamVisitors;
var visitors;
beforeEach(function() {
require('mock-modules').dumpCache();
conciseMethodVisitors = require('../es6-object-concise-method-visitors').visitorList;
restParamVisitors = require('../es6-rest-param-visitors').visitorList;
transformFn = require('../../src/jstransform').transform;
visitors = conciseMethodVisitors.concat(restParamVisitors);
});
function transform(code) {
return transformFn(visitors, code).code;
}
function expectTransform(code, result) {
expect(transform(code)).toEqual(result);
}
// Functional tests.
it('should transform concise method and return 42', function() {
/*global foo*/
var code = transform([
'var foo = {',
' bar(x) {',
' return x;',
' }',
'};'
].join('\n'));
eval(code);
expect(foo.bar(42)).toEqual(42);
});
it('should transform concise method with literal property', function() {
var code = transform([
'var foo = {',
' "bar 1"(x) {',
' return x;',
' }',
'};'
].join('\n'));
eval(code);
expect(foo['bar 1'](42)).toEqual(42);
});
it('should work with rest params', function() {
var code = transform([
'({',
' init(x, ...rest) {',
' return rest.concat(x);',
' }',
'}).init(1, 2, 3);'
].join('\n'));
expect(eval(code)).toEqual([2, 3, 1]);
});
// Source code tests.
it('should transform concise methods', function() {
// Should transform simple concise method.
expectTransform(
'var foo = {bar() {}};',
'var foo = {bar:function() {}};'
);
// Should transform inner objects.
expectTransform(
'({bar(x) { return {baz(y) {}}; }});',
'({bar:function(x) { return {baz:function(y) {}}; }});'
);
});
it('should preserve generators', function() {
// Identifier properties
expectTransform(
'var foo = {*bar(x) {yield x;}};',
'var foo = {bar:function*(x) {yield x;}};'
);
// Literal properties
expectTransform(
'var foo = {*"abc"(x) {yield x;}, *42(x) {yield x;}};',
'var foo = {"abc":function*(x) {yield x;}, 42:function*(x) {yield x;}};'
);
});
it('should handle reserved words', function() {
expectTransform(
'({delete(x) {}})',
'({"delete":function(x) {}})'
);
});
});

View File

@@ -0,0 +1,110 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails dmitrys@fb.com
*/
/*jshint evil:true*/
require('mock-modules').autoMockOff();
describe('es6-object-short-notation-visitors', function() {
var transformFn;
var destructuringVisitors;
var shortObjectsVisitors;
var visitors;
beforeEach(function() {
require('mock-modules').dumpCache();
transformFn = require('../../src/jstransform').transform;
shortObjectsVisitors = require('../es6-object-short-notation-visitors').visitorList;
destructuringVisitors = require('../es6-destructuring-visitors').visitorList;
visitors = shortObjectsVisitors.concat(destructuringVisitors);
});
function transform(code) {
return transformFn(visitors, code).code;
}
function expectTransform(code, result) {
expect(transform(code)).toEqual(result);
}
// Functional tests.
it('should transform short notation and return 5', function() {
var code = transform([
'(function(x, y) {',
' var data = {x, y};',
' return data.x + data.y;',
'})(2, 3);'
].join('\n'));
expect(eval(code)).toEqual(5);
});
it('should transform work with destructuring and return 10', function() {
var code = transform([
'var x = 5, y = 5;',
'(function({x, y}) {',
' var data = {x, y};',
' return data.x + data.y;',
'})({x, y});'
].join('\n'));
expect(eval(code)).toEqual(10);
});
// Source code tests.
it('should transform simple short notation', function() {
// Should transform simple short notation.
expectTransform(
'function foo(x, y) { return {x, y}; }',
'function foo(x, y) { return {x:x, y:y}; }'
);
// Should preserve lines transforming ugly code.
expectTransform([
'function',
'',
'foo (',
' x,',
' y',
'',
')',
'',
' {',
' return {',
' x,',
' y};',
'}'
].join('\n'), [
'function',
'',
'foo (',
' x,',
' y',
'',
')',
'',
' {',
' return {',
' x:x,',
' y:y};',
'}'
].join('\n'));
});
});

View File

@@ -0,0 +1,337 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails dmitrys@fb.com
*/
/*jshint evil:true*/
/*jshint -W117*/
require('mock-modules').autoMockOff();
describe('es6-rest-param-visitors', () => {
var transformFn;
var visitorSet;
var arrowFuncVisitors;
var classVisitors;
var restParamVisitors;
beforeEach(() => {
require('mock-modules').dumpCache();
arrowFuncVisitors = require('../es6-arrow-function-visitors').visitorList;
classVisitors = require('../es6-class-visitors').visitorList;
restParamVisitors = require('../es6-rest-param-visitors').visitorList;
callSpreadVisitors = require('../es6-call-spread-visitors').visitorList;
transformFn = require('../../src/jstransform').transform;
visitorSet =
arrowFuncVisitors
.concat(classVisitors)
.concat(restParamVisitors)
.concat(callSpreadVisitors);
});
function transform(code) {
return transformFn(visitorSet, code).code;
}
function expectTransform(code, result) {
expect(transform(code)).toEqual(result);
}
describe('function expressions', () => {
it('should capture 2 rest params, having 2 args', () => {
var code = transform([
'(function(x, y, ...args) {',
' return [x, y, args.length, args[0], args[1]];',
'})(1, 2, 3, 4);'
].join('\n'));
expect(eval(code)).toEqual([1, 2, 2, 3, 4]);
});
it('should transform rest parameters in nested functions', () => {
var code = transform([
'(function(x, ...args) {',
' return function(...params) {',
' return args.concat(params);',
' };',
'})(1, 2, 3)(4, 5);'
].join('\n'));
expect(eval(code)).toEqual([2, 3, 4, 5]);
});
it('should supply an array object', () => {
var code = transform([
'(function(...args) {',
' return Array.isArray(args);',
'})()'
].join('\n'));
expect(eval(code)).toBe(true);
});
});
describe('function declarations', () => {
it('should capture 2 rest params, having 2 args', () => {
var code = transform([
'function test(x, y, ...args) {',
' return [x, y, args.length, args[0], args[1]];',
'}'
].join('\n'));
eval(code);
expect(test(1, 2, 3, 4)).toEqual([1, 2, 2, 3, 4]);
});
it('should transform rest parameters in nested functions', () => {
var code = transform([
'function testOuter(x, ...args) {',
' function testInner(...params) {',
' return args.concat(params);',
' }',
' return testInner;',
'}'
].join('\n'));
eval(code);
expect(testOuter(1, 2, 3)(4, 5)).toEqual([2, 3, 4, 5]);
});
it('should supply an array object', () => {
var code = transform([
'function test(...args) {',
' return Array.isArray(args);',
'}'
].join('\n'));
eval(code);
expect(test()).toBe(true);
});
it('should work with temp var injections', () => {
// Use call spread as example. It injects a temp var into function scope.
var code = transform([
'function test(bar, ...args) {',
' return bar(...args);',
'}'
].join('\n'));
eval(code);
var fn = function() {
return Array.prototype.slice.call(arguments).map(i => i + 1);
};
expect(test(fn, 1, 2, 3)).toEqual([2, 3, 4]);
});
});
describe('arrow functions', () => {
it('should transform non-block bodied arrow functions', () => {
var code = transform([
'var test = (...args) => args;'
].join('\n'));
eval(code);
expect(test('foo', 'bar')).toEqual(['foo', 'bar']);
});
it('should capture 2 rest params, having 2 args', () => {
var code = transform([
'var test = (x, y, ...args) => {',
' return [x, y, args.length, args[0], args[1]];',
'}'
].join('\n'));
eval(code);
expect(test(1, 2, 3, 4)).toEqual([1, 2, 2, 3, 4]);
});
it('should transform rest parameters in nested arrow functions', () => {
var code = transform([
'var testOuter = (x, ...args) => {',
' var testInner = (...params) => {',
' return args.concat(params);',
' };',
' return testInner;',
'};'
].join('\n'));
eval(code);
expect(testOuter(1, 2, 3)(4, 5)).toEqual([2, 3, 4, 5]);
});
it('should supply an array object', () => {
var code = transform([
'var test = (...args) => {',
' return Array.isArray(args);',
'};'
].join('\n'));
eval(code);
expect(test()).toBe(true);
});
});
describe('class methods', () => {
it('should capture 2 rest params, having 2 args', () => {
var code = transform([
'class Foo {',
' constructor(x, y, ...args) {',
' this.ctor = [x, y, args.length, args[0], args[1]];',
' }',
' testMethod(x, y, ...args) {',
' return [x, y, args.length, args[0], args[1]];',
' }',
' static testMethod(x, y, ...args) {',
' return [x, y, args.length, args[0], args[1]];',
' }',
'}'
].join('\n'));
eval(code);
var fooInst = new Foo(1, 2, 3, 4);
expect(fooInst.ctor).toEqual([1, 2, 2, 3, 4]);
expect(fooInst.testMethod(1, 2, 3, 4)).toEqual([1, 2, 2, 3, 4]);
expect(Foo.testMethod(1, 2, 3, 4)).toEqual([1, 2, 2, 3, 4]);
});
it('should transform rest parameters in nested functions', () => {
var code = transform([
'class Foo {',
' constructor(x, ...args) {',
' function inner(...params) {',
' return args.concat(params);',
' }',
' this.ctor = inner;',
' }',
' testMethod(x, ...args) {',
' function inner(...params) {',
' return args.concat(params);',
' }',
' return inner;',
' }',
' static testMethod(x, ...args) {',
' function inner(...params) {',
' return args.concat(params);',
' }',
' return inner;',
' }',
'}'
].join('\n'));
eval(code);
var fooInst = new Foo(1, 2, 3);
expect(fooInst.ctor(4, 5)).toEqual([2, 3, 4, 5]);
expect(fooInst.testMethod(1, 2, 3)(4, 5)).toEqual([2, 3, 4, 5]);
expect(Foo.testMethod(1, 2, 3)(4, 5)).toEqual([2, 3, 4, 5]);
});
it('should supply an array object', () => {
var code = transform([
'class Foo {',
' constructor(...args) {',
' this.ctor = Array.isArray(args);',
' }',
' testMethod(...args) {',
' return Array.isArray(args);',
' }',
' static testMethod(...args) {',
' return Array.isArray(args);',
' }',
'}'
].join('\n'));
eval(code);
var fooInst = new Foo();
expect(fooInst.ctor).toBe(true);
expect(fooInst.testMethod()).toBe(true);
expect(Foo.testMethod()).toBe(true);
});
});
describe('whitespace preservation', () => {
it('1-line function decl with 2 args', () => {
expectTransform(
'function foo(x, y, ...args) { 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]; }'
);
});
it('1-line function expression with 1 arg', () => {
expectTransform(
'(function(x, ...args) { return args;});',
'(function(x ) {for (var args=[],$__0=1,$__1=arguments.length;' +
'$__0<$__1;$__0++) args.push(arguments[$__0]); return args;});'
);
});
it('1-line function expression with no args', () => {
expectTransform(
'map(function(...args) { 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); });'
);
});
it('preserves lines for ugly code', () => {
expectTransform([
'function',
'',
'foo (',
' x,',
' ...args',
'',
')',
'',
' {',
' return args;',
'}'
].join('\n'), [
'function',
'',
'foo (',
' x',
' ',
'',
')',
'',
' {for (var args=[],$__0=1,$__1=arguments.length;$__0<$__1;' +
'$__0++) args.push(arguments[$__0]);',
' return args;',
'}'
].join('\n'));
});
it('preserves inline comments', () => {
expectTransform(
'function foo(/*string*/foo, /*bool*/bar, ...args) { return args; }',
'function foo(/*string*/foo, /*bool*/bar ) {' +
'for (var args=[],$__0=2,$__1=arguments.length;$__0<$__1;$__0++) ' +
'args.push(arguments[$__0]); ' +
'return args; ' +
'}'
);
});
});
});

View File

@@ -0,0 +1,241 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails mroch@fb.com
*/
/*jshint evil:true*/
require('mock-modules').autoMockOff();
describe('ES6 Template Visitor', function() {
var transformFn;
var visitors;
beforeEach(function() {
require('mock-modules').dumpCache();
visitors = require('../es6-template-visitors').visitorList;
transformFn = require('../../src/jstransform').transform;
});
function transform(code) {
return transformFn(visitors, code).code;
}
function expectTransform(code, result) {
expect(transform(code)).toEqual(result);
}
function expectEval(code, result, setupFn) {
var actual;
if (setupFn) {
eval(setupFn);
}
eval('actual = ' + transform(code));
expect(actual).toEqual(result);
}
function expectEvalTag(code, tagFn, scope) {
/*jshint unused:false*/
if (scope) {
Object.keys(scope).forEach((key) => this[key] = scope[key]);
}
var tagCalls = 0;
var tag = function(...args) {
tagCalls++;
return tagFn.apply(this, args);
};
var result = transform(code);
expect(result.split('\n').length).toBe(code.split('\n').length);
eval(result);
expect(tagCalls).toBe(1);
}
function expectSiteObj(siteObj, cooked, raw) {
expect(Array.isArray(siteObj)).toBe(true);
expect(Object.isFrozen(siteObj)).toBe(true);
expect(Array.isArray(siteObj.raw)).toBe(true);
expect(Object.isFrozen(siteObj.raw)).toBe(true);
expect(siteObj.length).toBe(cooked.length);
expect(siteObj.raw.length).toBe(raw.length);
for (var ii = 0; ii < cooked.length; ii++) {
expect(siteObj[ii]).toEqual(cooked[ii]);
}
expect(siteObj.raw).toEqual(raw);
}
it('should transform simple literals', function() {
expectTransform('`foo bar`', '("foo bar")');
expectEval('`foo bar`', 'foo bar');
expectEval('`$`', '$');
expectEval('`$foo`', '$foo');
});
it('should properly escape templates containing quotes', function() {
expectTransform('`foo "bar"`', '("foo \\"bar\\"")');
expectEval('`foo "bar"`', 'foo "bar"');
expectTransform("`foo 'bar'`", '("foo \'bar\'")');
expectEval("`foo 'bar'`", "foo 'bar'");
// `foo \\"bar\\"` (foo, literal slash, "bar", literal slash)
expectTransform('`foo \\\\"bar\\\\"`', '("foo \\\\\\"bar\\\\\\"")');
expectEval('`foo \\\\\\"bar\\\\\\"`', 'foo \\"bar\\"');
});
it('should transform simple substitutions', function() {
expectTransform('`foo ${bar}`', '("foo " + bar)');
expectTransform('`${foo} bar`', '("" + foo + " bar")');
expectTransform('`${foo} ${bar}`', '("" + foo + " " + bar)');
expectTransform('`${foo}${bar}`', '("" + foo + bar)');
});
it('should transform expressions', function() {
expectTransform('`foo ${bar()}`', '("foo " + bar())');
expectTransform('`foo ${bar.baz}`', '("foo " + bar.baz)');
expectTransform('`foo ${bar + 5}`', '("foo " + (bar + 5))');
expectTransform('`${foo + 5} bar`', '("" + (foo + 5) + " bar")');
expectTransform('`${foo + 5} ${bar}`', '("" + (foo + 5) + " " + bar)');
expectTransform(
'`${(function(b) {alert(4);})(a)}`',
'("" + (function(b) {alert(4);})(a))');
expectTransform("`${4 + 5}`", '("" + (4 + 5))');
});
it('should transform tags with simple templates', function() {
/*jshint unused:false*/
var tag = function(elements) {
expectSiteObj(elements, ['foo bar'], ['foo bar']);
};
var result = transform("tag`foo bar`");
expect(result.split('\n').length).toBe(1);
eval(result);
var a = { b: tag };
eval(transform("a.b`foo bar`"));
eval(transform("a['b']`foo bar`"));
var getTag = function() { return tag; };
eval(transform("getTag()`foo bar`"));
eval(transform("(getTag())`foo bar`"));
});
it('should transform tags with substitutions', function() {
expectTransform(
"tag`foo ${bar} baz`",
'tag(function() { var siteObj = ["foo ", " baz"]; ' +
'siteObj.raw = ["foo ", " baz"]; Object.freeze(siteObj.raw); ' +
'Object.freeze(siteObj); return siteObj; }(), bar)'
);
expectEvalTag(
"tag`foo ${bar + 'abc'} baz`",
function(elements, ...args) {
expectSiteObj(elements, ['foo ', ' baz'], ['foo ', ' baz']);
expect(args.length).toBe(1);
expect(args[0]).toBe('barabc');
},
{bar: 'bar'}
);
expectEvalTag(
"tag`foo ${bar + 'abc'}`",
function(elements, ...args) {
expectSiteObj(elements, ['foo ', ''], ['foo ', '']);
expect(args.length).toBe(1);
expect(args[0]).toBe('barabc');
},
{bar: 'bar'}
);
expectEvalTag(
"tag`foo\n\n\nbar`",
(elements) => {
expectSiteObj(elements, ['foo\n\n\nbar'], ['foo\n\n\nbar']);
}
);
expectEvalTag(
"tag`a\nb\n${c}\nd`",
(elements, ...args) => {
expectSiteObj(elements, ['a\nb\n', '\nd'], ['a\nb\n', '\nd']);
expect(args.length).toBe(1);
expect(args[0]).toBe('c');
},
{c: 'c'}
);
});
it('should maintain line numbers', function() {
expectTransform("`foo\n\nbar`", '("foo\\n\\nbar"\n\n)');
expectTransform("`foo\n${bar}\nbaz`", '("foo\\n" + \nbar + "\\nbaz"\n)');
expectTransform("`foo\\nbar`", '("foo\\nbar")');
expectTransform(
"tag`a\nb\n${c}${d}\ne`",
'tag(function() { var siteObj = ["a\\nb\\n", "", "\\ne"]; ' +
'siteObj.raw = ["a\\nb\\n", "", "\\ne"]; Object.freeze(siteObj.raw); ' +
'Object.freeze(siteObj); return siteObj; }(), \n\nc, d\n)'
);
});
it('should handle multiple lines', function() {
expectEval("`foo\n\nbar`", 'foo\n\nbar');
expectEval("`foo\\nbar`", 'foo\nbar');
expectEval("`foo\\\\nbar`", 'foo\\nbar');
expectEval("`foo\n${bar}\nbaz`", 'foo\nabc\nbaz', 'var bar = "abc";');
});
it('should handle numeric expressions', function() {
expectEval("`foo${1}bar${2}`", 'foo1bar2');
expectEval("`foo${1}${2}bar`", 'foo12bar');
expectEval("`${1}${2}foo`", '12foo');
expectEval("`${1}${2}`", '12');
});
it('should handle primitive literals', function() {
expectTransform("`${42}`", '("" + 42)');
expectEval("`${42}`", '42');
expectTransform("`${'42'}`", '("" + \'42\')');
expectEval("`${'42'}`", '42');
expectTransform("`${true}`", '("" + true)');
expectEval("`${true}`", 'true');
expectTransform("`${null}`", '("" + null)');
expectEval("`${null}`", 'null');
// undefined is actually an Identifier but it falls into the same category
expectTransform("`${undefined}`", '("" + undefined)');
expectEval("`${undefined}`", 'undefined');
}),
it('should canonicalize line endings', function() {
// TODO: should this be '("foo\\nbar"\r\n)' to maintain the number of lines
// for editors that break on \r\n? I don't think we care in the transformed
// code.
expectTransform("`foo\r\nbar`", '("foo\\nbar"\n)');
// TODO: same as above but with trailing \r
expectTransform("`foo\rbar`", '("foo\\nbar")');
expectEval("`foo\r\nbar`", 'foo\nbar');
expectEval("`foo\rbar`", 'foo\nbar');
expectEval("`foo\r\n${bar}\r\nbaz`", 'foo\nabc\nbaz', 'var bar = "abc";');
expectEvalTag(
"tag`foo\rbar`",
(elements) => {
expectSiteObj(elements, ['foo\nbar'], ['foo\rbar']);
}
);
});
});

View File

@@ -0,0 +1,104 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails sema@fb.com
*/
/*jshint evil:true*/
require('mock-modules').autoMockOff();
describe('es7-rest-property-visitors', function() {
var transformFn;
var visitors;
beforeEach(function() {
require('mock-modules').dumpCache();
transformFn = require('../../src/jstransform').transform;
visitors = require('../es6-destructuring-visitors').visitorList;
});
function transform(code) {
var lines = Array.prototype.join.call(arguments, '\n');
return transformFn(visitors, lines).code;
}
// Semantic tests.
it('picks off remaining properties from an object', function() {
var code = transform(
'({ x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 });',
'([ x, y, z ]);'
);
expect(eval(code)).toEqual([1, 2, { a: 3, b: 4 }]);
});
it('picks off remaining properties from a nested object', function() {
var code = transform(
'var complex = {',
' x: { a: 1, b: 2, c: 3 },',
' y: [4, 5, 6]',
'};',
'var {',
' x: { a: xa, ...xbc },',
' y: [y0, ...y12]',
'} = complex;',
'([ xa, xbc, y0, y12 ]);'
);
expect(eval(code)).toEqual([ 1, { b: 2, c: 3 }, 4, [5, 6] ]);
});
it('only extracts own properties', function() {
var code = transform(
'var obj = Object.create({ x: 1 });',
'obj.y = 2;',
'({ ...y } = obj);',
'(y);'
);
expect(eval(code)).toEqual({ y: 2 });
});
it('only extracts own properties, except when they are explicit', function() {
var code = transform(
'var obj = Object.create({ x: 1, y: 2 });',
'obj.z = 3;',
'({ y, ...z } = obj);',
'([ y, z ]);'
);
expect(eval(code)).toEqual([ 2, { z: 3 } ]);
});
it('avoids passing extra properties when they are picked off', function() {
var code = transform(
'function base({ a, b, x }) { return [ a, b, x ]; }',
'function wrapper({ x, y, ...restConfig }) {',
' return base(restConfig);',
'}',
'wrapper({ x: 1, y: 2, a: 3, b: 4 });'
);
expect(eval(code)).toEqual([ 3, 4, undefined ]);
});
// Syntax tests.
it('throws on leading rest properties', function () {
expect(() => transform('({ ...x, y, z } = obj)')).toThrow();
});
it('throws on multiple rest properties', function () {
expect(() => transform('({ x, ...y, ...z } = obj)')).toThrow();
});
// TODO: Ideally identifier reuse should fail to transform
// it('throws on identifier reuse', function () {
// expect(() => transform('({ x: { ...z }, y: { ...z } } = obj)')).toThrow();
// });
});

View File

@@ -0,0 +1,167 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails sema@fb.com
*/
/*jshint evil:true*/
require('mock-modules').autoMockOff();
describe('es7-spread-property-visitors', function() {
var transformFn;
var originalAssign = Object.assign;
var visitors;
// These are placeholder variables in scope that we can use to assert that a
// specific variable reference was passed, rather than an object clone of it.
var x = 123456;
var y = 789012;
var z = 345678;
beforeEach(function() {
require('mock-modules').dumpCache();
transformFn = require('../../src/jstransform').transform;
visitors = require('../es7-spread-property-visitors').visitorList;
});
function transform(code) {
return transformFn(visitors, code).code;
}
function expectTransform(code, result) {
expect(transform(code)).toEqual(result);
}
function expectObjectAssign(code) {
var objectAssignMock = jest.genMockFunction();
Object.assign = objectAssignMock;
eval(transform(code));
return expect(objectAssignMock);
}
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.
it('uses Object.assign with an empty new target object', function() {
expectObjectAssign(
'var xy = { ...x, y: 2 }'
).toBeCalledWith({}, x, { y: 2 });
});
it('coalesces consecutive properties into a single object', function() {
expectObjectAssign(
'var xyz = { ...x, y: 2, z: z }'
).toBeCalledWith({}, x, { y: 2, z: z });
});
it('avoids an unnecessary empty object when spread is not first', function() {
expectObjectAssign(
'var xy = { x: 1, ...y }'
).toBeCalledWith({ x: 1}, y);
});
it('passes the same value multiple times to Object.assign', function() {
expectObjectAssign(
'var xyz = { x: 1, y: 2, ...z, ...z }'
).toBeCalledWith({ x: 1, y: 2}, z, z);
});
it('keeps object literals as separate arguments to assign', function() {
expectObjectAssign(
'var xyz = { x: 1, ...({ y: 2 }), z: 3 }'
).toBeCalledWith({ x: 1}, { y: 2 }, {z: 3 });
});
it('does not call assign when there are no spread properties', function() {
expectObjectAssign(
'var xy = { x: 1, y: 2 }'
).not.toBeCalled();
});
// Syntax tests.
it('should preserve extra whitespace', function() {
expectTransform(
'let xyz = { x: 1, y : \n 2, ... \nz, ... z }',
'let xyz = Object.assign({ x: 1, y : \n 2}, \nz, z )'
);
});
it('should preserve parenthesis', function() {
expectTransform(
'let xyz = { x: 1, ...({ y: 2 }), z: 3 }',
'let xyz = Object.assign({ x: 1}, ({ y: 2 }), {z: 3 })'
);
});
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
it('should not transform destructuring assignment', function() {
expectTransform(
'let { x, ...y } = z',
'let { x, ...y } = z'
);
});
// Accessors are unsupported. We leave it unprocessed so that other,
// chained transforms, can take over.
it('should not transform when there are getters', function() {
expectTransform(
'let xy = { ...x, get x() { } }',
'let xy = { ...x, get x() { } }'
);
});
it('should not transform when there are setters', function() {
expectTransform(
'let xy = { set x(v) { }, ...y }',
'let xy = { set x(v) { }, ...y }'
);
});
it('should silently ignore falsy values', function() {
/*globals obj*/
var code = transform([
'var x = null;',
'var y = { y: "y" };',
'var obj = { ...x, ...y, ...{ ...false, z: "z", ...y } };'
].join('\n'));
eval(code);
expect(obj).toEqual({ y: 'y', z: 'z' });
});
});

View File

@@ -0,0 +1,95 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails dmitrys@fb.com
*/
/*jshint evil:true*/
require('mock-modules').autoMockOff();
describe('es7-trailing-comma-visitors', function() {
var transformFn;
var visitors;
beforeEach(function() {
require('mock-modules').dumpCache();
transformFn = require('../../src/jstransform').transform;
visitors = require('../es6-arrow-function-visitors').visitorList
.concat(require('../es6-rest-param-visitors').visitorList)
.concat(require('../es7-trailing-comma-visitors').visitorList);
});
function transform(code) {
return transformFn(visitors, code).code;
}
function expectTransform(code, result) {
expect(transform(code)).toEqual(result);
}
it('should eliminate trailing commas in function calls', function() {
expectTransform(
'Math.max(1, 2 , )',
'Math.max(1, 2 )'
);
});
it('should eliminate trailing commas when creating new objects', function() {
expectTransform(
'var pet = new Animal(\'cat\', NATURE_EVIL,\t);',
'var pet = new Animal(\'cat\', NATURE_EVIL\t);'
);
});
it('should eliminate trailing commas in function expressions', function() {
expectTransform(
'var Animal = function(species, nature,) { this.species = species; };',
'var Animal = function(species, nature) { this.species = species; };'
);
});
it('should eliminate trailing commas in function definitions', function() {
expectTransform(
'function Animal(species, nature,) { this.species = species; };',
'function Animal(species, nature) { this.species = species; };'
);
});
it('should eliminate trailing commas in method definitions', function() {
expectTransform(
'class Animal{ setNature(nature,) { this.nature = nature; } }',
'class Animal{ setNature(nature) { this.nature = nature; } }'
);
});
it('should eliminate trailing commas in arrow functions', function() {
expectTransform(
'var cube = ((x,) => x * x * x);',
'var cube = (function(x) {return x * x * x;});'
);
expectTransform(
'var multiply = ((x,y,z,) => x * y * z);',
'var multiply = (function(x,y,z) {return x * y * z;});'
);
});
it('should eliminate trailing commas after default arguments', function() {
expectTransform(
'(function foo(x = 5,) {})',
'(function foo(x = 5) {})'
);
expectTransform(
'(function foo({x} = {},) {})',
'(function foo({x} = {}) {})'
);
});
});

View File

@@ -0,0 +1,56 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/*jshint evil:true, multistr:true*/
var tests = require('./../type-syntax-test');
var jstransform = require('../../../src/jstransform');
var visitors = require('../../type-syntax').visitorList;
var fs = require('fs');
var out = '/**\n\
* WARNING: This file is autogenerated by\n\
* src/visitors/flow/__tests__/gen/generate-type-syntax-test.js\n\
* Do NOT modify this file directly! Instead, add your tests to\n\
* src/visitors/flow/__tests__/type-syntax-test.js and run\n\
* src/visitors/flow/__tests__/gen/generate-type-syntax-test.js\n\
*/\n\n';
var data = {};
Object.keys(tests).forEach(function(sectionName) {
var section = tests[sectionName];
var sectionData = {};
section.forEach(function(testCode) {
var code;
sectionData[testCode] = {};
var testData = {};
try {
code = jstransform.transform(visitors, testCode).code;
testData.raworiginal = testCode;
testData.transformed = code;
try {
eval(code);
testData.eval = 'No error';
} catch (e) {
testData.eval = e.message;
}
} catch (e) {
testData.error = e.message;
}
sectionData[testCode] = testData;
});
data[sectionName] = sectionData;
});
out += 'module.exports = ' + JSON.stringify(data, null, 2) + ';\n';
fs.writeFileSync(__dirname + '/type-syntax-test.rec.js', out);
console.log('Recorded type-syntax-test.js output into type-syntax-test.rec.js');

View File

@@ -0,0 +1,712 @@
/**
* WARNING: This file is autogenerated by
* src/visitors/flow/__tests__/gen/generate-type-syntax-test.js
* Do NOT modify this file directly! Instead, add your tests to
* src/visitors/flow/__tests__/type-syntax-test.js and run
* src/visitors/flow/__tests__/gen/generate-type-syntax-test.js
*/
module.exports = {
"TypeAnnotations": {
"function foo(numVal: any){}": {
"raworiginal": "function foo(numVal: any){}",
"transformed": "function foo(numVal ){}",
"eval": "No error"
},
"function foo(numVal: number){}": {
"raworiginal": "function foo(numVal: number){}",
"transformed": "function foo(numVal ){}",
"eval": "No error"
},
"function foo(numVal: number, strVal: string){}": {
"raworiginal": "function foo(numVal: number, strVal: string){}",
"transformed": "function foo(numVal , strVal ){}",
"eval": "No error"
},
"function foo(numVal: number, untypedVal){}": {
"raworiginal": "function foo(numVal: number, untypedVal){}",
"transformed": "function foo(numVal , untypedVal){}",
"eval": "No error"
},
"function foo(untypedVal, numVal: number){}": {
"raworiginal": "function foo(untypedVal, numVal: number){}",
"transformed": "function foo(untypedVal, numVal ){}",
"eval": "No error"
},
"function foo(nullableNum: ?number){}": {
"raworiginal": "function foo(nullableNum: ?number){}",
"transformed": "function foo(nullableNum ){}",
"eval": "No error"
},
"function foo(callback: () => void){}": {
"raworiginal": "function foo(callback: () => void){}",
"transformed": "function foo(callback ){}",
"eval": "No error"
},
"function foo(callback: () => number){}": {
"raworiginal": "function foo(callback: () => number){}",
"transformed": "function foo(callback ){}",
"eval": "No error"
},
"function foo(callback: (_:bool) => number){}": {
"raworiginal": "function foo(callback: (_:bool) => number){}",
"transformed": "function foo(callback ){}",
"eval": "No error"
},
"function foo(callback: (_1:bool, _2:string) => number){}": {
"raworiginal": "function foo(callback: (_1:bool, _2:string) => number){}",
"transformed": "function foo(callback ){}",
"eval": "No error"
},
"function foo(callback: (_1:bool, ...foo:Array<number>) => number){}": {
"raworiginal": "function foo(callback: (_1:bool, ...foo:Array<number>) => number){}",
"transformed": "function foo(callback ){}",
"eval": "No error"
},
"function foo():number{}": {
"raworiginal": "function foo():number{}",
"transformed": "function foo() {}",
"eval": "No error"
},
"function foo():() => void{}": {
"raworiginal": "function foo():() => void{}",
"transformed": "function foo() {}",
"eval": "No error"
},
"function foo():(_:bool) => number{}": {
"raworiginal": "function foo():(_:bool) => number{}",
"transformed": "function foo() {}",
"eval": "No error"
},
"function foo():(_?:bool) => number{}": {
"raworiginal": "function foo():(_?:bool) => number{}",
"transformed": "function foo() {}",
"eval": "No error"
},
"function foo(): {} {}": {
"raworiginal": "function foo(): {} {}",
"transformed": "function foo() {}",
"eval": "No error"
},
"function foo<T>() {}": {
"raworiginal": "function foo<T>() {}",
"transformed": "function foo () {}",
"eval": "No error"
},
"function foo<T: Foo>() {}": {
"raworiginal": "function foo<T: Foo>() {}",
"transformed": "function foo () {}",
"eval": "No error"
},
"function foo<T,S>() {}": {
"raworiginal": "function foo<T,S>() {}",
"transformed": "function foo () {}",
"eval": "No error"
},
"a=function<T,S>() {}": {
"raworiginal": "a=function<T,S>() {}",
"transformed": "a=function () {}",
"eval": "No error"
},
"a={set fooProp(value:number){}}": {
"raworiginal": "a={set fooProp(value:number){}}",
"transformed": "a={set fooProp(value ){}}",
"eval": "No error"
},
"a={set fooProp(value:number): void{}}": {
"raworiginal": "a={set fooProp(value:number): void{}}",
"transformed": "a={set fooProp(value ) {}}",
"eval": "No error"
},
"a={get fooProp(): number {}}": {
"raworiginal": "a={get fooProp(): number {}}",
"transformed": "a={get fooProp() {}}",
"eval": "No error"
},
"class Foo {set fooProp(value:number){}}": {
"raworiginal": "class Foo {set fooProp(value:number){}}",
"transformed": "class Foo {set fooProp(value ){}}",
"eval": "Unexpected reserved word"
},
"class Foo {set fooProp(value:number): void{}}": {
"raworiginal": "class Foo {set fooProp(value:number): void{}}",
"transformed": "class Foo {set fooProp(value ) {}}",
"eval": "Unexpected reserved word"
},
"class Foo {get fooProp(): number{}}": {
"raworiginal": "class Foo {get fooProp(): number{}}",
"transformed": "class Foo {get fooProp() {}}",
"eval": "Unexpected reserved word"
},
"var numVal:number;": {
"raworiginal": "var numVal:number;",
"transformed": "var numVal ;",
"eval": "No error"
},
"var numVal:number = otherNumVal;": {
"raworiginal": "var numVal:number = otherNumVal;",
"transformed": "var numVal = otherNumVal;",
"eval": "otherNumVal is not defined"
},
"var a: {numVal: number};": {
"raworiginal": "var a: {numVal: number};",
"transformed": "var a ;",
"eval": "No error"
},
"var a: {numVal: number;};": {
"raworiginal": "var a: {numVal: number;};",
"transformed": "var a ;",
"eval": "No error"
},
"var a: {numVal: number; [indexer: string]: number};": {
"raworiginal": "var a: {numVal: number; [indexer: string]: number};",
"transformed": "var a ;",
"eval": "No error"
},
"var a: ?{numVal: number};": {
"raworiginal": "var a: ?{numVal: number};",
"transformed": "var a ;",
"eval": "No error"
},
"var a: {numVal: number; strVal: string}": {
"raworiginal": "var a: {numVal: number; strVal: string}",
"transformed": "var a ",
"eval": "No error"
},
"var a: {subObj: {strVal: string}}": {
"raworiginal": "var a: {subObj: {strVal: string}}",
"transformed": "var a ",
"eval": "No error"
},
"var a: {subObj: ?{strVal: string}}": {
"raworiginal": "var a: {subObj: ?{strVal: string}}",
"transformed": "var a ",
"eval": "No error"
},
"var a: {param1: number; param2: string}": {
"raworiginal": "var a: {param1: number; param2: string}",
"transformed": "var a ",
"eval": "No error"
},
"var a: {param1: number; param2?: string}": {
"raworiginal": "var a: {param1: number; param2?: string}",
"transformed": "var a ",
"eval": "No error"
},
"var a: {add(x:number, ...y:Array<string>): void}": {
"raworiginal": "var a: {add(x:number, ...y:Array<string>): void}",
"transformed": "var a ",
"eval": "No error"
},
"var a: { id<T>(x: T): T; }": {
"raworiginal": "var a: { id<T>(x: T): T; }",
"transformed": "var a ",
"eval": "No error"
},
"var a:Array<number> = [1, 2, 3]": {
"raworiginal": "var a:Array<number> = [1, 2, 3]",
"transformed": "var a = [1, 2, 3]",
"eval": "No error"
},
"a = class Foo<T> { }": {
"raworiginal": "a = class Foo<T> { }",
"transformed": "a = class Foo<T> { }",
"eval": "Unexpected reserved word"
},
"a = class Foo<T> extends Bar<T> { }": {
"raworiginal": "a = class Foo<T> extends Bar<T> { }",
"transformed": "a = class Foo<T> extends Bar<T> { }",
"eval": "Unexpected reserved word"
},
"class Foo<T> {}": {
"raworiginal": "class Foo<T> {}",
"transformed": "class Foo<T> {}",
"eval": "Unexpected reserved word"
},
"class Foo<T: Bar> {}": {
"raworiginal": "class Foo<T: Bar> {}",
"transformed": "class Foo<T > {}",
"eval": "Unexpected reserved word"
},
"class Foo<T> extends Bar<T> { }": {
"raworiginal": "class Foo<T> extends Bar<T> { }",
"transformed": "class Foo<T> extends Bar<T> { }",
"eval": "Unexpected reserved word"
},
"class Foo<T> extends mixin(Bar) { }": {
"raworiginal": "class Foo<T> extends mixin(Bar) { }",
"transformed": "class Foo<T> extends mixin(Bar) { }",
"eval": "Unexpected reserved word"
},
"class Foo<T> { bar<U>():number { return 42; }}": {
"raworiginal": "class Foo<T> { bar<U>():number { return 42; }}",
"transformed": "class Foo<T> { bar<U>() { return 42; }}",
"eval": "Unexpected reserved word"
},
"class Foo { \"bar\"<T>() { } }": {
"raworiginal": "class Foo { \"bar\"<T>() { } }",
"transformed": "class Foo { \"bar\"<T>() { } }",
"eval": "Unexpected reserved word"
},
"function foo(requiredParam, optParam?) {}": {
"raworiginal": "function foo(requiredParam, optParam?) {}",
"transformed": "function foo(requiredParam, optParam ) {}",
"eval": "No error"
},
"class Foo { prop1:string; prop2:number; }": {
"raworiginal": "class Foo { prop1:string; prop2:number; }",
"transformed": "class Foo { }",
"eval": "Unexpected reserved word"
},
"var x : number | string = 4;": {
"raworiginal": "var x : number | string = 4;",
"transformed": "var x = 4;",
"eval": "No error"
},
"class Array { concat(items:number | string) {}; }": {
"raworiginal": "class Array { concat(items:number | string) {}; }",
"transformed": "class Array { concat(items ) {}; }",
"eval": "Unexpected reserved word"
},
"var x : () => number | () => string = fn;": {
"raworiginal": "var x : () => number | () => string = fn;",
"transformed": "var x = fn;",
"eval": "fn is not defined"
},
"var x: typeof Y = Y;": {
"raworiginal": "var x: typeof Y = Y;",
"transformed": "var x = Y;",
"eval": "Y is not defined"
},
"var x: typeof Y | number = Y;": {
"raworiginal": "var x: typeof Y | number = Y;",
"transformed": "var x = Y;",
"eval": "Y is not defined"
},
"var {x}: {x: string; } = { x: \"hello\" };": {
"raworiginal": "var {x}: {x: string; } = { x: \"hello\" };",
"transformed": "var {x} = { x: \"hello\" };",
"eval": "Unexpected token {"
},
"var {x}: {x: string } = { x: \"hello\" };": {
"raworiginal": "var {x}: {x: string } = { x: \"hello\" };",
"transformed": "var {x} = { x: \"hello\" };",
"eval": "Unexpected token {"
},
"var [x]: Array<string> = [ \"hello\" ];": {
"raworiginal": "var [x]: Array<string> = [ \"hello\" ];",
"transformed": "var [x] = [ \"hello\" ];",
"eval": "Unexpected token ["
},
"function foo({x}: { x: string; }) {}": {
"raworiginal": "function foo({x}: { x: string; }) {}",
"transformed": "function foo({x} ) {}",
"eval": "Unexpected token {"
},
"function foo([x]: Array<string>) {}": {
"raworiginal": "function foo([x]: Array<string>) {}",
"transformed": "function foo([x] ) {}",
"eval": "Unexpected token ["
},
"function foo(...rest: Array<number>) {}": {
"raworiginal": "function foo(...rest: Array<number>) {}",
"transformed": "function foo(...rest ) {}",
"eval": "Unexpected token ."
},
"(function (...rest: Array<number>) {})": {
"raworiginal": "(function (...rest: Array<number>) {})",
"transformed": "(function (...rest ) {})",
"eval": "Unexpected token ."
},
"((...rest: Array<number>) => rest)": {
"raworiginal": "((...rest: Array<number>) => rest)",
"transformed": "((...rest ) => rest)",
"eval": "Unexpected token ."
},
"var a: Map<string, Array<string> >": {
"raworiginal": "var a: Map<string, Array<string> >",
"transformed": "var a ",
"eval": "No error"
},
"var a: Map<string, Array<string>>": {
"raworiginal": "var a: Map<string, Array<string>>",
"transformed": "var a ",
"eval": "No error"
},
"var a: number[]": {
"raworiginal": "var a: number[]",
"transformed": "var a ",
"eval": "No error"
},
"var a: ?string[]": {
"raworiginal": "var a: ?string[]",
"transformed": "var a ",
"eval": "No error"
},
"var a: Promise<bool>[]": {
"raworiginal": "var a: Promise<bool>[]",
"transformed": "var a ",
"eval": "No error"
},
"var a:(...rest:Array<number>) => number": {
"raworiginal": "var a:(...rest:Array<number>) => number",
"transformed": "var a ",
"eval": "No error"
}
},
"Type Alias": {
"type FBID = number;": {
"raworiginal": "type FBID = number;",
"transformed": " ",
"eval": "No error"
},
"type Foo<T> = Bar<T>": {
"raworiginal": "type Foo<T> = Bar<T>",
"transformed": " ",
"eval": "No error"
}
},
"Interfaces": {
"interface A {}": {
"raworiginal": "interface A {}",
"transformed": " ",
"eval": "No error"
},
"interface A extends B {}": {
"raworiginal": "interface A extends B {}",
"transformed": " ",
"eval": "No error"
},
"interface A<T> extends B<T>, C<T> {}": {
"raworiginal": "interface A<T> extends B<T>, C<T> {}",
"transformed": " ",
"eval": "No error"
},
"interface A { foo: () => number; }": {
"raworiginal": "interface A { foo: () => number; }",
"transformed": " ",
"eval": "No error"
},
"interface Dictionary { [index: string]: string; length: number; }": {
"raworiginal": "interface Dictionary { [index: string]: string; length: number; }",
"transformed": " ",
"eval": "No error"
},
"class Foo implements Bar {}": {
"raworiginal": "class Foo implements Bar {}",
"transformed": "class Foo implements Bar {}",
"eval": "Unexpected reserved word"
},
"class Foo extends Bar implements Bat, Man<number> {}": {
"raworiginal": "class Foo extends Bar implements Bat, Man<number> {}",
"transformed": "class Foo extends Bar implements Bat, Man<number> {}",
"eval": "Unexpected reserved word"
},
"class Foo extends class Bar implements Bat {} {}": {
"raworiginal": "class Foo extends class Bar implements Bat {} {}",
"transformed": "class Foo extends class Bar implements Bat {} {}",
"eval": "Unexpected reserved word"
},
"class Foo extends class Bar implements Bat {} implements Man {}": {
"raworiginal": "class Foo extends class Bar implements Bat {} implements Man {}",
"transformed": "class Foo extends class Bar implements Bat {} implements Man {}",
"eval": "Unexpected reserved word"
}
},
"Type Grouping": {
"var a: (number)": {
"raworiginal": "var a: (number)",
"transformed": "var a ",
"eval": "No error"
},
"var a: (() => number) | () => string": {
"raworiginal": "var a: (() => number) | () => string",
"transformed": "var a ",
"eval": "No error"
},
"var a: number & (string | bool)": {
"raworiginal": "var a: number & (string | bool)",
"transformed": "var a ",
"eval": "No error"
},
"var a: (typeof A)": {
"raworiginal": "var a: (typeof A)",
"transformed": "var a ",
"eval": "No error"
}
},
"XJS": {
"<a />": {
"raworiginal": "<a />",
"transformed": "<a />",
"eval": "Unexpected token <"
},
"<n:a n:v />": {
"raworiginal": "<n:a n:v />",
"transformed": "<n:a n:v />",
"eval": "Unexpected token <"
},
"<a n:foo=\"bar\"> {value} <b><c /></b></a>": {
"raworiginal": "<a n:foo=\"bar\"> {value} <b><c /></b></a>",
"transformed": "<a n:foo=\"bar\"> {value} <b><c /></b></a>",
"eval": "Unexpected token <"
},
"<a b={\" \"} c=\" \" d=\"&amp;\" e=\"id=1&group=2\" f=\"&#123456789\" g=\"&#123*;\" h=\"&#x;\" />": {
"raworiginal": "<a b={\" \"} c=\" \" d=\"&amp;\" e=\"id=1&group=2\" f=\"&#123456789\" g=\"&#123*;\" h=\"&#x;\" />",
"transformed": "<a b={\" \"} c=\" \" d=\"&amp;\" e=\"id=1&group=2\" f=\"&#123456789\" g=\"&#123*;\" h=\"&#x;\" />",
"eval": "Unexpected token <"
},
"<a\n/>": {
"raworiginal": "<a\n/>",
"transformed": "<a\n/>",
"eval": "Unexpected token <"
},
"<日本語></日本語>": {
"raworiginal": "<日本語></日本語>",
"transformed": "<日本語></日本語>",
"eval": "Unexpected token <"
},
"<AbC-def\n test=\"&#x0026;&#38;\">\nbar\nbaz\r\n</AbC-def>": {
"raworiginal": "<AbC-def\n test=\"&#x0026;&#38;\">\nbar\nbaz\r\n</AbC-def>",
"transformed": "<AbC-def\n test=\"&#x0026;&#38;\">\nbar\nbaz\r\n</AbC-def>",
"eval": "Unexpected token <"
},
"<a b={x ? <c /> : <d />} />": {
"raworiginal": "<a b={x ? <c /> : <d />} />",
"transformed": "<a b={x ? <c /> : <d />} />",
"eval": "Unexpected token <"
},
"<a>{}</a>": {
"raworiginal": "<a>{}</a>",
"transformed": "<a>{}</a>",
"eval": "Unexpected token <"
},
"<a>{/* this is a comment */}</a>": {
"raworiginal": "<a>{/* this is a comment */}</a>",
"transformed": "<a>{/* this is a comment */}</a>",
"eval": "Unexpected token <"
},
"<div>@test content</div>": {
"raworiginal": "<div>@test content</div>",
"transformed": "<div>@test content</div>",
"eval": "Unexpected token <"
},
"<div><br />7x invalid-js-identifier</div>": {
"raworiginal": "<div><br />7x invalid-js-identifier</div>",
"transformed": "<div><br />7x invalid-js-identifier</div>",
"eval": "Unexpected token <"
},
"<LeftRight left=<a /> right=<b>monkeys</b> />": {
"raworiginal": "<LeftRight left=<a /> right=<b>monkeys</b> />",
"transformed": "<LeftRight left=<a /> right=<b>monkeys</b> />",
"eval": "Unexpected token <"
},
"<a.b></a.b>": {
"raworiginal": "<a.b></a.b>",
"transformed": "<a.b></a.b>",
"eval": "Unexpected token <"
},
"<a.b.c></a.b.c>": {
"raworiginal": "<a.b.c></a.b.c>",
"transformed": "<a.b.c></a.b.c>",
"eval": "Unexpected token <"
},
"(<div />) < x;": {
"raworiginal": "(<div />) < x;",
"transformed": "(<div />) < x;",
"eval": "Unexpected token <"
},
"<div {...props} />": {
"raworiginal": "<div {...props} />",
"transformed": "<div {...props} />",
"eval": "Unexpected token <"
},
"<div {...props} post=\"attribute\" />": {
"raworiginal": "<div {...props} post=\"attribute\" />",
"transformed": "<div {...props} post=\"attribute\" />",
"eval": "Unexpected token <"
},
"<div pre=\"leading\" pre2=\"attribute\" {...props}></div>": {
"raworiginal": "<div pre=\"leading\" pre2=\"attribute\" {...props}></div>",
"transformed": "<div pre=\"leading\" pre2=\"attribute\" {...props}></div>",
"eval": "Unexpected token <"
},
"<a> </a>": {
"raworiginal": "<a> </a>",
"transformed": "<a> </a>",
"eval": "Unexpected token <"
}
},
"Call Properties": {
"var a : { (): number }": {
"raworiginal": "var a : { (): number }",
"transformed": "var a ",
"eval": "No error"
},
"var a : { (): number; }": {
"raworiginal": "var a : { (): number; }",
"transformed": "var a ",
"eval": "No error"
},
"var a : { (): number; y: string; (x: string): string }": {
"raworiginal": "var a : { (): number; y: string; (x: string): string }",
"transformed": "var a ",
"eval": "No error"
},
"var a : { <T>(x: T): number; }": {
"raworiginal": "var a : { <T>(x: T): number; }",
"transformed": "var a ",
"eval": "No error"
},
"interface A { (): number; }": {
"raworiginal": "interface A { (): number; }",
"transformed": " ",
"eval": "No error"
}
},
"String Literal Types": {
"function createElement(tagName: \"div\"): HTMLDivElement {}": {
"raworiginal": "function createElement(tagName: \"div\"): HTMLDivElement {}",
"transformed": "function createElement(tagName ) {}",
"eval": "No error"
},
"function createElement(tagName: 'div'): HTMLDivElement {}": {
"raworiginal": "function createElement(tagName: 'div'): HTMLDivElement {}",
"transformed": "function createElement(tagName ) {}",
"eval": "No error"
}
},
"Qualified Generic Type": {
"var a : A.B": {
"raworiginal": "var a : A.B",
"transformed": "var a ",
"eval": "No error"
},
"var a : A.B.C": {
"raworiginal": "var a : A.B.C",
"transformed": "var a ",
"eval": "No error"
},
"var a : A.B<T>": {
"raworiginal": "var a : A.B<T>",
"transformed": "var a ",
"eval": "No error"
},
"var a : typeof A.B<T>": {
"raworiginal": "var a : typeof A.B<T>",
"transformed": "var a ",
"eval": "No error"
}
},
"Declare Statements": {
"declare var foo": {
"raworiginal": "declare var foo",
"transformed": " ",
"eval": "No error"
},
"declare var foo;": {
"raworiginal": "declare var foo;",
"transformed": " ",
"eval": "No error"
},
"declare function foo(): void": {
"raworiginal": "declare function foo(): void",
"transformed": " ",
"eval": "No error"
},
"declare function foo(): void;": {
"raworiginal": "declare function foo(): void;",
"transformed": " ",
"eval": "No error"
},
"declare function foo<T>(): void;": {
"raworiginal": "declare function foo<T>(): void;",
"transformed": " ",
"eval": "No error"
},
"declare function foo(x: number, y: string): void;": {
"raworiginal": "declare function foo(x: number, y: string): void;",
"transformed": " ",
"eval": "No error"
},
"declare class A {}": {
"raworiginal": "declare class A {}",
"transformed": " ",
"eval": "No error"
},
"declare class A<T> extends B<T> { x: number }": {
"raworiginal": "declare class A<T> extends B<T> { x: number }",
"transformed": " ",
"eval": "No error"
},
"declare class A { static foo(): number; static x : string }": {
"raworiginal": "declare class A { static foo(): number; static x : string }",
"transformed": " ",
"eval": "No error"
},
"declare class A { static [ indexer: number]: string }": {
"raworiginal": "declare class A { static [ indexer: number]: string }",
"transformed": " ",
"eval": "No error"
},
"declare class A { static () : number }": {
"raworiginal": "declare class A { static () : number }",
"transformed": " ",
"eval": "No error"
}
},
"Declare Module": {
"declare module A {}": {
"raworiginal": "declare module A {}",
"transformed": " ",
"eval": "No error"
},
"declare module \"./a/b.js\" {}": {
"raworiginal": "declare module \"./a/b.js\" {}",
"transformed": " ",
"eval": "No error"
},
"declare module A { declare var x: number; }": {
"raworiginal": "declare module A { declare var x: number; }",
"transformed": " ",
"eval": "No error"
},
"declare module A { declare function foo(): number; }": {
"raworiginal": "declare module A { declare function foo(): number; }",
"transformed": " ",
"eval": "No error"
},
"declare module A { declare class B { foo(): number; } }": {
"raworiginal": "declare module A { declare class B { foo(): number; } }",
"transformed": " ",
"eval": "No error"
}
},
"Typecasts": {
"(xxx: number)": {
"raworiginal": "(xxx: number)",
"transformed": "(xxx )",
"eval": "xxx is not defined"
},
"({xxx: 0, yyy: \"hey\"}: {xxx: number; yyy: string})": {
"raworiginal": "({xxx: 0, yyy: \"hey\"}: {xxx: number; yyy: string})",
"transformed": "({xxx: 0, yyy: \"hey\"} )",
"eval": "No error"
},
"((xxx) => xxx + 1: (xxx: number) => number)": {
"raworiginal": "((xxx) => xxx + 1: (xxx: number) => number)",
"transformed": "((xxx) => xxx + 1 )",
"eval": "Unexpected token >"
},
"((xxx: number), (yyy: string))": {
"raworiginal": "((xxx: number), (yyy: string))",
"transformed": "((xxx ), (yyy ))",
"eval": "xxx is not defined"
},
"((xxx: any): number)": {
"raworiginal": "((xxx: any): number)",
"transformed": "((xxx ) )",
"eval": "xxx is not defined"
}
}
};

View File

@@ -0,0 +1,142 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails jeffmo@fb.com
*/
'use strict';
require('mock-modules').autoMockOff();
describe('jsx', function() {
var jsx = require('../jsx-helpers');
var utils = require('jstransform/src/utils');
var esprima = require('esprima-fb');
function runWithLiteral(text, isLast) {
var source = '<div>' + text + '</div>';
var ast = esprima.parse(source, { range: true });
var state = utils.createState(source);
var literal = ast.body[0].expression.children[0];
state.g.position = literal.range[0];
jsx.renderJSXLiteral(literal, isLast, state);
return state;
}
function runWithAttribute(inlineAttribute, isLast) {
var source = '<div ' + inlineAttribute + ' />';
var ast = esprima.parse(source, { range: true });
var state = utils.createState(source);
var attribute = ast.body[0].expression.openingElement.attributes[0];
state.g.position = attribute.value.range[0];
jsx.renderJSXExpressionContainer(
function() {},
attribute.value,
isLast,
[],
state
);
return state;
}
it('should render simple literal', function() {
var state = runWithLiteral('a', true);
expect(state.g.buffer).toEqual('"a"');
});
it('should render simple literal with single space before', function() {
var state = runWithLiteral(' a', true);
expect(state.g.buffer).toEqual('" a"');
});
it('should render simple literal with single space before' +
' from multiple spaces', function() {
var state = runWithLiteral(' \t a', true);
expect(state.g.buffer).toEqual('" a"');
});
it('should render simple literal with single space after', function() {
var state = runWithLiteral('a ', true);
expect(state.g.buffer).toEqual('"a "');
});
it('should render simple literal with single space after' +
' from multiple spaces', function() {
var state = runWithLiteral('a \t \t ', true);
expect(state.g.buffer).toEqual('"a "');
});
it('should render multiline literal as last', function() {
var state = runWithLiteral(
' sdfsdfsdf\n' +
' sdlkfjsdfljs\n' +
' ',
true);
expect(state.g.buffer).toEqual(
'" sdfsdfsdf" + " " +\n' +
' "sdlkfjsdfljs"\n' +
' ');
});
it('should render multiline literal as not last', function() {
var state = runWithLiteral(
' sdfsdfsdf\n' +
' sdlkfjsdfljs\n' +
' ',
false);
expect(state.g.buffer).toEqual(
'" sdfsdfsdf" + " " +\n' +
' "sdlkfjsdfljs", \n' +
' ');
});
it('should render attribute expressions', function() {
var state = runWithAttribute('attr={"foo"}', true);
expect(state.g.buffer).toEqual('"foo"');
});
it('should render attribute expressions as not last', function() {
var state = runWithAttribute('attr={"foo"}', false);
expect(state.g.buffer).toEqual('"foo", ');
});
it('should render attribute expressions with spaces', function() {
var state = runWithAttribute('attr={ "foo"\n }', true);
expect(state.g.buffer).toEqual(' "foo"\n ');
});
it('should render attribute expressions with commas before trailing ' +
'whitespace', function() {
var state = runWithAttribute('attr={\n"foo"\n }', false);
expect(state.g.buffer).toEqual('\n"foo", \n ');
});
it('should render empty child expressions with comments', function() {
var source = '<div>{/*comment*/}</div>';
var ast = esprima.parse(source, {range: true});
var child = ast.body[0].expression.children[0];
var state = utils.createState(source, child);
state.g.position = child.range[0];
jsx.renderJSXExpressionContainer(function() {}, child, true, [], state);
expect(state.g.buffer).toBe('/*comment*/');
});
it('should not render commas after empty child expressions even if they\'re' +
'not last', function() {
var source = '<div>{/*comment*/}</div>';
var ast = esprima.parse(source, {range: true});
var child = ast.body[0].expression.children[0];
var state = utils.createState(source, child);
state.g.position = child.range[0];
jsx.renderJSXExpressionContainer(function() {}, child, false, [], state);
expect(state.g.buffer).toBe('/*comment*/');
});
});

View File

@@ -0,0 +1,175 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/
'use strict';
require('mock-modules').autoMockOff();
var transformFn = require('jstransform').transform;
var visitors = require('../react-display-name-visitors').visitorList;
function transform(code) {
return transformFn(visitors, code);
}
describe('react displayName jsx', function() {
it('should only inject displayName if missing', function() {
var code = [
'"use strict";',
'var Whateva = React.createClass({',
' displayName: "Whateva",',
' render: function() {',
' return null;',
' }',
'});'
].join('\n');
var result = [
'"use strict";',
'var Whateva = React.createClass({',
' displayName: "Whateva",',
' render: function() {',
' return null;',
' }',
'});'
].join('\n');
expect(transform(code).code).toEqual(result);
});
it('should inject displayName in simple assignment', () => {
var code = [
'var Component = React.createClass({',
' render: function() {',
' return null;',
' }',
'});'
].join('\n');
var result = [
'var Component = React.createClass({displayName: "Component",',
' render: function() {',
' return null;',
' }',
'});'
].join('\n');
expect(transform(code).code).toEqual(result);
});
it('should inject displayName in simple assignment without var', () => {
var code = [
'var Component;',
'Component = React.createClass({',
' render: function() {',
' return null;',
' }',
'});'
].join('\n');
var result = [
'var Component;',
'Component = React.createClass({displayName: "Component",',
' render: function() {',
' return null;',
' }',
'});'
].join('\n');
expect(transform(code).code).toEqual(result);
});
it('should inject displayName in property assignment', () => {
var code = [
'ns.Component = React.createClass({',
' render: function() {',
' return null;',
' }',
'});'
].join('\n');
var result = [
'ns.Component = React.createClass({displayName: "ns.Component",',
' render: function() {',
' return null;',
' }',
'});'
].join('\n');
expect(transform(code).code).toEqual(result);
});
it('should inject displayName in chained property assignment', () => {
var code = [
'ns.ns1.Component = React.createClass({',
' render: function() {',
' return null;',
' }',
'});'
].join('\n');
var result = [
'ns.ns1.Component = React.createClass({displayName: "ns.ns1.Component",',
' render: function() {',
' return null;',
' }',
'});'
].join('\n');
expect(transform(code).code).toEqual(result);
});
it('should inject displayName in exports property assignment', () => {
var code = [
'exports.Component = React.createClass({',
' render: function() {',
' return null;',
' }',
'});'
].join('\n');
var result = [
'exports.Component = React.createClass({displayName: "Component",',
' render: function() {',
' return null;',
' }',
'});'
].join('\n');
expect(transform(code).code).toEqual(result);
});
it('should inject displayName in object declaration', () => {
var code = [
'exports = {',
' Component: React.createClass({',
' render: function() {',
' return null;',
' }',
' })',
'};'
].join('\n');
var result = [
'exports = {',
' Component: React.createClass({displayName: "Component",',
' render: function() {',
' return null;',
' }',
' })',
'};'
].join('\n');
expect(transform(code).code).toEqual(result);
});
});

View File

@@ -0,0 +1,394 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/
/*jshint evil:true, unused:false*/
'use strict';
require('mock-modules').autoMockOff();
var transformFn = require('jstransform').transform;
var visitors = require('../react-jsx-visitors').visitorList;
function transform(code) {
return transformFn(visitors, code);
}
describe('react jsx', function() {
// These are placeholder variables in scope that we can use to assert that a
// specific variable reference was passed, rather than an object clone of it.
var x = 123456;
var y = 789012;
var z = 345678;
var expectObjectAssign = function(code) {
/*eslint-disable no-unused-vars, no-eval*/
var Component = jest.genMockFunction();
var Child = jest.genMockFunction();
var objectAssignMock = jest.genMockFunction();
React.__spread = objectAssignMock;
eval(transform(code).code);
return expect(objectAssignMock);
/*eslint-enable*/
};
var React = {
createElement: jest.genMockFunction()
};
it('should convert simple tags', function() {
var code = 'var x = <div></div>;';
var result = 'var x = React.createElement("div", null);';
expect(transform(code).code).toEqual(result);
});
it('should convert simple text', function() {
var code = 'var x = <div>text</div>;';
var result = 'var x = React.createElement("div", null, "text");';
expect(transform(code).code).toEqual(result);
});
it('should have correct comma in nested children', function() {
var code = [
'var x = <div>',
' <div><br /></div>',
' <Component>{foo}<br />{bar}</Component>',
' <br />',
'</div>;'
].join('\n');
var result = [
'var x = React.createElement("div", null, ',
' React.createElement("div", null, ' +
'React.createElement("br", null)), ',
' React.createElement(Component, null, foo, ' +
'React.createElement("br", null), bar), ',
' React.createElement("br", null)',
');'
].join('\n');
expect(transform(code).code).toEqual(result);
});
it('should avoid wrapping in extra parens if not needed', function() {
// Try with a single composite child, wrapped in a div.
var code = [
'var x = <div>',
' <Component />',
'</div>;'
].join('\n');
var result = [
'var x = React.createElement("div", null, ',
' React.createElement(Component, null)',
');'
].join('\n');
expect(transform(code).code).toEqual(result);
// Try with a single interpolated child, wrapped in a div.
code = [
'var x = <div>',
' {this.props.children}',
'</div>;'
].join('\n');
result = [
'var x = React.createElement("div", null, ',
' this.props.children',
');'
].join('\n');
expect(transform(code).code).toEqual(result);
// Try with a single interpolated child, wrapped in a composite.
code = [
'var x = <Composite>',
' {this.props.children}',
'</Composite>;'
].join('\n');
result = [
'var x = React.createElement(Composite, null, ',
' this.props.children',
');'
].join('\n');
expect(transform(code).code).toEqual(result);
// Try with a single composite child, wrapped in a composite.
code = [
'var x = <Composite>',
' <Composite2 />',
'</Composite>;'
].join('\n');
result = [
'var x = React.createElement(Composite, null, ',
' React.createElement(Composite2, null)',
');'
].join('\n');
expect(transform(code).code).toEqual(result);
});
it('should insert commas after expressions before whitespace', function() {
var code = [
'var x =',
' <div',
' attr1={',
' "foo" + "bar"',
' }',
' attr2={',
' "foo" + "bar" +',
' ',
' "baz" + "bug"',
' }',
' attr3={',
' "foo" + "bar" +',
' "baz" + "bug"',
' // Extra line here.',
' }',
' attr4="baz">',
' </div>;'
].join('\n');
var result = [
'var x =',
' React.createElement("div", {',
' attr1: ',
' "foo" + "bar", ',
' ',
' attr2: ',
' "foo" + "bar" +',
' ',
' "baz" + "bug", ',
' ',
' attr3: ',
' "foo" + "bar" +',
' "baz" + "bug", ',
' // Extra line here.',
' ',
' attr4: "baz"}',
' );'
].join('\n');
expect(transform(code).code).toEqual(result);
});
it('should properly handle comments adjacent to children', function() {
var code = [
'var x = (',
' <div>',
' {/* A comment at the beginning */}',
' {/* A second comment at the beginning */}',
' <span>',
' {/* A nested comment */}',
' </span>',
' {/* A sandwiched comment */}',
' <br />',
' {/* A comment at the end */}',
' {/* A second comment at the end */}',
' </div>',
');'
].join('\n');
var result = [
'var x = (',
' React.createElement("div", null, ',
' /* A comment at the beginning */',
' /* A second comment at the beginning */',
' React.createElement("span", null',
' /* A nested comment */',
' ), ',
' /* A sandwiched comment */',
' React.createElement("br", null)',
' /* A comment at the end */',
' /* A second comment at the end */',
' )',
');'
].join('\n');
expect(transform(code).code).toBe(result);
});
it('should properly handle comments between props', function() {
var code = [
'var x = (',
' <div',
' /* a multi-line',
' comment */',
' attr1="foo">',
' <span // a double-slash comment',
' attr2="bar"',
' />',
' </div>',
');'
].join('\n');
var result = [
'var x = (',
' React.createElement("div", {',
' /* a multi-line',
' comment */',
' attr1: "foo"}, ',
' React.createElement("span", {// a double-slash comment',
' attr2: "bar"}',
' )',
' )',
');'
].join('\n');
expect(transform(code).code).toBe(result);
});
it('should not strip tags with a single child of &nbsp;', function() {
var code = [
'<div>&nbsp;</div>;'
].join('\n');
var result = [
'React.createElement("div", null, "\u00A0");'
].join('\n');
expect(transform(code).code).toBe(result);
});
it('should not strip &nbsp; even coupled with other whitespace', function() {
var code = [
'<div>&nbsp; </div>;'
].join('\n');
var result = [
'React.createElement("div", null, "\u00A0 ");'
].join('\n');
expect(transform(code).code).toBe(result);
});
it('should handle hasOwnProperty correctly', function() {
var code = '<hasOwnProperty>testing</hasOwnProperty>;';
var result = 'React.createElement("hasOwnProperty", null, "testing");';
expect(transform(code).code).toBe(result);
});
it('should allow constructor as prop', function() {
var code = '<Component constructor="foo" />;';
var result = 'React.createElement(Component, {constructor: "foo"});';
expect(transform(code).code).toBe(result);
});
it('should allow JS namespacing', function() {
var code = '<Namespace.Component />;';
var result = 'React.createElement(Namespace.Component, null);';
expect(transform(code).code).toBe(result);
});
it('should allow deeper JS namespacing', function() {
var code = '<Namespace.DeepNamespace.Component />;';
var result =
'React.createElement(Namespace.DeepNamespace.Component, null);';
expect(transform(code).code).toBe(result);
});
it('should disallow XML namespacing', function() {
var code = '<Namespace:Component />;';
expect(() => transform(code)).toThrow();
});
it('wraps props in React.__spread for spread attributes', function() {
var code =
'<Component { ... x } y\n' +
'={2 } z />';
var result =
'React.createElement(Component, React.__spread({}, x , {y: \n' +
'2, z: true}))';
expect(transform(code).code).toBe(result);
});
it('adds appropriate newlines when using spread attribute', function() {
var code =
'<Component\n' +
' {...this.props}\n' +
' sound="moo" />';
var result =
'React.createElement(Component, React.__spread({}, \n' +
' this.props, \n' +
' {sound: "moo"}))';
expect(transform(code).code).toBe(result);
});
it('handles overparenthesized JS', function() {
var code =
'<foo a={(b)} c={(d)}>Foo {(e+f //A line comment\n' +
'/* A multiline comment */)\n' +
'} bar\n' +
'</foo>';
var result = 'React.createElement("foo", {a: (b), c: (d)}, "Foo ", (e+f //A line comment\n' +
'/* A multiline comment */), \n' +
'" bar"\n' +
')';
expect(transform(code).code).toBe(result);
});
it('should transform known hyphenated tags', function() {
var code = '<font-face />;';
var result = 'React.createElement("font-face", null);';
expect(transform(code).code).toBe(result);
});
it('does not call React.__spread when there are no spreads', function() {
expectObjectAssign(
'<Component x={y} />'
).not.toBeCalled();
});
it('should not throw for unknown hyphenated tags', function() {
var code = '<x-component />;';
expect(function() {
transform(code);
}).not.toThrow();
});
it('calls assign with a new target object for spreads', function() {
expectObjectAssign(
'<Component {...x} />'
).toBeCalledWith({}, x);
});
it('calls assign with an empty object when the spread is first', function() {
expectObjectAssign(
'<Component { ...x } y={2} />'
).toBeCalledWith({}, x, {y: 2});
});
it('coalesces consecutive properties into a single object', function() {
expectObjectAssign(
'<Component { ... x } y={2} z />'
).toBeCalledWith({}, x, {y: 2, z: true});
});
it('avoids an unnecessary empty object when spread is not first', function() {
expectObjectAssign(
'<Component x={1} {...y} />'
).toBeCalledWith({x: 1}, y);
});
it('passes the same value multiple times to React.__spread', function() {
expectObjectAssign(
'<Component x={1} y="2" {...z} {...z}><Child /></Component>'
).toBeCalledWith({x: 1, y: '2'}, z, z);
});
it('evaluates sequences before passing them to React.__spread', function() {
expectObjectAssign(
'<Component x="1" {...(z = { y: 2 }, z)} z={3}>Text</Component>'
).toBeCalledWith({x: '1'}, {y: 2}, {z: 3});
});
});

View File

@@ -0,0 +1,87 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails jeffmo@fb.com
*/
/*jshint evil:true*/
jest.autoMockOff();
describe('reserved-words', function() {
var transformFn;
var visitors;
beforeEach(function() {
require('mock-modules').dumpCache();
visitors = require('../reserved-words-visitors').visitorList;
transformFn = require('../../src/jstransform').transform;
});
function transform(code, opts) {
// No need for visitors as long as we are not in es5 mode.
return transformFn(visitors, code, opts).code;
}
describe('reserved words in member expressions', function() {
it('should transform to reserved word members to computed', function() {
var code = 'foo.delete;';
expect(transform(code)).toEqual('foo["delete"];');
});
it('should handle parenthesis', function() {
var code = '(foo++).delete;';
expect(transform(code)).toEqual('(foo++)["delete"];');
code = '(foo.bar()).delete;';
expect(transform(code)).toEqual('(foo.bar())["delete"];');
});
it('should handle call expressions', function() {
var code = 'foo.return();';
expect(transform(code)).toEqual('foo["return"]();');
});
it('should only quote ES3 reserved words', function() {
var code = 'foo.await();';
expect(transform(code)).toEqual('foo.await();');
});
it('should work with newlines', function() {
var code = 'foo.\ncatch();';
expect(transform(code)).toEqual('foo\n["catch"]();');
// This one is weird but it works.
code = 'foo.\n catch();';
expect(transform(code)).toEqual('foo\n ["catch"]();');
code = 'foo\n.catch();';
expect(transform(code)).toEqual('foo\n["catch"]();');
code = 'foo\n .catch();';
expect(transform(code)).toEqual('foo\n ["catch"]();');
});
});
describe('reserved words in properties', function() {
it('should quote reserved words in properties', function() {
var code = 'var x = {null: 1};';
expect(transform(code)).toEqual('var x = {"null": 1};');
});
it('should only quote ES3 reserved words', function() {
var code = 'var x = {await: 1};';
expect(transform(code)).toEqual('var x = {await: 1};');
});
});
});

View File

@@ -0,0 +1,146 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails oncall+jsinfra@xmail.facebook.com
*/
/*jshint evil:true*/
require('mock-modules').autoMockOff();
describe('trailing-comma', function() {
var transformFn;
var visitors;
beforeEach(function() {
require('mock-modules').dumpCache();
visitors = require('../trailing-comma-visitors').visitorList;
transformFn = require('../../src/jstransform').transform;
});
function transform(code) {
return transformFn(visitors, code).code;
}
// Functional tests.
it('should strip commas from arrays', function() {
var code = [
'([',
' 1,',
' 2, /* last item */',
']);'
].join('\n');
expect(eval(transform(code))).toEqual([1, 2]);
});
it('should strip commas from objects', function() {
var code = [
'({',
' x: 1,',
' y: 2, // last item',
'});'
].join('\n');
expect(eval(transform(code))).toEqual({x: 1, y: 2});
});
// Syntax tests.
it('should transform code with trailing comma in an array', function() {
var code = [
'var arr = [',
' 1,',
' [2, 3,],',
' 4 /* last, item */ ,',
'];'
].join('\n');
var result = [
'var arr = [',
' 1,',
' [2, 3],',
' 4 /* last item */ ',
'];'
].join('\n');
expect(transform(code)).toEqual(result);
});
it('should transform code with trailing comma in an array', function() {
var code = [
'var arr = [',
' 1,',
' 2, /* last item */',
'];'
].join('\n');
var result = [
'var arr = [',
' 1,',
' 2 /* last item */',
'];'
].join('\n');
expect(transform(code)).toEqual(result);
});
it('should not strip trailing parenthesis', function() {
var code = [
'var result = [',
' value1,',
' (condition ? 1 : 2),',
'];'
].join('\n');
var result = [
'var result = [',
' value1,',
' (condition ? 1 : 2)',
'];'
].join('\n');
expect(transform(code)).toEqual(result);
});
it('should transform code with trailing comma in an object', function() {
var code = [
'var obj = {',
' x: 1,',
' y: 2, /*last item*/',
'};'
].join('\n');
var result = [
'var obj = {',
' x: 1,',
' y: 2 /*last item*/',
'};'
].join('\n');
expect(transform(code)).toEqual(result);
});
it('should NOT transform code with trailing hole in an array', function() {
var code = [
'var arr = [',
' 1,',
' 2,',
' , /*last hole*/',
'];'
].join('\n');
var result = [
'var arr = [',
' 1,',
' 2,',
' , /*last hole*/',
'];'
].join('\n');
expect(transform(code)).toEqual(result);
});
});

View File

@@ -0,0 +1,83 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/*jshint evil:true*/
require('mock-modules').autoMockOff();
describe('static type syntax syntax', function() {
var flowSyntaxVisitors;
var jstransform;
beforeEach(function() {
require('mock-modules').dumpCache();
flowSyntaxVisitors = require('../type-syntax.js').visitorList;
jstransform = require('jstransform');
});
function transform(code, visitors) {
var opts = {sourceType: 'nonStrictModule'};
code = jstransform.transform(
flowSyntaxVisitors,
code.join('\n'),
opts
).code;
if (visitors) {
code = jstransform.transform(visitors, code, opts).code;
}
return code;
}
describe('type alias', () => {
it('strips type aliases', () => {
/*global type*/
/*global sanityCheck*/
var code = transform([
'var type = 42;',
'type FBID = number;',
'type type = string',
'type += 42;'
]);
eval(code);
expect(type).toBe(84);
});
it('strips import-type statements', () => {
var code = transform([
'import type DefaultExport from "MyModule";',
'var sanityCheck = 42;',
]);
eval(code);
expect(sanityCheck).toBe(42);
});
it('strips export-type statements', () => {
var code = transform([
'export type foo = number;',
'var sanityCheck = 42;',
]);
eval(code);
expect(sanityCheck).toBe(42);
});
it('catches up correctly', () => {
var code = transform([
"var X = require('X');",
'type FBID = number;',
]);
expect(code).toBe([
"var X = require('X');",
' '
].join('\n'));
});
});
});

View File

@@ -0,0 +1,291 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/*jshint evil:true*/
/*jshint -W117*/
require('mock-modules').autoMockOff();
describe('static type class syntax', function() {
var classSyntaxVisitors;
var visitorList;
var flowSyntaxVisitors;
var jstransform;
beforeEach(function() {
require('mock-modules').dumpCache();
classSyntaxVisitors =
require('../es6-class-visitors').visitorList;
flowSyntaxVisitors = require('../type-syntax.js').visitorList;
jstransform = require('jstransform');
visitorList = classSyntaxVisitors;
});
function transform(code, visitors) {
visitors = visitors ? visitorList.concat(visitors) : visitorList;
code = jstransform.transform(
flowSyntaxVisitors,
code.join('\n')
).code;
return jstransform.transform(
visitors,
code
).code;
}
describe('param type annotations', () => {
it('strips single param annotation', () => {
var code = transform([
'class Foo {',
' method1(param1: bool) {',
' return param1;',
' }',
'}',
'',
'var Bar = class {',
' method1(param1: bool) {',
' return param1;',
' }',
'}'
]);
eval(code);
expect((new Foo()).method1(42)).toBe(42);
expect((new Bar()).method1(42)).toBe(42);
});
it('strips multiple param annotations', () => {
var code = transform([
'class Foo {',
' method1(param1: bool, param2: number) {',
' return [param1, param2];',
' }',
'}',
'',
'var Bar = class {',
' method1(param1: bool, param2: number) {',
' return [param1, param2];',
' }',
'}'
]);
eval(code);
expect((new Foo()).method1(true, 42)).toEqual([true, 42]);
expect((new Bar()).method1(true, 42)).toEqual([true, 42]);
});
it('strips higher-order param annotations', () => {
var code = transform([
'class Foo {',
' method1(param1: (_:bool) => number) {',
' return param1;',
' }',
'}',
'',
'var Bar = class {',
' method1(param1: (_:bool) => number) {',
' return param1;',
' }',
'}'
]);
eval(code);
var callback = function(param) {
return param ? 42 : 0;
};
expect((new Foo()).method1(callback)).toBe(callback);
expect((new Bar()).method1(callback)).toBe(callback);
});
it('strips annotated params next to non-annotated params', () => {
var code = transform([
'class Foo {',
' method1(param1, param2: number) {',
' return [param1, param2];',
' }',
'}',
'',
'var Bar = class {',
' method1(param1, param2: number) {',
' return [param1, param2];',
' }',
'}'
]);
eval(code);
expect((new Foo()).method1('p1', 42)).toEqual(['p1', 42]);
expect((new Bar()).method1('p1', 42)).toEqual(['p1', 42]);
});
it('strips annotated params before a rest parameter', () => {
var restParamVisitors =
require('../es6-rest-param-visitors').visitorList;
var code = transform([
'class Foo {',
' method1(param1: number, ...args) {',
' return [param1, args];',
' }',
'}',
'',
'var Bar = class {',
' method1(param1: number, ...args) {',
' return [param1, args];',
' }',
'}'
], restParamVisitors);
eval(code);
expect((new Foo()).method1(42, 43, 44)).toEqual([42, [43, 44]]);
expect((new Bar()).method1(42, 43, 44)).toEqual([42, [43, 44]]);
});
});
describe('return type annotations', () => {
it('strips method return types', () => {
var code = transform([
'class Foo {',
' method1(param1:number): () => number {',
' return function() { return param1; };',
' }',
'}',
'',
'var Bar = class {',
' method1(param1:number): () => number {',
' return function() { return param1; };',
' }',
'}'
]);
eval(code);
expect((new Foo()).method1(42)()).toBe(42);
expect((new Bar()).method1(42)()).toBe(42);
});
});
describe('parametric type annotation', () => {
it('strips parametric class type annotations', () => {
var code = transform([
'class Foo<T> {',
' method1(param1) {',
' return param1;',
' }',
'}',
'',
// TODO: Need to add support to esprima for this
// 'var Bar = class<T> {',
// ' method1(param1) {',
// ' return param1;',
// ' }',
// '}'
]);
eval(code);
expect((new Foo()).method1(42)).toBe(42);
// expect((new Bar()).method1(42)).toBe(42);
});
it('strips multi-parameter class type annotations', () => {
var code = transform([
'class Foo<T,S> {',
' method1(param1) {',
' return param1;',
' }',
'}',
'',
// TODO: Need to add support to esprima for this
// 'var Bar = class<T> {',
// ' method1(param1) {',
// ' return param1;',
// ' }',
// '}'
]);
eval(code);
expect((new Foo()).method1(42)).toBe(42);
// expect(bar(42)).toBe(42);
});
it('strips parametric method type annotations', () => {
var code = transform([
'class Foo<T> {',
' method1<T>(param1) {',
' return param1;',
' }',
'}',
'',
// TODO: Need to add support to esprima for this
// 'var Bar = class<T> {',
// ' method1<T>(param1) {',
// ' return param1;',
// ' }',
// '}'
]);
eval(code);
expect((new Foo()).method1(42)).toBe(42);
// expect((new Bar()).method1(42)).toBe(42);
});
it('strips multi-parameter class type annotations', () => {
var code = transform([
'class Foo<T,S> {',
' method1<T,S>(param1) {',
' return param1;',
' }',
'}',
'',
// TODO: Need to add support to esprima for this
// 'var Bar = class<T> {',
// ' method1(param1) {',
// ' return param1;',
// ' }',
// '}'
]);
eval(code);
expect((new Foo()).method1(42)).toBe(42);
// expect(bar(42)).toBe(42);
});
});
describe('class property annotations', () => {
it('strips single class property', () => {
var code = transform([
'class Foo {',
' prop1: T;',
'}'
]);
eval(code);
expect((new Foo()).prop1).toEqual(undefined);
});
it('strips multiple adjacent class properties', () => {
var code = transform([
'class Foo {',
' prop1: T;',
' prop2: U;',
'}'
]);
eval(code);
expect((new Foo()).prop1).toEqual(undefined);
expect((new Foo()).prop2).toEqual(undefined);
});
it('strips class properties between methods', () => {
var code = transform([
'class Foo {',
' method1() {}',
' prop1: T;',
' method2() {}',
' prop2: U;',
' method3() {}',
'}'
]);
eval(code);
expect((new Foo()).prop1).toEqual(undefined);
expect((new Foo()).prop2).toEqual(undefined);
});
});
});

View File

@@ -0,0 +1,291 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/*jshint evil:true*/
/*jshint -W117*/
require('mock-modules').autoMockOff();
describe('static type function syntax', function() {
var flowSyntaxVisitors;
var jstransform;
beforeEach(function() {
require('mock-modules').dumpCache();
flowSyntaxVisitors = require('../type-syntax.js').visitorList;
jstransform = require('jstransform');
});
function transform(code, visitors) {
code = code.join('\n');
// We run the flow transform first
code = jstransform.transform(
flowSyntaxVisitors,
code
).code;
if (visitors) {
code = jstransform.transform(
visitors,
code
).code;
}
return code;
}
describe('param type annotations', () => {
it('strips single param annotation', () => {
var code = transform([
'function foo(param1: bool) {',
' return param1;',
'}',
'',
'var bar = function(param1: bool) {',
' return param1;',
'}'
]);
eval(code);
expect(foo(42)).toBe(42);
expect(bar(42)).toBe(42);
});
it('strips multiple param annotations', () => {
var code = transform([
'function foo(param1: bool, param2: number) {',
' return [param1, param2];',
'}',
'',
'var bar = function(param1: bool, param2: number) {',
' return [param1, param2];',
'}'
]);
eval(code);
expect(foo(true, 42)).toEqual([true, 42]);
expect(bar(true, 42)).toEqual([true, 42]);
});
it('strips higher-order param annotations', () => {
var code = transform([
'function foo(param1: (_:bool) => number) {',
' return param1;',
'}',
'',
'var bar = function(param1: (_:bool) => number) {',
' return param1;',
'}'
]);
eval(code);
var callback = function(param) {
return param ? 42 : 0;
};
expect(foo(callback)).toBe(callback);
expect(bar(callback)).toBe(callback);
});
it('strips annotated params next to non-annotated params', () => {
var code = transform([
'function foo(param1, param2: number) {',
' return [param1, param2];',
'}',
'',
'var bar = function(param1, param2: number) {',
' return [param1, param2];',
'}'
]);
eval(code);
expect(foo('p1', 42)).toEqual(['p1', 42]);
expect(bar('p1', 42)).toEqual(['p1', 42]);
});
it('strips annotated params before a rest parameter', () => {
var restParamVisitors =
require('../es6-rest-param-visitors').visitorList;
var code = transform([
'function foo(param1: number, ...args) {',
' return [param1, args];',
'}',
'',
'var bar = function(param1: number, ...args) {',
' return [param1, args];',
'}'
], restParamVisitors);
eval(code);
expect(foo(42, 43, 44)).toEqual([42, [43, 44]]);
expect(bar(42, 43, 44)).toEqual([42, [43, 44]]);
});
it('strips annotated rest parameter', () => {
var restParamVisitors =
require('../es6-rest-param-visitors').visitorList;
var code = transform([
'function foo(param1: number, ...args: Array<number>) {',
' return [param1, args];',
'}',
'',
'var bar = function(param1: number, ...args: Array<number>) {',
' return [param1, args];',
'}'
], restParamVisitors);
eval(code);
expect(foo(42, 43, 44)).toEqual([42, [43, 44]]);
expect(bar(42, 43, 44)).toEqual([42, [43, 44]]);
});
it('strips optional param marker without type annotation', () => {
var code = transform([
'function foo(param1?, param2 ?) {',
' return 42;',
'}'
]);
eval(code);
expect(foo()).toBe(42);
});
it('strips optional param marker with type annotation', () => {
var code = transform([
'function foo(param1?:number, param2 ?: string, param3 ? : bool) {',
' return 42;',
'}'
]);
eval(code);
expect(foo()).toBe(42);
});
});
describe('return type annotations', () => {
it('strips function return types', () => {
var code = transform([
'function foo(param1:number): () => number {',
' return function() { return param1; };',
'}',
'',
'var bar = function(param1:number): () => number {',
' return function() { return param1; };',
'}'
]);
eval(code);
expect(foo(42)()).toBe(42);
expect(bar(42)()).toBe(42);
});
it('strips void return types', () => {
var code = transform([
'function foo(param1): void {',
' param1();',
'}',
'',
'var bar = function(param1): void {',
' param1();',
'}'
]);
eval(code);
var counter = 0;
function testFn() {
counter++;
}
foo(testFn);
expect(counter).toBe(1);
bar(testFn);
expect(counter).toBe(2);
});
it('strips void return types with rest params', () => {
var code = transform(
[
'function foo(param1, ...rest): void {',
' param1();',
'}',
'',
'var bar = function(param1, ...rest): void {',
' param1();',
'}'
],
require('../es6-rest-param-visitors').visitorList
);
eval(code);
var counter = 0;
function testFn() {
counter++;
}
foo(testFn);
expect(counter).toBe(1);
bar(testFn);
expect(counter).toBe(2);
});
it('strips object return types', () => {
var code = transform([
'function foo(param1:number): {num: number} {',
' return {num: param1};',
'}',
'',
'var bar = function(param1:number): {num: number} {',
' return {num: param1};',
'}'
]);
eval(code);
expect(foo(42)).toEqual({num: 42});
expect(bar(42)).toEqual({num: 42});
});
});
describe('parametric type annotation', () => {
it('strips parametric type annotations', () => {
var code = transform([
'function foo<T>(param1) {',
' return param1;',
'}',
'',
'var bar = function<T>(param1) {',
' return param1;',
'}',
]);
eval(code);
expect(foo(42)).toBe(42);
expect(bar(42)).toBe(42);
});
it('strips multi-parameter type annotations', () => {
var restParamVisitors =
require('../es6-rest-param-visitors').visitorList;
var code = transform([
'function foo<T, S>(param1) {',
' return param1;',
'}',
'',
'var bar = function<T,S>(param1) {',
' return param1;',
'}'
], restParamVisitors);
eval(code);
expect(foo(42)).toBe(42);
expect(bar(42)).toBe(42);
});
});
describe('arrow functions', () => {
// TODO: We don't currently support arrow functions, but we should
// soon! The only reason we don't now is because we don't
// need it at this very moment and I'm in a rush to get the
// basics in.
});
});

View File

@@ -0,0 +1,65 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/*jshint evil:true*/
require('mock-modules').autoMockOff();
describe('static type interface syntax', function() {
var flowSyntaxVisitors;
var jstransform;
beforeEach(function() {
require('mock-modules').dumpCache();
flowSyntaxVisitors = require('../type-syntax.js').visitorList;
jstransform = require('jstransform');
});
function transform(code, visitors) {
code = jstransform.transform(
flowSyntaxVisitors,
code.join('\n')
).code;
if (visitors) {
code = jstransform.transform(
visitors,
code
).code;
}
return code;
}
describe('Interface Declaration', () => {
it('strips interface declarations', () => {
/*global interface*/
var code = transform([
'var interface = 42;',
'interface A { foo: () => number; }',
'if (true) interface += 42;',
'interface A<T> extends B, C<T> { foo: () => number; }',
'interface += 42;'
]);
eval(code);
expect(interface).toBe(126);
});
it('catches up correctly', () => {
var code = transform([
"var X = require('X');",
'interface A { foo: () => number; }',
]);
expect(code).toBe([
"var X = require('X');",
' '
].join('\n'));
});
});
});

View File

@@ -0,0 +1,179 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/*jshint evil:true*/
/*jshint -W117*/
require('mock-modules').autoMockOff();
describe('static type object-method syntax', function() {
var flowSyntaxVisitors;
var jstransform;
var visitorList;
beforeEach(function() {
require('mock-modules').dumpCache();
flowSyntaxVisitors = require('../type-syntax.js').visitorList;
jstransform = require('jstransform');
objMethodVisitors =
require('../es6-object-concise-method-visitors');
visitorList = objMethodVisitors.visitorList;
});
function transform(code, visitors) {
visitors = visitors ? visitorList.concat(visitors) : visitorList;
// We run the flow transform first
code = jstransform.transform(
flowSyntaxVisitors,
code.join('\n')
).code;
code = jstransform.transform(
visitors,
code
).code;
return code;
}
describe('param type annotations', () => {
it('strips single param annotation', () => {
var code = transform([
'var foo = {',
' bar(param1: bool) {',
' return param1;',
' }',
'};',
]);
eval(code);
expect(foo.bar(42)).toBe(42);
});
it('strips multiple param annotations', () => {
var code = transform([
'var foo = {',
' bar(param1: bool, param2: number) {',
' return [param1, param2];',
' }',
'};'
]);
eval(code);
expect(foo.bar(true, 42)).toEqual([true, 42]);
});
it('strips higher-order param annotations', () => {
var code = transform([
'var foo = {',
' bar(param1: (_:bool) => number) {',
' return param1;',
' }',
'};'
]);
eval(code);
var callback = function(param) {
return param ? 42 : 0;
};
expect(foo.bar(callback)).toBe(callback);
});
it('strips annotated params next to non-annotated params', () => {
var code = transform([
'var foo = {',
' bar(param1, param2: number) {',
' return [param1, param2];',
' }',
'}',
]);
eval(code);
expect(foo.bar('p1', 42)).toEqual(['p1', 42]);
});
it('strips annotated params before a rest parameter', () => {
var restParamVisitors =
require('../es6-rest-param-visitors').visitorList;
var code = transform([
'var foo = {',
' bar(param1: number, ...args) {',
' return [param1, args];',
' }',
'}',
], restParamVisitors);
eval(code);
expect(foo.bar(42, 43, 44)).toEqual([42, [43, 44]]);
});
it('strips annotated rest parameter', () => {
var restParamVisitors =
require('../es6-rest-param-visitors').visitorList;
var code = transform([
'var foo = {',
' bar(param1: number, ...args: Array<number>): Array<any> {',
' return [param1, args];',
' }',
'}',
], restParamVisitors);
eval(code);
expect(foo.bar(42, 43, 44)).toEqual([42, [43, 44]]);
});
});
describe('return type annotations', () => {
it('strips function return types', () => {
var code = transform([
'var foo = {',
' bar(param1:number): () => number {',
' return function() { return param1; };',
' }',
'}',
]);
eval(code);
expect(foo.bar(42)()).toBe(42);
});
});
describe('parametric type annotation', () => {
// TODO: Fix esprima parsing for these cases
/*
it('strips parametric type annotations', () => {
// TODO: Doesnt parse
var code = transform([
'var foo = {',
' bar<T>(param1) {',
' return param1;',
' }',
'}',
]);
eval(code);
expect(foo.bar(42)).toBe(42);
});
it('strips multi-parameter type annotations', () => {
// TODO: Doesnt parse
var restParamVisitors =
require('../es6-rest-param-visitors').visitorList;
var code = transform([
'var foo = {',
' bar<T, S>(param1) {',
' return param1;',
' }',
'}',
], restParamVisitors);
eval(code);
expect(foo.bar(42)).toBe(42);
});
*/
});
});

View File

@@ -0,0 +1,84 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/*jshint evil:true*/
/*jshint -W117*/
require('mock-modules').autoMockOff();
describe('static type pattern syntax', function() {
var flowSyntaxVisitors;
var jstransform;
beforeEach(function() {
require('mock-modules').dumpCache();
flowSyntaxVisitors = require('../type-syntax.js').visitorList;
jstransform = require('jstransform');
destructuringVisitors =
require('../es6-destructuring-visitors');
visitorList = destructuringVisitors.visitorList;
});
function transform(code, visitors) {
visitors = visitors ? visitorList.concat(visitors) : visitorList;
// We run the flow transform first
code = jstransform.transform(
flowSyntaxVisitors,
code.join('\n')
).code;
code = jstransform.transform(
visitors,
code
).code;
return code;
}
describe('Object Pattern', () => {
it('strips function argument type annotation', () => {
var code = transform([
'function foo({x, y}: {x: number; y: number}) { return x+y; }',
'var thirty = foo({x: 10, y: 20});'
]);
eval(code);
expect(thirty).toBe(30);
});
it('strips var declaration type annotation', () => {
var code = transform([
'var {x, y}: {x: number; y: string} = { x: 42, y: "hello" };'
]);
eval(code);
expect(x).toBe(42);
expect(y).toBe("hello");
});
});
describe('Array Pattern', () => {
it('strips function argument type annotation', () => {
var code = transform([
'function foo([x, y]: Array<number>) { return x+y; }',
'var thirty = foo([10, 20]);'
]);
eval(code);
expect(thirty).toBe(30);
});
it('strips var declaration type annotation', () => {
var code = transform([
'var [x, y]: Array<number> = [42, "hello"];'
]);
eval(code);
expect(x).toBe(42);
expect(y).toBe("hello");
});
});
});

View File

@@ -0,0 +1,207 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/*jshint evil: true*/
/*jshint loopfunc: true*/
if (!!module.parent) {
module.exports = {
'TypeAnnotations': [
'function foo(numVal: any){}',
'function foo(numVal: number){}',
'function foo(numVal: number, strVal: string){}',
'function foo(numVal: number, untypedVal){}',
'function foo(untypedVal, numVal: number){}',
'function foo(nullableNum: ?number){}',
'function foo(callback: () => void){}',
'function foo(callback: () => number){}',
'function foo(callback: (_:bool) => number){}',
'function foo(callback: (_1:bool, _2:string) => number){}',
'function foo(callback: (_1:bool, ...foo:Array<number>) => number){}',
'function foo():number{}',
'function foo():() => void{}',
'function foo():(_:bool) => number{}',
'function foo():(_?:bool) => number{}',
'function foo(): {} {}',
'function foo<T>() {}',
'function foo<T: Foo>() {}',
'function foo<T,S>() {}',
'a=function<T,S>() {}',
'a={set fooProp(value:number){}}',
'a={set fooProp(value:number): void{}}',
'a={get fooProp(): number {}}',
'class Foo {set fooProp(value:number){}}',
'class Foo {set fooProp(value:number): void{}}',
'class Foo {get fooProp(): number{}}',
'var numVal:number;',
'var numVal:number = otherNumVal;',
'var a: {numVal: number};',
'var a: {numVal: number;};',
'var a: {numVal: number; [indexer: string]: number};',
'var a: ?{numVal: number};',
'var a: {numVal: number; strVal: string}',
'var a: {subObj: {strVal: string}}',
'var a: {subObj: ?{strVal: string}}',
'var a: {param1: number; param2: string}',
'var a: {param1: number; param2?: string}',
'var a: {add(x:number, ...y:Array<string>): void}',
'var a: { id<T>(x: T): T; }',
'var a:Array<number> = [1, 2, 3]',
'a = class Foo<T> { }',
'a = class Foo<T> extends Bar<T> { }',
'class Foo<T> {}',
'class Foo<T: Bar> {}',
'class Foo<T> extends Bar<T> { }',
'class Foo<T> extends mixin(Bar) { }',
'class Foo<T> { bar<U>():number { return 42; }}',
'class Foo { "bar"<T>() { } }',
'function foo(requiredParam, optParam?) {}',
'class Foo { prop1:string; prop2:number; }',
'var x : number | string = 4;',
'class Array { concat(items:number | string) {}; }',
'var x : () => number | () => string = fn;',
'var x: typeof Y = Y;',
'var x: typeof Y | number = Y;',
'var {x}: {x: string; } = { x: "hello" };',
'var {x}: {x: string } = { x: "hello" };',
'var [x]: Array<string> = [ "hello" ];',
'function foo({x}: { x: string; }) {}',
'function foo([x]: Array<string>) {}',
'function foo(...rest: Array<number>) {}',
'(function (...rest: Array<number>) {})',
'((...rest: Array<number>) => rest)',
'var a: Map<string, Array<string> >',
'var a: Map<string, Array<string>>',
'var a: number[]',
'var a: ?string[]',
'var a: Promise<bool>[]',
'var a:(...rest:Array<number>) => number'
],
'Type Alias': [
'type FBID = number;',
'type Foo<T> = Bar<T>',
],
'Interfaces': [
'interface A {}',
'interface A extends B {}',
'interface A<T> extends B<T>, C<T> {}',
'interface A { foo: () => number; }',
'interface Dictionary { [index: string]: string; length: number; }',
'class Foo implements Bar {}',
'class Foo extends Bar implements Bat, Man<number> {}',
'class Foo extends class Bar implements Bat {} {}',
'class Foo extends class Bar implements Bat {} implements Man {}',
],
'Type Grouping': [
'var a: (number)',
'var a: (() => number) | () => string',
'var a: number & (string | bool)',
'var a: (typeof A)',
],
'XJS': [
'<a />',
'<n:a n:v />',
'<a n:foo="bar"> {value} <b><c /></b></a>',
'<a b={" "} c=" " d="&amp;" e="id=1&group=2" f="&#123456789" g="&#123*;" h="&#x;" />',
'<a\n/>',
'<日本語></日本語>',
'<AbC-def\n test="&#x0026;&#38;">\nbar\nbaz\r\n</AbC-def>',
'<a b={x ? <c /> : <d />} />',
'<a>{}</a>',
'<a>{/* this is a comment */}</a>',
'<div>@test content</div>',
'<div><br />7x invalid-js-identifier</div>',
'<LeftRight left=<a /> right=<b>monkeys</b> />',
'<a.b></a.b>',
'<a.b.c></a.b.c>',
'(<div />) < x;',
'<div {...props} />',
'<div {...props} post="attribute" />',
'<div pre="leading" pre2="attribute" {...props}></div>',
'<a> </a>',
],
'Call Properties': [
'var a : { (): number }',
'var a : { (): number; }',
'var a : { (): number; y: string; (x: string): string }',
'var a : { <T>(x: T): number; }',
'interface A { (): number; }',
],
'String Literal Types': [
'function createElement(tagName: "div"): HTMLDivElement {}',
'function createElement(tagName: \'div\'): HTMLDivElement {}',
],
'Qualified Generic Type': [
'var a : A.B',
'var a : A.B.C',
'var a : A.B<T>',
'var a : typeof A.B<T>',
],
'Declare Statements': [
'declare var foo',
'declare var foo;',
'declare function foo(): void',
'declare function foo(): void;',
'declare function foo<T>(): void;',
'declare function foo(x: number, y: string): void;',
'declare class A {}',
'declare class A<T> extends B<T> { x: number }',
'declare class A { static foo(): number; static x : string }',
'declare class A { static [ indexer: number]: string }',
'declare class A { static () : number }',
],
'Declare Module': [
'declare module A {}',
'declare module "./a/b.js" {}',
'declare module A { declare var x: number; }',
'declare module A { declare function foo(): number; }',
'declare module A { declare class B { foo(): number; } }',
],
'Typecasts': [
'(xxx: number)',
'({xxx: 0, yyy: "hey"}: {xxx: number; yyy: string})',
// distinguish between function type params and typecasts
'((xxx) => xxx + 1: (xxx: number) => number)',
// parens disambiguate groups from casts
'((xxx: number), (yyy: string))',
'((xxx: any): number)',
],
};
} else {
require('mock-modules').autoMockOff();
var tests = require('./gen/type-syntax-test.rec.js');
var transform = require('../../src/jstransform').transform;
var visitors = require('../type-syntax').visitorList;
describe('transforms match expectations', function() {
Object.keys(tests).forEach(function(sectionName) {
var section = tests[sectionName];
Object.keys(section).forEach(function(testCode) {
it('transforms "' + testCode + '"', function() {
expect(transform(visitors, testCode).code).toBe(section[testCode].transformed);
});
it('evals "' + testCode + '"', function() {
var transformed = transform(visitors, testCode).code;
var expected = section[testCode].eval;
var evalFn = function() {
eval(transformed);
};
if (expected === 'No error') {
expect(evalFn).not.toThrow();
} else {
expect(evalFn).toThrow(expected);
}
});
});
});
});
}

View File

@@ -0,0 +1,201 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/*jshint evil:true*/
/*jshint -W117*/
require('mock-modules').autoMockOff();
describe('static type variable declaration syntax', function() {
var flowSyntaxVisitors;
var jstransform;
beforeEach(function() {
require('mock-modules').dumpCache();
flowSyntaxVisitors = require('../type-syntax.js').visitorList;
jstransform = require('jstransform');
});
function transform(code, visitors) {
code = code.join('\n');
// We run the flow transform first
code = jstransform.transform(
flowSyntaxVisitors,
code
).code;
if (visitors) {
code = jstransform.transform(
visitors,
code
).code;
}
return code;
}
describe('basic annotations', () => {
it('strips single annotated declarator without initializer', () => {
var code = transform([
'var myNum = 42;',
'function foo() {',
' var myNum:number;',
' return myNum;',
'}'
]);
eval(code);
expect(foo()).toBe(undefined);
});
it('strips single annotated declarator with initializer', () => {
var code = transform([
'var myNum:number = 42;',
]);
eval(code);
expect(myNum).toBe(42);
});
it('strips single annotated nullable declarator with initializer', () => {
var code = transform([
'var myNum:?number = 42;',
]);
eval(code);
expect(myNum).toBe(42);
});
it(
'strips single annotated nullable declarator without initializer',
() => {
var code = transform([
'var myNum:?number;',
'myNum = null;',
]);
eval(code);
expect(myNum).toBe(null);
}
);
it('strips multiple annotation declarations without initializers', () => {
var code = transform([
'var num1 = 42;',
'var num2 = 43;',
'function foo() {',
' var num1:number, num2:number;',
' return [num1, num2];',
'}'
]);
eval(code);
expect(foo()).toEqual([undefined, undefined]);
});
it('strips multiple annotation declarations without initializers', () => {
var code = transform([
'var num1:number = 42, num2:number = 43;'
]);
eval(code);
expect(num1).toBe(42);
expect(num2).toBe(43);
});
});
describe('function type annotations', () => {
it('strips function type annotations without initializer', () => {
var code = transform([
'var myFunc = function() { return "NOPE"; };',
'function foo() {',
' var myFunc:(_:bool) => number;',
' return myFunc;',
'}'
]);
eval(code);
expect(foo()).toBe(undefined);
});
it('strips function type annotations with initializer', () => {
var code = transform([
'var myFunc:(_:bool) => number = function(p1) {',
' return 42;',
'};'
]);
eval(code);
expect(myFunc()).toBe(42);
});
});
describe('object type annotations', () => {
it('strips empty object type annotations without initializer', () => {
var code = transform([
'var myObj = "NOPE";',
'function foo() {',
' var myObj:{};',
' return myObj;',
'}'
]);
eval(code);
expect(foo()).toBe(undefined);
});
it('strips empty object type annotations with initializer', () => {
var code = transform([
'var myObj:{} = {YEP: true};'
]);
eval(code);
expect(myObj.YEP).toBe(true);
});
it('strips empty nullable object type annotations with initializer', () => {
var code = transform([
'var myObj:?{} = {YEP: true};'
]);
eval(code);
expect(myObj.YEP).toBe(true);
});
it('strips object type with basic property annotation', () => {
var code = transform([
'var myObj:{arrProp:Array} = {YEP: [true]};'
]);
eval(code);
expect(myObj.YEP).toEqual([true]);
});
it('strips object type with multiple property annotations', () => {
var code = transform([
'var myObj:{numProp: number; strProp: string} = {',
' numProp: 42,',
' strProp: "YEP"',
'};'
]);
eval(code);
expect(myObj.numProp).toBe(42);
expect(myObj.strProp).toBe("YEP");
});
it('strips object type with parametric property annotation', () => {
var code = transform([
'var myObj:{arrProp:Array<bool>} = {YEP: [true]};'
]);
eval(code);
expect(myObj.YEP).toEqual([true]);
});
it('strips object type with function property annotation', () => {
var code = transform([
'var myObj:{myMethod:() => void} = {',
' myMethod: function() {',
' return 42;',
' }',
'};'
]);
eval(code);
expect(myObj.myMethod()).toBe(42);
});
});
});

View File

@@ -0,0 +1,118 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails oncall+jsinfra@xmail.facebook.com
*/
/*jshint evil:true*/
require('mock-modules').autoMockOff();
describe('undefined to void 0', function() {
var transformFn;
var visitors;
beforeEach(function() {
require('mock-modules').dumpCache();
visitors = require('../undefined-to-void-0-visitors').visitorList;
transformFn = require('../../src/jstransform').transform;
});
function transform(code) {
return transformFn(visitors, code).code;
}
it('should transform when undefined is not in scope', function() {
var code = [
'(function() {',
' foo.undefined = bar;',
' ({undefined: foo});',
' var bar = undefined;',
' bar = undefined;',
' foo.bar = undefined;',
' undefined.foo = bar;',
' foo[undefined] = bar;',
' ({foo: undefined});',
'})(undefined);'
].join('\n');
var result = [
'(function() {',
' foo.undefined = bar;',
' ({undefined: foo});',
' var bar = (void 0);',
' bar = (void 0);',
' foo.bar = (void 0);',
' (void 0).foo = bar;',
' foo[(void 0)] = bar;',
' ({foo: (void 0)});',
'})((void 0));'
].join('\n');
expect(transform(code)).toEqual(result);
});
it('should not transform when undefined is function argument', function() {
var code = [
'(function(undefined) {', // declared here
' return undefined;',
'})(1);'
].join('\n');
var result = [
'(function(undefined) {', // declared here
' return undefined;',
'})(1);'
].join('\n');
expect(transform(code)).toEqual(result);
});
it('should not transform when undefined is defined in local scope', function() {
var code = [
'(function() {',
' var undefined;', // declared here
' undefined = 1;', // assignment to declared variable
' return undefined;',
'})();'
].join('\n');
var result = [
'(function() {',
' var undefined;', // declared here
' undefined = 1;', // assignment to declared variable
' return undefined;',
'})();'
].join('\n');
expect(transform(code)).toEqual(result);
});
it('should not transform when undefined is defined in lexical scope', function() {
var code = [
'(function(undefined) {', // declared here
' return (function() {',
' return undefined;', // respects lexical scope
' })();',
'})();'
].join('\n');
var result = [
'(function(undefined) {', // declared here
' return (function() {',
' return undefined;', // respects lexical scope
' })();',
'})();'
].join('\n');
expect(transform(code)).toEqual(result);
});
});

View File

@@ -0,0 +1,153 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/*global exports:true*/
/**
* Desugars ES6 Arrow functions to ES3 function expressions.
* If the function contains `this` expression -- automatically
* binds the function to current value of `this`.
*
* Single parameter, simple expression:
*
* [1, 2, 3].map(x => x * x);
*
* [1, 2, 3].map(function(x) { return x * x; });
*
* Several parameters, complex block:
*
* this.users.forEach((user, idx) => {
* return this.isActive(idx) && this.send(user);
* });
*
* this.users.forEach(function(user, idx) {
* return this.isActive(idx) && this.send(user);
* }.bind(this));
*
*/
var restParamVisitors = require('./es6-rest-param-visitors');
var destructuringVisitors = require('./es6-destructuring-visitors');
var Syntax = require('esprima-fb').Syntax;
var utils = require('../src/utils');
/**
* @public
*/
function visitArrowFunction(traverse, node, path, state) {
var notInExpression = (path[0].type === Syntax.ExpressionStatement);
// Wrap a function into a grouping operator, if it's not
// in the expression position.
if (notInExpression) {
utils.append('(', state);
}
utils.append('function', state);
renderParams(traverse, node, path, state);
// Skip arrow.
utils.catchupWhiteSpace(node.body.range[0], state);
var renderBody = node.body.type == Syntax.BlockStatement
? renderStatementBody
: renderExpressionBody;
path.unshift(node);
renderBody(traverse, node, path, state);
path.shift();
// Bind the function only if `this` value is used
// inside it or inside any sub-expression.
var containsBindingSyntax =
utils.containsChildMatching(node.body, function(node) {
return node.type === Syntax.ThisExpression
|| (node.type === Syntax.Identifier
&& node.name === "super")
|| (node.type === Syntax.JSXIdentifier
&& node.name === 'this');
});
if (containsBindingSyntax) {
utils.append('.bind(this)', state);
}
utils.catchupWhiteSpace(node.range[1], state);
// Close wrapper if not in the expression.
if (notInExpression) {
utils.append(')', state);
}
return false;
}
function renderParams(traverse, node, path, state) {
// To preserve inline typechecking directives, we
// distinguish between parens-free and paranthesized single param.
if (isParensFreeSingleParam(node, state) || !node.params.length) {
utils.append('(', state);
}
if (node.params.length !== 0) {
path.unshift(node);
traverse(node.params, path, state);
path.unshift();
}
utils.append(')', state);
}
function isParensFreeSingleParam(node, state) {
return node.params.length === 1 &&
state.g.source[state.g.position] !== '(';
}
function renderExpressionBody(traverse, node, path, state) {
// Wrap simple expression bodies into a block
// with explicit return statement.
utils.append('{', state);
// Special handling of rest param.
if (node.rest) {
utils.append(
restParamVisitors.renderRestParamSetup(node, state),
state
);
}
// Special handling of destructured params.
destructuringVisitors.renderDestructuredComponents(
node,
utils.updateState(state, {
localScope: {
parentNode: state.parentNode,
parentScope: state.parentScope,
identifiers: state.identifiers,
tempVarIndex: 0
}
})
);
utils.append('return ', state);
renderStatementBody(traverse, node, path, state);
utils.append(';}', state);
}
function renderStatementBody(traverse, node, path, state) {
traverse(node.body, path, state);
utils.catchup(node.body.range[1], state);
}
visitArrowFunction.test = function(node, path, state) {
return node.type === Syntax.ArrowFunctionExpression;
};
exports.visitorList = [
visitArrowFunction
];

View File

@@ -0,0 +1,112 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/*global exports:true*/
/**
* Implements ES6 call spread.
*
* instance.method(a, b, c, ...d)
*
* instance.method.apply(instance, [a, b, c].concat(d))
*
*/
var Syntax = require('esprima-fb').Syntax;
var utils = require('../src/utils');
function process(traverse, node, path, state) {
utils.move(node.range[0], state);
traverse(node, path, state);
utils.catchup(node.range[1], state);
}
function visitCallSpread(traverse, node, path, state) {
utils.catchup(node.range[0], state);
if (node.type === Syntax.NewExpression) {
// Input = new Set(1, 2, ...list)
// Output = new (Function.prototype.bind.apply(Set, [null, 1, 2].concat(list)))
utils.append('new (Function.prototype.bind.apply(', state);
process(traverse, node.callee, path, state);
} else if (node.callee.type === Syntax.MemberExpression) {
// Input = get().fn(1, 2, ...more)
// Output = (_ = get()).fn.apply(_, [1, 2].apply(more))
var tempVar = utils.injectTempVar(state);
utils.append('(' + tempVar + ' = ', state);
process(traverse, node.callee.object, path, state);
utils.append(')', state);
if (node.callee.property.type === Syntax.Identifier) {
utils.append('.', state);
process(traverse, node.callee.property, path, state);
} else {
utils.append('[', state);
process(traverse, node.callee.property, path, state);
utils.append(']', state);
}
utils.append('.apply(' + tempVar, state);
} else {
// Input = max(1, 2, ...list)
// Output = max.apply(undefined, [1, 2].concat(list))
var needsToBeWrappedInParenthesis =
node.callee.type === Syntax.FunctionDeclaration ||
node.callee.type === Syntax.FunctionExpression;
if (needsToBeWrappedInParenthesis) {
utils.append('(', state);
}
process(traverse, node.callee, path, state);
if (needsToBeWrappedInParenthesis) {
utils.append(')', state);
}
utils.append('.apply(undefined', state);
}
utils.append(', ', state);
var args = node.arguments.slice();
var spread = args.pop();
if (args.length || node.type === Syntax.NewExpression) {
utils.append('[', state);
if (node.type === Syntax.NewExpression) {
utils.append('null' + (args.length ? ', ' : ''), state);
}
while (args.length) {
var arg = args.shift();
utils.move(arg.range[0], state);
traverse(arg, path, state);
if (args.length) {
utils.catchup(args[0].range[0], state);
} else {
utils.catchup(arg.range[1], state);
}
}
utils.append('].concat(', state);
process(traverse, spread.argument, path, state);
utils.append(')', state);
} else {
process(traverse, spread.argument, path, state);
}
utils.append(node.type === Syntax.NewExpression ? '))' : ')', state);
utils.move(node.range[1], state);
return false;
}
visitCallSpread.test = function(node, path, state) {
return (
(
node.type === Syntax.CallExpression ||
node.type === Syntax.NewExpression
) &&
node.arguments.length > 0 &&
node.arguments[node.arguments.length - 1].type === Syntax.SpreadElement
);
};
exports.visitorList = [
visitCallSpread
];

581
node_modules/jstransform/visitors/es6-class-visitors.js generated vendored Normal file
View File

@@ -0,0 +1,581 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/*jslint node:true*/
/**
* @typechecks
*/
'use strict';
var base62 = require('base62');
var Syntax = require('esprima-fb').Syntax;
var utils = require('../src/utils');
var reservedWordsHelper = require('./reserved-words-helper');
var declareIdentInLocalScope = utils.declareIdentInLocalScope;
var initScopeMetadata = utils.initScopeMetadata;
var SUPER_PROTO_IDENT_PREFIX = '____SuperProtoOf';
var _anonClassUUIDCounter = 0;
var _mungedSymbolMaps = {};
function resetSymbols() {
_anonClassUUIDCounter = 0;
_mungedSymbolMaps = {};
}
/**
* Used to generate a unique class for use with code-gens for anonymous class
* expressions.
*
* @param {object} state
* @return {string}
*/
function _generateAnonymousClassName(state) {
var mungeNamespace = state.mungeNamespace || '';
return '____Class' + mungeNamespace + base62.encode(_anonClassUUIDCounter++);
}
/**
* Given an identifier name, munge it using the current state's mungeNamespace.
*
* @param {string} identName
* @param {object} state
* @return {string}
*/
function _getMungedName(identName, state) {
var mungeNamespace = state.mungeNamespace;
var shouldMinify = state.g.opts.minify;
if (shouldMinify) {
if (!_mungedSymbolMaps[mungeNamespace]) {
_mungedSymbolMaps[mungeNamespace] = {
symbolMap: {},
identUUIDCounter: 0
};
}
var symbolMap = _mungedSymbolMaps[mungeNamespace].symbolMap;
if (!symbolMap[identName]) {
symbolMap[identName] =
base62.encode(_mungedSymbolMaps[mungeNamespace].identUUIDCounter++);
}
identName = symbolMap[identName];
}
return '$' + mungeNamespace + identName;
}
/**
* Extracts super class information from a class node.
*
* Information includes name of the super class and/or the expression string
* (if extending from an expression)
*
* @param {object} node
* @param {object} state
* @return {object}
*/
function _getSuperClassInfo(node, state) {
var ret = {
name: null,
expression: null
};
if (node.superClass) {
if (node.superClass.type === Syntax.Identifier) {
ret.name = node.superClass.name;
} else {
// Extension from an expression
ret.name = _generateAnonymousClassName(state);
ret.expression = state.g.source.substring(
node.superClass.range[0],
node.superClass.range[1]
);
}
}
return ret;
}
/**
* Used with .filter() to find the constructor method in a list of
* MethodDefinition nodes.
*
* @param {object} classElement
* @return {boolean}
*/
function _isConstructorMethod(classElement) {
return classElement.type === Syntax.MethodDefinition &&
classElement.key.type === Syntax.Identifier &&
classElement.key.name === 'constructor';
}
/**
* @param {object} node
* @param {object} state
* @return {boolean}
*/
function _shouldMungeIdentifier(node, state) {
return (
!!state.methodFuncNode &&
!utils.getDocblock(state).hasOwnProperty('preventMunge') &&
/^_(?!_)/.test(node.name)
);
}
/**
* @param {function} traverse
* @param {object} node
* @param {array} path
* @param {object} state
*/
function visitClassMethod(traverse, node, path, state) {
if (!state.g.opts.es5 && (node.kind === 'get' || node.kind === 'set')) {
throw new Error(
'This transform does not support ' + node.kind + 'ter methods for ES6 ' +
'classes. (line: ' + node.loc.start.line + ', col: ' +
node.loc.start.column + ')'
);
}
state = utils.updateState(state, {
methodNode: node
});
utils.catchup(node.range[0], state);
path.unshift(node);
traverse(node.value, path, state);
path.shift();
return false;
}
visitClassMethod.test = function(node, path, state) {
return node.type === Syntax.MethodDefinition;
};
/**
* @param {function} traverse
* @param {object} node
* @param {array} path
* @param {object} state
*/
function visitClassFunctionExpression(traverse, node, path, state) {
var methodNode = path[0];
var isGetter = methodNode.kind === 'get';
var isSetter = methodNode.kind === 'set';
state = utils.updateState(state, {
methodFuncNode: node
});
if (methodNode.key.name === 'constructor') {
utils.append('function ' + state.className, state);
} else {
var methodAccessorComputed = methodNode.computed;
var methodAccessor;
var prototypeOrStatic = methodNode.static ? '' : '.prototype';
var objectAccessor = state.className + prototypeOrStatic;
if (methodNode.key.type === Syntax.Identifier) {
// foo() {}
methodAccessor = methodNode.key.name;
if (_shouldMungeIdentifier(methodNode.key, state)) {
methodAccessor = _getMungedName(methodAccessor, state);
}
if (isGetter || isSetter) {
methodAccessor = JSON.stringify(methodAccessor);
} else if (reservedWordsHelper.isReservedWord(methodAccessor)) {
methodAccessorComputed = true;
methodAccessor = JSON.stringify(methodAccessor);
}
} else if (methodNode.key.type === Syntax.Literal) {
// 'foo bar'() {} | get 'foo bar'() {} | set 'foo bar'() {}
methodAccessor = JSON.stringify(methodNode.key.value);
methodAccessorComputed = true;
}
if (isSetter || isGetter) {
utils.append(
'Object.defineProperty(' +
objectAccessor + ',' +
methodAccessor + ',' +
'{configurable:true,' +
methodNode.kind + ':function',
state
);
} else {
if (state.g.opts.es3) {
if (methodAccessorComputed) {
methodAccessor = '[' + methodAccessor + ']';
} else {
methodAccessor = '.' + methodAccessor;
}
utils.append(
objectAccessor +
methodAccessor + '=function' + (node.generator ? '*' : ''),
state
);
} else {
if (!methodAccessorComputed) {
methodAccessor = JSON.stringify(methodAccessor);
}
utils.append(
'Object.defineProperty(' +
objectAccessor + ',' +
methodAccessor + ',' +
'{writable:true,configurable:true,' +
'value:function' + (node.generator ? '*' : ''),
state
);
}
}
}
utils.move(methodNode.key.range[1], state);
utils.append('(', state);
var params = node.params;
if (params.length > 0) {
utils.catchupNewlines(params[0].range[0], state);
for (var i = 0; i < params.length; i++) {
utils.catchup(node.params[i].range[0], state);
path.unshift(node);
traverse(params[i], path, state);
path.shift();
}
}
var closingParenPosition = utils.getNextSyntacticCharOffset(')', state);
utils.catchupWhiteSpace(closingParenPosition, state);
var openingBracketPosition = utils.getNextSyntacticCharOffset('{', state);
utils.catchup(openingBracketPosition + 1, state);
if (!state.scopeIsStrict) {
utils.append('"use strict";', state);
state = utils.updateState(state, {
scopeIsStrict: true
});
}
utils.move(node.body.range[0] + '{'.length, state);
path.unshift(node);
traverse(node.body, path, state);
path.shift();
utils.catchup(node.body.range[1], state);
if (methodNode.key.name !== 'constructor') {
if (isGetter || isSetter || !state.g.opts.es3) {
utils.append('})', state);
}
utils.append(';', state);
}
return false;
}
visitClassFunctionExpression.test = function(node, path, state) {
return node.type === Syntax.FunctionExpression
&& path[0].type === Syntax.MethodDefinition;
};
function visitClassMethodParam(traverse, node, path, state) {
var paramName = node.name;
if (_shouldMungeIdentifier(node, state)) {
paramName = _getMungedName(node.name, state);
}
utils.append(paramName, state);
utils.move(node.range[1], state);
}
visitClassMethodParam.test = function(node, path, state) {
if (!path[0] || !path[1]) {
return;
}
var parentFuncExpr = path[0];
var parentClassMethod = path[1];
return parentFuncExpr.type === Syntax.FunctionExpression
&& parentClassMethod.type === Syntax.MethodDefinition
&& node.type === Syntax.Identifier;
};
/**
* @param {function} traverse
* @param {object} node
* @param {array} path
* @param {object} state
*/
function _renderClassBody(traverse, node, path, state) {
var className = state.className;
var superClass = state.superClass;
// Set up prototype of constructor on same line as `extends` for line-number
// preservation. This relies on function-hoisting if a constructor function is
// defined in the class body.
if (superClass.name) {
// If the super class is an expression, we need to memoize the output of the
// expression into the generated class name variable and use that to refer
// to the super class going forward. Example:
//
// class Foo extends mixin(Bar, Baz) {}
// --transforms to--
// function Foo() {} var ____Class0Blah = mixin(Bar, Baz);
if (superClass.expression !== null) {
utils.append(
'var ' + superClass.name + '=' + superClass.expression + ';',
state
);
}
var keyName = superClass.name + '____Key';
var keyNameDeclarator = '';
if (!utils.identWithinLexicalScope(keyName, state)) {
keyNameDeclarator = 'var ';
declareIdentInLocalScope(keyName, initScopeMetadata(node), state);
}
utils.append(
'for(' + keyNameDeclarator + keyName + ' in ' + superClass.name + '){' +
'if(' + superClass.name + '.hasOwnProperty(' + keyName + ')){' +
className + '[' + keyName + ']=' +
superClass.name + '[' + keyName + '];' +
'}' +
'}',
state
);
var superProtoIdentStr = SUPER_PROTO_IDENT_PREFIX + superClass.name;
if (!utils.identWithinLexicalScope(superProtoIdentStr, state)) {
utils.append(
'var ' + superProtoIdentStr + '=' + superClass.name + '===null?' +
'null:' + superClass.name + '.prototype;',
state
);
declareIdentInLocalScope(superProtoIdentStr, initScopeMetadata(node), state);
}
utils.append(
className + '.prototype=Object.create(' + superProtoIdentStr + ');',
state
);
utils.append(
className + '.prototype.constructor=' + className + ';',
state
);
utils.append(
className + '.__superConstructor__=' + superClass.name + ';',
state
);
}
// If there's no constructor method specified in the class body, create an
// empty constructor function at the top (same line as the class keyword)
if (!node.body.body.filter(_isConstructorMethod).pop()) {
utils.append('function ' + className + '(){', state);
if (!state.scopeIsStrict) {
utils.append('"use strict";', state);
}
if (superClass.name) {
utils.append(
'if(' + superClass.name + '!==null){' +
superClass.name + '.apply(this,arguments);}',
state
);
}
utils.append('}', state);
}
utils.move(node.body.range[0] + '{'.length, state);
traverse(node.body, path, state);
utils.catchupWhiteSpace(node.range[1], state);
}
/**
* @param {function} traverse
* @param {object} node
* @param {array} path
* @param {object} state
*/
function visitClassDeclaration(traverse, node, path, state) {
var className = node.id.name;
var superClass = _getSuperClassInfo(node, state);
state = utils.updateState(state, {
mungeNamespace: className,
className: className,
superClass: superClass
});
_renderClassBody(traverse, node, path, state);
return false;
}
visitClassDeclaration.test = function(node, path, state) {
return node.type === Syntax.ClassDeclaration;
};
/**
* @param {function} traverse
* @param {object} node
* @param {array} path
* @param {object} state
*/
function visitClassExpression(traverse, node, path, state) {
var className = node.id && node.id.name || _generateAnonymousClassName(state);
var superClass = _getSuperClassInfo(node, state);
utils.append('(function(){', state);
state = utils.updateState(state, {
mungeNamespace: className,
className: className,
superClass: superClass
});
_renderClassBody(traverse, node, path, state);
utils.append('return ' + className + ';})()', state);
return false;
}
visitClassExpression.test = function(node, path, state) {
return node.type === Syntax.ClassExpression;
};
/**
* @param {function} traverse
* @param {object} node
* @param {array} path
* @param {object} state
*/
function visitPrivateIdentifier(traverse, node, path, state) {
utils.append(_getMungedName(node.name, state), state);
utils.move(node.range[1], state);
}
visitPrivateIdentifier.test = function(node, path, state) {
if (node.type === Syntax.Identifier && _shouldMungeIdentifier(node, state)) {
// Always munge non-computed properties of MemberExpressions
// (a la preventing access of properties of unowned objects)
if (path[0].type === Syntax.MemberExpression && path[0].object !== node
&& path[0].computed === false) {
return true;
}
// Always munge identifiers that were declared within the method function
// scope
if (utils.identWithinLexicalScope(node.name, state, state.methodFuncNode)) {
return true;
}
// Always munge private keys on object literals defined within a method's
// scope.
if (path[0].type === Syntax.Property
&& path[1].type === Syntax.ObjectExpression) {
return true;
}
// Always munge function parameters
if (path[0].type === Syntax.FunctionExpression
|| path[0].type === Syntax.FunctionDeclaration
|| path[0].type === Syntax.ArrowFunctionExpression) {
for (var i = 0; i < path[0].params.length; i++) {
if (path[0].params[i] === node) {
return true;
}
}
}
}
return false;
};
/**
* @param {function} traverse
* @param {object} node
* @param {array} path
* @param {object} state
*/
function visitSuperCallExpression(traverse, node, path, state) {
var superClassName = state.superClass.name;
if (node.callee.type === Syntax.Identifier) {
if (_isConstructorMethod(state.methodNode)) {
utils.append(superClassName + '.call(', state);
} else {
var protoProp = SUPER_PROTO_IDENT_PREFIX + superClassName;
if (state.methodNode.key.type === Syntax.Identifier) {
protoProp += '.' + state.methodNode.key.name;
} else if (state.methodNode.key.type === Syntax.Literal) {
protoProp += '[' + JSON.stringify(state.methodNode.key.value) + ']';
}
utils.append(protoProp + ".call(", state);
}
utils.move(node.callee.range[1], state);
} else if (node.callee.type === Syntax.MemberExpression) {
utils.append(SUPER_PROTO_IDENT_PREFIX + superClassName, state);
utils.move(node.callee.object.range[1], state);
if (node.callee.computed) {
// ["a" + "b"]
utils.catchup(node.callee.property.range[1] + ']'.length, state);
} else {
// .ab
utils.append('.' + node.callee.property.name, state);
}
utils.append('.call(', state);
utils.move(node.callee.range[1], state);
}
utils.append('this', state);
if (node.arguments.length > 0) {
utils.append(',', state);
utils.catchupWhiteSpace(node.arguments[0].range[0], state);
traverse(node.arguments, path, state);
}
utils.catchupWhiteSpace(node.range[1], state);
utils.append(')', state);
return false;
}
visitSuperCallExpression.test = function(node, path, state) {
if (state.superClass && node.type === Syntax.CallExpression) {
var callee = node.callee;
if (callee.type === Syntax.Identifier && callee.name === 'super'
|| callee.type == Syntax.MemberExpression
&& callee.object.name === 'super') {
return true;
}
}
return false;
};
/**
* @param {function} traverse
* @param {object} node
* @param {array} path
* @param {object} state
*/
function visitSuperMemberExpression(traverse, node, path, state) {
var superClassName = state.superClass.name;
utils.append(SUPER_PROTO_IDENT_PREFIX + superClassName, state);
utils.move(node.object.range[1], state);
}
visitSuperMemberExpression.test = function(node, path, state) {
return state.superClass
&& node.type === Syntax.MemberExpression
&& node.object.type === Syntax.Identifier
&& node.object.name === 'super';
};
exports.resetSymbols = resetSymbols;
exports.visitorList = [
visitClassDeclaration,
visitClassExpression,
visitClassFunctionExpression,
visitClassMethod,
visitClassMethodParam,
visitPrivateIdentifier,
visitSuperCallExpression,
visitSuperMemberExpression
];

View File

@@ -0,0 +1,274 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/*global exports:true*/
/**
* Implements ES6 destructuring assignment and pattern matchng.
*
* function init({port, ip, coords: [x, y]}) {
* return (x && y) ? {id, port} : {ip};
* };
*
* function init($__0) {
* var
* port = $__0.port,
* ip = $__0.ip,
* $__1 = $__0.coords,
* x = $__1[0],
* y = $__1[1];
* return (x && y) ? {id, port} : {ip};
* }
*
* var x, {ip, port} = init({ip, port});
*
* var x, $__0 = init({ip, port}), ip = $__0.ip, port = $__0.port;
*
*/
var Syntax = require('esprima-fb').Syntax;
var utils = require('../src/utils');
var reservedWordsHelper = require('./reserved-words-helper');
var restParamVisitors = require('./es6-rest-param-visitors');
var restPropertyHelpers = require('./es7-rest-property-helpers');
// -------------------------------------------------------
// 1. Structured variable declarations.
//
// var [a, b] = [b, a];
// var {x, y} = {y, x};
// -------------------------------------------------------
function visitStructuredVariable(traverse, node, path, state) {
// Allocate new temp for the pattern.
utils.append(utils.getTempVar(state.localScope.tempVarIndex) + '=', state);
// Skip the pattern and assign the init to the temp.
utils.catchupWhiteSpace(node.init.range[0], state);
traverse(node.init, path, state);
utils.catchup(node.init.range[1], state);
// Render the destructured data.
utils.append(',' + getDestructuredComponents(node.id, state), state);
state.localScope.tempVarIndex++;
return false;
}
visitStructuredVariable.test = function(node, path, state) {
return node.type === Syntax.VariableDeclarator &&
isStructuredPattern(node.id);
};
function isStructuredPattern(node) {
return node.type === Syntax.ObjectPattern ||
node.type === Syntax.ArrayPattern;
}
// Main function which does actual recursive destructuring
// of nested complex structures.
function getDestructuredComponents(node, state) {
var tmpIndex = state.localScope.tempVarIndex;
var components = [];
var patternItems = getPatternItems(node);
for (var idx = 0; idx < patternItems.length; idx++) {
var item = patternItems[idx];
if (!item) {
continue;
}
if (item.type === Syntax.SpreadElement) {
// Spread/rest of an array.
// TODO(dmitrys): support spread in the middle of a pattern
// and also for function param patterns: [x, ...xs, y]
components.push(item.argument.name +
'=Array.prototype.slice.call(' +
utils.getTempVar(tmpIndex) + ',' + idx + ')'
);
continue;
}
if (item.type === Syntax.SpreadProperty) {
var restExpression = restPropertyHelpers.renderRestExpression(
utils.getTempVar(tmpIndex),
patternItems
);
components.push(item.argument.name + '=' + restExpression);
continue;
}
// Depending on pattern type (Array or Object), we get
// corresponding pattern item parts.
var accessor = getPatternItemAccessor(node, item, tmpIndex, idx);
var value = getPatternItemValue(node, item);
// TODO(dmitrys): implement default values: {x, y=5}
if (value.type === Syntax.Identifier) {
// Simple pattern item.
components.push(value.name + '=' + accessor);
} else {
// Complex sub-structure.
components.push(
utils.getTempVar(++state.localScope.tempVarIndex) + '=' + accessor +
',' + getDestructuredComponents(value, state)
);
}
}
return components.join(',');
}
function getPatternItems(node) {
return node.properties || node.elements;
}
function getPatternItemAccessor(node, patternItem, tmpIndex, idx) {
var tmpName = utils.getTempVar(tmpIndex);
if (node.type === Syntax.ObjectPattern) {
if (reservedWordsHelper.isReservedWord(patternItem.key.name)) {
return tmpName + '["' + patternItem.key.name + '"]';
} 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 if (node.type === Syntax.ArrayPattern) {
return tmpName + '[' + idx + ']';
}
}
function getPatternItemValue(node, patternItem) {
return node.type === Syntax.ObjectPattern
? patternItem.value
: patternItem;
}
// -------------------------------------------------------
// 2. Assignment expression.
//
// [a, b] = [b, a];
// ({x, y} = {y, x});
// -------------------------------------------------------
function visitStructuredAssignment(traverse, node, path, state) {
var exprNode = node.expression;
utils.append('var ' + utils.getTempVar(state.localScope.tempVarIndex) + '=', state);
utils.catchupWhiteSpace(exprNode.right.range[0], state);
traverse(exprNode.right, path, state);
utils.catchup(exprNode.right.range[1], state);
utils.append(
';' + getDestructuredComponents(exprNode.left, state) + ';',
state
);
utils.catchupWhiteSpace(node.range[1], state);
state.localScope.tempVarIndex++;
return false;
}
visitStructuredAssignment.test = function(node, path, state) {
// We consider the expression statement rather than just assignment
// expression to cover case with object patters which should be
// wrapped in grouping operator: ({x, y} = {y, x});
return node.type === Syntax.ExpressionStatement &&
node.expression.type === Syntax.AssignmentExpression &&
isStructuredPattern(node.expression.left);
};
// -------------------------------------------------------
// 3. Structured parameter.
//
// function foo({x, y}) { ... }
// -------------------------------------------------------
function visitStructuredParameter(traverse, node, path, state) {
utils.append(utils.getTempVar(getParamIndex(node, path)), state);
utils.catchupWhiteSpace(node.range[1], state);
return true;
}
function getParamIndex(paramNode, path) {
var funcNode = path[0];
var tmpIndex = 0;
for (var k = 0; k < funcNode.params.length; k++) {
var param = funcNode.params[k];
if (param === paramNode) {
break;
}
if (isStructuredPattern(param)) {
tmpIndex++;
}
}
return tmpIndex;
}
visitStructuredParameter.test = function(node, path, state) {
return isStructuredPattern(node) && isFunctionNode(path[0]);
};
function isFunctionNode(node) {
return (node.type == Syntax.FunctionDeclaration ||
node.type == Syntax.FunctionExpression ||
node.type == Syntax.MethodDefinition ||
node.type == Syntax.ArrowFunctionExpression);
}
// -------------------------------------------------------
// 4. Function body for structured parameters.
//
// function foo({x, y}) { x; y; }
// -------------------------------------------------------
function visitFunctionBodyForStructuredParameter(traverse, node, path, state) {
var funcNode = path[0];
utils.catchup(funcNode.body.range[0] + 1, state);
renderDestructuredComponents(funcNode, state);
if (funcNode.rest) {
utils.append(
restParamVisitors.renderRestParamSetup(funcNode, state),
state
);
}
return true;
}
function renderDestructuredComponents(funcNode, state) {
var destructuredComponents = [];
for (var k = 0; k < funcNode.params.length; k++) {
var param = funcNode.params[k];
if (isStructuredPattern(param)) {
destructuredComponents.push(
getDestructuredComponents(param, state)
);
state.localScope.tempVarIndex++;
}
}
if (destructuredComponents.length) {
utils.append('var ' + destructuredComponents.join(',') + ';', state);
}
}
visitFunctionBodyForStructuredParameter.test = function(node, path, state) {
return node.type === Syntax.BlockStatement && isFunctionNode(path[0]);
};
exports.visitorList = [
visitStructuredVariable,
visitStructuredAssignment,
visitStructuredParameter,
visitFunctionBodyForStructuredParameter
];
exports.renderDestructuredComponents = renderDestructuredComponents;

View File

@@ -0,0 +1,123 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/*global exports:true*/
/**
* Implements ES6 for-of loop, making optimization
* for arrays. If an object is not an array, instantiates
* an iterator for this object, assuming the runtime
* support for the iterator is implemented.
*
* ES6:
*
* for (var v of <expr>) <body>
*
* Compiled ES3:
*
* for (var v,
* _iter = <expr>,
* _isArray = Array.isArray(_iter),
* _k = 0,
* _iter = _isArray ? _iter : _iter[Symbol.iterator]();;
* ) {
*
* if (_isArray) {
* if (_k >= _iter.length) break;
* v = _iter[_k++];
* } else {
* _k = _iter.next();
* if (_k.done) break;
* v = _k.value;
* }
*
* <body>
* }
*/
var Syntax = require('esprima-fb').Syntax;
var utils = require('../src/utils');
function process(traverse, node, path, state) {
utils.move(node.range[0], state);
traverse(node, path, state);
utils.catchup(node.range[1], state);
}
function visitForOfStatement(traverse, node, path, state) {
var iter = utils.injectTempVar(state);
var isArray = utils.injectTempVar(state);
var k = utils.injectTempVar(state);
var variable;
if (node.left.type === Syntax.VariableDeclaration) {
variable = node.left.declarations[0].id.name;
utils.append('var ' + variable + ';', state);
} else {
variable = node.left.name;
}
utils.append('for(', state);
utils.append(iter + '=', state);
process(traverse, node.right, path, state);
// Setup iterator with optimization for arrays.
utils.append(
',' + isArray + '=Array.isArray(' + iter + '),' +
k + '=0,' +
iter + '=' + isArray + '?' + iter + ':' +
iter + '[/*global Symbol*/typeof Symbol=="function"' +
'?Symbol.iterator:"@@iterator"]();;',
state
);
// Jump to the body creating block if needed.
if (node.body.type === Syntax.BlockStatement) {
utils.catchup(node.body.range[0] + 1, state);
} else {
utils.catchup(node.body.range[0], state);
utils.append('{', state);
}
// Arrays case.
utils.append(
'if(' + isArray + '){' +
'if(' + k + '>=' + iter + '.length) break;',
state
);
utils.append(variable + '=' + iter + '[' + k + '++];', state);
// Iterators case.
utils.append(
'}else{' + k + '=' + iter + '.next();' +
'if(' + k + '.done) break;',
state
);
utils.append(variable + '=' + k + '.value;}', state);
traverse(node.body, path, state);
utils.catchup(node.body.range[1], state);
if (node.body.type !== Syntax.BlockStatement) {
utils.append('}', state);
}
return false;
}
visitForOfStatement.test = function(node, path, state) {
return node.type === Syntax.ForOfStatement;
};
exports.visitorList = [
visitForOfStatement,
];

View File

@@ -0,0 +1,106 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/*jslint node: true*/
var es6ObjectConciseMethods = require('./es6-object-concise-method-visitors');
var es7SpreadProperties = require('./es7-spread-property-visitors');
var Syntax = require('esprima-fb').Syntax;
var utils = require('../src/utils');
function process(traverse, node, path, state) {
utils.catchupWhiteSpace(node.range[0], state);
traverse(node, path, state);
utils.catchup(node.range[1], state);
}
/**
* Note: This visitor is capable of handling the following transforms too:
* - ES7 object literal spread,
* - ES6 object concise methods,
* - ES6 object short notation,
* This is because of limitations in the jstransform framework, which isn't
* capable of feeding the output of one visitor to another. Additionally,
* any attempt to share logic between these visitors only increases code
* complixity. And so, we are forced to create a single complex one that
* handles all cases.
*
* {alpha: 12, \'beta\': 34, ['gam' + 'ma']: 56} // before
* (_={},_.alpha=12,_['beta']=34,_['gam' + 'ma']=56,_) // after
*/
function es6ObjectComputedProperties(traverse, node, path, state) {
var obj = utils.injectTempVar(state);
utils.append('(' + obj + '={}', state);
for (var ii = 0; ii < node.properties.length; ++ii) {
var property = node.properties[ii];
utils.append(',', state);
if (property.type === Syntax.SpreadProperty) {
utils.append('Object.assign(' + obj, state);
var nextComputedPropertyIndex = ii + 1;
while (
nextComputedPropertyIndex < node.properties.length &&
!node.properties[nextComputedPropertyIndex].computed
) {
nextComputedPropertyIndex += 1;
}
utils.catchupWhiteSpace(node.properties[ii].range[0], state);
var lastWasSpread = es7SpreadProperties.renderSpreadProperties(
traverse,
node.properties.slice(ii, nextComputedPropertyIndex),
path,
state,
true // previousWasSpread
);
utils.append((lastWasSpread ? '' : '}') + ')', state);
ii = nextComputedPropertyIndex - 1;
continue;
// short notation / dot access
} else if (
property.type === Syntax.Property &&
property.key.type === Syntax.Identifier &&
!property.computed
) {
utils.append(obj + '.' + property.key.name + '=', state);
// literals / computed properties
} else if (property.type === Syntax.Property) {
utils.append(obj + '[', state);
process(traverse, property.key, path, state);
utils.append(']=', state);
}
// concise methods
if (property.method === true) {
utils.catchupWhiteSpace(property.key.range[1], state);
es6ObjectConciseMethods.renderConciseMethod(traverse, property, path, state);
}
process(traverse, property.value, path, state);
}
utils.catchupWhiteSpace(node.range[1], state);
utils.append(',' + obj + ')', state);
return false;
}
es6ObjectComputedProperties.test = function(node, path, state) {
if (node.type !== Syntax.ObjectExpression) {
return false;
}
for (var ii = 0; ii < node.properties.length; ++ii) {
if (node.properties[ii].computed) {
return true;
}
}
return false;
};
exports.visitorList = [
es6ObjectComputedProperties,
];

View File

@@ -0,0 +1,70 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/*jslint node:true*/
/**
* Desugars concise methods of objects to function expressions.
*
* var foo = {
* method(x, y) { ... }
* };
*
* var foo = {
* method: function(x, y) { ... }
* };
*
*/
var Syntax = require('esprima-fb').Syntax;
var utils = require('../src/utils');
var reservedWordsHelper = require('./reserved-words-helper');
function visitObjectConciseMethod(traverse, node, path, state) {
var isGenerator = node.value.generator;
if (isGenerator) {
utils.catchupWhiteSpace(node.range[0] + 1, state);
}
if (reservedWordsHelper.isReservedWord(node.key.name)) {
utils.catchup(node.key.range[0], state);
utils.append('"', state);
utils.catchup(node.key.range[1], state);
utils.append('"', state);
}
utils.catchup(node.key.range[1], state);
utils.append(':', state);
renderConciseMethod(traverse, node, path, state);
return false;
}
// This method is also used by es6-object-computed-property-visitor to render
// the method for concise computed properties.
function renderConciseMethod(traverse, property, path, state) {
if (property.computed) {
var closingSquareBracketIndex = state.g.source.indexOf(']', property.key.range[1]);
utils.catchup(closingSquareBracketIndex, state);
utils.move(closingSquareBracketIndex + 1, state);
}
utils.append("function" + (property.value.generator ? "*" : ""), state);
path.unshift(property);
traverse(property.value, path, state);
path.shift();
}
visitObjectConciseMethod.test = function(node, path, state) {
return node.type === Syntax.Property &&
node.value.type === Syntax.FunctionExpression &&
node.method === true;
};
exports.renderConciseMethod = renderConciseMethod;
exports.visitorList = [
visitObjectConciseMethod
];

View File

@@ -0,0 +1,46 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/*jslint node: true*/
/**
* Desugars ES6 Object Literal short notations into ES3 full notation.
*
* // Easier return values.
* function foo(x, y) {
* return {x, y}; // {x: x, y: y}
* };
*
* // Destructuring.
* function init({port, ip, coords: {x, y}}) { ... }
*
*/
var Syntax = require('esprima-fb').Syntax;
var utils = require('../src/utils');
/**
* @public
*/
function visitObjectLiteralShortNotation(traverse, node, path, state) {
utils.catchup(node.key.range[1], state);
utils.append(':' + node.key.name, state);
return false;
}
visitObjectLiteralShortNotation.test = function(node, path, state) {
return node.type === Syntax.Property &&
node.kind === 'init' &&
node.shorthand === true &&
path[0].type !== Syntax.ObjectPattern;
};
exports.visitorList = [
visitObjectLiteralShortNotation
];

View File

@@ -0,0 +1,100 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/*jslint node:true*/
/**
* 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) {
* for (var args = [], $__0 = 1, $__1 = arguments.length; $__0 < $__1; $__0++)
* args.push(arguments[$__0]);
* args.forEach(...);
* }
*
*/
var Syntax = require('esprima-fb').Syntax;
var utils = require('../src/utils');
function _nodeIsFunctionWithRestParam(node) {
return (node.type === Syntax.FunctionDeclaration
|| node.type === Syntax.FunctionExpression
|| node.type === Syntax.ArrowFunctionExpression)
&& node.rest;
}
function visitFunctionParamsWithRestParam(traverse, node, path, state) {
if (node.parametricType) {
utils.catchup(node.parametricType.range[0], state);
path.unshift(node);
traverse(node.parametricType, path, state);
path.shift();
}
// Render params.
if (node.params.length) {
path.unshift(node);
traverse(node.params, path, state);
path.shift();
} else {
// -3 is for ... of the rest.
utils.catchup(node.rest.range[0] - 3, state);
}
utils.catchupWhiteSpace(node.rest.range[1], state);
utils.catchup(node.body.range[0], state);
path.unshift(node);
traverse(node.body, path, state);
path.shift();
return false;
}
visitFunctionParamsWithRestParam.test = function(node, path, state) {
return _nodeIsFunctionWithRestParam(node);
};
function renderRestParamSetup(functionNode, state) {
var idx = state.localScope.tempVarIndex++;
var len = state.localScope.tempVarIndex++;
return 'for (var ' + functionNode.rest.name + '=[],' +
utils.getTempVar(idx) + '=' + functionNode.params.length + ',' +
utils.getTempVar(len) + '=arguments.length;' +
utils.getTempVar(idx) + '<' + utils.getTempVar(len) + ';' +
utils.getTempVar(idx) + '++) ' +
functionNode.rest.name + '.push(arguments[' + utils.getTempVar(idx) + ']);';
}
function visitFunctionBodyWithRestParam(traverse, node, path, state) {
utils.catchup(node.range[0] + 1, state);
var parentNode = path[0];
utils.append(renderRestParamSetup(parentNode, state), state);
return true;
}
visitFunctionBodyWithRestParam.test = function(node, path, state) {
return node.type === Syntax.BlockStatement
&& _nodeIsFunctionWithRestParam(path[0]);
};
exports.renderRestParamSetup = renderRestParamSetup;
exports.visitorList = [
visitFunctionParamsWithRestParam,
visitFunctionBodyWithRestParam
];

View File

@@ -0,0 +1,153 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/*jslint node:true*/
/**
* @typechecks
*/
'use strict';
var Syntax = require('esprima-fb').Syntax;
var utils = require('../src/utils');
/**
* http://people.mozilla.org/~jorendorff/es6-draft.html#sec-12.1.9
*/
function visitTemplateLiteral(traverse, node, path, state) {
var templateElements = node.quasis;
utils.append('(', state);
for (var ii = 0; ii < templateElements.length; ii++) {
var templateElement = templateElements[ii];
if (templateElement.value.raw !== '') {
utils.append(getCookedValue(templateElement), state);
if (!templateElement.tail) {
// + between element and substitution
utils.append(' + ', state);
}
// maintain line numbers
utils.move(templateElement.range[0], state);
utils.catchupNewlines(templateElement.range[1], state);
} else { // templateElement.value.raw === ''
// Concatenat adjacent substitutions, e.g. `${x}${y}`. Empty templates
// appear before the first and after the last element - nothing to add in
// those cases.
if (ii === 0) {
utils.append('"" + ', state);
}
if (ii > 0 && !templateElement.tail) {
// + between substitution and substitution
utils.append(' + ', state);
}
}
utils.move(templateElement.range[1], state);
if (!templateElement.tail) {
var substitution = node.expressions[ii];
if (substitution.type === Syntax.Identifier ||
substitution.type === Syntax.Literal ||
substitution.type === Syntax.MemberExpression ||
substitution.type === Syntax.CallExpression) {
utils.catchup(substitution.range[1], state);
} else {
utils.append('(', state);
traverse(substitution, path, state);
utils.catchup(substitution.range[1], state);
utils.append(')', state);
}
// if next templateElement isn't empty...
if (templateElements[ii + 1].value.cooked !== '') {
utils.append(' + ', state);
}
}
}
utils.move(node.range[1], state);
utils.append(')', state);
return false;
}
visitTemplateLiteral.test = function(node, path, state) {
return node.type === Syntax.TemplateLiteral;
};
/**
* http://people.mozilla.org/~jorendorff/es6-draft.html#sec-12.2.6
*/
function visitTaggedTemplateExpression(traverse, node, path, state) {
var template = node.quasi;
var numQuasis = template.quasis.length;
// print the tag
utils.move(node.tag.range[0], state);
traverse(node.tag, path, state);
utils.catchup(node.tag.range[1], state);
// print array of template elements
utils.append('(function() { var siteObj = [', state);
for (var ii = 0; ii < numQuasis; ii++) {
utils.append(getCookedValue(template.quasis[ii]), state);
if (ii !== numQuasis - 1) {
utils.append(', ', state);
}
}
utils.append(']; siteObj.raw = [', state);
for (ii = 0; ii < numQuasis; ii++) {
utils.append(getRawValue(template.quasis[ii]), state);
if (ii !== numQuasis - 1) {
utils.append(', ', state);
}
}
utils.append(
']; Object.freeze(siteObj.raw); Object.freeze(siteObj); return siteObj; }()',
state
);
// print substitutions
if (numQuasis > 1) {
for (ii = 0; ii < template.expressions.length; ii++) {
var expression = template.expressions[ii];
utils.append(', ', state);
// maintain line numbers by calling catchupWhiteSpace over the whole
// previous TemplateElement
utils.move(template.quasis[ii].range[0], state);
utils.catchupNewlines(template.quasis[ii].range[1], state);
utils.move(expression.range[0], state);
traverse(expression, path, state);
utils.catchup(expression.range[1], state);
}
}
// print blank lines to push the closing ) down to account for the final
// TemplateElement.
utils.catchupNewlines(node.range[1], state);
utils.append(')', state);
return false;
}
visitTaggedTemplateExpression.test = function(node, path, state) {
return node.type === Syntax.TaggedTemplateExpression;
};
function getCookedValue(templateElement) {
return JSON.stringify(templateElement.value.cooked);
}
function getRawValue(templateElement) {
return JSON.stringify(templateElement.value.raw);
}
exports.visitorList = [
visitTemplateLiteral,
visitTaggedTemplateExpression
];

View File

@@ -0,0 +1,73 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/*jslint node:true*/
/**
* Desugars ES7 rest properties into ES5 object iteration.
*/
var Syntax = require('esprima-fb').Syntax;
// TODO: This is a pretty massive helper, it should only be defined once, in the
// transform's runtime environment. We don't currently have a runtime though.
var restFunction =
'(function(source, exclusion) {' +
'var rest = {};' +
'var hasOwn = Object.prototype.hasOwnProperty;' +
'if (source == null) {' +
'throw new TypeError();' +
'}' +
'for (var key in source) {' +
'if (hasOwn.call(source, key) && !hasOwn.call(exclusion, key)) {' +
'rest[key] = source[key];' +
'}' +
'}' +
'return rest;' +
'})';
function getPropertyNames(properties) {
var names = [];
for (var i = 0; i < properties.length; i++) {
var property = properties[i];
if (property.type === Syntax.SpreadProperty) {
continue;
}
if (property.type === Syntax.Identifier) {
names.push(property.name);
} else {
names.push(property.key.name);
}
}
return names;
}
function getRestFunctionCall(source, exclusion) {
return restFunction + '(' + source + ',' + exclusion + ')';
}
function getSimpleShallowCopy(accessorExpression) {
// This could be faster with 'Object.assign({}, ' + accessorExpression + ')'
// but to unify code paths and avoid a ES6 dependency we use the same
// helper as for the exclusion case.
return getRestFunctionCall(accessorExpression, '{}');
}
function renderRestExpression(accessorExpression, excludedProperties) {
var excludedNames = getPropertyNames(excludedProperties);
if (!excludedNames.length) {
return getSimpleShallowCopy(accessorExpression);
}
return getRestFunctionCall(
accessorExpression,
'{' + excludedNames.join(':1,') + ':1}'
);
}
exports.renderRestExpression = renderRestExpression;

View File

@@ -0,0 +1,126 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/*global exports:true*/
/**
* Implements ES7 object spread property.
* https://gist.github.com/sebmarkbage/aa849c7973cb4452c547
*
* { ...a, x: 1 }
*
* Object.assign({}, a, {x: 1 })
*
*/
var Syntax = require('esprima-fb').Syntax;
var utils = require('../src/utils');
function visitObjectLiteralSpread(traverse, node, path, state) {
utils.catchup(node.range[0], state);
utils.append('Object.assign({', state);
// Skip the original {
utils.move(node.range[0] + 1, state);
var lastWasSpread = renderSpreadProperties(
traverse,
node.properties,
path,
state,
false // previousWasSpread
);
// 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);
// Skip the trailing }
utils.move(node.range[1], state);
if (!lastWasSpread) {
utils.append('}', state);
}
utils.append(')', state);
return false;
}
// This method is also used by es6-object-computed-property-visitor.
function renderSpreadProperties(traverse, properties, path, state, previousWasSpread) {
for (var i = 0; i < properties.length; i++) {
var property = properties[i];
if (property.type === Syntax.SpreadProperty) {
// Close the previous object or initial object
if (!previousWasSpread) {
utils.append('}', state);
}
if (i === 0) {
// Normally there will be a comma when we catch up, but not before
// the first property.
utils.append(',', state);
}
utils.catchup(property.range[0], state);
// skip ...
utils.move(property.range[0] + 3, state);
traverse(property.argument, path, state);
utils.catchup(property.range[1], state);
previousWasSpread = true;
} else {
utils.catchup(property.range[0], state);
if (previousWasSpread) {
utils.append('{', state);
}
traverse(property, path, state);
utils.catchup(property.range[1], state);
previousWasSpread = false;
}
}
return previousWasSpread;
}
visitObjectLiteralSpread.test = function(node, path, state) {
if (node.type !== Syntax.ObjectExpression) {
return false;
}
// Tight loop optimization
var hasAtLeastOneSpreadProperty = false;
for (var i = 0; i < node.properties.length; i++) {
var property = node.properties[i];
if (property.type === Syntax.SpreadProperty) {
hasAtLeastOneSpreadProperty = true;
} else if (property.kind !== 'init') {
return false;
}
}
return hasAtLeastOneSpreadProperty;
};
exports.renderSpreadProperties = renderSpreadProperties;
exports.visitorList = [
visitObjectLiteralSpread
];

View File

@@ -0,0 +1,103 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
var Syntax = require('esprima-fb').Syntax;
var utils = require('../src/utils');
/**
* Strips trailing commas from function calls. Transforms:
*
* foo('bar',)
*
* into:
*
* foo('bar')
*/
function visitFunctionCallArguments(traverse, node, path, state) {
utils.catchup(node.callee.range[0], state);
traverse(node.callee, [node].concat(path), state);
var args = node['arguments'];
for (var index = 0; index < args.length; ++index) {
utils.catchup(args[index].range[0], state);
traverse(args[index], [node].concat(path), state);
utils.catchup(args[index].range[1], state);
}
// delete first comma between the last argument and the closing parenthesis
utils.catchup(node.range[1], state, function(value) {
return value.replace(",", '');
});
return false;
}
visitFunctionCallArguments.test = function(node, path, state) {
return (
node.type === Syntax.CallExpression ||
node.type === Syntax.NewExpression
) && (
node['arguments'].length > 0
);
};
/**
* Strips trailing commas from function expressions / function declarations /
* method calls. Transforms:
*
* var fnExp = function(bar,) { ... };
* function fnDec(bar,) { ... };
* class Test { foo(bar, ) { ... } };
*
* into:
*
* var fnExp = function(bar) { ... };
* function fnDec(bar) { ... };
* class Test { foo(bar) { ... } };
*/
function visitFunctionDefinitionArguments(traverse, node, path, state) {
var end = node.range[1];
if (node.type === Syntax.MethodDefinition) {
node = node.value;
}
for (var index = 0; index < node.params.length; ++index) {
utils.catchup(node.params[index].range[0], state);
traverse(node.params[index], [node].concat(path), state);
utils.catchup(node.params[index].range[1], state);
}
// delete first comma between the last argument and the closing parenthesis
utils.catchup(node.body.range[0], state, function(value) {
var commaIndex = value.substr(0, value.indexOf(")")).indexOf(",");
return commaIndex > -1 ? value.replace(/,/, '') : value;
});
traverse(node.body, [node].concat(path), state);
utils.catchup(end, state);
return false;
}
visitFunctionDefinitionArguments.test = function(node, path, state) {
return (
node.type === Syntax.FunctionExpression ||
node.type === Syntax.FunctionDeclaration ||
node.type === Syntax.MethodDefinition
) && (
node.params && node.params.length > 0 || // function expression/declaration
node.value && node.value.params.length > 0 // method definition
);
};
exports.visitorList = [
visitFunctionCallArguments,
visitFunctionDefinitionArguments,
];

161
node_modules/jstransform/visitors/index.js generated vendored Normal file
View File

@@ -0,0 +1,161 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/*global exports:true*/
'use strict';
var es6ArrowFunction = require('./es6-arrow-function-visitors');
var es6CallSpread = require('./es6-call-spread-visitors');
var es6Class = require('./es6-class-visitors');
var es6Destructuring = require('./es6-destructuring-visitors');
var es6ForOf = require('./es6-for-of-visitors');
var es6ObjectComputedProperty =
require('./es6-object-computed-property-visitors');
var es6ObjectConciseMethod = require('./es6-object-concise-method-visitors');
var es6ObjectShortNotation = require('./es6-object-short-notation-visitors');
var es6RestParam = require('./es6-rest-param-visitors');
var es6Template = require('./es6-template-visitors');
var es7SpreadProperty = require('./es7-spread-property-visitors');
var es7TrailingComma = require('./es7-trailing-comma-visitors');
var reactDisplayName = require('./react-display-name-visitors');
var reactJSX = require('./react-jsx-visitors');
var reservedWords = require('./reserved-words-visitors');
var trailingComma = require('./trailing-comma-visitors');
var undefinedToVoid0 = require('./undefined-to-void-0-visitors');
// Map from transformName => orderedListOfVisitors.
var transformVisitors = {
'es6-arrow-function': es6ArrowFunction.visitorList,
'es6-call-spread': es6CallSpread.visitorList,
'es6-class': es6Class.visitorList,
'es6-destructuring': es6Destructuring.visitorList,
'es6-for-of': es6ForOf.visitorList,
'es6-object-computed-property': es6ObjectComputedProperty.visitorList,
'es6-object-concise-method': es6ObjectConciseMethod.visitorList,
'es6-object-short-notation': es6ObjectShortNotation.visitorList,
'es6-rest-param': es6RestParam.visitorList,
'es6-template': es6Template.visitorList,
'es7-spread-property': es7SpreadProperty.visitorList,
'es7-trailing-comma': es7TrailingComma.visitorList,
'react-display-name': reactDisplayName.visitorList,
'react-jsx': reactJSX.visitorList,
'reserved-words': reservedWords.visitorList,
'trailing-comma': trailingComma.visitorList,
'undefined-to-void-0': undefinedToVoid0.visitorList
};
// Sets of transforms. Useful for quickly building up with simple options.
var transformSets = {
'es6': [
'es6-arrow-function',
'es6-call-spread',
'es6-class',
'es6-destructuring',
'es6-for-of',
'es6-object-computed-property',
'es6-object-concise-method',
'es6-object-short-notation',
'es6-rest-param',
'es6-template'
],
'es7': [
'es7-spread-property',
'es7-trailing-comma'
],
'react': [
'react-jsx',
'react-display-name'
],
'target:es3': [
'reserved-words',
'trailing-comma'
],
'utility': [
'undefined-to-void-0'
]
};
// harmony is all newer transforms. Define it here so we don't duplicate.
transformSets.harmony = transformSets.es6.concat(transformSets.es7);
// Specifies the order in which each transform should run.
var transformRunOrder = [
'reserved-words',
'es6-destructuring',
'es6-arrow-function',
// needs to be before concice-methods, short-notation, spread-property
'es6-object-computed-property',
'es6-object-concise-method',
'es6-object-short-notation',
'es6-class',
'es6-rest-param',
'es6-template',
'es6-call-spread',
'es6-for-of',
'es7-spread-property',
// These are 2 distinct transforms - 'trailing-comma' handles array & object
// literals. 'es7-trailing-comma' handles extra arguments in function calls.
'trailing-comma',
'es7-trailing-comma',
'react-jsx',
'react-display-name',
'undefined-to-void-0'
];
/**
* Given a list of transform names, return the ordered list of visitors to be
* passed to the transform() function.
*
* @param {array?} excludes
* @return {array}
*/
function getAllVisitors(excludes) {
var ret = [];
for (var i = 0, il = transformRunOrder.length; i < il; i++) {
if (!excludes || excludes.indexOf(transformRunOrder[i]) === -1) {
ret = ret.concat(transformVisitors[transformRunOrder[i]]);
}
}
return ret;
}
/**
* Given a list of visitor set names, return the ordered list of visitors to be
* passed to jstransform.
*
* @param {array}
* @return {array}
*/
function getVisitorsBySet(sets) {
var visitorsToInclude = sets.reduce(function(visitors, set) {
if (!transformSets.hasOwnProperty(set)) {
throw new Error('Unknown visitor set: ' + set);
}
transformSets[set].forEach(function(visitor) {
visitors[visitor] = true;
});
return visitors;
}, {});
var visitorList = [];
for (var i = 0; i < transformRunOrder.length; i++) {
if (visitorsToInclude.hasOwnProperty(transformRunOrder[i])) {
visitorList = visitorList.concat(transformVisitors[transformRunOrder[i]]);
}
}
return visitorList;
}
exports.getVisitorsBySet = getVisitorsBySet;
exports.getAllVisitors = getAllVisitors;
exports.transformVisitors = transformVisitors;

138
node_modules/jstransform/visitors/jsx-helpers.js generated vendored Normal file
View File

@@ -0,0 +1,138 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/*global exports:true*/
'use strict';
var Syntax = require('esprima-fb').Syntax;
var utils = require('../src/utils');
function commaAfterLastParen(value) {
var state = 'normal';
var commaPos = 0;
for (var i = 0; i < value.length; ++i) {
if (state === 'normal') {
if (value.substr(i, 2) === '//') {
state = 'singleline';
i += 1;
} else if (value.substr(i, 2) === '/*') {
state = 'multiline';
i += 1;
} else if (value.charAt(i).trim() !== '') {
commaPos = i + 1;
}
} else if (state === 'singleline' && value.charAt(i) === '\n') {
state = 'normal';
} else if (state === 'multiline' &&
value.charAt(i) === '*' &&
i + 1 < value.length &&
value.charAt(i + 1) === '/') {
i += 1;
state = 'normal';
}
}
return value.substring(0, commaPos) + ', ' + trimLeft(value.substring(commaPos));
}
function renderJSXLiteral(object, isLast, state, start, end) {
var lines = object.value.split(/\r\n|\n|\r/);
if (start) {
utils.append(start, state);
}
var lastNonEmptyLine = 0;
lines.forEach(function(line, index) {
if (line.match(/[^ \t]/)) {
lastNonEmptyLine = index;
}
});
lines.forEach(function(line, index) {
var isFirstLine = index === 0;
var isLastLine = index === lines.length - 1;
var isLastNonEmptyLine = index === lastNonEmptyLine;
// replace rendered whitespace tabs with spaces
var trimmedLine = line.replace(/\t/g, ' ');
// trim whitespace touching a newline
if (!isFirstLine) {
trimmedLine = trimmedLine.replace(/^[ ]+/, '');
}
if (!isLastLine) {
trimmedLine = trimmedLine.replace(/[ ]+$/, '');
}
if (!isFirstLine) {
utils.append(line.match(/^[ \t]*/)[0], state);
}
if (trimmedLine || isLastNonEmptyLine) {
utils.append(
JSON.stringify(trimmedLine) +
(!isLastNonEmptyLine ? ' + " " +' : ''),
state);
if (isLastNonEmptyLine) {
if (end) {
utils.append(end, state);
}
if (!isLast) {
utils.append(', ', state);
}
}
// only restore tail whitespace if line had literals
if (trimmedLine && !isLastLine) {
utils.append(line.match(/[ \t]*$/)[0], state);
}
}
if (!isLastLine) {
utils.append('\n', state);
}
});
utils.move(object.range[1], state);
}
function renderJSXExpressionContainer(traverse, object, isLast, path, state) {
// Plus 1 to skip `{`.
utils.move(object.range[0] + 1, state);
utils.catchup(object.expression.range[0], state);
traverse(object.expression, path, state);
if (!isLast && object.expression.type !== Syntax.JSXEmptyExpression) {
// If we need to append a comma, make sure to do so after the expression.
utils.catchup(object.expression.range[1], state, trimLeft);
utils.catchup(object.range[1] - 1, state, commaAfterLastParen);
} else {
// Minus 1 to skip `}`.
utils.catchup(object.range[1] - 1, state, trimLeft);
}
utils.move(object.range[1], state);
return false;
}
function quoteAttrName(attr) {
// Quote invalid JS identifiers.
if (!/^[a-z_$][a-z\d_$]*$/i.test(attr)) {
return '"' + attr + '"';
}
return attr;
}
function trimLeft(value) {
return value.replace(/^[ ]+/, '');
}
exports.renderJSXExpressionContainer = renderJSXExpressionContainer;
exports.renderJSXLiteral = renderJSXLiteral;
exports.quoteAttrName = quoteAttrName;
exports.trimLeft = trimLeft;

View File

@@ -0,0 +1,126 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/*global exports:true*/
'use strict';
var Syntax = require('esprima-fb').Syntax;
var utils = require('../src/utils');
function shouldAddDisplayName(object) {
if (object &&
object.type === Syntax.CallExpression &&
object.callee.type === Syntax.MemberExpression &&
object.callee.object.type === Syntax.Identifier &&
object.callee.object.name === 'React' &&
object.callee.property.type === Syntax.Identifier &&
object.callee.property.name === 'createClass' &&
object.arguments.length === 1 &&
object.arguments[0].type === Syntax.ObjectExpression) {
// Verify that the displayName property isn't already set
var properties = object.arguments[0].properties;
var safe = properties.every(function(property) {
var value = property.key.type === Syntax.Identifier ?
property.key.name :
property.key.value;
return value !== 'displayName';
});
return safe;
}
return false;
}
/**
* If `expr` is an Identifier or MemberExpression node made of identifiers and
* dot accesses, return a list of the identifier parts. Other nodes return null.
*
* Examples:
*
* MyComponent -> ['MyComponent']
* namespace.MyComponent -> ['namespace', 'MyComponent']
* namespace['foo'] -> null
* namespace['foo'].bar -> ['bar']
*/
function flattenIdentifierOrMemberExpression(expr) {
if (expr.type === Syntax.Identifier) {
return [expr.name];
} else if (expr.type === Syntax.MemberExpression) {
if (!expr.computed && expr.property.type === Syntax.Identifier) {
var flattenedObject = flattenIdentifierOrMemberExpression(expr.object);
if (flattenedObject) {
flattenedObject.push(expr.property.name);
return flattenedObject;
} else {
return [expr.property.name];
}
}
}
return null;
}
/**
* Transforms the following:
*
* var MyComponent = React.createClass({
* render: ...
* });
*
* into:
*
* var MyComponent = React.createClass({
* displayName: 'MyComponent',
* render: ...
* });
*
* Also catches:
*
* MyComponent = React.createClass(...);
* namespace.MyComponent = React.createClass(...);
* exports.MyComponent = React.createClass(...);
* module.exports = {MyComponent: React.createClass(...)};
*/
function visitReactDisplayName(traverse, object, path, state) {
var left, right;
if (object.type === Syntax.AssignmentExpression) {
left = object.left;
right = object.right;
} else if (object.type === Syntax.Property) {
left = object.key;
right = object.value;
} else if (object.type === Syntax.VariableDeclarator) {
left = object.id;
right = object.init;
}
if (right && shouldAddDisplayName(right)) {
var displayNamePath = flattenIdentifierOrMemberExpression(left);
if (displayNamePath) {
if (displayNamePath.length > 1 && displayNamePath[0] === 'exports') {
displayNamePath.shift();
}
var displayName = displayNamePath.join('.');
utils.catchup(right.arguments[0].range[0] + 1, state);
utils.append('displayName: "' + displayName + '",', state);
}
}
}
visitReactDisplayName.test = function(object, path, state) {
return (
object.type === Syntax.AssignmentExpression ||
object.type === Syntax.Property ||
object.type === Syntax.VariableDeclarator
);
};
exports.visitorList = [
visitReactDisplayName
];

242
node_modules/jstransform/visitors/react-jsx-visitors.js generated vendored Normal file
View File

@@ -0,0 +1,242 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';
/*global exports:true*/
var Syntax = require('esprima-fb').Syntax;
var utils = require('../src/utils');
var jsxHelpers = require('./jsx-helpers');
var renderJSXExpressionContainer = jsxHelpers.renderJSXExpressionContainer;
var renderJSXLiteral = jsxHelpers.renderJSXLiteral;
var quoteAttrName = jsxHelpers.quoteAttrName;
var trimLeft = jsxHelpers.trimLeft;
/**
* Customized desugar processor for React JSX. Currently:
*
* <X> </X> => React.createElement(X, null)
* <X prop="1" /> => React.createElement(X, {prop: '1'}, null)
* <X prop="2"><Y /></X> => React.createElement(X, {prop:'2'},
* React.createElement(Y, null)
* )
* <div /> => React.createElement("div", null)
*/
/**
* Removes all non-whitespace/parenthesis characters
*/
var reNonWhiteParen = /([^\s\(\)])/g;
function stripNonWhiteParen(value) {
return value.replace(reNonWhiteParen, '');
}
var tagConvention = /^[a-z]|\-/;
function isTagName(name) {
return tagConvention.test(name);
}
function visitReactTag(traverse, object, path, state) {
var openingElement = object.openingElement;
var nameObject = openingElement.name;
var attributesObject = openingElement.attributes;
utils.catchup(openingElement.range[0], state, trimLeft);
if (nameObject.type === Syntax.JSXNamespacedName && nameObject.namespace) {
throw new Error('Namespace tags are not supported. ReactJSX is not XML.');
}
// We assume that the React runtime is already in scope
utils.append('React.createElement(', state);
if (nameObject.type === Syntax.JSXIdentifier && isTagName(nameObject.name)) {
utils.append('"' + nameObject.name + '"', state);
utils.move(nameObject.range[1], state);
} else {
// Use utils.catchup in this case so we can easily handle
// JSXMemberExpressions which look like Foo.Bar.Baz. This also handles
// JSXIdentifiers that aren't fallback tags.
utils.move(nameObject.range[0], state);
utils.catchup(nameObject.range[1], state);
}
utils.append(', ', state);
var hasAttributes = attributesObject.length;
var hasAtLeastOneSpreadProperty = attributesObject.some(function(attr) {
return attr.type === Syntax.JSXSpreadAttribute;
});
// if we don't have any attributes, pass in null
if (hasAtLeastOneSpreadProperty) {
utils.append('React.__spread({', state);
} else if (hasAttributes) {
utils.append('{', state);
} else {
utils.append('null', state);
}
// keep track of if the previous attribute was a spread attribute
var previousWasSpread = false;
// write attributes
attributesObject.forEach(function(attr, index) {
var isLast = index === attributesObject.length - 1;
if (attr.type === Syntax.JSXSpreadAttribute) {
// Close the previous object or initial object
if (!previousWasSpread) {
utils.append('}, ', state);
}
// Move to the expression start, ignoring everything except parenthesis
// and whitespace.
utils.catchup(attr.range[0], state, stripNonWhiteParen);
// Plus 1 to skip `{`.
utils.move(attr.range[0] + 1, state);
utils.catchup(attr.argument.range[0], state, stripNonWhiteParen);
traverse(attr.argument, path, state);
utils.catchup(attr.argument.range[1], state);
// Move to the end, ignoring parenthesis and the closing `}`
utils.catchup(attr.range[1] - 1, state, stripNonWhiteParen);
if (!isLast) {
utils.append(', ', state);
}
utils.move(attr.range[1], state);
previousWasSpread = true;
return;
}
// If the next attribute is a spread, we're effective last in this object
if (!isLast) {
isLast = attributesObject[index + 1].type === Syntax.JSXSpreadAttribute;
}
if (attr.name.namespace) {
throw new Error(
'Namespace attributes are not supported. ReactJSX is not XML.');
}
var name = attr.name.name;
utils.catchup(attr.range[0], state, trimLeft);
if (previousWasSpread) {
utils.append('{', state);
}
utils.append(quoteAttrName(name), state);
utils.append(': ', state);
if (!attr.value) {
state.g.buffer += 'true';
state.g.position = attr.name.range[1];
if (!isLast) {
utils.append(', ', state);
}
} else {
utils.move(attr.name.range[1], state);
// Use catchupNewlines to skip over the '=' in the attribute
utils.catchupNewlines(attr.value.range[0], state);
if (attr.value.type === Syntax.Literal) {
renderJSXLiteral(attr.value, isLast, state);
} else {
renderJSXExpressionContainer(traverse, attr.value, isLast, path, state);
}
}
utils.catchup(attr.range[1], state, trimLeft);
previousWasSpread = false;
});
if (!openingElement.selfClosing) {
utils.catchup(openingElement.range[1] - 1, state, trimLeft);
utils.move(openingElement.range[1], state);
}
if (hasAttributes && !previousWasSpread) {
utils.append('}', state);
}
if (hasAtLeastOneSpreadProperty) {
utils.append(')', state);
}
// filter out whitespace
var childrenToRender = object.children.filter(function(child) {
return !(child.type === Syntax.Literal
&& typeof child.value === 'string'
&& child.value.match(/^[ \t]*[\r\n][ \t\r\n]*$/));
});
if (childrenToRender.length > 0) {
var lastRenderableIndex;
childrenToRender.forEach(function(child, index) {
if (child.type !== Syntax.JSXExpressionContainer ||
child.expression.type !== Syntax.JSXEmptyExpression) {
lastRenderableIndex = index;
}
});
if (lastRenderableIndex !== undefined) {
utils.append(', ', state);
}
childrenToRender.forEach(function(child, index) {
utils.catchup(child.range[0], state, trimLeft);
var isLast = index >= lastRenderableIndex;
if (child.type === Syntax.Literal) {
renderJSXLiteral(child, isLast, state);
} else if (child.type === Syntax.JSXExpressionContainer) {
renderJSXExpressionContainer(traverse, child, isLast, path, state);
} else {
traverse(child, path, state);
if (!isLast) {
utils.append(', ', state);
}
}
utils.catchup(child.range[1], state, trimLeft);
});
}
if (openingElement.selfClosing) {
// everything up to />
utils.catchup(openingElement.range[1] - 2, state, trimLeft);
utils.move(openingElement.range[1], state);
} else {
// everything up to </ sdflksjfd>
utils.catchup(object.closingElement.range[0], state, trimLeft);
utils.move(object.closingElement.range[1], state);
}
utils.append(')', state);
return false;
}
visitReactTag.test = function(object, path, state) {
return object.type === Syntax.JSXElement;
};
exports.visitorList = [
visitReactTag
];

View File

@@ -0,0 +1,67 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
var KEYWORDS = [
'break', 'do', 'in', 'typeof', 'case', 'else', 'instanceof', 'var', 'catch',
'export', 'new', 'void', 'class', 'extends', 'return', 'while', 'const',
'finally', 'super', 'with', 'continue', 'for', 'switch', 'yield', 'debugger',
'function', 'this', 'default', 'if', 'throw', 'delete', 'import', 'try'
];
var FUTURE_RESERVED_WORDS = [
'enum', 'await', 'implements', 'package', 'protected', 'static', 'interface',
'private', 'public'
];
var LITERALS = [
'null',
'true',
'false'
];
// https://people.mozilla.org/~jorendorff/es6-draft.html#sec-reserved-words
var RESERVED_WORDS = [].concat(
KEYWORDS,
FUTURE_RESERVED_WORDS,
LITERALS
);
var reservedWordsMap = Object.create(null);
RESERVED_WORDS.forEach(function(k) {
reservedWordsMap[k] = true;
});
/**
* This list should not grow as new reserved words are introdued. This list is
* of words that need to be quoted because ES3-ish browsers do not allow their
* use as identifier names.
*/
var ES3_FUTURE_RESERVED_WORDS = [
'enum', 'implements', 'package', 'protected', 'static', 'interface',
'private', 'public'
];
var ES3_RESERVED_WORDS = [].concat(
KEYWORDS,
ES3_FUTURE_RESERVED_WORDS,
LITERALS
);
var es3ReservedWordsMap = Object.create(null);
ES3_RESERVED_WORDS.forEach(function(k) {
es3ReservedWordsMap[k] = true;
});
exports.isReservedWord = function(word) {
return !!reservedWordsMap[word];
};
exports.isES3ReservedWord = function(word) {
return !!es3ReservedWordsMap[word];
};

View File

@@ -0,0 +1,67 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/*global exports:true*/
var Syntax = require('esprima-fb').Syntax;
var utils = require('../src/utils');
var reserverdWordsHelper = require('./reserved-words-helper');
/**
* Code adapted from https://github.com/spicyj/es3ify
* The MIT License (MIT)
* Copyright (c) 2014 Ben Alpert
*/
function visitProperty(traverse, node, path, state) {
utils.catchup(node.key.range[0], state);
utils.append('"', state);
utils.catchup(node.key.range[1], state);
utils.append('"', state);
utils.catchup(node.value.range[0], state);
traverse(node.value, path, state);
return false;
}
visitProperty.test = function(node) {
return node.type === Syntax.Property &&
node.key.type === Syntax.Identifier &&
!node.method &&
!node.shorthand &&
!node.computed &&
reserverdWordsHelper.isES3ReservedWord(node.key.name);
};
function visitMemberExpression(traverse, node, path, state) {
traverse(node.object, path, state);
// Member expression range does not include parenthesis, so simply specifying
// node.object.range[1] as the start position doesn't work. Neither does
// node.property.range[0] - 1 as that won't catch expressions that span
// newlines (period on previous line). So instead we'll catchup and remove
// any periods.
utils.catchup(node.property.range[0], state, function(s) {
return s.replace('.', '');
});
utils.append('[', state);
utils.catchupWhiteSpace(node.property.range[0], state);
utils.append('"', state);
utils.catchup(node.property.range[1], state);
utils.append('"]', state);
return false;
}
visitMemberExpression.test = function(node) {
return node.type === Syntax.MemberExpression &&
node.property.type === Syntax.Identifier &&
reserverdWordsHelper.isES3ReservedWord(node.property.name);
};
exports.visitorList = [
visitProperty,
visitMemberExpression
];

View File

@@ -0,0 +1,74 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
var Syntax = require('esprima-fb').Syntax;
var utils = require('../src/utils');
/**
* Strips trailing commas from array and object expressions. Transforms:
*
* var arr = [
* foo,
* bar,
* ];
*
* var obj = {
* foo: 1,
* bar: 2,
* };
*
* into:
*
* var arr = [
* foo,
* bar
* ];
*
* var obj = {
* foo: 1,
* bar: 2
* };
*
*/
function visitArrayOrObjectExpression(traverse, node, path, state) {
var items = node.elements || node.properties;
var lastItem = items[items.length - 1];
// Transform items if needed.
path.unshift(node);
traverse(items, path, state);
path.shift();
// Catch up to the end of the last item.
utils.catchup(lastItem.range[1], state);
// Strip any non-whitespace between the last item and the end.
utils.catchup(node.range[1] - 1, state, function(value) {
return value.replace(/,/g, '');
});
return false;
}
visitArrayOrObjectExpression.test = function(node, path, state) {
return (node.type === Syntax.ArrayExpression ||
node.type === Syntax.ObjectExpression) &&
(node.elements || node.properties).length > 0 &&
// We don't want to run the transform on arrays with trailing holes, since
// it would change semantics.
!hasTrailingHole(node);
};
function hasTrailingHole(node) {
return node.elements && node.elements.length > 0 &&
node.elements[node.elements.length - 1] === null;
}
exports.visitorList = [
visitArrayOrObjectExpression
];

190
node_modules/jstransform/visitors/type-syntax.js generated vendored Normal file
View File

@@ -0,0 +1,190 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
var esprima = require('esprima-fb');
var utils = require('../src/utils');
var Syntax = esprima.Syntax;
function _isFunctionNode(node) {
return node.type === Syntax.FunctionDeclaration
|| node.type === Syntax.FunctionExpression
|| node.type === Syntax.ArrowFunctionExpression;
}
function visitClassProperty(traverse, node, path, state) {
utils.catchup(node.range[0], state);
utils.catchupWhiteOut(node.range[1], state);
return false;
}
visitClassProperty.test = function(node, path, state) {
return node.type === Syntax.ClassProperty;
};
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 visitTypeCast(traverse, node, path, state) {
path.unshift(node);
traverse(node.expression, path, state);
path.shift();
utils.catchup(node.typeAnnotation.range[0], state);
utils.catchupWhiteOut(node.typeAnnotation.range[1], state);
return false;
}
visitTypeCast.test = function(node, path, state) {
return node.type === Syntax.TypeCastExpression;
};
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 visitDeclare(traverse, node, path, state) {
utils.catchupWhiteOut(node.range[1], state);
return false;
}
visitDeclare.test = function(node, path, state) {
switch (node.type) {
case Syntax.DeclareVariable:
case Syntax.DeclareFunction:
case Syntax.DeclareClass:
case Syntax.DeclareModule:
return true;
}
return false;
};
function visitFunctionParametricAnnotation(traverse, node, path, state) {
utils.catchup(node.range[0], state);
utils.catchupWhiteOut(node.range[1], state);
return false;
}
visitFunctionParametricAnnotation.test = function(node, path, state) {
return node.type === Syntax.TypeParameterDeclaration
&& path[0]
&& _isFunctionNode(path[0])
&& node === path[0].typeParameters;
};
function visitFunctionReturnAnnotation(traverse, node, path, state) {
utils.catchup(node.range[0], state);
utils.catchupWhiteOut(node.range[1], state);
return false;
}
visitFunctionReturnAnnotation.test = function(node, path, state) {
return path[0] && _isFunctionNode(path[0]) && node === path[0].returnType;
};
function visitOptionalFunctionParameterAnnotation(traverse, node, path, state) {
utils.catchup(node.range[0] + node.name.length, state);
utils.catchupWhiteOut(node.range[1], state);
return false;
}
visitOptionalFunctionParameterAnnotation.test = function(node, path, state) {
return node.type === Syntax.Identifier
&& node.optional
&& path[0]
&& _isFunctionNode(path[0]);
};
function visitTypeAnnotatedIdentifier(traverse, node, path, state) {
utils.catchup(node.typeAnnotation.range[0], state);
utils.catchupWhiteOut(node.typeAnnotation.range[1], state);
return false;
}
visitTypeAnnotatedIdentifier.test = function(node, path, state) {
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.kind === "set" || node.kind === "get"))
|| (node.type === "MethodDefinition");
};
function visitImportType(traverse, node, path, state) {
utils.catchupWhiteOut(node.range[1], state);
return false;
}
visitImportType.test = function(node, path, state) {
return node.type === 'ImportDeclaration'
&& (node.importKind === 'type' || node.importKind === 'typeof');
};
function visitExportType(traverse, node, path, state) {
utils.catchupWhiteOut(node.range[1], state);
}
visitExportType.test = function(node, path, state) {
return node.type === 'ExportDeclaration'
&& node.declaration.type === 'TypeAlias';
};
exports.visitorList = [
visitClassProperty,
visitDeclare,
visitExportType,
visitImportType,
visitInterfaceDeclaration,
visitFunctionParametricAnnotation,
visitFunctionReturnAnnotation,
visitMethod,
visitOptionalFunctionParameterAnnotation,
visitTypeAlias,
visitTypeCast,
visitTypeAnnotatedIdentifier,
visitTypeAnnotatedObjectOrArrayPattern
];

View File

@@ -0,0 +1,141 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
var Syntax = require('esprima-fb').Syntax;
var utils = require('../src/utils');
/**
* Replaces `undefined` with `(void 0)` when it appears as an rvalue and is
* undeclared in the lexical scope.
*
* Example:
*
* (function() {
* foo.undefined = bar;
* ({undefined: foo});
*
* var bar = undefined;
* bar = undefined;
* foo.bar = undefined;
* undefined.foo = bar;
* foo[undefined] = bar;
* ({foo: undefined});
* })(undefined);
*
* (function(undefined) { // declared here
* return undefined;
* })(1);
*
* (function() {
* var undefined; // declared here
* undefined = 1; // assignment to declared variable
* return undefined;
* })();
*
* (function(undefined) { // declared here
* return (function() {
* return undefined; // respects lexical scope
* })();
* })();
*
* Becomes:
*
* (function() {
* foo.undefined = bar;
* ({undefined: foo});
*
* var bar = (void 0);
* bar = (void 0);
* foo.bar = (void 0);
* (void 0).foo = bar;
* foo[(void 0)] = bar;
* ({foo: (void 0)});
* })((void 0));
*
* (function(undefined) { // declared here
* return undefined;
* })(1);
*
* (function() {
* var undefined; // declared here
* undefined = 1; // assignment to declared variable
* return undefined;
* })();
*
* (function(undefined) { // declared here
* return (function() {
* return undefined; // respects lexical scope
* })();
* })();
*
*
*
* NOTE: Any assignment to `undefined` where `undefined` is not declared in the
* lexical scope will result in an exception.
*/
function visitIdentifierUndefined(traverse, node, path, state) {
utils.catchup(node.range[1], state, function(value) {
return '(void 0)';
});
}
visitIdentifierUndefined.test = function(node, path, state) {
if (
node.type === Syntax.Identifier
&& node.name === 'undefined'
&& !utils.identWithinLexicalScope('undefined', state)
) {
if (path[0]) {
switch (path[0].type) {
case Syntax.FunctionDeclaration:
case Syntax.FunctionExpression:
case Syntax.ArrowFunctionExpression:
// skips: function params
if (node !== path[0].body) {
return false;
}
break;
case Syntax.AssignmentExpression:
// throws for: `undefined = foo` (where `undefined` is not declared)
if (node === path[0].left) {
throw new Error(
'Illegal assignment to `undefined`. '
+ 'This breaks assumptions of the transform.'
);
}
break;
case Syntax.MemberExpression:
// skips: `foo.undefined` but not `foo[undefined]`
if (node === path[0].property && !path[0].computed) {
return false;
}
break;
case Syntax.VariableDeclarator:
// skips: `var undefined`
if (node !== path[0].init) {
return false;
}
break;
case Syntax.Property:
// skips: `undefined: foo`
if (node === path[0].key) {
return false;
}
break;
}
}
return true;
}
return false;
};
exports.visitorList = [
visitIdentifierUndefined
];