feat: Add Be and tbd skill, also added Roadmap file

This commit is contained in:
2026-05-10 16:32:12 -04:00
parent 3500ade13f
commit 0bb8885802
29587 changed files with 10611695 additions and 0 deletions

18
Skills/@be/node_modules/jstransform/.jshintrc generated vendored Normal file
View File

@@ -0,0 +1,18 @@
{
"boss": true,
"curly": true,
"devel": false,
"eqnull": false,
"expr": true,
"funcscope": true,
"globalstrict": false,
"loopfunc": false,
"newcap": true,
"node": true,
"noempty": true,
"nonstandard": false,
"onecase": false,
"trailing": true,
"undef": true,
"unused": true
}

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

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

201
Skills/@be/node_modules/jstransform/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

95
Skills/@be/node_modules/jstransform/README.md generated vendored Normal file
View File

@@ -0,0 +1,95 @@
# 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
## Examples
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 Syntax = require('esprima-fb').Syntax;
var utils = require('jstransform/src/utils');
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);
```

30
Skills/@be/node_modules/jstransform/package.json generated vendored Normal file
View File

@@ -0,0 +1,30 @@
{
"name": "jstransform",
"version": "3.0.0",
"description": "A simple AST visitor-based JS transformer",
"contributors": [
{"name": "Jeff Morrison", "email": "jeffmo@fb.com"}
],
"main": "src/jstransform",
"repository": {
"type": "git",
"url": "git@github.com:facebook/jstransform.git"
},
"keywords": [
"transformer",
"compiler",
"syntax",
"visitor"
],
"dependencies": {
"base62": "0.1.1",
"esprima-fb": "~3001.1.0-dev-harmony-fb",
"source-map": "0.1.31"
},
"licenses": [
{"type": "Apache-2.0", "url": "http://www.apache.org/licenses/LICENSE-2.0"}
],
"engines": {
"node": ">=0.8.8"
}
}

View File

@@ -0,0 +1,369 @@
/**
* Copyright 2013 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @emails jeffmo@fb.com javascript@lists.facebook.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, state) {
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, state) {
return path[0] && path[0].type === Syntax.Program;
});
testScopeBoundary(source, blahIdents, 2, function(node, path, state) {
// 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, state) {
// 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, state) {
return path[0] && path[0].type === Syntax.Program;
});
testScopeBoundary(source, blahIdents, 1, function(node, path, state) {
// 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, state) {
// 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('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, state) {
return path[0] && path[0].type === Syntax.Program;
});
testScopeBoundary(source, barIdents, 2, function(node, path, state) {
// 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, state) {
// 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 programIdents = ['foo', 'blah'];
var blahIdents = ['arguments', 'bar', 'baz'];
testScopeBoundary(source, programIdents, 2, function(node, path, state) {
return path[0] && path[0].type === Syntax.Program;
});
testScopeBoundary(source, blahIdents, 1, function(node, path, state) {
// 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';
});
});
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, state) {
return path[0] && path[0].type === Syntax.Program;
});
testScopeBoundary(source, bazIdents, 1, function(node, path, state) {
// 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, state) {
return path[0] && path[0].type === Syntax.Program;
});
testScopeBoundary(source, catchIdents, 1, function(node, path, state) {
// 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, state) {
return path[0] && path[0].type === Syntax.Program;
});
testScopeBoundary(source, catchIdents, 1, function(node, path, state) {
// 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, state) {
// 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, state) {
// 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, state) {
// 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'));
});
});
});

86
Skills/@be/node_modules/jstransform/src/docblock.js generated vendored Normal file
View File

@@ -0,0 +1,86 @@
/**
* Copyright 2013 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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;

243
Skills/@be/node_modules/jstransform/src/jstransform.js generated vendored Normal file
View File

@@ -0,0 +1,243 @@
/**
* Copyright 2013 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*jslint node: true*/
"use strict";
/**
* Syntax transfomer for javascript. Takes the source in, spits the source
* out.
*
* Parses input source with esprima, applies the given list of visitors to the
* AST tree, and returns the resulting output.
*/
var esprima = require('esprima-fb');
var utils = require('./utils');
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;
return node.type === Syntax.BlockStatement && parentIsFunction;
}
function _nodeIsBlockScopeBoundary(node, parentNode) {
if (node.type === Syntax.Program) {
return false;
}
return node.type === Syntax.BlockStatement
&& parentNode.type === Syntax.CatchClause;
}
/**
* @param {object} node
* @param {function} visitor
* @param {array} path
* @param {object} state
*/
function traverse(node, path, state) {
// Create a scope stack entry if this is the first node we've encountered in
// its local scope
var parentNode = path[0];
if (!Array.isArray(node) && state.localScope.parentNode !== parentNode) {
if (_nodeIsClosureScopeBoundary(node, parentNode)) {
var scopeIsStrict =
state.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) {
state = utils.updateState(state, {
scopeIsStrict: scopeIsStrict
});
} else {
state = utils.updateState(state, {
localScope: {
parentNode: parentNode,
parentScope: state.localScope,
identifiers: {}
},
scopeIsStrict: scopeIsStrict
});
// All functions have an implicit 'arguments' object in scope
state.localScope.identifiers['arguments'] = true;
// Include function arg identifiers in the scope boundaries of the
// function
if (parentNode.params.length > 0) {
var param;
for (var i = 0; i < parentNode.params.length; i++) {
param = parentNode.params[i];
if (param.type === Syntax.Identifier) {
state.localScope.identifiers[param.name] = true;
}
}
}
// Named FunctionExpressions scope their name within the body block of
// themselves only
if (parentNode.type === Syntax.FunctionExpression && parentNode.id) {
state.localScope.identifiers[parentNode.id.name] = true;
}
}
// 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)) {
state = utils.updateState(state, {
localScope: {
parentNode: parentNode,
parentScope: state.localScope,
identifiers: {}
}
});
if (parentNode.type === Syntax.CatchClause) {
state.localScope.identifiers[parentNode.param.name] = true;
}
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);
}
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 identifiers = state.localScope.identifiers;
switch (node.type) {
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) {
identifiers[node.id.name] = true;
}
return false;
case Syntax.VariableDeclarator:
if (path[0].kind === 'var') {
identifiers[node.id.name] = true;
}
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);
}
}
}
/**
* 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 = esprima.parse(source, {
comment: true,
loc: true,
range: true
});
} 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: 'transformed.js'});
}
traverse(ast, [], state);
utils.catchup(source.length, state);
var ret = {code: state.g.buffer};
if (options.sourceMap) {
ret.sourceMap = state.g.sourceMap;
ret.sourceMapFilename = options.filename || 'source.js';
}
return ret;
}
exports.transform = transform;

479
Skills/@be/node_modules/jstransform/src/utils.js generated vendored Normal file
View File

@@ -0,0 +1,479 @@
/**
* Copyright 2013 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*jslint node: true*/
/**
* 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: {}
},
/**
* 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 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,
/**
* 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,
/**
* Buffer containing the result
* @type {String}
*/
buffer: '',
/**
* Indentation offset (only negative offset is supported now)
* @type {Number}
*/
indentBy: 0,
/**
* 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;
}
/**
* Removes all non-whitespace characters
*/
var reNonWhite = /(\S)/g;
function stripNonWhite(value) {
return value.replace(reNonWhite, function() {
return '';
});
}
/**
* 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) {
for (var i = 0; i < -state.g.indentBy; i++) {
str = str.replace(/(^|\n)( {2}|\t)/g, '$1');
}
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 2
*
* @param {number} start
* @param {object} state
* @return {number}
*/
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;
}
function declareIdentInLocalScope(identName, state) {
state.localScope.identifiers[identName] = true;
}
/**
* 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 {function} visitor
* @param {array} path
* @param {object} state
*/
function analyzeAndTraverse(analyzer, traverser, node, path, state) {
var key, child;
if (node.type) {
if (analyzer(node, path, state) === false) {
return;
}
path.unshift(node);
}
for (key in node) {
// skip obviously wrong attributes
if (key === 'range' || key === 'loc') {
continue;
}
if (node.hasOwnProperty(key)) {
child = node[key];
if (typeof child === 'object' && child !== null) {
traverser(child, path, state);
}
}
}
node.type && path.shift();
}
/**
* 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) {
var foundMatchingChild = false;
function nodeTypeAnalyzer(node) {
if (node.type === type) {
foundMatchingChild = true;
return false;
}
}
function nodeTypeTraverser(child, path, state) {
if (!foundMatchingChild) {
foundMatchingChild = containsChildOfType(child, type);
}
}
analyzeAndTraverse(
nodeTypeAnalyzer,
nodeTypeTraverser,
node,
[]
);
return foundMatchingChild;
}
exports.append = append;
exports.catchup = catchup;
exports.catchupWhiteSpace = catchupWhiteSpace;
exports.catchupNewlines = catchupNewlines;
exports.containsChildOfType = containsChildOfType;
exports.createState = createState;
exports.declareIdentInLocalScope = declareIdentInLocalScope;
exports.getDocblock = getDocblock;
exports.identWithinLexicalScope = identWithinLexicalScope;
exports.identInLocalScope = identInLocalScope;
exports.indentBefore = indentBefore;
exports.move = move;
exports.updateIndent = updateIndent;
exports.updateState = updateState;
exports.analyzeAndTraverse = analyzeAndTraverse;

View File

@@ -0,0 +1,214 @@
/**
* Copyright 2013 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @emails dmitrys@fb.com javascript@lists.facebook.com
*/
/*jshint evil:true*/
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('../../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('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);'
);
// 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 typechecker annotation.
expectTransform(
'(/*string*/foo, /*bool*/bar) => foo;',
'function(/*string*/foo, /*bool*/bar) {return foo;};'
);
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,114 @@
/**
* Copyright 2013 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @emails dmitrys@fb.com javascript@lists.facebook.com
*/
/*jshint evil:true*/
require('mock-modules').autoMockOff();
describe('es6-object-short-notation-visitors', function() {
var transformFn;
var visitors;
beforeEach(function() {
require('mock-modules').dumpCache();
visitors = require('../es6-object-short-notation-visitors').visitorList;
transformFn = require('../../jstransform').transform;
});
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);
});
// 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 transform: short notation in complex object pattern.
expectTransform([
'function init({name, points: [{x, y}, {z, q}]}) {',
' return function([{data: {value, score}}]) {',
' return {z, q, score, name};',
' };',
'}'
].join('\n'), [
'function init({name:name, points: [{x:x, y:y}, {z:z, q:q}]}) {',
' return function([{data: {value:value, score:score}}]) {',
' return {z:z, q:q, score:score, name:name};',
' };',
'}'
].join('\n'));
// Should preserve lines transforming ugly code.
expectTransform([
'function',
'',
'foo ({',
' x,',
' y',
'',
'})',
'',
' {',
' return {',
' x,',
' y};',
'}'
].join('\n'), [
'function',
'',
'foo ({',
' x:x,',
' y:y',
'',
'})',
'',
' {',
' return {',
' x:x,',
' y:y};',
'}'
].join('\n'));
});
});

View File

@@ -0,0 +1,320 @@
/**
* Copyright 2013 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @emails dmitrys@fb.com javascript@lists.facebook.com
*/
/*jshint evil:true*/
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;
transformFn = require('../../jstransform').transform;
visitorSet =
arrowFuncVisitors
.concat(classVisitors)
.concat(restParamVisitors);
});
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);
});
});
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 ) {var args=Array.prototype.slice.call(arguments,2); return x + y + args[0]; }'
);
})
it('1-line function expression with 1 arg', () => {
expectTransform(
'(function(x, ...args) { return args;});',
'(function(x ) {var args=Array.prototype.slice.call(arguments,1); return args;});'
);
});
it('1-line function expression with no args', () => {
expectTransform(
'map(function(...args) { return args.map(log); });',
'map(function() {var args=Array.prototype.slice.call(arguments,0); return args.map(log); });'
);
});
it('preserves lines for ugly code', () => {
expectTransform([
'function',
'',
'foo (',
' x,',
' ...args',
'',
')',
'',
' {',
' return args;',
'}'
].join('\n'), [
'function',
'',
'foo (',
' x',
' ',
'',
')',
'',
' {var args=Array.prototype.slice.call(arguments,1);',
' return args;',
'}'
].join('\n'));
});
it('preserves inline comments', () => {
expectTransform(
'function foo(/*string*/foo, /*bool*/bar, ...args) { return args; }',
'function foo(/*string*/foo, /*bool*/bar ) {' +
'var args=Array.prototype.slice.call(arguments,2); ' +
'return args; ' +
'}'
);
});
});
});

View File

@@ -0,0 +1,205 @@
/**
* @emails mroch@fb.com javascript@lists.facebook.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('../../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) {
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)');
});
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))');
});
it('should transform tags with simple templates', function() {
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 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,117 @@
/**
* Copyright 2013 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*global exports:true*/
/**
* Desugars ES6 Arrow functions to ES3 function expressions.
* If the function contains `this` expression -- automatically
* binds the funciton 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 Syntax = require('esprima-fb').Syntax;
var utils = require('../src/utils');
/**
* @public
*/
function visitArrowFunction(traverse, node, path, state) {
// Prologue.
utils.append('function', state);
renderParams(node, 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.
if (utils.containsChildOfType(node.body, Syntax.ThisExpression)) {
utils.append('.bind(this)', state);
}
return false;
}
function renderParams(node, 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) {
utils.catchup(node.params[node.params.length - 1].range[1], state);
}
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);
if (node.rest) {
utils.append(
restParamVisitors.renderRestParamSetup(node),
state
);
}
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,478 @@
/**
* Copyright 2013 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*jslint node:true*/
/**
* @typechecks
*/
'use strict';
var base62 = require('base62');
var Syntax = require('esprima-fb').Syntax;
var utils = require('../src/utils');
var SUPER_PROTO_IDENT_PREFIX = '____SuperProtoOf';
var _anonClassUUIDCounter = 0;
var _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) {
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];
state = utils.updateState(state, {
methodFuncNode: node
});
if (methodNode.key.name === 'constructor') {
utils.append('function ' + state.className, state);
} else {
var methodName = methodNode.key.name;
if (_shouldMungeIdentifier(methodNode.key, state)) {
methodName = _getMungedName(methodName, state);
}
var prototypeOrStatic = methodNode.static ? '' : 'prototype.';
utils.append(
state.className + '.' + prototypeOrStatic + methodName + '=function',
state
);
}
utils.move(methodNode.key.range[1], state);
var params = node.params;
var paramName;
if (params.length > 0) {
for (var i = 0; i < params.length; i++) {
utils.catchup(node.params[i].range[0], state);
paramName = params[i].name;
if (_shouldMungeIdentifier(params[i], state)) {
paramName = _getMungedName(params[i].name, state);
}
utils.append(paramName, state);
utils.move(params[i].range[1], state);
}
} else {
utils.append('(', state);
}
utils.append(')', state);
utils.catchupWhiteSpace(node.body.range[0], state);
utils.append('{', state);
if (!state.scopeIsStrict) {
utils.append('"use strict";', state);
}
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') {
utils.append(';', state);
}
return false;
}
visitClassFunctionExpression.test = function(node, path, state) {
return node.type === Syntax.FunctionExpression
&& path[0].type === Syntax.MethodDefinition;
};
/**
* @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 ';
utils.declareIdentInLocalScope(keyName, 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
);
utils.declareIdentInLocalScope(superProtoIdentStr, 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) {
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) {
utils.append(superClassName + '.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.visitorList = [
visitClassDeclaration,
visitClassExpression,
visitClassFunctionExpression,
visitClassMethod,
visitPrivateIdentifier,
visitSuperCallExpression,
visitSuperMemberExpression
];

View File

@@ -0,0 +1,52 @@
/**
* Copyright 2013 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*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}
* };
*
* // Destrucruting.
* 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;
};
exports.visitorList = [
visitObjectLiteralShortNotation
];

View File

@@ -0,0 +1,81 @@
/**
* Copyright 2013 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*jslint node:true*/
/**
* Desugars ES6 rest parameters into ES3 arguments slicing.
*
* function printf(template, ...args) {
* args.forEach(...);
* };
*
* function printf(template) {
* var args = [].slice.call(arguments, 1);
* 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) {
// Render params.
if (node.params.length) {
utils.catchup(node.params[node.params.length - 1].range[1], state);
} else {
// -3 is for ... of the rest.
utils.catchup(node.rest.range[0] - 3, state);
}
utils.catchupWhiteSpace(node.rest.range[1], state);
}
visitFunctionParamsWithRestParam.test = function(node, path, state) {
return _nodeIsFunctionWithRestParam(node);
};
function renderRestParamSetup(functionNode) {
return 'var ' + functionNode.rest.name + '=Array.prototype.slice.call(' +
'arguments,' +
functionNode.params.length +
');';
}
function visitFunctionBodyWithRestParam(traverse, node, path, state) {
utils.catchup(node.range[0] + 1, state);
var parentNode = path[0];
utils.append(renderRestParamSetup(parentNode), state);
traverse(node.body, path, state);
return false;
}
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,147 @@
/**
* Copyright 2013 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*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);
}
utils.move(templateElement.range[1], state);
if (!templateElement.tail) {
var substitution = node.expressions[ii];
if (substitution.type === Syntax.Identifier ||
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
];