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

28
node_modules/jstransform/.jshintrc generated vendored Normal file
View File

@@ -0,0 +1,28 @@
{
"boss": true,
"curly": true,
"devel": false,
"eqnull": false,
"esnext": true,
"expr": true,
"funcscope": true,
"globals": {
"afterEach": true,
"beforeEach": true,
"describe": true,
"expect": true,
"it": true,
"jest": true
},
"globalstrict": false,
"laxbreak": true,
"loopfunc": false,
"newcap": true,
"node": true,
"noempty": true,
"nonstandard": false,
"onecase": false,
"trailing": true,
"undef": true,
"unused": "vars"
}

4
node_modules/jstransform/.npmignore generated vendored Normal file
View File

@@ -0,0 +1,4 @@
.DS_STORE
node_modules
*.swp
*~

8
node_modules/jstransform/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,8 @@
language: node_js
node_js:
- "0.10"
# Because of package.json dependency versions that start with '^', it's not
# possible to `npm install` using the version of npm packaged with node 0.8
# As a result, travis *always* fails for 0.8 now -- and thus it's not possible
# to test jstransform on 0.8 with Travis CI :(
# - "0.8"

31
node_modules/jstransform/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,31 @@
BSD License
For jstransform software
Copyright (c) 2013-present, Facebook, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name Facebook nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

33
node_modules/jstransform/PATENTS generated vendored Normal file
View File

@@ -0,0 +1,33 @@
Additional Grant of Patent Rights Version 2
"Software" means the jstransform software distributed by Facebook, Inc.
Facebook, Inc. ("Facebook") hereby grants to each recipient of the Software
("you") a perpetual, worldwide, royalty-free, non-exclusive, irrevocable
(subject to the termination provision below) license under any Necessary
Claims, to make, have made, use, sell, offer to sell, import, and otherwise
transfer the Software. For avoidance of doubt, no license is granted under
Facebook's rights in any patent claims that are infringed by (i) modifications
to the Software made by you or any third party or (ii) the Software in
combination with any software or other technology.
The license granted hereunder will terminate, automatically and without notice,
if you (or any of your subsidiaries, corporate affiliates or agents) initiate
directly or indirectly, or take a direct financial interest in, any Patent
Assertion: (i) against Facebook or any of its subsidiaries or corporate
affiliates, (ii) against any party if such Patent Assertion arises in whole or
in part from any software, technology, product or service of Facebook or any of
its subsidiaries or corporate affiliates, or (iii) against any party relating
to the Software. Notwithstanding the foregoing, if Facebook or any of its
subsidiaries or corporate affiliates files a lawsuit alleging patent
infringement against you in the first instance, and you respond by filing a
patent infringement counterclaim in that lawsuit against that party that is
unrelated to the Software, the license granted hereunder will not terminate
under section (i) of this paragraph due to such counterclaim.
A "Necessary Claim" is a claim of a patent owned by Facebook that is
necessarily infringed by the Software standing alone.
A "Patent Assertion" is any lawsuit or other action alleging direct, indirect,
or contributory infringement or inducement to infringe any patent, including a
cross-claim or counterclaim.

265
node_modules/jstransform/README.md generated vendored Normal file
View File

