initial commit
This commit is contained in:
473
node_modules/jstransform/src/__tests__/jstransform-test.js
generated
vendored
Normal file
473
node_modules/jstransform/src/__tests__/jstransform-test.js
generated
vendored
Normal 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]
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
58
node_modules/jstransform/src/__tests__/jstransform-utils-test.js
generated
vendored
Normal file
58
node_modules/jstransform/src/__tests__/jstransform-utils-test.js
generated
vendored
Normal 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
72
node_modules/jstransform/src/cli.js
generated
vendored
Normal 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
79
node_modules/jstransform/src/docblock.js
generated
vendored
Normal 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
27
node_modules/jstransform/src/inline-source-map.js
generated
vendored
Normal 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
292
node_modules/jstransform/src/jstransform.js
generated
vendored
Normal 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
165
node_modules/jstransform/src/simple.js
generated
vendored
Normal 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
701
node_modules/jstransform/src/utils.js
generated
vendored
Normal 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;
|
||||
Reference in New Issue
Block a user