mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Adds the transformFiles API
This commit is contained in:
+218
-13
@@ -1,11 +1,12 @@
|
||||
/// <reference path="core.ts"/>
|
||||
/// <reference path="utilities.ts"/>
|
||||
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
let NodeConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
|
||||
let SourceFileConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
|
||||
|
||||
export function createNode(kind: SyntaxKind, pos?: number, end?: number): Node {
|
||||
function createNode(kind: SyntaxKind, pos?: number, end?: number): Node {
|
||||
if (kind === SyntaxKind.SourceFile) {
|
||||
return new (SourceFileConstructor || (SourceFileConstructor = objectAllocator.getSourceFileConstructor()))(kind, pos, end);
|
||||
}
|
||||
@@ -14,7 +15,10 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
function allocateNode(kind: SyntaxKind, location?: TextRange) {
|
||||
return location ? createNode(kind, location.pos, location.end) : createSynthesizedNode(kind);
|
||||
}
|
||||
|
||||
export function createNodeArray<T extends Node>(elements?: T[], pos?: number, end?: number): NodeArray<T> {
|
||||
const array = <NodeArray<T>>(elements || []);
|
||||
array.pos = pos;
|
||||
@@ -23,7 +27,6 @@ namespace ts {
|
||||
return array;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function createModifiersArray(elements?: Modifier[], pos?: number, end?: number): ModifiersArray {
|
||||
const array = <ModifiersArray>(elements || []);
|
||||
array.pos = pos;
|
||||
@@ -33,19 +36,16 @@ namespace ts {
|
||||
return array;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function createSynthesizedNode(kind: SyntaxKind, startsOnNewLine?: boolean): Node {
|
||||
const node = <SynthesizedNode>createNode(kind, /*pos*/ -1, /*end*/ -1);
|
||||
node.startsOnNewLine = startsOnNewLine;
|
||||
return node;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function createSynthesizedNodeArray<T extends Node>(elements?: T[]): NodeArray<T> {
|
||||
return createNodeArray(elements, /*pos*/ -1, /*end*/ -1);
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function createSynthesizedModifiersArray(elements?: Modifier[]): ModifiersArray {
|
||||
return createModifiersArray(elements, /*pos*/ -1, /*end*/ -1);
|
||||
}
|
||||
@@ -61,7 +61,6 @@ namespace ts {
|
||||
* @param parent The parent for the new node.
|
||||
* @param original An optional pointer to the original source tree node.
|
||||
*/
|
||||
/* @internal */
|
||||
export function cloneNode<T extends Node>(node: T, location?: TextRange, flags?: NodeFlags, parent?: Node, original?: Node): T {
|
||||
// We don't use "clone" from core.ts here, as we need to preserve the prototype chain of
|
||||
// the original node. We also need to exclude specific properties and only include own-
|
||||
@@ -93,46 +92,252 @@ namespace ts {
|
||||
return clone;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function createNodeArrayNode<T extends Node>(elements: T[]): NodeArrayNode<T> {
|
||||
const node = <NodeArrayNode<T>>createSynthesizedNode(SyntaxKind.NodeArrayNode);
|
||||
node.nodes = createNodeArray(elements);
|
||||
return node;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function createReturn(expression?: Expression): ReturnStatement {
|
||||
const node = <ReturnStatement>createSynthesizedNode(SyntaxKind.ReturnStatement);
|
||||
node.expression = expression;
|
||||
return node;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function createStatement(expression: Expression): ExpressionStatement {
|
||||
const node = <ExpressionStatement>createSynthesizedNode(SyntaxKind.ExpressionStatement);
|
||||
node.expression = expression;
|
||||
return node;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function createVariableStatement(declarationList: VariableDeclarationList): VariableStatement {
|
||||
const node = <VariableStatement>createSynthesizedNode(SyntaxKind.VariableStatement);
|
||||
node.declarationList = declarationList;
|
||||
return node;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function createVariableDeclarationList(declarations: VariableDeclaration[]): VariableDeclarationList {
|
||||
const node = <VariableDeclarationList>createSynthesizedNode(SyntaxKind.VariableDeclarationList);
|
||||
node.declarations = createNodeArray(declarations);
|
||||
return node;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function createBlock(statements: Statement[]): Block {
|
||||
const block = <Block>createSynthesizedNode(SyntaxKind.Block);
|
||||
block.statements = createNodeArray(statements);
|
||||
return block;
|
||||
}
|
||||
|
||||
export function createVariableDeclaration(name: BindingPattern | Identifier, initializer?: Expression, location?: TextRange): VariableDeclaration {
|
||||
const node = <VariableDeclaration>allocateNode(SyntaxKind.VariableDeclaration, location);
|
||||
node.name = name;
|
||||
node.initializer = initializer;
|
||||
return node;
|
||||
}
|
||||
|
||||
export function createIdentifier(text: string): Identifier {
|
||||
const node = <Identifier>allocateNode(SyntaxKind.Identifier);
|
||||
node.text = text;
|
||||
return node;
|
||||
}
|
||||
|
||||
export function createTempVariable(tempKind: TempVariableKind): Identifier {
|
||||
const name = <Identifier>allocateNode(SyntaxKind.Identifier);
|
||||
name.tempKind = tempKind;
|
||||
getNodeId(name);
|
||||
return name;
|
||||
}
|
||||
|
||||
export function createLiteral(value: string): StringLiteral;
|
||||
export function createLiteral(value: number): LiteralExpression;
|
||||
export function createLiteral(value: string | number | boolean | void): PrimaryExpression;
|
||||
export function createLiteral<T extends PrimaryExpression>(value: string | number | boolean | void): T {
|
||||
if (typeof value === "string") {
|
||||
const node = <T & StringLiteral>allocateNode(SyntaxKind.StringLiteral);
|
||||
node.text = value;
|
||||
return node;
|
||||
}
|
||||
else if (typeof value === "number") {
|
||||
const node = <T & LiteralExpression>allocateNode(SyntaxKind.NumericLiteral);
|
||||
node.text = value.toString();
|
||||
return node;
|
||||
}
|
||||
else if (typeof value === "boolean") {
|
||||
return <T>allocateNode(value ? SyntaxKind.TrueKeyword : SyntaxKind.FalseKeyword);
|
||||
}
|
||||
else if (value === null) {
|
||||
return <T>allocateNode(SyntaxKind.NullKeyword);
|
||||
}
|
||||
}
|
||||
|
||||
export function createVoid(expression: UnaryExpression) {
|
||||
const node = <VoidExpression>allocateNode(SyntaxKind.VoidExpression);
|
||||
node.expression = expression;
|
||||
return node;
|
||||
}
|
||||
|
||||
export function createVoidZero() {
|
||||
return createVoid(createLiteral(0));
|
||||
}
|
||||
|
||||
export function createPropertyAccess(expression: Expression, name: string | Identifier) {
|
||||
const node = <PropertyAccessExpression>allocateNode(SyntaxKind.PropertyAccessExpression);
|
||||
node.expression = parenthesizeForAccess(expression);
|
||||
node.dotToken = createSynthesizedNode(SyntaxKind.DotToken);
|
||||
node.name = coerceIdentifier(name);
|
||||
return node;
|
||||
}
|
||||
|
||||
export function createElementAccess(expression: Expression, index: string | number | Expression) {
|
||||
const node = <ElementAccessExpression>allocateNode(SyntaxKind.ElementAccessExpression);
|
||||
node.expression = parenthesizeForAccess(expression);
|
||||
node.argumentExpression = coerceExpression(index);
|
||||
return node;
|
||||
}
|
||||
|
||||
export function createConditional(condition: Expression, whenTrue: Expression, whenFalse: Expression) {
|
||||
const node = <ConditionalExpression>allocateNode(SyntaxKind.ConditionalExpression);
|
||||
node.condition = condition;
|
||||
node.questionToken = createSynthesizedNode(SyntaxKind.QualifiedName);
|
||||
node.whenTrue = whenTrue;
|
||||
node.colonToken = createSynthesizedNode(SyntaxKind.ColonToken);
|
||||
node.whenFalse = whenFalse;
|
||||
return node;
|
||||
}
|
||||
|
||||
export function createBinary(left: Expression, operator: SyntaxKind, right: Expression, location?: TextRange) {
|
||||
const node = <BinaryExpression>allocateNode(SyntaxKind.BinaryExpression, location);
|
||||
node.left = parenthesizeForBinary(left, operator, BinaryOperand.Left);
|
||||
node.operatorToken = createSynthesizedNode(operator);
|
||||
node.right = parenthesizeForBinary(right, operator, BinaryOperand.Right);
|
||||
return node;
|
||||
}
|
||||
|
||||
export function createAssignment(left: Expression, right: Expression, location?: TextRange) {
|
||||
return createBinary(left, SyntaxKind.EqualsToken, right, location);
|
||||
}
|
||||
|
||||
export function createStrictEquality(left: Expression, right: Expression) {
|
||||
return createBinary(left, SyntaxKind.EqualsEqualsEqualsToken, right);
|
||||
}
|
||||
|
||||
export function createComma(left: Expression, right: Expression) {
|
||||
return <Expression>createBinary(left, SyntaxKind.CommaToken, right);
|
||||
}
|
||||
|
||||
export function createCall(expression: Expression, argumentsArray: Expression[]) {
|
||||
const node = <CallExpression>allocateNode(SyntaxKind.CallExpression);
|
||||
node.expression = parenthesizeForAccess(expression);
|
||||
node.arguments = createNodeArray(argumentsArray);
|
||||
return node;
|
||||
}
|
||||
|
||||
export function createArraySlice(array: Expression, start?: number | Expression) {
|
||||
const argumentsList: Expression[] = start !== undefined ? [coerceExpression(start)] : [];
|
||||
return createCall(createPropertyAccess(array, "slice"), argumentsList);
|
||||
}
|
||||
|
||||
export function parenthesizeExpression(expression: Expression) {
|
||||
const node = <ParenthesizedExpression>allocateNode(SyntaxKind.ParenthesizedExpression);
|
||||
node.expression = expression;
|
||||
return node;
|
||||
}
|
||||
|
||||
export function inlineExpressions(expressions: Expression[]) {
|
||||
return reduceLeft(expressions, createComma);
|
||||
}
|
||||
|
||||
function coerceIdentifier(value: string | Identifier) {
|
||||
if (typeof value === "string") {
|
||||
return createIdentifier(value);
|
||||
}
|
||||
else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
function coerceExpression(value: string | number | boolean | Expression): Expression {
|
||||
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
||||
return createLiteral(value);
|
||||
}
|
||||
else if (value === null) {
|
||||
return createLiteral(null);
|
||||
}
|
||||
else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
const enum BinaryOperand {
|
||||
Left,
|
||||
Right
|
||||
}
|
||||
|
||||
function parenthesizeForBinary(operand: Expression, operator: SyntaxKind, side: BinaryOperand) {
|
||||
// When diagnosing whether the expression needs parentheses, the decision should be based
|
||||
// on the innermost expression in a chain of nested type assertions.
|
||||
while (operand.kind === SyntaxKind.TypeAssertionExpression || operand.kind === SyntaxKind.AsExpression) {
|
||||
operand = (<AssertionExpression>operand).expression;
|
||||
}
|
||||
|
||||
// If the resulting expression is already parenthesized, we do not need to do any further processing.
|
||||
if (operand.kind === SyntaxKind.ParenthesizedExpression) {
|
||||
return operand;
|
||||
}
|
||||
|
||||
return needsParenthesesForBinary(operand, operator, side)
|
||||
? parenthesizeExpression(operand)
|
||||
: operand;
|
||||
}
|
||||
|
||||
function needsParenthesesForBinary(operand: Expression, operator: SyntaxKind, side: BinaryOperand) {
|
||||
const operandPrecedence = getExpressionPrecedence(operand);
|
||||
const operatorPrecedence = getOperatorPrecedence(SyntaxKind.BinaryExpression, operator);
|
||||
switch (compareValues(operandPrecedence, operatorPrecedence)) {
|
||||
case Comparison.LessThan:
|
||||
return true;
|
||||
case Comparison.EqualTo:
|
||||
return isRightAssociativeOperandOnLeftHandSide(operand, side)
|
||||
|| isModuloOperandOnRightHandSide(operand, operator, side);
|
||||
case Comparison.GreaterThan:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function isRightAssociativeOperandOnLeftHandSide(operand: Expression, side: BinaryOperand) {
|
||||
return side === BinaryOperand.Left
|
||||
&& getExpressionAssociativity(operand) === Associativity.Right;
|
||||
}
|
||||
|
||||
function isModuloOperandOnRightHandSide(operand: Expression, operator: SyntaxKind, side: BinaryOperand) {
|
||||
return side === BinaryOperand.Right
|
||||
&& operator !== SyntaxKind.PercentToken
|
||||
&& operand.kind === SyntaxKind.BinaryExpression
|
||||
&& (<BinaryExpression>operand).operatorToken.kind === SyntaxKind.PercentToken;
|
||||
}
|
||||
|
||||
function parenthesizeForAccess(expr: Expression): LeftHandSideExpression {
|
||||
// When diagnosing whether the expression needs parentheses, the decision should be based
|
||||
// on the innermost expression in a chain of nested type assertions.
|
||||
while (expr.kind === SyntaxKind.TypeAssertionExpression || expr.kind === SyntaxKind.AsExpression) {
|
||||
expr = (<AssertionExpression>expr).expression;
|
||||
}
|
||||
|
||||
// isLeftHandSideExpression is almost the correct criterion for when it is not necessary
|
||||
// to parenthesize the expression before a dot. The known exceptions are:
|
||||
//
|
||||
// NewExpression:
|
||||
// new C.x -> not the same as (new C).x
|
||||
// NumberLiteral
|
||||
// 1.x -> not the same as (1).x
|
||||
//
|
||||
if (isLeftHandSideExpression(expr) &&
|
||||
expr.kind !== SyntaxKind.NewExpression &&
|
||||
expr.kind !== SyntaxKind.NumericLiteral) {
|
||||
|
||||
return <LeftHandSideExpression>expr;
|
||||
}
|
||||
|
||||
return parenthesizeExpression(expr);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,18 @@
|
||||
namespace ts {
|
||||
/* @internal */ export let parseTime = 0;
|
||||
|
||||
let NodeConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
|
||||
let SourceFileConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
|
||||
|
||||
export function createNode(kind: SyntaxKind, pos?: number, end?: number): Node {
|
||||
if (kind === SyntaxKind.SourceFile) {
|
||||
return new (SourceFileConstructor || (SourceFileConstructor = objectAllocator.getSourceFileConstructor()))(kind, pos, end);
|
||||
}
|
||||
else {
|
||||
return new (NodeConstructor || (NodeConstructor = objectAllocator.getNodeConstructor()))(kind, pos, end);
|
||||
}
|
||||
}
|
||||
|
||||
function visitNode<T>(cbNode: (node: Node) => T, node: Node): T {
|
||||
if (node) {
|
||||
return cbNode(node);
|
||||
|
||||
@@ -0,0 +1,331 @@
|
||||
/// <reference path="visitor.ts" />
|
||||
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
export function getTransformers(compilerOptions: CompilerOptions) {
|
||||
const transformers: Transformer[] = [];
|
||||
// TODO(rbuckton): Add transformers
|
||||
return transformers;
|
||||
}
|
||||
|
||||
export function transformFiles(resolver: EmitResolver, host: EmitHost, sourceFiles: SourceFile[], transformers: Transformer[]) {
|
||||
const nodeToGeneratedName: Identifier[] = [];
|
||||
const generatedNameSet: Map<string> = {};
|
||||
const nodeEmitFlags: NodeEmitFlags[] = [];
|
||||
const lexicalEnvironmentVariableDeclarationsStack: VariableDeclaration[][] = [];
|
||||
const lexicalEnvironmentFunctionDeclarationsStack: FunctionDeclaration[][] = [];
|
||||
let lexicalEnvironmentStackOffset = 0;
|
||||
let hoistedVariableDeclarations: VariableDeclaration[];
|
||||
let hoistedFunctionDeclarations: FunctionDeclaration[];
|
||||
let currentSourceFile: SourceFile;
|
||||
|
||||
const context: TransformationContext = {
|
||||
getCompilerOptions: () => host.getCompilerOptions(),
|
||||
getEmitResolver: () => resolver,
|
||||
getNodeEmitFlags,
|
||||
setNodeEmitFlags,
|
||||
isUniqueName,
|
||||
getGeneratedNameForNode,
|
||||
nodeHasGeneratedName,
|
||||
makeUniqueName,
|
||||
hoistVariableDeclaration,
|
||||
hoistFunctionDeclaration,
|
||||
startLexicalEnvironment,
|
||||
endLexicalEnvironment
|
||||
};
|
||||
|
||||
// Chain together and initialize each transformer.
|
||||
const transformation = chain(...transformers)(context);
|
||||
|
||||
// Transform each source file.
|
||||
return map(sourceFiles, transformSourceFile);
|
||||
|
||||
/**
|
||||
* Transforms a source file.
|
||||
* @param sourceFile The source file to transform.
|
||||
*/
|
||||
function transformSourceFile(sourceFile: SourceFile) {
|
||||
if (isDeclarationFile(sourceFile)) {
|
||||
return sourceFile;
|
||||
}
|
||||
|
||||
currentSourceFile = sourceFile;
|
||||
const visited = transformation(sourceFile);
|
||||
currentSourceFile = undefined;
|
||||
return visited;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets flags that control emit behavior of a node.
|
||||
*/
|
||||
function getNodeEmitFlags(node: Node) {
|
||||
while (node) {
|
||||
const nodeId = getNodeId(node);
|
||||
if (nodeEmitFlags[nodeId] !== undefined) {
|
||||
return nodeEmitFlags[nodeId];
|
||||
}
|
||||
|
||||
node = node.original;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets flags that control emit behavior of a node.
|
||||
*/
|
||||
function setNodeEmitFlags(node: Node, flags: NodeEmitFlags) {
|
||||
nodeEmitFlags[getNodeId(node)] = flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a name that is unique within the current file and doesn't conflict with any names
|
||||
* in global scope. The name is formed by adding an '_n' suffix to the specified base name,
|
||||
* where n is a positive integer. Note that names generated by makeTempVariableName and
|
||||
* makeUniqueName are guaranteed to never conflict.
|
||||
*/
|
||||
function makeUniqueName(baseName: string): Identifier {
|
||||
// Find the first unique 'name_n', where n is a positive number
|
||||
if (baseName.charCodeAt(baseName.length - 1) !== CharacterCodes._) {
|
||||
baseName += "_";
|
||||
}
|
||||
|
||||
let i = 1;
|
||||
while (true) {
|
||||
const generatedName = baseName + i;
|
||||
if (isUniqueName(generatedName)) {
|
||||
return createIdentifier(generatedNameSet[generatedName] = generatedName);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the generated name for a node.
|
||||
*/
|
||||
function getGeneratedNameForNode(node: Node) {
|
||||
const id = getNodeId(node);
|
||||
return nodeToGeneratedName[id] || (nodeToGeneratedName[id] = generateNameForNode(node));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value indicating whether a node has a generated name.
|
||||
*/
|
||||
function nodeHasGeneratedName(node: Node) {
|
||||
const id = getNodeId(node);
|
||||
return nodeToGeneratedName[id] !== undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether the provided name is unique.
|
||||
*/
|
||||
function isUniqueName(name: string): boolean {
|
||||
return !resolver.hasGlobalName(name)
|
||||
&& !hasProperty(currentSourceFile.identifiers, name)
|
||||
&& !hasProperty(generatedNameSet, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether the provided name is unique within a container.
|
||||
*/
|
||||
function isUniqueLocalName(name: string, container: Node): boolean {
|
||||
container = getOriginalNode(container);
|
||||
for (let node = container; isNodeDescendentOf(node, container); node = node.nextContainer) {
|
||||
if (node.locals && hasProperty(node.locals, name)) {
|
||||
// We conservatively include alias symbols to cover cases where they're emitted as locals
|
||||
if (node.locals[name].flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a name for a node.
|
||||
*/
|
||||
function generateNameForNode(node: Node): Identifier {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.Identifier:
|
||||
return makeUniqueName((<Identifier>node).text);
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
return generateNameForModuleOrEnum(<ModuleDeclaration | EnumDeclaration>node);
|
||||
case SyntaxKind.ImportDeclaration:
|
||||
case SyntaxKind.ExportDeclaration:
|
||||
return generateNameForImportOrExportDeclaration(<ImportDeclaration | ExportDeclaration>node);
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.ExportAssignment:
|
||||
return generateNameForExportDefault();
|
||||
case SyntaxKind.ClassExpression:
|
||||
return generateNameForClassExpression();
|
||||
case SyntaxKind.ComputedPropertyName:
|
||||
case SyntaxKind.Parameter:
|
||||
case SyntaxKind.TaggedTemplateExpression:
|
||||
return createTempVariable(TempVariableKind.Auto);
|
||||
}
|
||||
}
|
||||
|
||||
function generateNameForModuleOrEnum(node: ModuleDeclaration | EnumDeclaration) {
|
||||
const name = node.name;
|
||||
// Use module/enum name itself if it is unique, otherwise make a unique variation
|
||||
return isUniqueLocalName(name.text, node) ? name : makeUniqueName(name.text);
|
||||
}
|
||||
|
||||
function generateNameForImportOrExportDeclaration(node: ImportDeclaration | ExportDeclaration) {
|
||||
const expr = getExternalModuleName(node);
|
||||
const baseName = expr.kind === SyntaxKind.StringLiteral
|
||||
? escapeIdentifier(makeIdentifierFromModuleName((<LiteralExpression>expr).text))
|
||||
: "module";
|
||||
return makeUniqueName(baseName);
|
||||
}
|
||||
|
||||
function generateNameForExportDefault() {
|
||||
return makeUniqueName("default");
|
||||
}
|
||||
|
||||
function generateNameForClassExpression() {
|
||||
return makeUniqueName("class");
|
||||
}
|
||||
|
||||
/**
|
||||
* Records a hoisted variable declaration within a lexical environment.
|
||||
*/
|
||||
function hoistVariableDeclaration(name: Identifier): void {
|
||||
if (!hoistedVariableDeclarations) {
|
||||
hoistedVariableDeclarations = [];
|
||||
}
|
||||
|
||||
hoistedVariableDeclarations.push(createVariableDeclaration(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Records a hoisted function declaration within a lexical environment.
|
||||
*/
|
||||
function hoistFunctionDeclaration(func: FunctionDeclaration): void {
|
||||
if (!hoistedFunctionDeclarations) {
|
||||
hoistedFunctionDeclarations = [];
|
||||
}
|
||||
|
||||
hoistedFunctionDeclarations.push(func);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a new lexical environment. Any existing hoisted variable or function declarations
|
||||
* are pushed onto a stack, and the related storage variables are reset.
|
||||
*/
|
||||
function startLexicalEnvironment(): void {
|
||||
// Save the current lexical environment. Rather than resizing the array we adjust the
|
||||
// stack size variable. This allows us to reuse existing array slots we've
|
||||
// already allocated between transformations to avoid allocation and GC overhead during
|
||||
// transformation.
|
||||
lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset] = hoistedVariableDeclarations;
|
||||
lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset] = hoistedFunctionDeclarations;
|
||||
lexicalEnvironmentStackOffset++;
|
||||
|
||||
hoistedVariableDeclarations = undefined;
|
||||
hoistedFunctionDeclarations = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends a lexical environment. The previous set of hoisted declarations are restored and
|
||||
* any hoisted declarations added in this environment are returned.
|
||||
*/
|
||||
function endLexicalEnvironment(): Statement[] {
|
||||
let statements: Statement[];
|
||||
if (hoistedVariableDeclarations || hoistedFunctionDeclarations) {
|
||||
statements = [];
|
||||
if (hoistedFunctionDeclarations) {
|
||||
for (const declaration of hoistedFunctionDeclarations) {
|
||||
statements.push(declaration);
|
||||
}
|
||||
}
|
||||
|
||||
if (hoistedVariableDeclarations) {
|
||||
statements.push(
|
||||
createVariableStatement(
|
||||
createVariableDeclarationList(hoistedVariableDeclarations)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the previous lexical environment.
|
||||
lexicalEnvironmentStackOffset--;
|
||||
hoistedVariableDeclarations = lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset];
|
||||
hoistedFunctionDeclarations = lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset];
|
||||
return statements;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* High-order function, creates a function that executes a function composition.
|
||||
* For example, `chain(a, b)` is the equivalent of `x => ((a', b') => y => b'(a'(y)))(a(x), b(x))`
|
||||
*
|
||||
* @param args The functions to chain.
|
||||
*/
|
||||
function chain<T, U>(...args: ((t: T) => (u: U) => U)[]): (t: T) => (u: U) => U;
|
||||
function chain<T, U>(a: (t: T) => (u: U) => U, b: (t: T) => (u: U) => U, c: (t: T) => (u: U) => U, d: (t: T) => (u: U) => U, e: (t: T) => (u: U) => U): (t: T) => (u: U) => U {
|
||||
if (e) {
|
||||
const args = arrayOf<(t: T) => (u: U) => U>(arguments);
|
||||
return t => compose(...map(args, f => f(t)));
|
||||
}
|
||||
else if (d) {
|
||||
return t => compose(a(t), b(t), c(t), d(t));
|
||||
}
|
||||
else if (c) {
|
||||
return t => compose(a(t), b(t), c(t));
|
||||
}
|
||||
else if (b) {
|
||||
return t => compose(a(t), b(t));
|
||||
}
|
||||
else if (a) {
|
||||
return t => compose(a(t));
|
||||
}
|
||||
else {
|
||||
return t => u => u;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* High-order function, composes functions. Note that functions are composed inside-out;
|
||||
* for example, `compose(a, b)` is the equivalent of `x => b(a(x))`.
|
||||
*
|
||||
* @param args The functions to compose.
|
||||
*/
|
||||
function compose<T>(...args: ((t: T) => T)[]): (t: T) => T;
|
||||
function compose<T>(a: (t: T) => T, b: (t: T) => T, c: (t: T) => T, d: (t: T) => T, e: (t: T) => T): (t: T) => T {
|
||||
if (e) {
|
||||
const args = arrayOf(arguments);
|
||||
return t => reduceLeft<(t: T) => T, T>(args, (u, f) => f(u), t);
|
||||
}
|
||||
else if (d) {
|
||||
return t => d(c(b(a(t))));
|
||||
}
|
||||
else if (c) {
|
||||
return t => c(b(a(t)));
|
||||
}
|
||||
else if (b) {
|
||||
return t => b(a(t));
|
||||
}
|
||||
else if (a) {
|
||||
return t => a(t);
|
||||
}
|
||||
else {
|
||||
return t => t;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes an array from an ArrayLike.
|
||||
*/
|
||||
function arrayOf<T>(arrayLike: ArrayLike<T>) {
|
||||
const array: T[] = [];
|
||||
for (let i = 0; i < arrayLike.length; i++) {
|
||||
array[i] = arrayLike[i];
|
||||
}
|
||||
return array;
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
"checker.ts",
|
||||
"factory.ts",
|
||||
"visitor.ts",
|
||||
"transformer.ts",
|
||||
"emitter.ts",
|
||||
"program.ts",
|
||||
"commandLineParser.ts",
|
||||
|
||||
@@ -495,9 +495,15 @@ namespace ts {
|
||||
// @kind(SyntaxKind.StaticKeyword)
|
||||
export interface Modifier extends Node { }
|
||||
|
||||
export const enum TempVariableKind {
|
||||
Auto, // Automatically generated identifier
|
||||
Loop, // Automatically generated identifier with a preference for '_i'
|
||||
}
|
||||
|
||||
// @kind(SyntaxKind.Identifier)
|
||||
export interface Identifier extends PrimaryExpression {
|
||||
text: string; // Text of identifier (with escapes converted to characters)
|
||||
tempKind?: TempVariableKind; // Specifies whether to auto-generate the text for an identifier.
|
||||
originalKeywordKind?: SyntaxKind; // Original syntaxKind which get set so that we can report an error later
|
||||
}
|
||||
|
||||
@@ -2782,6 +2788,34 @@ namespace ts {
|
||||
ArrayLiteralOrCallOrNewExcludes = ContainsSpreadElementExpression,
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export const enum NodeEmitFlags {
|
||||
EmitHelpers = 1 << 0,
|
||||
EmitExportStar = 1 << 1,
|
||||
UMDDefine = 1 << 2,
|
||||
NoLexicalEnvironment = 1 << 3,
|
||||
SingleLine = 1 << 4,
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export interface TransformationContext extends LexicalEnvironment {
|
||||
getCompilerOptions(): CompilerOptions;
|
||||
getEmitResolver(): EmitResolver;
|
||||
getNodeEmitFlags(node: Node): NodeEmitFlags;
|
||||
setNodeEmitFlags(node: Node, flags: NodeEmitFlags): void;
|
||||
hoistFunctionDeclaration(node: FunctionDeclaration): void;
|
||||
hoistVariableDeclaration(node: Identifier): void;
|
||||
isUniqueName(name: string): boolean;
|
||||
getGeneratedNameForNode(node: Node): Identifier;
|
||||
nodeHasGeneratedName(node: Node): boolean;
|
||||
makeUniqueName(baseName: string): Identifier;
|
||||
identifierSubstitution?: (node: Identifier) => Identifier;
|
||||
expressionSubstitution?: (node: Expression) => Expression;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export type Transformer = (context: TransformationContext) => (node: SourceFile) => SourceFile;
|
||||
|
||||
export interface TextSpan {
|
||||
start: number;
|
||||
length: number;
|
||||
|
||||
@@ -1607,6 +1607,202 @@ namespace ts {
|
||||
return node ? getNodeId(node) : 0;
|
||||
}
|
||||
|
||||
export const enum Associativity {
|
||||
Left,
|
||||
Right
|
||||
}
|
||||
|
||||
export function getExpressionAssociativity(expression: Expression) {
|
||||
const operator = getOperator(expression);
|
||||
const hasArguments = expression.kind === SyntaxKind.NewExpression && (<NewExpression>expression).arguments !== undefined;
|
||||
return getOperatorAssociativity(expression.kind, operator, hasArguments);
|
||||
}
|
||||
|
||||
export function getOperatorAssociativity(kind: SyntaxKind, operator: SyntaxKind, hasArguments?: boolean) {
|
||||
switch (kind) {
|
||||
case SyntaxKind.NewExpression:
|
||||
return hasArguments ? Associativity.Left : Associativity.Right;
|
||||
|
||||
case SyntaxKind.PrefixUnaryExpression:
|
||||
case SyntaxKind.TypeOfExpression:
|
||||
case SyntaxKind.VoidExpression:
|
||||
case SyntaxKind.DeleteExpression:
|
||||
case SyntaxKind.AwaitExpression:
|
||||
case SyntaxKind.ConditionalExpression:
|
||||
case SyntaxKind.YieldExpression:
|
||||
return Associativity.Right;
|
||||
|
||||
case SyntaxKind.BinaryExpression:
|
||||
switch (operator) {
|
||||
case SyntaxKind.AsteriskAsteriskToken:
|
||||
case SyntaxKind.EqualsToken:
|
||||
case SyntaxKind.PlusEqualsToken:
|
||||
case SyntaxKind.MinusEqualsToken:
|
||||
case SyntaxKind.AsteriskAsteriskEqualsToken:
|
||||
case SyntaxKind.AsteriskEqualsToken:
|
||||
case SyntaxKind.SlashEqualsToken:
|
||||
case SyntaxKind.PercentEqualsToken:
|
||||
case SyntaxKind.LessThanLessThanEqualsToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanEqualsToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
|
||||
case SyntaxKind.AmpersandEqualsToken:
|
||||
case SyntaxKind.CaretEqualsToken:
|
||||
case SyntaxKind.BarEqualsToken:
|
||||
return Associativity.Right;
|
||||
}
|
||||
}
|
||||
|
||||
return Associativity.Left;
|
||||
}
|
||||
|
||||
export function getExpressionPrecedence(expression: Expression) {
|
||||
const operator = getOperator(expression);
|
||||
const hasArguments = expression.kind === SyntaxKind.NewExpression && (<NewExpression>expression).arguments !== undefined;
|
||||
return getOperatorPrecedence(expression.kind, operator, hasArguments);
|
||||
}
|
||||
|
||||
function getOperator(expression: Expression) {
|
||||
if (expression.kind === SyntaxKind.BinaryExpression) {
|
||||
return (<BinaryExpression>expression).operatorToken.kind;
|
||||
}
|
||||
else if (expression.kind === SyntaxKind.PrefixUnaryExpression || expression.kind === SyntaxKind.PostfixUnaryExpression) {
|
||||
return (<PrefixUnaryExpression | PostfixUnaryExpression>expression).operator;
|
||||
}
|
||||
else {
|
||||
return expression.kind;
|
||||
}
|
||||
}
|
||||
|
||||
export function getOperatorPrecedence(nodeKind: SyntaxKind, operatorKind: SyntaxKind, hasArguments?: boolean) {
|
||||
switch (nodeKind) {
|
||||
case SyntaxKind.ThisKeyword:
|
||||
case SyntaxKind.SuperKeyword:
|
||||
case SyntaxKind.Identifier:
|
||||
case SyntaxKind.NullKeyword:
|
||||
case SyntaxKind.TrueKeyword:
|
||||
case SyntaxKind.FalseKeyword:
|
||||
case SyntaxKind.NumericLiteral:
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.ArrayLiteralExpression:
|
||||
case SyntaxKind.ObjectLiteralExpression:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.ArrowFunction:
|
||||
case SyntaxKind.ClassExpression:
|
||||
case SyntaxKind.JsxElement:
|
||||
case SyntaxKind.JsxSelfClosingElement:
|
||||
case SyntaxKind.RegularExpressionLiteral:
|
||||
case SyntaxKind.NoSubstitutionTemplateLiteral:
|
||||
case SyntaxKind.TemplateExpression:
|
||||
case SyntaxKind.ParenthesizedExpression:
|
||||
return 19;
|
||||
|
||||
case SyntaxKind.TaggedTemplateExpression:
|
||||
case SyntaxKind.PropertyAccessExpression:
|
||||
case SyntaxKind.ElementAccessExpression:
|
||||
return 18;
|
||||
|
||||
case SyntaxKind.NewExpression:
|
||||
return hasArguments ? 18 : 17;
|
||||
|
||||
case SyntaxKind.CallExpression:
|
||||
return 17;
|
||||
|
||||
case SyntaxKind.PostfixUnaryExpression:
|
||||
return 16;
|
||||
|
||||
case SyntaxKind.PrefixUnaryExpression:
|
||||
case SyntaxKind.TypeOfExpression:
|
||||
case SyntaxKind.VoidExpression:
|
||||
case SyntaxKind.DeleteExpression:
|
||||
case SyntaxKind.AwaitExpression:
|
||||
return 15;
|
||||
|
||||
case SyntaxKind.BinaryExpression:
|
||||
switch (operatorKind) {
|
||||
case SyntaxKind.ExclamationToken:
|
||||
case SyntaxKind.TildeToken:
|
||||
return 15;
|
||||
|
||||
case SyntaxKind.AsteriskAsteriskToken:
|
||||
case SyntaxKind.AsteriskToken:
|
||||
case SyntaxKind.SlashToken:
|
||||
case SyntaxKind.PercentToken:
|
||||
return 14;
|
||||
|
||||
case SyntaxKind.PlusToken:
|
||||
case SyntaxKind.MinusToken:
|
||||
return 13;
|
||||
|
||||
case SyntaxKind.LessThanLessThanToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
|
||||
return 12;
|
||||
|
||||
case SyntaxKind.LessThanToken:
|
||||
case SyntaxKind.LessThanEqualsToken:
|
||||
case SyntaxKind.GreaterThanToken:
|
||||
case SyntaxKind.GreaterThanEqualsToken:
|
||||
case SyntaxKind.InKeyword:
|
||||
case SyntaxKind.InstanceOfKeyword:
|
||||
return 11;
|
||||
|
||||
case SyntaxKind.EqualsEqualsToken:
|
||||
case SyntaxKind.EqualsEqualsEqualsToken:
|
||||
case SyntaxKind.ExclamationEqualsToken:
|
||||
case SyntaxKind.ExclamationEqualsEqualsToken:
|
||||
return 10;
|
||||
|
||||
case SyntaxKind.AmpersandToken:
|
||||
return 9;
|
||||
|
||||
case SyntaxKind.CaretToken:
|
||||
return 8;
|
||||
|
||||
case SyntaxKind.BarToken:
|
||||
return 7;
|
||||
|
||||
case SyntaxKind.AmpersandAmpersandToken:
|
||||
return 6;
|
||||
|
||||
case SyntaxKind.BarBarToken:
|
||||
return 5;
|
||||
|
||||
case SyntaxKind.EqualsToken:
|
||||
case SyntaxKind.PlusEqualsToken:
|
||||
case SyntaxKind.MinusEqualsToken:
|
||||
case SyntaxKind.AsteriskAsteriskEqualsToken:
|
||||
case SyntaxKind.AsteriskEqualsToken:
|
||||
case SyntaxKind.SlashEqualsToken:
|
||||
case SyntaxKind.PercentEqualsToken:
|
||||
case SyntaxKind.LessThanLessThanEqualsToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanEqualsToken:
|
||||
case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
|
||||
case SyntaxKind.AmpersandEqualsToken:
|
||||
case SyntaxKind.CaretEqualsToken:
|
||||
case SyntaxKind.BarEqualsToken:
|
||||
return 3;
|
||||
|
||||
case SyntaxKind.CommaToken:
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
case SyntaxKind.ConditionalExpression:
|
||||
return 4;
|
||||
|
||||
case SyntaxKind.YieldExpression:
|
||||
return 2;
|
||||
|
||||
case SyntaxKind.SpreadElementExpression:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
export function createDiagnosticCollection(): DiagnosticCollection {
|
||||
let nonFileDiagnostics: Diagnostic[] = [];
|
||||
const fileDiagnostics: Map<Diagnostic[]> = {};
|
||||
|
||||
Reference in New Issue
Block a user