@@ -0,0 +1,265 @@
# JSTransform [![Build Status](https://travis-ci.org/facebook/jstransform.svg?branch=master)](https://travis-ci.org/facebook/jstransform)
A simple utility for pluggable JS syntax transforms using the esprima parser.
* Makes it simple to write and plug-in syntax transformations
* Makes it simple to coalesce multiple syntax transformations in a single pass of the AST
* Gives complete control over the formatting of the output on a per-transformation basis
* Supports source map generation
* Comes pre-bundled with a small set of (optional) ES6 -> ES5 transforms
- - -
**Note:**
If you're looking for a library for writing new greenfield JS transformations, consider looking at [Babel](https://github.com/babel/babel) or [Recast](https://github.com/benjamn/recast) instead of jstransform. We are still supporting jstransform (and intend to for a little while), but longer term we would like to direct efforts toward other open source projects that do a far better job of supporting a multi-pass JS transformation pipeline. This is important when attempting to apply many transformations to a source file. jstransform does a single pass resulting in performance benefits, but the tradeoff is that many transformations are much harder to write.
- - -
## Usage
### Advanced API
```js
var jstranform = require('jstransform')
```
This is the API that jstransform has always supported. It gives very fine control over the transforms you use and what order they are run in. It also allows the use of custom transforms.
#### `jstransform.transform(visitors, code, options={})`
**`visitors`**
Array of visitors. See [the React JSX visitors](https://github.com/facebook/jstransform/blob/master/visitors/react-jsx-visitors.js) as an example of what a visitor looks like.
**`code`**
String of code to be transformed.
**`options`**
Object with options that will be passed through to esprima and transforms.
#### `jstransform.Syntax`
This is the `Syntax` object re-exported from `esprima`. This is available because visitors will need access to this in order to effectively write transforms. By re-exporting we avoid the problem of conflicting versions of esprima being used.
### Simple API
```js
var simple = require('jstransform/simple')
```
The simple API was added to mirror the new command line interface. It works similarly to how `react-tools` worked - there is no need to know exactly which transforms to run. Instead transforms are selected automatically based on the options.
#### `simple.transform(code, options={})`
**`code`**
String of code to be transformed.
**`options`**
Object with options. Available options are:
option | values | default
-------|--------|--------
`react` | `true`: enable React transforms (JSX, displayName) | `false`
`es6` | `true`: enable ES6 transforms | `false`
`es7` | `true`: enable ES7 transforms | `false`
`harmony` | `true`: shortcut to enable ES6 & ES7 transforms | `false`
`utility` | `true`: enable utility transforms (trailing commas in objects, arrays) | `false`
`target` | `es3`: generate ES3 compatible code<br>`es5`: generate ES5 compatible code | `es5`
`stripTypes` | `true`: strips out Flow type annotations | `false`
`sourceMap` | `true`: generate and return a Source Map | `false`
`sourceMapInline` | `true`: append inline source map at the end of the transformed source | `false`
`sourceFilename` | the output filename for the source map | `"source.js"`
`es6module` | `true`: parses the file as an ES6 module | `false`
`nonStrictEs6module` | `true`: parses the file as an ES6 module, except disables implicit strict-mode (i.e. CommonJS modules et al are allowed) | `false`
*Returns:* An Object with the following:
**`code`**: the transformed code
**`sourceMap`**: the source map object or `null`
#### `simple.transformFile(file, options={}, callback)`
**`file`**
String of path to a file to transform. Will be passed directly to `fs.readFile`.
**`options`**
See `transform` API.
**`callback`**
Function to call with the result, where `result` is return value of `transform`.
```js
callback(err, result)
```
#### `simple.transformFileSync(file, options={})`
The same as `transformFile` but the file is read synchronously.
### CLI
JSTransform now ships with a CLI. It was taken from the `react-tools` CLI so should be very familiar.
```sh
% jstransform --help
Usage: jstransform [options] <source directory> <output directory> [<module ID> [<module ID> ...]]
Options:
-h, --help output usage information
-V, --version output the version number
-c, --config [file] JSON configuration file (no file or - means STDIN)
-w, --watch Continually rebuild
-x, --extension <js | coffee | ...> File extension to assume when resolving module identifiers
--relativize Rewrite all module identifiers to be relative
--follow-requires Scan modules for required dependencies
--use-provides-module Respect @providesModules pragma in files
--cache-dir <directory> Alternate directory to use for disk cache
--no-cache-dir Disable the disk cache
--source-charset <utf8 | win1252 | ...> Charset of source (default: utf8)
--output-charset <utf8 | win1252 | ...> Charset of output (default: utf8)
--react Turns on the React JSX and React displayName transforms
--es6 Turns on available ES6 transforms
--es7 Turns on available ES7 transforms
--harmony Shorthand to enable all ES6 and ES7 transforms
--utility Turns on available utility transforms
--target [version] Specify your target version of ECMAScript. Valid values are "es3" and "es5". The default is "es5". "es3" will avoid uses of defineProperty and will quote reserved words. WARNING: "es5" is not properly supported, even with the use of es5shim, es5sham. If you need to support IE8, use "es3".
--strip-types Strips out type annotations.
--es6module Parses the file as a valid ES6 module. (Note that this means implicit strict mode)
--non-strict-es6module Parses the file as an ES6 module, except disables implicit strict-mode. (This is useful if you're porting non-ES6 modules to ES6, but haven't yet verified that they are strict-mode safe yet)
--source-map-inline Embed inline sourcemap in transformed source
--source-filename Filename to use when generating the inline sourcemap. Will default to filename when processing files
```
## Examples
### Advanced API
#### Using a pre-bundled or existing transform:
```js
/**
* Reads a source file that may (or may not) contain ES6 classes, transforms it
* to ES5 compatible code using the pre-bundled ES6 class visitors, and prints
* out the result.
*/
var es6ClassVisitors = require('jstransform/visitors/es6-class-visitors').visitorList;
var fs = require('fs');
var jstransform = require('jstransform');
var originalFileContents = fs.readFileSync('path/to/original/file.js', 'utf-8');
var transformedFileData = jstransform.transform(
es6ClassVisitors,
originalFileContents
);
console.log(transformedFileData.code);
```
#### Using multiple pre-bundled or existing transforms at once:
```js
/**
* Reads a source file that may (or may not) contain ES6 classes *or* arrow
* functions, transforms them to ES5 compatible code using the pre-bundled ES6
* visitors, and prints out the result.
*/
var es6ArrowFuncVisitors = require('jstransform/visitors/es6-arrow-function-visitors').visitorList;
var es6ClassVisitors = require('jstransform/visitors/es6-class-visitors').visitorList;
var jstransform = require('jstransform');
// Normally you'd read this from the filesystem, but I'll just use a string here
// to simplify the example.
var originalFileContents = "var a = (param1) => param1; class FooClass {}";
var transformedFileData = jstransform.transform(
es6ClassVisitors.concat(es6ArrowFuncVisitors),
originalFileContents
);
// var a = function(param1) {return param1;}; function FooClass(){"use strict";}
console.log(transformedFileData.code);
```
#### Writing a simple custom transform:
```js
/**
* Creates a custom transformation visitor that prefixes all calls to the
* `eval()` function with a call to `alert()` saying how much of a clown you are
* for using eval.
*/
var jstransform = require('jstransform');
var utils = require('jstransform/src/utils');
var Syntax = jstransform.Syntax;
function visitEvalCallExpressions(traverse, node, path, state) {
// Appends an alert() call to the output buffer *before* the visited node
// (in this case the eval call) is appended to the output buffer
utils.append('alert("...eval?...really?...");', state);
// Now we copy the eval expression to the output buffer from the original
// source
utils.catchup(node.range[1], state);
}
visitEvalCallExpressions.test = function(node, path, state) {
return node.type === Syntax.CallExpression
&& node.callee.type === Syntax.Identifier
&& node.callee.name === 'eval';
};
// Normally you'd read this from the filesystem, but I'll just use a string here
// to simplify the example.
var originalFileContents = "eval('foo');";
var transformedFileData = jstransform.transform(
[visitEvalCallExpressions], // Multiple visitors may be applied at once, so an
// array is always expected for the first argument
originalFileContents
);
// alert("...eval?...really?...");eval('foo');
console.log(transformedFileData.code);
```
### Simple API
#### Reading a file and applying tranforms
```js
var simple = require('jstransform/simple');
var fs = require('fs');
var originalCode = fs.readFileSync('path/to/file.js');
// Apply all available ES6 transforms
var transformed = simple.transform(originalCode, {es6: true});
console.log(transformed.code);
// Apply ES6 and ES7, generating ES3 compatible code (for IE8)
transformed = simple.transform(originalCode, {harmony: true, target: 'es3'});
console.log(transformed.code);
```
## Migration Guide
### Simple API
If you are coming from `react-tools` and using Node, the APIs are very similar. There are a couple important differences.
1. JSX will not be tranformed by default! You must specify `react: true` in the options.
2. The return value of `transform` is not the same. `react-tools.transform` only returned the resulting code. `simple.transform` always returns and object with a `code` property. However, if you were using `react-tools.transformWithDetails`, `simple.transform` is essentially the same.
### CLI
These are virtually identical but again, there are some important difference.
1. JSX will not be transformed by default! You must specify `--react`.
2. We are using a different executable - `jstransform` - (`jsx` doesn't make sense here).
3. There are a numbr of new options available. Namely `--utility`, `--es6` and `--es7` (`--harmony` is still available and will enable both).

12
node_modules/jstransform/bin/jstransform generated vendored Executable file
View File

@@ -0,0 +1,12 @@
#!/usr/bin/env node
/**
* 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.
*/
require('../src/cli');

10
node_modules/jstransform/jestEnvironment.js generated vendored Normal file
View File

@@ -0,0 +1,10 @@
/**
* 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.
*/
require.requireActual('./polyfill/Object.es6.js');

22
node_modules/jstransform/jestPreprocessor.js generated vendored Normal file
View File

@@ -0,0 +1,22 @@
/**
* 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 jstransform = require('./src/jstransform');
var arrowFuncVisitors = require('./visitors/es6-arrow-function-visitors');
var restParamVisitors = require('./visitors/es6-rest-param-visitors');
var es7SpreadPropertyVisitors = require('./visitors/es7-spread-property-visitors');
exports.process = function(sourceText, sourcePath) {
return jstransform.transform(
arrowFuncVisitors.visitorList
.concat(restParamVisitors.visitorList)
.concat(es7SpreadPropertyVisitors.visitorList),
sourceText
).code;
};

View File

@@ -0,0 +1,26 @@
'use strict';
function ToObject(val) {
if (val == null) {
throw new TypeError('Object.assign cannot be called with null or undefined');
}
return Object(val);
}
module.exports = Object.assign || function (target, source) {
var from;
var keys;
var to = ToObject(target);
for (var s = 1; s < arguments.length; s++) {
from = arguments[s];
keys = Object.keys(Object(from));
for (var i = 0; i < keys.length; i++) {
to[keys[i]] = from[keys[i]];
}
}
return to;
};

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,38 @@
{
"name": "object-assign",
"version": "2.1.1",
"description": "ES6 Object.assign() ponyfill",
"license": "MIT",
"repository": "sindresorhus/object-assign",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
"url": "http://sindresorhus.com"
},
"engines": {
"node": ">=0.10.0"
},
"scripts": {
"test": "mocha"
},
"files": [
"index.js"
],
"keywords": [
"object",
"assign",
"extend",
"properties",
"es6",
"ecmascript",
"harmony",
"ponyfill",
"prollyfill",
"polyfill",
"shim",
"browser"
],
"devDependencies": {
"mocha": "*"
}
}

View File

@@ -0,0 +1,51 @@
# object-assign [![Build Status](https://travis-ci.org/sindresorhus/object-assign.svg?branch=master)](https://travis-ci.org/sindresorhus/object-assign)
> ES6 [`Object.assign()`](http://www.2ality.com/2014/01/object-assign.html) ponyfill
> Ponyfill: A polyfill that doesn't overwrite the native method
## Install
```sh
$ npm install --save object-assign
```
## Usage
```js
var objectAssign = require('object-assign');
objectAssign({foo: 0}, {bar: 1});
//=> {foo: 0, bar: 1}
// multiple sources
objectAssign({foo: 0}, {bar: 1}, {baz: 2});
//=> {foo: 0, bar: 1, baz: 2}
// overwrites equal keys
objectAssign({foo: 0}, {foo: 1}, {foo: 2});
//=> {foo: 2}
// ignores null and undefined sources
objectAssign({foo: 0}, null, {bar: 1}, undefined);
//=> {foo: 0, bar: 1}
```
## API
### objectAssign(target, source, [source, ...])
Assigns enumerable own properties of `source` objects to the `target` object and returns the `target` object. Additional `source` objects will overwrite previous ones.
## Resources
- [ES6 spec - Object.assign](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.assign)
## License
MIT © [Sindre Sorhus](http://sindresorhus.com)

50
node_modules/jstransform/package.json generated vendored Normal file
View File

@@ -0,0 +1,50 @@
{
"name": "jstransform",
"version": "11.0.3",
"description": "A simple AST visitor-based JS transformer",
"contributors": [
{
"name": "Jeff Morrison",
"email": "jeffmo@fb.com"
}
],
"bin": "bin/jstransform",
"main": "src/jstransform",
"repository": {
"type": "git",
"url": "git@github.com:facebook/jstransform.git"
},
"keywords": [
"transformer",
"compiler",
"syntax",
"visitor"
],
"dependencies": {
"base62": "^1.1.0",
"commoner": "^0.10.1",
"esprima-fb": "^15001.1.0-dev-harmony-fb",
"object-assign": "^2.0.0",
"source-map": "^0.4.2"
},
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.8.8"
},
"devDependencies": {
"jest-cli": "^0.4.0",
"jshint": "^2.6.3"
},
"jest": {
"scriptPreprocessor": "<rootDir>/jestPreprocessor.js",
"setupEnvScriptFile": "<rootDir>/jestEnvironment.js",
"testPathIgnorePatterns": [
"/node_modules/",
"/__tests__/[^/]*/.+"
]
},
"scripts": {
"prepublish": "jest && jshint --config=.jshintrc --exclude=node_modules .",
"test": "jest && jshint --config=.jshintrc --exclude=node_modules ."
}
}

