jstransform
Version:
A simple AST visitor-based JS transformer
191 lines (168 loc) • 6.07 kB
JavaScript
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
var esprima = require('esprima-fb');
var utils = require('../src/utils');
var Syntax = esprima.Syntax;
function _isFunctionNode(node) {
return node.type === Syntax.FunctionDeclaration
|| node.type === Syntax.FunctionExpression
|| node.type === Syntax.ArrowFunctionExpression;
}
function visitClassProperty(traverse, node, path, state) {
utils.catchup(node.range[0], state);
utils.catchupWhiteOut(node.range[1], state);
return false;
}
visitClassProperty.test = function(node, path, state) {
return node.type === Syntax.ClassProperty;
};
function visitTypeAlias(traverse, node, path, state) {
utils.catchupWhiteOut(node.range[1], state);
return false;
}
visitTypeAlias.test = function(node, path, state) {
return node.type === Syntax.TypeAlias;
};
function visitTypeCast(traverse, node, path, state) {
path.unshift(node);
traverse(node.expression, path, state);
path.shift();
utils.catchup(node.typeAnnotation.range[0], state);
utils.catchupWhiteOut(node.typeAnnotation.range[1], state);
return false;
}
visitTypeCast.test = function(node, path, state) {
return node.type === Syntax.TypeCastExpression;
};
function visitInterfaceDeclaration(traverse, node, path, state) {
utils.catchupWhiteOut(node.range[1], state);
return false;
}
visitInterfaceDeclaration.test = function(node, path, state) {
return node.type === Syntax.InterfaceDeclaration;
};
function visitDeclare(traverse, node, path, state) {
utils.catchupWhiteOut(node.range[1], state);
return false;
}
visitDeclare.test = function(node, path, state) {
switch (node.type) {
case Syntax.DeclareVariable:
case Syntax.DeclareFunction:
case Syntax.DeclareClass:
case Syntax.DeclareModule:
return true;
}
return false;
};
function visitFunctionParametricAnnotation(traverse, node, path, state) {
utils.catchup(node.range[0], state);
utils.catchupWhiteOut(node.range[1], state);
return false;
}
visitFunctionParametricAnnotation.test = function(node, path, state) {
return node.type === Syntax.TypeParameterDeclaration
&& path[0]
&& _isFunctionNode(path[0])
&& node === path[0].typeParameters;
};
function visitFunctionReturnAnnotation(traverse, node, path, state) {
utils.catchup(node.range[0], state);
utils.catchupWhiteOut(node.range[1], state);
return false;
}
visitFunctionReturnAnnotation.test = function(node, path, state) {
return path[0] && _isFunctionNode(path[0]) && node === path[0].returnType;
};
function visitOptionalFunctionParameterAnnotation(traverse, node, path, state) {
utils.catchup(node.range[0] + node.name.length, state);
utils.catchupWhiteOut(node.range[1], state);
return false;
}
visitOptionalFunctionParameterAnnotation.test = function(node, path, state) {
return node.type === Syntax.Identifier
&& node.optional
&& path[0]
&& _isFunctionNode(path[0]);
};
function visitTypeAnnotatedIdentifier(traverse, node, path, state) {
utils.catchup(node.typeAnnotation.range[0], state);
utils.catchupWhiteOut(node.typeAnnotation.range[1], state);
return false;
}
visitTypeAnnotatedIdentifier.test = function(node, path, state) {
return node.type === Syntax.Identifier && node.typeAnnotation;
};
function visitTypeAnnotatedObjectOrArrayPattern(traverse, node, path, state) {
utils.catchup(node.typeAnnotation.range[0], state);
utils.catchupWhiteOut(node.typeAnnotation.range[1], state);
return false;
}
visitTypeAnnotatedObjectOrArrayPattern.test = function(node, path, state) {
var rightType = node.type === Syntax.ObjectPattern
|| node.type === Syntax.ArrayPattern;
return rightType && node.typeAnnotation;
};
/**
* Methods cause trouble, since esprima parses them as a key/value pair, where
* the location of the value starts at the method body. For example
* { bar(x:number,...y:Array<number>):number {} }
* is parsed as
* { bar: function(x: number, ...y:Array<number>): number {} }
* except that the location of the FunctionExpression value is 40-something,
* which is the location of the function body. This means that by the time we
* visit the params, rest param, and return type organically, we've already
* catchup()'d passed them.
*/
function visitMethod(traverse, node, path, state) {
path.unshift(node);
traverse(node.key, path, state);
path.unshift(node.value);
traverse(node.value.params, path, state);
node.value.rest && traverse(node.value.rest, path, state);
node.value.returnType && traverse(node.value.returnType, path, state);
traverse(node.value.body, path, state);
path.shift();
path.shift();
return false;
}
visitMethod.test = function(node, path, state) {
return (node.type === "Property" && (node.method || node.kind === "set" || node.kind === "get"))
|| (node.type === "MethodDefinition");
};
function visitImportType(traverse, node, path, state) {
utils.catchupWhiteOut(node.range[1], state);
return false;
}
visitImportType.test = function(node, path, state) {
return node.type === 'ImportDeclaration'
&& (node.importKind === 'type' || node.importKind === 'typeof');
};
function visitExportType(traverse, node, path, state) {
utils.catchupWhiteOut(node.range[1], state);
}
visitExportType.test = function(node, path, state) {
return node.type === 'ExportDeclaration'
&& node.declaration.type === 'TypeAlias';
};
exports.visitorList = [
visitClassProperty,
visitDeclare,
visitExportType,
visitImportType,
visitInterfaceDeclaration,
visitFunctionParametricAnnotation,
visitFunctionReturnAnnotation,
visitMethod,
visitOptionalFunctionParameterAnnotation,
visitTypeAlias,
visitTypeCast,
visitTypeAnnotatedIdentifier,
visitTypeAnnotatedObjectOrArrayPattern
];