Move to using jstransform and esprima-fb npm modules

This commit is contained in:
JeffMo
2013-08-19 14:37:25 -07:00
parent 2c8f907b2c
commit 2d048f1f34
13 changed files with 26 additions and 1086 deletions

View File

@@ -2,7 +2,7 @@
"use strict";
var visitors = require('../vendor/fbtransform/visitors').transformVisitors;
var transform = require('../vendor/fbtransform/lib/transform').transform;
var transform = require('jstransform').transform;
var propagate = require("../vendor/constants").propagate;
require("commoner").resolve(function(id) {

View File

@@ -2,7 +2,7 @@
var React = require('./build/modules/React');
var visitors = require('./vendor/fbtransform/visitors').transformVisitors;
var transform = require('./vendor/fbtransform/lib/transform').transform;
var transform = require('jstransform').transform;
module.exports = {
React: React,

View File

@@ -35,11 +35,10 @@
"test": "grunt build && grunt test"
},
"dependencies": {
"base62": "~0.1.1",
"commoner": "~0.8.4",
"esprima": "https://github.com/facebook/esprima/tarball/a3e0ea3979eb8d54d8bfade220c272903f928b1e",
"recast": "~0.4.8",
"source-map": "~0.1.22"
"esprima-fb": "1001.1001.1000-dev-harmony-fb",
"jstransform": "~1.0.0",
"recast": "~0.4.8"
},
"devDependencies": {
"browserify": "~2.24.1",

View File

@@ -20,10 +20,10 @@
var runScripts;
var headEl;
var transform = require('./fbtransform/lib/transform').transform;
var transform = require('jstransform').transform;
var visitors = require('./fbtransform/visitors').transformVisitors;
var transform = transform.bind(null, visitors.react);
var docblock = require('./fbtransform/lib/docblock');
var docblock = require('jstransform/src/docblock');
exports.transform = transform;

View File

@@ -1,86 +0,0 @@
/**
* 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*(\/\*\*(.|\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 = /(\n|^) *\*/g;
var multilineRe = /(?:^|\n) *(@[^\n]*?) *\n *([^@\n\s][^@\n]+?) *\n/g;
var propertyRe = /(?:^|\n) *@(\S+) *([^\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;

View File

@@ -1,141 +0,0 @@
/**
* 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*/
/*jslint node: true*/
"use strict";
/**
* Syntax transfomer for javascript. Takes the source in, spits the source
* out. Tries to maintain readability and preserve whitespace and line numbers
* where posssible.
*
* Support
* - ES6 class transformation + private property munging, see ./classes.js
* - React XHP style syntax transformations, see ./react.js
* - Bolt XHP style syntax transformations, see ./bolt.js
*
* The general flow is the following:
* - Parse the source with our customized esprima-parser
* https://github.com/voloko/esprima. We have to customize the parser to
* support non-standard XHP-style syntax. We parse the source range: true
* option that forces esprima to return positions in the source within
* resulting parse tree.
*
* - Traverse resulting syntax tree, trying to apply a set of visitors to each
* node. Each visitor should provide a .test() function that tests if the
* visitor can process a given node.
*
* - Visitor is responsible for code generation for a given syntax node.
* Generated code is stored in state.g.buffer that is passed to every
* visitor. It's up to the visitor to process the code the way it sees fit.
* All of the current visitors however use both the node and the original
* source to generate transformed code. They use nodes to generate new
* code and they copy the original source, preserving whitespace and comments,
* for the parts they don't care about.
*/
var esprima = require('esprima');
var createState = require('./utils').createState;
var catchup = require('./utils').catchup;
/**
* @param {object} object
* @param {function} visitor
* @param {array} path
* @param {object} state
*/
function traverse(object, path, state) {
var key, child;
if (walker(traverse, object, path, state) === false) {
return;
}
path.unshift(object);
for (key in object) {
// skip obviously wrong attributes
if (key === 'range' || key === 'loc') {
continue;
}
if (object.hasOwnProperty(key)) {
child = object[key];
if (typeof child === 'object' && child !== null) {
child.range && catchup(child.range[0], state);
traverse(child, path, state);
child.range && catchup(child.range[1], state);
}
}
}
path.shift();
}
function walker(traverse, object, path, state) {
var visitors = state.g.visitors;
for (var i = 0; i < visitors.length; i++) {
if (visitors[i].test(object, path, state)) {
return visitors[i](traverse, object, path, state);
}
}
}
function runPass(source, visitors, 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 = createState(source, options);
state.g.originalProgramAST = ast;
state.g.visitors = visitors;
if (options.sourceMap) {
var SourceMapGenerator = require('source-map').SourceMapGenerator;
state.g.sourceMap = new SourceMapGenerator({ file: 'transformed.js' });
}
traverse(ast, [], state);
catchup(source.length, state);
return 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 state = runPass(source, visitors, options);
var sourceMap = state.g.sourceMap;
if (sourceMap) {
return {
sourceMap: sourceMap,
sourceMapFilename: options.filename || 'source.js',
code: state.g.buffer
};
} else {
return {
code: state.g.buffer
};
}
}
exports.transform = transform;

View File

@@ -1,332 +0,0 @@
/**
* 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*/
/**
* State represents the given parser state. It has a 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, transformOptions) {
return {
/**
* Name of the super class variable
* @type {String}
*/
superVar: '',
/**
* Name of the enclosing class scope
* @type {String}
*/
scopeName: '',
/**
* 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) {
return {
g: state.g,
superVar: update.superVar || state.superVar,
scopeName: update.scopeName || state.scopeName
};
}
/**
* 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;
}
/**
* Applies `catchup` but passing in a function that removes any non-whitespace
* characters.
*/
var re = /(\S)/g;
function stripNonWhite(value) {
return value.replace(re, function() {
return '';
});
}
/**
* Catches up as `catchup` but turns each non-white character into a space.
*/
function catchupWhiteSpace(end, state) {
catchup(end, state, stripNonWhite);
}
/**
* 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} string
* @param {Object} state
*/
function append(string, state) {
if (state.g.sourceMap && string) {
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 = string.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 += string;
}
/**
* 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;
}
exports.catchup = catchup;
exports.catchupWhiteSpace = catchupWhiteSpace;
exports.append = append;
exports.move = move;
exports.updateIndent = updateIndent;
exports.indentBefore = indentBefore;
exports.updateState = updateState;
exports.createState = createState;
exports.getDocblock = getDocblock;

View File

@@ -3,7 +3,7 @@
/*global exports:true*/
"use strict";
var transform = require('./lib/transform').transform;
var transform = require('jstransform').transform;
var visitors = require('./visitors');
/**

View File

@@ -1,492 +0,0 @@
/**
* 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*/
"use strict";
/**
* Desugarizer for ES6 minimal class proposal. See
* http://wiki.ecmascript.org/doku.php?id=harmony:proposals
*
* Does not require any runtime. Preserves whitespace and comments.
* Supports a class declaration with methods, super calls and inheritance.
* Currently does not support for getters and setters, since there's a very
* low probability we're going to use them anytime soon.
*
* Additional features:
* - Any member with private name (the name with prefix _, such _name) inside
* the class's scope will be munged. This would will to eliminate the case
* of sub-class accidentally overriding the super-class's provate properties
* also discouage people from accessing private members that they should not
* access. However, quoted property names don't get munged.
*
* class SkinnedMesh extends require('THREE').Mesh {
*
* update(camera) {
* camera.code = 'iphone'
* super.update(camera);
* }
*
* /
* * @constructor
* /
* constructor(geometry, materials) {
* super(geometry, materials);
*
* super.update(1);
*
* this.identityMatrix = new THREE.Matrix4();
* this.bones = [];
* this.boneMatrices = [];
* this._name = 'foo';
* }
*
* /
* * some other code
* /
* readMore() {
*
* }
*
* _doSomething() {
*
* }
* }
*
* should be converted to
*
* var SkinnedMesh = (function() {
* var __super = require('parent').Mesh;
*
* /
* * @constructor
* /
* function SkinnedMesh(geometry, materials) {
* __super.call(this, geometry, materials);
*
* __super.prototype.update.call(this, 1);
*
* this.identityMatrix = new THREE.Matrix4();
* this.bones = [];
* this.boneMatrices = [];
* this.$SkinnedMesh_name = 'foo';
* }
* SkinnedMesh.prototype = Object.create(__super.prototype);
* SkinnedMesh.prototype.constructor = SkinnedMesh;
*
* /
* * @param camera
* /
* SkinnedMesh.prototype.update = function(camera) {
* camera.code = 'iphone'
* __super.prototype.update.call(this, camera);
* };
*
* SkinnedMesh.prototype.readMore = function() {
*
* };
*
* SkinnedMesh.prototype.$SkinnedMesh_doSomething = function() {
*
* };
*
* return SkinnedMesh;
* })();
*
*/
var Syntax = require('esprima').Syntax;
var base62 = require('base62');
var catchup = require('../lib/utils').catchup;
var append = require('../lib/utils').append;
var move = require('../lib/utils').move;
var indentBefore = require('../lib/utils').indentBefore;
var updateIndent = require('../lib/utils').updateIndent;
var updateState = require('../lib/utils').updateState;
function findConstructorIndex(object) {
var classElements = object.body && object.body.body || [];
for (var i = 0; i < classElements.length; i++) {
if (classElements[i].type === Syntax.MethodDefinition &&
classElements[i].key.name === 'constructor') {
return i;
}
}
return -1;
}
var _mungedSymbolMaps = {};
function getMungedName(scopeName, name, minify) {
if (minify) {
if (!_mungedSymbolMaps[scopeName]) {
_mungedSymbolMaps[scopeName] = {
symbolMap: {},
identifierUUIDCounter: 0
};
}
var symbolMap = _mungedSymbolMaps[scopeName].symbolMap;
if (!symbolMap[name]) {
symbolMap[name] =
base62.encode(_mungedSymbolMaps[scopeName].identifierUUIDCounter);
_mungedSymbolMaps[scopeName].identifierUUIDCounter++;
}
name = symbolMap[name];
}
return '$' + scopeName + name;
}
function shouldMungeName(scopeName, name, state) {
// only run when @preventMunge is not present in the docblock
if (state.g.preventMunge === undefined) {
var docblock = require('../lib/docblock');
state.g.preventMunge = docblock.parseAsObject(
docblock.extract(state.g.source)).preventMunge !== undefined;
}
// Starts with only a single underscore (i.e. don't count double-underscores)
return !state.g.preventMunge && scopeName ? /^_(?!_)/.test(name) : false;
}
function getProtoOfPrototypeVariableName(superVar) {
return superVar + 'ProtoOfPrototype';
}
function getSuperKeyName(superVar) {
return superVar + 'Key';
}
function getSuperProtoOfPrototypeVariable(superVariableName, indent) {
var string = (indent +
'var $proto = $superName && $superName.prototype ? ' +
'$superName.prototype : $superName;\n'
).replace(/\$proto/g, getProtoOfPrototypeVariableName(superVariableName))
.replace(/\$superName/g, superVariableName);
return string;
}
function getInheritanceSetup(superClassToken, className, indent, superName) {
var string = '';
if (superClassToken) {
string += getStaticMethodsOnConstructorSetup(className, indent, superName);
string += getPrototypeOnConstructorSetup(className, indent, superName);
string += getConstructorPropertySetup(className, indent);
}
return string;
}
function getStaticMethodsOnConstructorSetup(className, indent, superName) {
var string = ( indent +
'for (var $keyName in $superName) {\n' + indent +
' if ($superName.hasOwnProperty($keyName)) {\n' + indent +
' $className[$keyName] = $superName[$keyName];\n' + indent +
' }\n' + indent +
'}\n')
.replace(/\$className/g, className)
.replace(/\$keyName/g, getSuperKeyName(superName))
.replace(/\$superName/g, superName);
return string;
}
function getPrototypeOnConstructorSetup(className, indent, superName) {
var string = ( indent +
'$className.prototype = Object.create($protoPrototype);\n')
.replace(/\$protoPrototype/g, getProtoOfPrototypeVariableName(superName))
.replace(/\$className/g, className);
return string;
}
function getConstructorPropertySetup(className, indent) {
var string = ( indent +
'$className.prototype.constructor = $className;\n')
.replace(/\$className/g, className);
return string;
}
function getSuperConstructorSetup(superClassToken, indent, superName) {
if (!superClassToken) return '';
var string = ( '\n' + indent +
' if ($superName && $superName.prototype) {\n' + indent +
' $superName.apply(this, arguments);\n' + indent +
' }\n' + indent)
.replace(/\$superName/g, superName);
return string;
}
function getMemberFunctionCall(superVar, propertyName, superArgs) {
var string = (
'$superPrototype.$propertyName.call($superArguments)')
.replace(/\$superPrototype/g, getProtoOfPrototypeVariableName(superVar))
.replace(/\$propertyName/g, propertyName)
.replace(/\$superArguments/g, superArgs);
return string;
}
function getCallParams(classElement, state) {
var params = classElement.value.params;
if (!params.length) {
return '';
}
return state.g.source.substring(
params[0].range[0],
params[params.length - 1].range[1]);
}
function getSuperArguments(callExpression, state) {
var args = callExpression.arguments;
if (!args.length) {
return 'this';
}
return 'this, ' + state.g.source.substring(
args[0].range[0],
args[args.length - 1].range[1]);
}
// The seed is used to generate the name for an anonymous class,
// and this seed should be unique per browser's session.
// The value of the seed looks like this: 1229588505.2969012.
var classIDSeed = Date.now() % (60 * 60 * 1000) + Math.random();
/**
* Generates a name for an anonymous class. The generated value looks like
* this: "Classkc6pcn_mniza1yvi"
* @param {String} scopeName
* @return {string} the scope name for Anonymous Class
*/
function generateAnonymousClassName(scopeName) {
classIDSeed++;
return 'Class' +
(classIDSeed).toString(36).replace('.', '_') +
(scopeName || '');
}
function renderMethods(traverse, object, name, path, state) {
var classElements = object.body && object.body.body || [];
move(object.body.range[0] + 1, state);
for (var i = 0; i < classElements.length; i++) {
if (classElements[i].key.name !== 'constructor') {
catchup(classElements[i].range[0], state);
var memberName = classElements[i].key.name;
if (shouldMungeName(state.scopeName, memberName, state)) {
memberName = getMungedName(
state.scopeName,
memberName,
state.g.opts.minify
);
}
var prototypeOrStatic;
if (classElements[i]['static']) {
prototypeOrStatic = '';
} else {
prototypeOrStatic = 'prototype.';
}
append(name + '.' + prototypeOrStatic + memberName + ' = ', state);
renderMethod(traverse, classElements[i], null, path, state);
append(';', state);
}
move(classElements[i].range[1], state);
}
if (classElements.length) {
append('\n', state);
}
move(object.range[1], state);
}
function renderMethod(traverse, method, name, path, state) {
append(name ? 'function ' + name + '(' : 'function(', state);
append(getCallParams(method, state) + ') {', state);
move(method.value.body.range[0] + 1, state);
traverse(method.value.body, path, state);
catchup(method.value.body.range[1] - 1, state);
append('}', state);
}
function renderSuperClass(traverse, superClass, path, state) {
append('var ' + state.superVar + ' = ', state);
move(superClass.range[0], state);
traverse(superClass, path, state);
catchup(superClass.range[1], state);
append(';\n', state);
}
function renderConstructor(traverse, object, name, indent, path, state) {
var classElements = object.body && object.body.body || [];
var constructorIndex = findConstructorIndex(object);
var constructor = constructorIndex === -1 ?
null :
classElements[constructorIndex];
if (constructor) {
move(constructorIndex === 0 ?
object.body.range[0] + 1 :
classElements[constructorIndex - 1].range[1], state);
catchup(constructor.range[0], state);
renderMethod(traverse, constructor, name, path, state);
append('\n', state);
} else {
if (object.superClass) {
append('\n' + indent, state);
}
append('function ', state);
if (object.id) {
move(object.id.range[0], state);
}
append(name, state);
if (object.id) {
move(object.id.range[1], state);
}
append('(){ ', state);
if (object.body) {
move(object.body.range[0], state);
}
append(getSuperConstructorSetup(
object.superClass,
indent,
state.superVar), state);
append('}\n', state);
}
}
var superId = 0;
function renderClassBody(traverse, object, path, state) {
var name = object.id ? object.id.name : 'constructor';
var superClass = object.superClass;
var indent = updateIndent(
indentBefore(object.range[0], state) + ' ',
state);
state = updateState(
state,
{
scopeName: object.id ? object.id.name :
generateAnonymousClassName(state.scopeName),
superVar: superClass ? '__super' + superId++ : ''
});
// super class
if (superClass) {
append(indent, state);
renderSuperClass(traverse, superClass, path, state);
append(getSuperProtoOfPrototypeVariable(state.superVar, indent), state);
}
renderConstructor(traverse, object, name, indent, path, state);
append(getInheritanceSetup(superClass, name, indent, state.superVar), state);
renderMethods(traverse, object, name, path, state);
}
/**
* @public
*/
function visitClassExpression(traverse, object, path, state) {
var indent = updateIndent(
indentBefore(object.range[0], state) + ' ',
state);
var name = object.id ? object.id.name : 'constructor';
append('(function() {\n', state);
renderClassBody(traverse, object, path, state);
append(indent + 'return ' + name + ';\n', state);
append(indent.substring(0, indent.length - 2) + '})()', state);
return false
}
visitClassExpression.test = function(object, path, state) {
return object.type === Syntax.ClassExpression;
};
/**
* @public
*/
function visitClassDeclaration(traverse, object, path, state) {
state.g.indentBy--;
renderClassBody(traverse, object, path, state);
state.g.indentBy++;
return false;
}
visitClassDeclaration.test = function(object, path, state) {
return object.type === Syntax.ClassDeclaration;
};
/**
* @public
*/
function visitSuperCall(traverse, object, path, state) {
if (path[0].type === Syntax.CallExpression) {
append(state.superVar +
'.call(' + getSuperArguments(path[0], state) + ')', state);
move(path[0].range[1], state);
} else if (path[0].type === Syntax.MemberExpression) {
append(getMemberFunctionCall(
state.superVar,
path[0].property.name,
getSuperArguments(path[1], state)), state);
move(path[1].range[1], state);
}
return false;
}
visitSuperCall.test = function(object, path, state) {
return state.superVar && object.type === Syntax.Identifier &&
object.name === 'super';
};
/**
* @public
*/
function visitPrivateProperty(traverse, object, path, state) {
var type = path[0] ? path[0].type : null;
if (type !== Syntax.Property) {
if (type === Syntax.MemberExpression) {
type = path[0].object ? path[0].object.type : null;
if (type === Syntax.Identifier &&
path[0].object.range[0] === object.range[0]) {
// Identifier is a variable that appears "private".
return;
}
} else {
// Other syntax that are neither Property nor MemberExpression.
return;
}
}
var oldName = object.name;
var newName = getMungedName(
state.scopeName,
oldName,
state.g.opts.minify
);
append(newName, state);
move(object.range[1], state);
}
visitPrivateProperty.test = function(object, path, state) {
return object.type === Syntax.Identifier &&
shouldMungeName(state.scopeName, object.name, state);
};
exports.visitClassDeclaration = visitClassDeclaration;
exports.visitClassExpression = visitClassExpression;
exports.visitSuperCall = visitSuperCall;
exports.visitPrivateProperty = visitPrivateProperty;

View File

@@ -16,12 +16,12 @@
/*global exports:true*/
"use strict";
var Syntax = require('esprima').Syntax;
var Syntax = require('esprima-fb').Syntax;
var catchup = require('../lib/utils').catchup;
var append = require('../lib/utils').append;
var move = require('../lib/utils').move;
var getDocblock = require('../lib/utils').getDocblock;
var catchup = require('jstransform/src/utils').catchup;
var append = require('jstransform/src/utils').append;
var move = require('jstransform/src/utils').move;
var getDocblock = require('jstransform/src/utils').getDocblock;
var FALLBACK_TAGS = require('./xjs').knownTags;
var renderXJSExpressionContainer =

View File

@@ -16,10 +16,10 @@
/*global exports:true*/
"use strict";
var Syntax = require('esprima').Syntax;
var catchup = require('../lib/utils').catchup;
var append = require('../lib/utils').append;
var getDocblock = require('../lib/utils').getDocblock;
var Syntax = require('esprima-fb').Syntax;
var catchup = require('jstransform/src/utils').catchup;
var append = require('jstransform/src/utils').append;
var getDocblock = require('jstransform/src/utils').getDocblock;
/**
* Transforms the following:

View File

@@ -15,10 +15,10 @@
*/
/*global exports:true*/
"use strict";
var append = require('../lib/utils').append;
var catchup = require('../lib/utils').catchup;
var move = require('../lib/utils').move;
var Syntax = require('esprima').Syntax;
var append = require('jstransform/src/utils').append;
var catchup = require('jstransform/src/utils').catchup;
var move = require('jstransform/src/utils').move;
var Syntax = require('esprima-fb').Syntax;
var knownTags = {
a: true,

View File

@@ -1,5 +1,5 @@
/*global exports:true*/
var classes = require('./transforms/classes');
var es6Classes = require('jstransform/visitors/es6-class-visitors').visitorList;
var react = require('./transforms/react');
var reactDisplayName = require('./transforms/reactDisplayName');
@@ -7,21 +7,13 @@ var reactDisplayName = require('./transforms/reactDisplayName');
* Map from transformName => orderedListOfVisitors.
*/
var transformVisitors = {
'es6-classes': [
classes.visitClassExpression,
classes.visitClassDeclaration,
classes.visitSuperCall,
classes.visitPrivateProperty
'es6-classes': es6Classes,
'react': [
react.visitReactTag,
reactDisplayName.visitReactDisplayName
]
};
transformVisitors.react = transformVisitors[
"es6-classes"
].concat([
react.visitReactTag,
reactDisplayName.visitReactDisplayName
]);
/**
* Specifies the order in which each transform should run.
*/