44
node_modules/jstransform/polyfill/Object.es6.js generated vendored Normal file
View File

@@ -0,0 +1,44 @@
/**
* 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.
* @provides Object.es6
* @polyfill
*/
// https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.assign
if (!Object.assign) {
Object.assign = function(target, sources) {
if (target === null || target === undefined) {
throw new TypeError('Object.assign target cannot be null or undefined');
}
var to = Object(target);
var hasOwnProperty = Object.prototype.hasOwnProperty;
for (var nextIndex = 1; nextIndex < arguments.length; nextIndex++) {
var nextSource = arguments[nextIndex];
if (nextSource === null || nextSource === undefined) {
continue;
}
var from = Object(nextSource);
// We don't currently support accessors nor proxies. Therefore this
// copy cannot throw. If we ever supported this then we must handle
// exceptions and side-effects.
for (var key in from) {
if (hasOwnProperty.call(from, key)) {
to[key] = from[key];
}
}
}
return to;
};
}

13
node_modules/jstransform/simple.js generated vendored Normal file
View File

@@ -0,0 +1,13 @@
/**
* 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.
*/
// Just re-export the simple API. This will eventually go away.
// require('jstransform') will give the simple API and the current API will be
// moved to something else (perhaps 'jstranform/advanced')
module.exports = require('./src/simple');

View File

@@ -0,0 +1,473 @@
/**
* 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
*/
require('mock-modules').autoMockOff();
describe('jstransform', function() {
var transformFn;
var Syntax = require('esprima-fb').Syntax;
beforeEach(function() {
require('mock-modules').dumpCache();
transformFn = require('../jstransform').transform;
});
function _runVisitor(source, nodeCount, visitor) {
var actualVisitationCount = 0;
function shimVisitor(traverse, node, path, state) {
actualVisitationCount++;
return visitor(traverse, node, path, state);
}
shimVisitor.test = visitor.test;
transformFn([shimVisitor], source);
expect(actualVisitationCount).toBe(nodeCount);
}
function testScopeBoundary(source, localIdents, nodeCount, visitorTest) {
function visitor(traverse, node, path, state) {
var actualLocalIdents = Object.keys(state.localScope.identifiers);
expect(actualLocalIdents.sort()).toEqual(localIdents.sort());
}
visitor.test = visitorTest;
_runVisitor(source, nodeCount, visitor);
}
function testParentScope(source, parentIdents, nodeCount, visitorTest) {
function visitor(traverse, node, path, state) {
parentIdents = parentIdents && parentIdents.sort();
var parentScope = state.localScope.parentScope;
var actualParentIdents =
parentScope && Object.keys(parentScope.identifiers).sort();
expect(actualParentIdents).toEqual(parentIdents);
}
visitor.test = visitorTest;
_runVisitor(source, nodeCount, visitor);
}
describe('closure scope boundaries', function() {
it('creates a scope boundary around Program scope', function() {
var source =
'var foo;' +
'var bar, baz;' +
'function blah() {}';
var idents = ['foo', 'bar', 'baz', 'blah'];
testScopeBoundary(source, idents, 3, function(node, path) {
return path[0] && path[0].type === Syntax.Program;
});
});
it('creates a scope boundary around FunctionDeclarations', function() {
var source =
'var foo;' +
'function blah() {' +
' var bar;' +
' function nested() {' +
' var baz;' +
' }' +
'}';
var programIdents = ['foo', 'blah'];
var blahIdents = ['arguments', 'bar', 'nested'];
var nestedIdents = ['arguments', 'baz'];
testScopeBoundary(source, programIdents, 2, function(node, path) {
return path[0] && path[0].type === Syntax.Program;
});
testScopeBoundary(source, blahIdents, 2, function(node, path) {
// All direct children of blah()
return path[0] && path[0].type === Syntax.BlockStatement &&
path[1] && path[1].type === Syntax.FunctionDeclaration &&
path[1].id.name === 'blah';
});
testScopeBoundary(source, nestedIdents, 1, function(node, path) {
// All direct children of nested()
return path[0] && path[0].type === Syntax.BlockStatement &&
path[1] && path[1].type === Syntax.FunctionDeclaration &&
path[1].id.name === 'nested';
});
});
it('creates a scope boundary around MethodDefinitions', function() {
var source =
'var foo;' +
'class ClassA {' +
' blah() {' +
' var bar;' +
' }' +
' another() {' +
' var baz;' +
' }' +
'}';
var programIdents = ['foo', 'ClassA'];
var blahIdents = ['arguments', 'bar'];
var anotherIdents = ['arguments', 'baz'];
testScopeBoundary(source, programIdents, 2, function(node, path) {
return path[0] && path[0].type === Syntax.Program;
});
testScopeBoundary(source, blahIdents, 1, function(node, path) {
// All direct children of blah()
return path[0] && path[0].type === Syntax.BlockStatement &&
path[1] && path[1].type === Syntax.FunctionExpression &&
path[2] && path[2].type === Syntax.MethodDefinition &&
path[2].key.name === 'blah';
});
testScopeBoundary(source, anotherIdents, 1, function(node, path) {
// All direct children of another()
return path[0] && path[0].type === Syntax.BlockStatement &&
path[1] && path[1].type === Syntax.FunctionExpression &&
path[2] && path[2].type === Syntax.MethodDefinition &&
path[2].key.name === 'another';
});
});
it('creates a scope boundary around concise ArrowFunc exprs', function() {
var source =
'var foo;' +
'var bar = baz => baz;';
var programIdents = ['foo', 'bar'];
var barIdents = ['arguments', 'baz'];
testScopeBoundary(source, programIdents, 2, function(node, path) {
return path[0] && path[0].type === Syntax.Program;
});
testScopeBoundary(source, barIdents, 1, function(node, path) {
return path[0] && path[0].type === Syntax.ArrowFunctionExpression
&& path[0].body === node;
});
});
it('uses VariableDeclarations to determine scope boundary', function() {
var source =
'var foo = 1;' +
'function bar() {' +
' foo++;' +
' function baz() {' +
' var foo = 2;' +
' }' +
'}';
var programIdents = ['foo', 'bar'];
var barIdents = ['arguments', 'baz'];
var bazIdents = ['arguments', 'foo'];
testScopeBoundary(source, programIdents, 2, function(node, path) {
return path[0] && path[0].type === Syntax.Program;
});
testScopeBoundary(source, barIdents, 2, function(node, path) {
// All direct children of blah()
return path[0] && path[0].type === Syntax.BlockStatement &&
path[1] && path[1].type === Syntax.FunctionDeclaration &&
path[1].id.name === 'bar';
});
testScopeBoundary(source, bazIdents, 1, function(node, path) {
// All direct children of baz()
return path[0] && path[0].type === Syntax.BlockStatement &&
path[1] && path[1].type === Syntax.FunctionDeclaration &&
path[1].id.name === 'baz';
});
});
it('includes function args in functions scope boundary', function() {
var source =
'var foo;' +
'function blah(bar) {' +
' var baz;' +
'}' +
'var blah2 = bar2 => {var baz;};' +
'var blah3 = bar3 => bar3;';
var programIdents = ['foo', 'blah', 'blah2', 'blah3'];
var blahIdents = ['arguments', 'bar', 'baz'];
var blah2Idents = ['arguments', 'bar2', 'baz'];
var blah3Idents = ['arguments', 'bar3'];
testScopeBoundary(source, programIdents, 4, function(node, path) {
return path[0] && path[0].type === Syntax.Program;
});
testScopeBoundary(source, blahIdents, 1, function(node, path) {
// All direct children of blah()
return path[0] && path[0].type === Syntax.BlockStatement &&
path[1] && path[1].type === Syntax.FunctionDeclaration &&
path[1].id.name === 'blah';
});
testScopeBoundary(source, blah2Idents, 1, function(node, path) {
// All direct children of blah2()
return path[0] && path[0].type === Syntax.BlockStatement &&
path[1] && path[1].type === Syntax.ArrowFunctionExpression &&
path[2].id.name === 'blah2';
});
testScopeBoundary(source, blah3Idents, 1, function(node, path) {
// All direct children of blah3()
return path[0] && path[0].type === Syntax.ArrowFunctionExpression &&
path[0].body === node &&
path[1].id.name === 'blah3';
});
});
it('includes rest param args in function scope boundaries', function() {
var source =
'var foo;' +
'function blah(...bar) {' +
' var baz;' +
'}' +
'var blah2 = (...bar2) => {var baz;};' +
'var blah3 = (...bar3) => bar3;';
var programIdents = ['foo', 'blah', 'blah2', 'blah3'];
var blahIdents = ['arguments', 'bar', 'baz'];
var blah2Idents = ['arguments', 'bar2', 'baz'];
var blah3Idents = ['arguments', 'bar3'];
testScopeBoundary(source, programIdents, 4, function(node, path) {
return path[0] && path[0].type === Syntax.Program;
});
testScopeBoundary(source, blahIdents, 1, function(node, path) {
// All direct children of blah()
return path[0] && path[0].type === Syntax.BlockStatement &&
path[1] && path[1].type === Syntax.FunctionDeclaration &&
path[1].id.name === 'blah';
});
testScopeBoundary(source, blah2Idents, 1, function(node, path) {
// All direct children of blah2()
return path[0] && path[0].type === Syntax.BlockStatement &&
path[1] && path[1].type === Syntax.ArrowFunctionExpression &&
path[2].id.name === 'blah2';
});
testScopeBoundary(source, blah3Idents, 1, function(node, path) {
// All direct children of blah3()
return path[0] && path[0].type === Syntax.ArrowFunctionExpression &&
path[0].body === node &&
path[1].id.name === 'blah3';
});
});
it('puts FunctionExpression names within function scope', function() {
var source =
'var foo;' +
'var bar = function baz() {' +
' var blah;' +
'};';
var programIdents = ['foo', 'bar'];
var bazIdents = ['arguments', 'baz', 'blah'];
testScopeBoundary(source, programIdents, 2, function(node, path) {
return path[0] && path[0].type === Syntax.Program;
});
testScopeBoundary(source, bazIdents, 1, function(node, path) {
// All direct children of baz()
return path[0] && path[0].type === Syntax.BlockStatement &&
path[1] && path[1].type === Syntax.FunctionExpression &&
path[1].id.name === 'baz';
});
});
});
describe('block scope boundaries', function() {
it('creates a scope boundary around CatchClauses with params', function() {
var source =
'var blah = 0;' +
'try {' +
'} catch (e) {' +
' blah++;' +
'}';
var programIdents = ['blah'];
var catchIdents = ['e'];
testScopeBoundary(source, programIdents, 2, function(node, path) {
return path[0] && path[0].type === Syntax.Program;
});
testScopeBoundary(source, catchIdents, 1, function(node, path) {
// All direct children of catch(e) block
return path[0] && path[0].type === Syntax.BlockStatement &&
path[1] && path[1].type === Syntax.CatchClause;
});
});
it('includes vars defined in CatchClauses in the parent scope', function() {
var source =
'try {' +
'} catch (e) {' +
' var blah;' +
'}';
var programIdents = ['blah'];
var catchIdents = ['e'];
testScopeBoundary(source, programIdents, 1, function(node, path) {
return path[0] && path[0].type === Syntax.Program;
});
testScopeBoundary(source, catchIdents, 1, function(node, path) {
// All direct children of catch(e) block
return path[0] && path[0].type === Syntax.BlockStatement &&
path[1] && path[1].type === Syntax.CatchClause;
});
});
});
describe('scope chain linking', function() {
it('links parent scope boundaries', function() {
var source =
'var foo;' +
'function blah() {' +
' var bar;' +
' function nested() {' +
' var baz;' +
' }' +
'}';
var programIdents = ['foo', 'blah'];
var blahIdents = ['arguments', 'bar', 'nested'];
testParentScope(source, programIdents, 2, function(node, path) {
// All direct children of blah()
return path[0] && path[0].type === Syntax.BlockStatement &&
path[1] && path[1].type === Syntax.FunctionDeclaration &&
path[1].id.name === 'blah';
});
testParentScope(source, blahIdents, 1, function(node, path) {
// All direct children of nested()
return path[0] && path[0].type === Syntax.BlockStatement &&
path[1] && path[1].type === Syntax.FunctionDeclaration &&
path[1].id.name === 'nested';
});
});
it('nests MethodDefinition boundaries under parent scope', function() {
var source =
'var foo;' +
'class ClassA {' +
' blah() {' +
' var bar;' +
' }' +
'}';
var programIdents = ['foo', 'ClassA'];
testParentScope(source, programIdents, 1, function(node, path) {
// All direct children of blah()
return path[0] && path[0].type === Syntax.BlockStatement &&
path[1] && path[1].type === Syntax.FunctionExpression &&
path[2] && path[2].type === Syntax.MethodDefinition &&
path[2].key.name === 'blah';
});
});
});
describe('"use strict" tracking', function() {
function testStrictness(expectedStrict, source) {
var visitedNodes = 0;
function visitor(traverse, node, path, state) {
visitedNodes++;
expect(state.scopeIsStrict).toBe(expectedStrict);
}
visitor.test = function(node, path, state) {
return node.type === Syntax.Literal
&& node.value === 'testStr';
};
transformFn([visitor], source);
expect(visitedNodes).toBe(1);
}
it('detects program-level strictness', function() {
testStrictness(false, '"testStr";');
testStrictness(true, '"use strict"; "testStr";');
});
it('detects non-inherited strictness', function() {
testStrictness(true, [
'function foo() {',
' "use strict";',
' "testStr";',
'}'
].join('\n'));
});
it('detects program-inherited strictness', function() {
testStrictness(true, [
'"use strict";',
'function foo() {',
' "testStr";',
'}'
].join('\n'));
});
it('detects function-inherited strictness', function() {
testStrictness(true, [
'function foo() {',
' "use strict";',
' function bar() {',
' "testStr";',
' }',
'}'
].join('\n'));
});
it('does not detect sibling strictness', function() {
testStrictness(false, [
'function foo() {',
' "use strict";',
'}',
'function bar() {',
' "testStr";',
'}'
].join('\n'));
});
});
describe('visitors', function() {
it('should visit nodes in order', function() {
var source = [
'// Foo comment',
'function foo() {}',
'',
'// Bar comment',
'function bar() {}'
].join('\n');
var actualNodes = [];
function visitFunction(traverse, node, path, state) {
actualNodes.push([node.id.name, node.range[0]]);
}
visitFunction.test = function(node, path, state) {
return node.type === Syntax.FunctionDeclaration;
};
function visitComments(traverse, node, path, state) {
actualNodes.push([node.value, node.range[0]]);
}
visitComments.test = function(node, path, state) {
return node.type === 'Line';
};
transformFn([visitComments, visitFunction], source);
expect(actualNodes).toEqual([
[' Foo comment', 0],
['foo', 15],
[' Bar comment', 34],
['bar', 49]
]);
});
});
});

View File

@@ -0,0 +1,58 @@
/**
* 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*/
require('mock-modules').autoMockOff();
describe('jstransform-utils', function() {
var transform, utils;
var Syntax = require('esprima-fb').Syntax;
beforeEach(function() {
require('mock-modules').dumpCache();
transform = require('../jstransform').transform;
utils = require('../utils');
});
describe('temporary variables', function() {
it('should inject temporary variables at the start of functions', function() {
function visitFunctionBlock(traverse, node, path, state) {
utils.catchup(node.range[0] + 1, state);
var x = utils.injectTempVar(state);
var y = utils.injectTempVar(state);
traverse(node.body, path, state);
utils.append('return ' + x + ' + ' + y + ';', state);
utils.catchup(node.range[1], state);
return false;
}
visitFunctionBlock.test = function(node, path, state) {
var parentType = path.length && path[0].type;
return node.type === Syntax.BlockStatement && (
parentType === Syntax.FunctionDeclaration ||
parentType === Syntax.FunctionExpression
);
};
expect(transform(
[visitFunctionBlock],
'var x = function() {};'
).code).toEqual(
'var x = function() {var $__0, $__1;return $__0 + $__1;};'
);
expect(eval(transform(
[visitFunctionBlock],
'2 + (function sum(x, y)\t{ $__0 = x; $__1 = y; }(3, 5))'
).code)).toEqual(10);
});
});
});

72
node_modules/jstransform/src/cli.js generated vendored Normal file
View File

@@ -0,0 +1,72 @@
/**
* 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 assign = require('object-assign');
var transform = require('./simple').transform;
require('commoner').version(
require('../package.json').version
).resolve(function(id) {
return this.readModuleP(id);
}).option(
'--react',
'Turns on the React JSX and React displayName transforms'
).option(
'--es6',
'Turns on available ES6 transforms'
).option(
'--es7',
'Turns on available ES7 transforms'
).option(
'--harmony',
'Shorthand to enable all ES6 and ES7 transforms'
).option(
'--utility',
'Turns on available utility transforms'
).option(
'--target [version]',
'Specify your target version of ECMAScript. Valid values are "es3" and ' +
'"es5". The default is "es5". "es3" will avoid uses of defineProperty and ' +
'will quote reserved words. WARNING: "es5" is not properly supported, even ' +
'with the use of es5shim, es5sham. If you need to support IE8, use "es3".',
'es5'
).option(
'--strip-types',
'Strips out type annotations.'
).option(
'--es6module',
'Parses the file as a valid ES6 module. ' +
'(Note that this means implicit strict mode)'
).option(
'--non-strict-es6module',
'Parses the file as an ES6 module, except disables implicit strict-mode. ' +
'(This is useful if you\'re porting non-ES6 modules to ES6, but haven\'t ' +
'yet verified that they are strict-mode safe yet)'
).option(
'--source-map-inline',
'Embed inline sourcemap in transformed source'
).option(
'--source-filename',
'Filename to use when generating the inline sourcemap. Will default to ' +
'filename when processing files'
).process(function(id, source) {
// This is where JSX, ES6, etc. desugaring happens.
// We don't do any pre-processing of options so that the command line and the
// JS API both expose the same set of options. We will set the sourceFilename
// to something more correct than "source.js".
var options;
if (id !== '<stdin>') {
options = assign({sourceFilename: id + '.js'}, this.options);
} else {
options = this.options;
}
var result = transform(source, options);
return result.code;
});

79
node_modules/jstransform/src/docblock.js generated vendored Normal file
View File

@@ -0,0 +1,79 @@
/**
* 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 docblockRe = /^\s*(\/\*\*(.|\r?\n)*?\*\/)/;
var ltrimRe = /^\s*/;
/**
* @param {String} contents
* @return {String}
*/
function extract(contents) {
var match = contents.match(docblockRe);
if (match) {
return match[0].replace(ltrimRe, '') || '';
}
return '';
}
var commentStartRe = /^\/\*\*?/;
var commentEndRe = /\*+\/$/;
var wsRe = /[\t ]+/g;
var stringStartRe = /(\r?\n|^) *\*/g;
var multilineRe = /(?:^|\r?\n) *(@[^\r\n]*?) *\r?\n *([^@\r\n\s][^@\r\n]+?) *\r?\n/g;
var propertyRe = /(?:^|\r?\n) *@(\S+) *([^\r\n]*)/g;
/**
* @param {String} contents
* @return {Array}
*/
function parse(docblock) {
docblock = docblock
.replace(commentStartRe, '')
.replace(commentEndRe, '')
.replace(wsRe, ' ')
.replace(stringStartRe, '$1');
// Normalize multi-line directives
var prev = '';
while (prev != docblock) {
prev = docblock;
docblock = docblock.replace(multilineRe, "\n$1 $2\n");
}
docblock = docblock.trim();
var result = [];
var match;
while (match = propertyRe.exec(docblock)) {
result.push([match[1], match[2]]);
}
return result;
}
/**
* Same as parse but returns an object of prop: value instead of array of paris
* If a property appers more than once the last one will be returned
*
* @param {String} contents
* @return {Object}
*/
function parseAsObject(docblock) {
var pairs = parse(docblock);
var result = {};
for (var i = 0; i < pairs.length; i++) {
result[pairs[i][0]] = pairs[i][1];
}
return result;
}
exports.extract = extract;
exports.parse = parse;
exports.parseAsObject = parseAsObject;

27
node_modules/jstransform/src/inline-source-map.js generated vendored Normal file
View File

@@ -0,0 +1,27 @@
/**
* 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';
/*eslint-disable no-undef*/
var Buffer = require('buffer').Buffer;
function inlineSourceMap(sourceMap, sourceCode, sourceFilename) {
// This can be used with a sourcemap that has already has toJSON called on it.
// Check first.
var json = sourceMap;
if (typeof sourceMap.toJSON === 'function') {
json = sourceMap.toJSON();
}
json.sources = [sourceFilename];
json.sourcesContent = [sourceCode];
var base64 = new Buffer(JSON.stringify(json)).toString('base64');
return '//# sourceMappingURL=data:application/json;base64,' + base64;
}
module.exports = inlineSourceMap;

292
node_modules/jstransform/src/jstransform.js generated vendored Normal file
View File

@@ -0,0 +1,292 @@
/**
* 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*/
"use strict";
var esprima = require('esprima-fb');
var utils = require('./utils');
var getBoundaryNode = utils.getBoundaryNode;
var declareIdentInScope = utils.declareIdentInLocalScope;
var initScopeMetadata = utils.initScopeMetadata;
var Syntax = esprima.Syntax;
/**
* @param {object} node
* @param {object} parentNode
* @return {boolean}
*/
function _nodeIsClosureScopeBoundary(node, parentNode) {
if (node.type === Syntax.Program) {
return true;
}
var parentIsFunction =
parentNode.type === Syntax.FunctionDeclaration
|| parentNode.type === Syntax.FunctionExpression
|| parentNode.type === Syntax.ArrowFunctionExpression;
var parentIsCurlylessArrowFunc =
parentNode.type === Syntax.ArrowFunctionExpression
&& node === parentNode.body;
return parentIsFunction
&& (node.type === Syntax.BlockStatement || parentIsCurlylessArrowFunc);
}
function _nodeIsBlockScopeBoundary(node, parentNode) {
if (node.type === Syntax.Program) {
return false;
}
return node.type === Syntax.BlockStatement
&& parentNode.type === Syntax.CatchClause;
}
/**
* @param {object} node
* @param {array} path
* @param {object} state
*/
function traverse(node, path, state) {
/*jshint -W004*/
// Create a scope stack entry if this is the first node we've encountered in
// its local scope
var startIndex = null;
var parentNode = path[0];
if (!Array.isArray(node) && state.localScope.parentNode !== parentNode) {
if (_nodeIsClosureScopeBoundary(node, parentNode)) {
var scopeIsStrict = state.scopeIsStrict;
if (!scopeIsStrict
&& (node.type === Syntax.BlockStatement
|| node.type === Syntax.Program)) {
scopeIsStrict =
node.body.length > 0
&& node.body[0].type === Syntax.ExpressionStatement
&& node.body[0].expression.type === Syntax.Literal
&& node.body[0].expression.value === 'use strict';
}
if (node.type === Syntax.Program) {
startIndex = state.g.buffer.length;
state = utils.updateState(state, {
scopeIsStrict: scopeIsStrict
});
} else {
startIndex = state.g.buffer.length + 1;
state = utils.updateState(state, {
localScope: {
parentNode: parentNode,
parentScope: state.localScope,
identifiers: {},
tempVarIndex: 0,
tempVars: []
},
scopeIsStrict: scopeIsStrict
});
// All functions have an implicit 'arguments' object in scope
declareIdentInScope('arguments', initScopeMetadata(node), state);
// Include function arg identifiers in the scope boundaries of the
// function
if (parentNode.params.length > 0) {
var param;
var metadata = initScopeMetadata(parentNode, path.slice(1), path[0]);
for (var i = 0; i < parentNode.params.length; i++) {
param = parentNode.params[i];
if (param.type === Syntax.Identifier) {
declareIdentInScope(param.name, metadata, state);
}
}
}
// Include rest arg identifiers in the scope boundaries of their
// functions
if (parentNode.rest) {
var metadata = initScopeMetadata(
parentNode,
path.slice(1),
path[0]
);
declareIdentInScope(parentNode.rest.name, metadata, state);
}
// Named FunctionExpressions scope their name within the body block of
// themselves only
if (parentNode.type === Syntax.FunctionExpression && parentNode.id) {
var metaData =
initScopeMetadata(parentNode, path.parentNodeslice, parentNode);
declareIdentInScope(parentNode.id.name, metaData, state);
}
}
// Traverse and find all local identifiers in this closure first to
// account for function/variable declaration hoisting
collectClosureIdentsAndTraverse(node, path, state);
}
if (_nodeIsBlockScopeBoundary(node, parentNode)) {
startIndex = state.g.buffer.length;
state = utils.updateState(state, {
localScope: {
parentNode: parentNode,
parentScope: state.localScope,
identifiers: {},
tempVarIndex: 0,
tempVars: []
}
});
if (parentNode.type === Syntax.CatchClause) {
var metadata = initScopeMetadata(
parentNode,
path.slice(1),
parentNode
);
declareIdentInScope(parentNode.param.name, metadata, state);
}
collectBlockIdentsAndTraverse(node, path, state);
}
}
// Only catchup() before and after traversing a child node
function traverser(node, path, state) {
node.range && utils.catchup(node.range[0], state);
traverse(node, path, state);
node.range && utils.catchup(node.range[1], state);
}
utils.analyzeAndTraverse(walker, traverser, node, path, state);
// Inject temp variables into the scope.
if (startIndex !== null) {
utils.injectTempVarDeclarations(state, startIndex);
}
}
function collectClosureIdentsAndTraverse(node, path, state) {
utils.analyzeAndTraverse(
visitLocalClosureIdentifiers,
collectClosureIdentsAndTraverse,
node,
path,
state
);
}
function collectBlockIdentsAndTraverse(node, path, state) {
utils.analyzeAndTraverse(
visitLocalBlockIdentifiers,
collectBlockIdentsAndTraverse,
node,
path,
state
);
}
function visitLocalClosureIdentifiers(node, path, state) {
var metaData;
switch (node.type) {
case Syntax.ArrowFunctionExpression:
case Syntax.FunctionExpression:
// Function expressions don't get their names (if there is one) added to
// the closure scope they're defined in
return false;
case Syntax.ClassDeclaration:
case Syntax.ClassExpression:
case Syntax.FunctionDeclaration:
if (node.id) {
metaData = initScopeMetadata(getBoundaryNode(path), path.slice(), node);
declareIdentInScope(node.id.name, metaData, state);
}
return false;
case Syntax.VariableDeclarator:
// Variables have function-local scope
if (path[0].kind === 'var') {
metaData = initScopeMetadata(getBoundaryNode(path), path.slice(), node);
declareIdentInScope(node.id.name, metaData, state);
}
break;
}
}
function visitLocalBlockIdentifiers(node, path, state) {
// TODO: Support 'let' here...maybe...one day...or something...
if (node.type === Syntax.CatchClause) {
return false;
}
}
function walker(node, path, state) {
var visitors = state.g.visitors;
for (var i = 0; i < visitors.length; i++) {
if (visitors[i].test(node, path, state)) {
return visitors[i](traverse, node, path, state);
}
}
}
var _astCache = {};
function getAstForSource(source, options) {
if (_astCache[source] && !options.disableAstCache) {
return _astCache[source];
}
var ast = esprima.parse(source, {
comment: true,
loc: true,
range: true,
sourceType: options.sourceType
});
if (!options.disableAstCache) {
_astCache[source] = ast;
}
return ast;
}
/**
* Applies all available transformations to the source
* @param {array} visitors
* @param {string} source
* @param {?object} options
* @return {object}
*/
function transform(visitors, source, options) {
options = options || {};
var ast;
try {
ast = getAstForSource(source, options);
} catch (e) {
e.message = 'Parse Error: ' + e.message;
throw e;
}
var state = utils.createState(source, ast, options);
state.g.visitors = visitors;
if (options.sourceMap) {
var SourceMapGenerator = require('source-map').SourceMapGenerator;
state.g.sourceMap = new SourceMapGenerator({file: options.filename || 'transformed.js'});
}
traverse(ast, [], state);
utils.catchup(source.length, state);
var ret = {code: state.g.buffer, extra: state.g.extra};
if (options.sourceMap) {
ret.sourceMap = state.g.sourceMap;
ret.sourceMapFilename = options.filename || 'source.js';
}
return ret;
}
exports.transform = transform;
exports.Syntax = Syntax;

165
node_modules/jstransform/src/simple.js generated vendored Normal file
View File

@@ -0,0 +1,165 @@
/**
* 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';
/*eslint-disable no-undef*/
var assign = require('object-assign');
var visitors = require('../visitors');
var jstransform = require('./jstransform');
var typesSyntax = require('../visitors/type-syntax');
var inlineSourceMap = require('./inline-source-map');
var fs = require('fs');
var DEFAULT_OPTIONS = {
react: false,
es6: false,
es7: false,
harmony: false,
utility: false,
target: 'es5',
stripTypes: false,
sourceMap: false,
sourceMapInline: false,
sourceFilename: 'source.js',
es6module: false,
nonStrictEs6module: false
};
/**
* Transforms the given code with the given options.
*
* @param {string} code
* @param {object} options
* @return {object}
*/
function transform(code, options) {
options = assign({}, DEFAULT_OPTIONS, options);
// Process options
var transformOptions = {};
if (options.sourceMap || options.sourceMapInline) {
transformOptions.sourceMap = true;
transformOptions.filename = options.sourceFilename || 'source.js';
}
if (options.es6module) {
transformOptions.sourceType = 'module';
}
if (options.nonStrictEs6module) {
transformOptions.sourceType = 'nonStrictModule';
}
// Instead of doing any fancy validation, only look for 'es3'. If we have
// that, then use it. Otherwise use 'es5'.
transformOptions.es3 = options.target === 'es3';
transformOptions.es5 = !transformOptions.es3;
// Determine visitors to use
var visitorSets = [];
if (options.react) {
visitorSets.push('react');
}
if (options.harmony) {
visitorSets.push('harmony');
}
if (options.es6) {
visitorSets.push('es6');
}
if (options.es7) {
visitorSets.push('es7');
}
if (options.utility) {
visitorSets.push('utility');
}
if (options.target === 'es3') {
visitorSets.push('target:es3');
}
if (options.stripTypes) {
// Stripping types needs to happen before the other transforms
// unfortunately, due to bad interactions. For example,
// es6-rest-param-visitors conflict with stripping rest param type
// annotation
code = jstransform.transform(
typesSyntax.visitorList,
code,
transformOptions
).code;
}
var visitorList = visitors.getVisitorsBySet(visitorSets);
var result = jstransform.transform(visitorList, code, transformOptions);
// Only copy some things off.
var output = {
code: result.code,
sourceMap: null
};
// Convert sourceMap to JSON.
var sourceMap;
if (result.sourceMap) {
sourceMap = result.sourceMap.toJSON();
sourceMap.sources = [transformOptions.filename];
sourceMap.sourcesContent = [code];
}
// This differentiates options.sourceMap from options.sourceMapInline.
if (options.sourceMap) {
output.sourceMap = sourceMap;
}
if (options.sourceMapInline) {
var map = inlineSourceMap(
result.sourceMap,
code,
transformOptions.filename
);
output.code = output.code + '\n' + map;
}
return output;
}
function transformFile(file, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
options = assign({sourceFilename: file}, options);
fs.readFile(file, 'utf-8', function(err, contents) {
if (err) {
return callback(err, null);
}
var result = transform(contents, options);
callback(null, result);
});
}
function transformFileSync(file, options) {
options = assign({sourceFilename: file}, options);
var contents = fs.readFileSync(file, 'utf-8');
return transform(contents, options);
}
module.exports = {
transform: transform,
transformFile: transformFile,
transformFileSync: transformFileSync
};

701
node_modules/jstransform/src/utils.js generated vendored Normal file
View File

@@ -0,0 +1,701 @@
/**
* 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 Syntax = require('esprima-fb').Syntax;
var leadingIndentRegexp = /(^|\n)( {2}|\t)/g;
var nonWhiteRegexp = /(\S)/g;
/**
* A `state` object represents the state of the parser. It has "local" and
* "global" parts. Global contains parser position, source, etc. Local contains
* scope based properties like current class name. State should contain all the
* info required for transformation. It's the only mandatory object that is
* being passed to every function in transform chain.
*
* @param {string} source
* @param {object} transformOptions
* @return {object}
*/
function createState(source, rootNode, transformOptions) {
return {
/**
* A tree representing the current local scope (and its lexical scope chain)
* Useful for tracking identifiers from parent scopes, etc.
* @type {Object}
*/
localScope: {
parentNode: rootNode,
parentScope: null,
identifiers: {},
tempVarIndex: 0,
tempVars: []
},
/**
* The name (and, if applicable, expression) of the super class
* @type {Object}
*/
superClass: null,
/**
* The namespace to use when munging identifiers
* @type {String}
*/
mungeNamespace: '',
/**
* Ref to the node for the current MethodDefinition
* @type {Object}
*/
methodNode: null,
/**
* Ref to the node for the FunctionExpression of the enclosing
* MethodDefinition
* @type {Object}
*/
methodFuncNode: null,
/**
* Name of the enclosing class
* @type {String}
*/
className: null,
/**
* Whether we're currently within a `strict` scope
* @type {Bool}
*/
scopeIsStrict: null,
/**
* Indentation offset
* @type {Number}
*/
indentBy: 0,
/**
* Global state (not affected by updateState)
* @type {Object}
*/
g: {
/**
* A set of general options that transformations can consider while doing
* a transformation:
*
* - minify
* Specifies that transformation steps should do their best to minify
* the output source when possible. This is useful for places where
* minification optimizations are possible with higher-level context
* info than what jsxmin can provide.
*
* For example, the ES6 class transform will minify munged private
* variables if this flag is set.
*/
opts: transformOptions,
/**
* Current position in the source code
* @type {Number}
*/
position: 0,
/**
* Auxiliary data to be returned by transforms
* @type {Object}
*/
extra: {},
/**
* Buffer containing the result
* @type {String}
*/
buffer: '',
/**
* Source that is being transformed
* @type {String}
*/
source: source,
/**
* Cached parsed docblock (see getDocblock)
* @type {object}
*/
docblock: null,
/**
* Whether the thing was used
* @type {Boolean}
*/
tagNamespaceUsed: false,
/**
* If using bolt xjs transformation
* @type {Boolean}
*/
isBolt: undefined,
/**
* Whether to record source map (expensive) or not
* @type {SourceMapGenerator|null}
*/
sourceMap: null,
/**
* Filename of the file being processed. Will be returned as a source
* attribute in the source map
*/
sourceMapFilename: 'source.js',
/**
* Only when source map is used: last line in the source for which
* source map was generated
* @type {Number}
*/
sourceLine: 1,
/**
* Only when source map is used: last line in the buffer for which
* source map was generated
* @type {Number}
*/
bufferLine: 1,
/**
* The top-level Program AST for the original file.
*/
originalProgramAST: null,
sourceColumn: 0,
bufferColumn: 0
}
};
}
/**
* Updates a copy of a given state with "update" and returns an updated state.
*
* @param {object} state
* @param {object} update
* @return {object}
*/
function updateState(state, update) {
var ret = Object.create(state);
Object.keys(update).forEach(function(updatedKey) {
ret[updatedKey] = update[updatedKey];
});
return ret;
}
/**
* Given a state fill the resulting buffer from the original source up to
* the end
*
* @param {number} end
* @param {object} state
* @param {?function} contentTransformer Optional callback to transform newly
* added content.
*/
function catchup(end, state, contentTransformer) {
if (end < state.g.position) {
// cannot move backwards
return;
}
var source = state.g.source.substring(state.g.position, end);
var transformed = updateIndent(source, state);
if (state.g.sourceMap && transformed) {
// record where we are
state.g.sourceMap.addMapping({
generated: { line: state.g.bufferLine, column: state.g.bufferColumn },
original: { line: state.g.sourceLine, column: state.g.sourceColumn },
source: state.g.sourceMapFilename
});
// record line breaks in transformed source
var sourceLines = source.split('\n');
var transformedLines = transformed.split('\n');
// Add line break mappings between last known mapping and the end of the
// added piece. So for the code piece
// (foo, bar);
// > var x = 2;
// > var b = 3;
// var c =
// only add lines marked with ">": 2, 3.
for (var i = 1; i < sourceLines.length - 1; i++) {
state.g.sourceMap.addMapping({
generated: { line: state.g.bufferLine, column: 0 },
original: { line: state.g.sourceLine, column: 0 },
source: state.g.sourceMapFilename
});
state.g.sourceLine++;
state.g.bufferLine++;
}
// offset for the last piece
if (sourceLines.length > 1) {
state.g.sourceLine++;
state.g.bufferLine++;
state.g.sourceColumn = 0;
state.g.bufferColumn = 0;
}
state.g.sourceColumn += sourceLines[sourceLines.length - 1].length;
state.g.bufferColumn +=
transformedLines[transformedLines.length - 1].length;
}
state.g.buffer +=
contentTransformer ? contentTransformer(transformed) : transformed;
state.g.position = end;
}
/**
* Returns original source for an AST node.
* @param {object} node
* @param {object} state
* @return {string}
*/
function getNodeSourceText(node, state) {
return state.g.source.substring(node.range[0], node.range[1]);
}
function _replaceNonWhite(value) {
return value.replace(nonWhiteRegexp, ' ');
}
/**
* Removes all non-whitespace characters
*/
function _stripNonWhite(value) {
return value.replace(nonWhiteRegexp, '');
}
/**
* Finds the position of the next instance of the specified syntactic char in
* the pending source.
*
* NOTE: This will skip instances of the specified char if they sit inside a
* comment body.
*
* NOTE: This function also assumes that the buffer's current position is not
* already within a comment or a string. This is rarely the case since all
* of the buffer-advancement utility methods tend to be used on syntactic
* nodes' range values -- but it's a small gotcha that's worth mentioning.
*/
function getNextSyntacticCharOffset(char, state) {
var pendingSource = state.g.source.substring(state.g.position);
var pendingSourceLines = pendingSource.split('\n');
var charOffset = 0;
var line;
var withinBlockComment = false;
var withinString = false;
lineLoop: while ((line = pendingSourceLines.shift()) !== undefined) {
var lineEndPos = charOffset + line.length;
charLoop: for (; charOffset < lineEndPos; charOffset++) {
var currChar = pendingSource[charOffset];
if (currChar === '"' || currChar === '\'') {
withinString = !withinString;
continue charLoop;
} else if (withinString) {
continue charLoop;
} else if (charOffset + 1 < lineEndPos) {
var nextTwoChars = currChar + line[charOffset + 1];
if (nextTwoChars === '//') {
charOffset = lineEndPos + 1;
continue lineLoop;
} else if (nextTwoChars === '/*') {
withinBlockComment = true;
charOffset += 1;
continue charLoop;
} else if (nextTwoChars === '*/') {
withinBlockComment = false;
charOffset += 1;
continue charLoop;
}
}
if (!withinBlockComment && currChar === char) {
return charOffset + state.g.position;
}
}
// Account for '\n'
charOffset++;
withinString = false;
}
throw new Error('`' + char + '` not found!');
}
/**
* Catches up as `catchup` but replaces non-whitespace chars with spaces.
*/
function catchupWhiteOut(end, state) {
catchup(end, state, _replaceNonWhite);
}
/**
* Catches up as `catchup` but removes all non-whitespace characters.
*/
function catchupWhiteSpace(end, state) {
catchup(end, state, _stripNonWhite);
}
/**
* Removes all non-newline characters
*/
var reNonNewline = /[^\n]/g;
function stripNonNewline(value) {
return value.replace(reNonNewline, function() {
return '';
});
}
/**
* Catches up as `catchup` but removes all non-newline characters.
*
* Equivalent to appending as many newlines as there are in the original source
* between the current position and `end`.
*/
function catchupNewlines(end, state) {
catchup(end, state, stripNonNewline);
}
/**
* Same as catchup but does not touch the buffer
*
* @param {number} end
* @param {object} state
*/
function move(end, state) {
// move the internal cursors
if (state.g.sourceMap) {
if (end < state.g.position) {
state.g.position = 0;
state.g.sourceLine = 1;
state.g.sourceColumn = 0;
}
var source = state.g.source.substring(state.g.position, end);
var sourceLines = source.split('\n');
if (sourceLines.length > 1) {
state.g.sourceLine += sourceLines.length - 1;
state.g.sourceColumn = 0;
}
state.g.sourceColumn += sourceLines[sourceLines.length - 1].length;
}
state.g.position = end;
}
/**
* Appends a string of text to the buffer
*
* @param {string} str
* @param {object} state
*/
function append(str, state) {
if (state.g.sourceMap && str) {
state.g.sourceMap.addMapping({
generated: { line: state.g.bufferLine, column: state.g.bufferColumn },
original: { line: state.g.sourceLine, column: state.g.sourceColumn },
source: state.g.sourceMapFilename
});
var transformedLines = str.split('\n');
if (transformedLines.length > 1) {
state.g.bufferLine += transformedLines.length - 1;
state.g.bufferColumn = 0;
}
state.g.bufferColumn +=
transformedLines[transformedLines.length - 1].length;
}
state.g.buffer += str;
}
/**
* Update indent using state.indentBy property. Indent is measured in
* double spaces. Updates a single line only.
*
* @param {string} str
* @param {object} state
* @return {string}
*/
function updateIndent(str, state) {
/*jshint -W004*/
var indentBy = state.indentBy;
if (indentBy < 0) {
for (var i = 0; i < -indentBy; i++) {
str = str.replace(leadingIndentRegexp, '$1');
}
} else {
for (var i = 0; i < indentBy; i++) {
str = str.replace(leadingIndentRegexp, '$1$2$2');
}
}
return str;
}
/**
* Calculates indent from the beginning of the line until "start" or the first
* character before start.
* @example
* " foo.bar()"
* ^
* start
* indent will be " "
*
* @param {number} start
* @param {object} state
* @return {string}
*/
function indentBefore(start, state) {
var end = start;
start = start - 1;
while (start > 0 && state.g.source[start] != '\n') {
if (!state.g.source[start].match(/[ \t]/)) {
end = start;
}
start--;
}
return state.g.source.substring(start + 1, end);
}
function getDocblock(state) {
if (!state.g.docblock) {
var docblock = require('./docblock');
state.g.docblock =
docblock.parseAsObject(docblock.extract(state.g.source));
}
return state.g.docblock;
}
function identWithinLexicalScope(identName, state, stopBeforeNode) {
var currScope = state.localScope;
while (currScope) {
if (currScope.identifiers[identName] !== undefined) {
return true;
}
if (stopBeforeNode && currScope.parentNode === stopBeforeNode) {
break;
}
currScope = currScope.parentScope;
}
return false;
}
function identInLocalScope(identName, state) {
return state.localScope.identifiers[identName] !== undefined;
}
/**
* @param {object} boundaryNode
* @param {?array} path
* @return {?object} node
*/
function initScopeMetadata(boundaryNode, path, node) {
return {
boundaryNode: boundaryNode,
bindingPath: path,
bindingNode: node
};
}
function declareIdentInLocalScope(identName, metaData, state) {
state.localScope.identifiers[identName] = {
boundaryNode: metaData.boundaryNode,
path: metaData.bindingPath,
node: metaData.bindingNode,
state: Object.create(state)
};
}
function getLexicalBindingMetadata(identName, state) {
var currScope = state.localScope;
while (currScope) {
if (currScope.identifiers[identName] !== undefined) {
return currScope.identifiers[identName];
}
currScope = currScope.parentScope;
}
}
function getLocalBindingMetadata(identName, state) {
return state.localScope.identifiers[identName];
}
/**
* Apply the given analyzer function to the current node. If the analyzer
* doesn't return false, traverse each child of the current node using the given
* traverser function.
*
* @param {function} analyzer
* @param {function} traverser
* @param {object} node
* @param {array} path
* @param {object} state
*/
function analyzeAndTraverse(analyzer, traverser, node, path, state) {
if (node.type) {
if (analyzer(node, path, state) === false) {
return;
}
path.unshift(node);
}
getOrderedChildren(node).forEach(function(child) {
traverser(child, path, state);
});
node.type && path.shift();
}
/**
* It is crucial that we traverse in order, or else catchup() on a later
* node that is processed out of order can move the buffer past a node
* that we haven't handled yet, preventing us from modifying that node.
*
* This can happen when a node has multiple properties containing children.
* For example, XJSElement nodes have `openingElement`, `closingElement` and
* `children`. If we traverse `openingElement`, then `closingElement`, then
* when we get to `children`, the buffer has already caught up to the end of
* the closing element, after the children.
*
* This is basically a Schwartzian transform. Collects an array of children,
* each one represented as [child, startIndex]; sorts the array by start
* index; then traverses the children in that order.
*/
function getOrderedChildren(node) {
var queue = [];
for (var key in node) {
if (node.hasOwnProperty(key)) {
enqueueNodeWithStartIndex(queue, node[key]);
}
}
queue.sort(function(a, b) { return a[1] - b[1]; });
return queue.map(function(pair) { return pair[0]; });
}
/**
* Helper function for analyzeAndTraverse which queues up all of the children
* of the given node.
*
* Children can also be found in arrays, so we basically want to merge all of
* those arrays together so we can sort them and then traverse the children
* in order.
*
* One example is the Program node. It contains `body` and `comments`, both
* arrays. Lexographically, comments are interspersed throughout the body
* nodes, but esprima's AST groups them together.
*/
function enqueueNodeWithStartIndex(queue, node) {
if (typeof node !== 'object' || node === null) {
return;
}
if (node.range) {
queue.push([node, node.range[0]]);
} else if (Array.isArray(node)) {
for (var ii = 0; ii < node.length; ii++) {
enqueueNodeWithStartIndex(queue, node[ii]);
}
}
}
/**
* Checks whether a node or any of its sub-nodes contains
* a syntactic construct of the passed type.
* @param {object} node - AST node to test.
* @param {string} type - node type to lookup.
*/
function containsChildOfType(node, type) {
return containsChildMatching(node, function(node) {
return node.type === type;
});
}
function containsChildMatching(node, matcher) {
var foundMatchingChild = false;
function nodeTypeAnalyzer(node) {
if (matcher(node) === true) {
foundMatchingChild = true;
return false;
}
}
function nodeTypeTraverser(child, path, state) {
if (!foundMatchingChild) {
foundMatchingChild = containsChildMatching(child, matcher);
}
}
analyzeAndTraverse(
nodeTypeAnalyzer,
nodeTypeTraverser,
node,
[]
);
return foundMatchingChild;
}
var scopeTypes = {};
scopeTypes[Syntax.ArrowFunctionExpression] = true;
scopeTypes[Syntax.FunctionExpression] = true;
scopeTypes[Syntax.FunctionDeclaration] = true;
scopeTypes[Syntax.Program] = true;
function getBoundaryNode(path) {
for (var ii = 0; ii < path.length; ++ii) {
if (scopeTypes[path[ii].type]) {
return path[ii];
}
}
throw new Error(
'Expected to find a node with one of the following types in path:\n' +
JSON.stringify(Object.keys(scopeTypes))
);
}
function getTempVar(tempVarIndex) {
return '$__' + tempVarIndex;
}
function injectTempVar(state) {
var tempVar = '$__' + (state.localScope.tempVarIndex++);
state.localScope.tempVars.push(tempVar);
return tempVar;
}
function injectTempVarDeclarations(state, index) {
if (state.localScope.tempVars.length) {
state.g.buffer =
state.g.buffer.slice(0, index) +
'var ' + state.localScope.tempVars.join(', ') + ';' +
state.g.buffer.slice(index);
state.localScope.tempVars = [];
}
}
exports.analyzeAndTraverse = analyzeAndTraverse;
exports.append = append;
exports.catchup = catchup;
exports.catchupNewlines = catchupNewlines;
exports.catchupWhiteOut = catchupWhiteOut;
exports.catchupWhiteSpace = catchupWhiteSpace;
exports.containsChildMatching = containsChildMatching;
exports.containsChildOfType = containsChildOfType;
exports.createState = createState;
exports.declareIdentInLocalScope = declareIdentInLocalScope;
exports.getBoundaryNode = getBoundaryNode;
exports.getDocblock = getDocblock;
exports.getLexicalBindingMetadata = getLexicalBindingMetadata;
exports.getLocalBindingMetadata = getLocalBindingMetadata;
exports.getNextSyntacticCharOffset = getNextSyntacticCharOffset;
exports.getNodeSourceText = getNodeSourceText;
exports.getOrderedChildren = getOrderedChildren;
exports.getTempVar = getTempVar;
exports.identInLocalScope = identInLocalScope;
exports.identWithinLexicalScope = identWithinLexicalScope;
exports.indentBefore = indentBefore;
exports.initScopeMetadata = initScopeMetadata;
exports.injectTempVar = injectTempVar;
exports.injectTempVarDeclarations = injectTempVarDeclarations;
exports.move = move;
exports.scopeTypes = scopeTypes;
exports.updateIndent = updateIndent;
exports.updateState = updateState;

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
];