refactor(compiler): replace Comment nodes with leadingComments property (#38811)
Common AST formats such as TS and Babel do not use a separate node for comments, but instead attach comments to other AST nodes. Previously this was worked around in TS by creating a `NotEmittedStatement` AST node to attach the comment to. But Babel does not have this facility, so it will not be a viable approach for the linker. This commit refactors the output AST, to remove the `CommentStmt` and `JSDocCommentStmt` nodes. Instead statements have a collection of `leadingComments` that are rendered/attached to the final AST nodes when being translated or printed. PR Close #38811
This commit is contained in:
parent
7fb388f929
commit
d795a00137
@ -32,6 +32,7 @@ ts_library(
|
|||||||
"//packages/compiler-cli/src/ngtsc/perf",
|
"//packages/compiler-cli/src/ngtsc/perf",
|
||||||
"//packages/compiler-cli/src/ngtsc/reflection",
|
"//packages/compiler-cli/src/ngtsc/reflection",
|
||||||
"//packages/compiler-cli/src/ngtsc/shims",
|
"//packages/compiler-cli/src/ngtsc/shims",
|
||||||
|
"//packages/compiler-cli/src/ngtsc/translator",
|
||||||
"//packages/compiler-cli/src/ngtsc/typecheck",
|
"//packages/compiler-cli/src/ngtsc/typecheck",
|
||||||
"@npm//@bazel/typescript",
|
"@npm//@bazel/typescript",
|
||||||
"@npm//@types/node",
|
"@npm//@types/node",
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
* Use of this source code is governed by an MIT-style license that can be
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {CommentStmt, ConstantPool, Expression, Statement, WrappedNodeExpr, WritePropExpr} from '@angular/compiler';
|
import {ConstantPool, Expression, jsDocComment, LeadingComment, Statement, WrappedNodeExpr, WritePropExpr} from '@angular/compiler';
|
||||||
import MagicString from 'magic-string';
|
import MagicString from 'magic-string';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
@ -166,11 +166,11 @@ export class Renderer {
|
|||||||
sourceFile: ts.SourceFile, compiledClass: CompiledClass, imports: ImportManager,
|
sourceFile: ts.SourceFile, compiledClass: CompiledClass, imports: ImportManager,
|
||||||
annotateForClosureCompiler: boolean): string {
|
annotateForClosureCompiler: boolean): string {
|
||||||
const name = this.host.getInternalNameOfClass(compiledClass.declaration);
|
const name = this.host.getInternalNameOfClass(compiledClass.declaration);
|
||||||
const statements: Statement[][] = compiledClass.compilation.map(c => {
|
const leadingComment =
|
||||||
return createAssignmentStatements(
|
annotateForClosureCompiler ? jsDocComment([{tagName: 'nocollapse'}]) : undefined;
|
||||||
name, c.name, c.initializer, annotateForClosureCompiler ? '* @nocollapse ' : undefined);
|
const statements: Statement[] = compiledClass.compilation.map(
|
||||||
});
|
c => createAssignmentStatement(name, c.name, c.initializer, leadingComment));
|
||||||
return this.renderStatements(sourceFile, Array.prototype.concat.apply([], statements), imports);
|
return this.renderStatements(sourceFile, statements, imports);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -213,16 +213,16 @@ export function renderConstantPool(
|
|||||||
* compiled decorator to be applied to the class.
|
* compiled decorator to be applied to the class.
|
||||||
* @param analyzedClass The info about the class whose statement we want to create.
|
* @param analyzedClass The info about the class whose statement we want to create.
|
||||||
*/
|
*/
|
||||||
function createAssignmentStatements(
|
function createAssignmentStatement(
|
||||||
receiverName: ts.DeclarationName, propName: string, initializer: Expression,
|
receiverName: ts.DeclarationName, propName: string, initializer: Expression,
|
||||||
leadingComment?: string): Statement[] {
|
leadingComment?: LeadingComment): Statement {
|
||||||
const receiver = new WrappedNodeExpr(receiverName);
|
const receiver = new WrappedNodeExpr(receiverName);
|
||||||
const statements =
|
const statement =
|
||||||
[new WritePropExpr(
|
new WritePropExpr(
|
||||||
receiver, propName, initializer, /* type */ undefined, /* sourceSpan */ undefined)
|
receiver, propName, initializer, /* type */ undefined, /* sourceSpan */ undefined)
|
||||||
.toStmt()];
|
.toStmt();
|
||||||
if (leadingComment !== undefined) {
|
if (leadingComment !== undefined) {
|
||||||
statements.unshift(new CommentStmt(leadingComment, true));
|
statement.addLeadingComment(leadingComment);
|
||||||
}
|
}
|
||||||
return statements;
|
return statement;
|
||||||
}
|
}
|
||||||
|
@ -1663,12 +1663,12 @@ runInEachFileSystem(() => {
|
|||||||
JSON.stringify({angularCompilerOptions: {annotateForClosureCompiler: true}}));
|
JSON.stringify({angularCompilerOptions: {annotateForClosureCompiler: true}}));
|
||||||
mainNgcc({basePath: '/dist', propertiesToConsider: ['es2015']});
|
mainNgcc({basePath: '/dist', propertiesToConsider: ['es2015']});
|
||||||
const jsContents = fs.readFile(_(`/dist/local-package/index.js`));
|
const jsContents = fs.readFile(_(`/dist/local-package/index.js`));
|
||||||
expect(jsContents).toContain('/** @nocollapse */ \nAppComponent.ɵcmp =');
|
expect(jsContents).toContain('/** @nocollapse */\nAppComponent.ɵcmp =');
|
||||||
});
|
});
|
||||||
it('should default to not give closure annotated output', () => {
|
it('should default to not give closure annotated output', () => {
|
||||||
mainNgcc({basePath: '/dist', propertiesToConsider: ['es2015']});
|
mainNgcc({basePath: '/dist', propertiesToConsider: ['es2015']});
|
||||||
const jsContents = fs.readFile(_(`/dist/local-package/index.js`));
|
const jsContents = fs.readFile(_(`/dist/local-package/index.js`));
|
||||||
expect(jsContents).not.toContain('/** @nocollapse */');
|
expect(jsContents).not.toContain('@nocollapse');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -103,7 +103,7 @@ export class ModuleWithProvidersScanner {
|
|||||||
this.emitter.emit(ngModule, decl.getSourceFile(), ImportFlags.ForceNewImport);
|
this.emitter.emit(ngModule, decl.getSourceFile(), ImportFlags.ForceNewImport);
|
||||||
const ngModuleType = new ExpressionType(ngModuleExpr);
|
const ngModuleType = new ExpressionType(ngModuleExpr);
|
||||||
const mwpNgType = new ExpressionType(
|
const mwpNgType = new ExpressionType(
|
||||||
new ExternalExpr(Identifiers.ModuleWithProviders), /* modifiers */ null, [ngModuleType]);
|
new ExternalExpr(Identifiers.ModuleWithProviders), [/* modifiers */], [ngModuleType]);
|
||||||
|
|
||||||
dts.addTypeReplacement(decl, mwpNgType);
|
dts.addTypeReplacement(decl, mwpNgType);
|
||||||
}
|
}
|
||||||
|
@ -6,4 +6,4 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export {Import, ImportManager, NamedImport, translateExpression, translateStatement, translateType} from './src/translator';
|
export {attachComments, Import, ImportManager, NamedImport, translateExpression, translateStatement, translateType} from './src/translator';
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ArrayType, AssertNotNull, BinaryOperator, BinaryOperatorExpr, BuiltinType, BuiltinTypeName, CastExpr, ClassStmt, CommaExpr, CommentStmt, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, Expression, ExpressionStatement, ExpressionType, ExpressionVisitor, ExternalExpr, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, JSDocCommentStmt, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, MapType, NotExpr, ParseSourceSpan, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, Statement, StatementVisitor, StmtModifier, ThrowStmt, TryCatchStmt, Type, TypeofExpr, TypeVisitor, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr} from '@angular/compiler';
|
import {ArrayType, AssertNotNull, BinaryOperator, BinaryOperatorExpr, BuiltinType, BuiltinTypeName, CastExpr, ClassStmt, CommaExpr, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, Expression, ExpressionStatement, ExpressionType, ExpressionVisitor, ExternalExpr, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, LeadingComment, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, MapType, NotExpr, ParseSourceSpan, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, Statement, StatementVisitor, StmtModifier, ThrowStmt, TryCatchStmt, Type, TypeofExpr, TypeVisitor, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr} from '@angular/compiler';
|
||||||
import {LocalizedString, UnaryOperator, UnaryOperatorExpr} from '@angular/compiler/src/output/output_ast';
|
import {LocalizedString, UnaryOperator, UnaryOperatorExpr} from '@angular/compiler/src/output/output_ast';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
@ -134,34 +134,45 @@ class ExpressionTranslatorVisitor implements ExpressionVisitor, StatementVisitor
|
|||||||
private scriptTarget: Exclude<ts.ScriptTarget, ts.ScriptTarget.JSON>) {}
|
private scriptTarget: Exclude<ts.ScriptTarget, ts.ScriptTarget.JSON>) {}
|
||||||
|
|
||||||
visitDeclareVarStmt(stmt: DeclareVarStmt, context: Context): ts.VariableStatement {
|
visitDeclareVarStmt(stmt: DeclareVarStmt, context: Context): ts.VariableStatement {
|
||||||
const nodeFlags =
|
const isConst =
|
||||||
((this.scriptTarget >= ts.ScriptTarget.ES2015) && stmt.hasModifier(StmtModifier.Final)) ?
|
this.scriptTarget >= ts.ScriptTarget.ES2015 && stmt.hasModifier(StmtModifier.Final);
|
||||||
ts.NodeFlags.Const :
|
const varDeclaration = ts.createVariableDeclaration(
|
||||||
ts.NodeFlags.None;
|
/* name */ stmt.name,
|
||||||
return ts.createVariableStatement(
|
/* type */ undefined,
|
||||||
undefined,
|
/* initializer */ stmt.value?.visitExpression(this, context.withExpressionMode));
|
||||||
ts.createVariableDeclarationList(
|
const declarationList = ts.createVariableDeclarationList(
|
||||||
[ts.createVariableDeclaration(
|
/* declarations */[varDeclaration],
|
||||||
stmt.name, undefined,
|
/* flags */ isConst ? ts.NodeFlags.Const : ts.NodeFlags.None);
|
||||||
stmt.value && stmt.value.visitExpression(this, context.withExpressionMode))],
|
const varStatement = ts.createVariableStatement(undefined, declarationList);
|
||||||
nodeFlags));
|
return attachComments(varStatement, stmt.leadingComments);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitDeclareFunctionStmt(stmt: DeclareFunctionStmt, context: Context): ts.FunctionDeclaration {
|
visitDeclareFunctionStmt(stmt: DeclareFunctionStmt, context: Context): ts.FunctionDeclaration {
|
||||||
return ts.createFunctionDeclaration(
|
const fnDeclaration = ts.createFunctionDeclaration(
|
||||||
undefined, undefined, undefined, stmt.name, undefined,
|
/* decorators */ undefined,
|
||||||
|
/* modifiers */ undefined,
|
||||||
|
/* asterisk */ undefined,
|
||||||
|
/* name */ stmt.name,
|
||||||
|
/* typeParameters */ undefined,
|
||||||
|
/* parameters */
|
||||||
stmt.params.map(param => ts.createParameter(undefined, undefined, undefined, param.name)),
|
stmt.params.map(param => ts.createParameter(undefined, undefined, undefined, param.name)),
|
||||||
undefined,
|
/* type */ undefined,
|
||||||
|
/* body */
|
||||||
ts.createBlock(
|
ts.createBlock(
|
||||||
stmt.statements.map(child => child.visitStatement(this, context.withStatementMode))));
|
stmt.statements.map(child => child.visitStatement(this, context.withStatementMode))));
|
||||||
|
return attachComments(fnDeclaration, stmt.leadingComments);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitExpressionStmt(stmt: ExpressionStatement, context: Context): ts.ExpressionStatement {
|
visitExpressionStmt(stmt: ExpressionStatement, context: Context): ts.ExpressionStatement {
|
||||||
return ts.createStatement(stmt.expr.visitExpression(this, context.withStatementMode));
|
return attachComments(
|
||||||
|
ts.createStatement(stmt.expr.visitExpression(this, context.withStatementMode)),
|
||||||
|
stmt.leadingComments);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitReturnStmt(stmt: ReturnStatement, context: Context): ts.ReturnStatement {
|
visitReturnStmt(stmt: ReturnStatement, context: Context): ts.ReturnStatement {
|
||||||
return ts.createReturn(stmt.value.visitExpression(this, context.withExpressionMode));
|
return attachComments(
|
||||||
|
ts.createReturn(stmt.value.visitExpression(this, context.withExpressionMode)),
|
||||||
|
stmt.leadingComments);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitDeclareClassStmt(stmt: ClassStmt, context: Context) {
|
visitDeclareClassStmt(stmt: ClassStmt, context: Context) {
|
||||||
@ -174,14 +185,15 @@ class ExpressionTranslatorVisitor implements ExpressionVisitor, StatementVisitor
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitIfStmt(stmt: IfStmt, context: Context): ts.IfStatement {
|
visitIfStmt(stmt: IfStmt, context: Context): ts.IfStatement {
|
||||||
return ts.createIf(
|
const thenBlock = ts.createBlock(
|
||||||
stmt.condition.visitExpression(this, context),
|
stmt.trueCase.map(child => child.visitStatement(this, context.withStatementMode)));
|
||||||
|
const elseBlock = stmt.falseCase.length > 0 ?
|
||||||
ts.createBlock(
|
ts.createBlock(
|
||||||
stmt.trueCase.map(child => child.visitStatement(this, context.withStatementMode))),
|
stmt.falseCase.map(child => child.visitStatement(this, context.withStatementMode))) :
|
||||||
stmt.falseCase.length > 0 ?
|
undefined;
|
||||||
ts.createBlock(stmt.falseCase.map(
|
const ifStatement =
|
||||||
child => child.visitStatement(this, context.withStatementMode))) :
|
ts.createIf(stmt.condition.visitExpression(this, context), thenBlock, elseBlock);
|
||||||
undefined);
|
return attachComments(ifStatement, stmt.leadingComments);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitTryCatchStmt(stmt: TryCatchStmt, context: Context) {
|
visitTryCatchStmt(stmt: TryCatchStmt, context: Context) {
|
||||||
@ -189,25 +201,9 @@ class ExpressionTranslatorVisitor implements ExpressionVisitor, StatementVisitor
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitThrowStmt(stmt: ThrowStmt, context: Context): ts.ThrowStatement {
|
visitThrowStmt(stmt: ThrowStmt, context: Context): ts.ThrowStatement {
|
||||||
return ts.createThrow(stmt.error.visitExpression(this, context.withExpressionMode));
|
return attachComments(
|
||||||
}
|
ts.createThrow(stmt.error.visitExpression(this, context.withExpressionMode)),
|
||||||
|
stmt.leadingComments);
|
||||||
visitCommentStmt(stmt: CommentStmt, context: Context): ts.NotEmittedStatement {
|
|
||||||
const commentStmt = ts.createNotEmittedStatement(ts.createLiteral(''));
|
|
||||||
ts.addSyntheticLeadingComment(
|
|
||||||
commentStmt,
|
|
||||||
stmt.multiline ? ts.SyntaxKind.MultiLineCommentTrivia :
|
|
||||||
ts.SyntaxKind.SingleLineCommentTrivia,
|
|
||||||
stmt.comment, /** hasTrailingNewLine */ false);
|
|
||||||
return commentStmt;
|
|
||||||
}
|
|
||||||
|
|
||||||
visitJSDocCommentStmt(stmt: JSDocCommentStmt, context: Context): ts.NotEmittedStatement {
|
|
||||||
const commentStmt = ts.createNotEmittedStatement(ts.createLiteral(''));
|
|
||||||
const text = stmt.toString();
|
|
||||||
const kind = ts.SyntaxKind.MultiLineCommentTrivia;
|
|
||||||
ts.setSyntheticLeadingComments(commentStmt, [{kind, text, pos: -1, end: -1}]);
|
|
||||||
return commentStmt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
visitReadVarExpr(ast: ReadVarExpr, context: Context): ts.Identifier {
|
visitReadVarExpr(ast: ReadVarExpr, context: Context): ts.Identifier {
|
||||||
@ -784,4 +780,31 @@ function createTemplateTail(cooked: string, raw: string): ts.TemplateTail {
|
|||||||
const node: ts.TemplateLiteralLikeNode = ts.createTemplateHead(cooked, raw);
|
const node: ts.TemplateLiteralLikeNode = ts.createTemplateHead(cooked, raw);
|
||||||
(node.kind as ts.SyntaxKind) = ts.SyntaxKind.TemplateTail;
|
(node.kind as ts.SyntaxKind) = ts.SyntaxKind.TemplateTail;
|
||||||
return node as ts.TemplateTail;
|
return node as ts.TemplateTail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attach the given `leadingComments` to the `statement` node.
|
||||||
|
*
|
||||||
|
* @param statement The statement that will have comments attached.
|
||||||
|
* @param leadingComments The comments to attach to the statement.
|
||||||
|
*/
|
||||||
|
export function attachComments<T extends ts.Statement>(
|
||||||
|
statement: T, leadingComments?: LeadingComment[]): T {
|
||||||
|
if (leadingComments === undefined) {
|
||||||
|
return statement;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const comment of leadingComments) {
|
||||||
|
const commentKind = comment.multiline ? ts.SyntaxKind.MultiLineCommentTrivia :
|
||||||
|
ts.SyntaxKind.SingleLineCommentTrivia;
|
||||||
|
if (comment.multiline) {
|
||||||
|
ts.addSyntheticLeadingComment(
|
||||||
|
statement, commentKind, comment.toString(), comment.trailingNewline);
|
||||||
|
} else {
|
||||||
|
for (const line of comment.text.split('\n')) {
|
||||||
|
ts.addSyntheticLeadingComment(statement, commentKind, line, comment.trailingNewline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return statement;
|
||||||
|
}
|
||||||
|
@ -245,7 +245,8 @@ export class Environment {
|
|||||||
*/
|
*/
|
||||||
referenceExternalType(moduleName: string, name: string, typeParams?: Type[]): ts.TypeNode {
|
referenceExternalType(moduleName: string, name: string, typeParams?: Type[]): ts.TypeNode {
|
||||||
const external = new ExternalExpr({moduleName, name});
|
const external = new ExternalExpr({moduleName, name});
|
||||||
return translateType(new ExpressionType(external, null, typeParams), this.importManager);
|
return translateType(
|
||||||
|
new ExpressionType(external, [/* modifiers */], typeParams), this.importManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
getPreludeStatements(): ts.Statement[] {
|
getPreludeStatements(): ts.Statement[] {
|
||||||
|
@ -6,10 +6,10 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AssertNotNull, BinaryOperator, BinaryOperatorExpr, BuiltinMethod, BuiltinVar, CastExpr, ClassStmt, CommaExpr, CommentStmt, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, ExpressionStatement, ExpressionVisitor, ExternalExpr, ExternalReference, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, JSDocCommentStmt, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, NotExpr, ParseSourceFile, ParseSourceSpan, PartialModule, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, Statement, StatementVisitor, StmtModifier, ThrowStmt, TryCatchStmt, TypeofExpr, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr} from '@angular/compiler';
|
import {AssertNotNull, BinaryOperator, BinaryOperatorExpr, BuiltinMethod, BuiltinVar, CastExpr, ClassStmt, CommaExpr, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, ExpressionStatement, ExpressionVisitor, ExternalExpr, ExternalReference, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, LeadingComment, leadingComment, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, LocalizedString, NotExpr, ParseSourceFile, ParseSourceSpan, PartialModule, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, Statement, StatementVisitor, StmtModifier, ThrowStmt, TryCatchStmt, TypeofExpr, UnaryOperator, UnaryOperatorExpr, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr} from '@angular/compiler';
|
||||||
import {LocalizedString, UnaryOperator, UnaryOperatorExpr} from '@angular/compiler/src/output/output_ast';
|
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
|
import {attachComments} from '../ngtsc/translator';
|
||||||
import {error} from './util';
|
import {error} from './util';
|
||||||
|
|
||||||
export interface Node {
|
export interface Node {
|
||||||
@ -31,30 +31,25 @@ export class TypeScriptNodeEmitter {
|
|||||||
// stmts.
|
// stmts.
|
||||||
const statements: any[] = [].concat(
|
const statements: any[] = [].concat(
|
||||||
...stmts.map(stmt => stmt.visitStatement(converter, null)).filter(stmt => stmt != null));
|
...stmts.map(stmt => stmt.visitStatement(converter, null)).filter(stmt => stmt != null));
|
||||||
const preambleStmts: ts.Statement[] = [];
|
|
||||||
if (preamble) {
|
|
||||||
const commentStmt = this.createCommentStatement(sourceFile, preamble);
|
|
||||||
preambleStmts.push(commentStmt);
|
|
||||||
}
|
|
||||||
const sourceStatements =
|
const sourceStatements =
|
||||||
[...preambleStmts, ...converter.getReexports(), ...converter.getImports(), ...statements];
|
[...converter.getReexports(), ...converter.getImports(), ...statements];
|
||||||
|
if (preamble) {
|
||||||
|
// We always attach the preamble comment to a `NotEmittedStatement` node, because tsickle uses
|
||||||
|
// this node type as a marker of the preamble to ensure that it adds its own new nodes after
|
||||||
|
// the preamble.
|
||||||
|
const preambleCommentHolder = ts.createNotEmittedStatement(sourceFile);
|
||||||
|
// Preamble comments are passed through as-is, which means that they must already contain a
|
||||||
|
// leading `*` if they should be a JSDOC comment.
|
||||||
|
ts.addSyntheticLeadingComment(
|
||||||
|
preambleCommentHolder, ts.SyntaxKind.MultiLineCommentTrivia, preamble,
|
||||||
|
/* hasTrailingNewline */ true);
|
||||||
|
sourceStatements.unshift(preambleCommentHolder);
|
||||||
|
}
|
||||||
|
|
||||||
converter.updateSourceMap(sourceStatements);
|
converter.updateSourceMap(sourceStatements);
|
||||||
const newSourceFile = ts.updateSourceFileNode(sourceFile, sourceStatements);
|
const newSourceFile = ts.updateSourceFileNode(sourceFile, sourceStatements);
|
||||||
return [newSourceFile, converter.getNodeMap()];
|
return [newSourceFile, converter.getNodeMap()];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a not emitted statement containing the given comment. */
|
|
||||||
createCommentStatement(sourceFile: ts.SourceFile, comment: string): ts.Statement {
|
|
||||||
if (comment.startsWith('/*') && comment.endsWith('*/')) {
|
|
||||||
comment = comment.substr(2, comment.length - 4);
|
|
||||||
}
|
|
||||||
const commentStmt = ts.createNotEmittedStatement(sourceFile);
|
|
||||||
ts.setSyntheticLeadingComments(
|
|
||||||
commentStmt,
|
|
||||||
[{kind: ts.SyntaxKind.MultiLineCommentTrivia, text: comment, pos: -1, end: -1}]);
|
|
||||||
ts.setEmitFlags(commentStmt, ts.EmitFlags.CustomPrologue);
|
|
||||||
return commentStmt;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -288,10 +283,13 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor {
|
|||||||
recordLastSourceRange();
|
recordLastSourceRange();
|
||||||
}
|
}
|
||||||
|
|
||||||
private record<T extends ts.Node>(ngNode: Node, tsNode: T|null): RecordedNode<T> {
|
private postProcess<T extends ts.Node>(ngNode: Node, tsNode: T|null): RecordedNode<T> {
|
||||||
if (tsNode && !this._nodeMap.has(tsNode)) {
|
if (tsNode && !this._nodeMap.has(tsNode)) {
|
||||||
this._nodeMap.set(tsNode, ngNode);
|
this._nodeMap.set(tsNode, ngNode);
|
||||||
}
|
}
|
||||||
|
if (tsNode !== null && ngNode instanceof Statement) {
|
||||||
|
attachComments(tsNode as unknown as ts.Statement, ngNode.leadingComments);
|
||||||
|
}
|
||||||
return tsNode as RecordedNode<T>;
|
return tsNode as RecordedNode<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,19 +345,19 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor {
|
|||||||
// Note: We need to add an explicit variable and export declaration so that
|
// Note: We need to add an explicit variable and export declaration so that
|
||||||
// the variable can be referred in the same file as well.
|
// the variable can be referred in the same file as well.
|
||||||
const tsVarStmt =
|
const tsVarStmt =
|
||||||
this.record(stmt, ts.createVariableStatement(/* modifiers */[], varDeclList));
|
this.postProcess(stmt, ts.createVariableStatement(/* modifiers */[], varDeclList));
|
||||||
const exportStmt = this.record(
|
const exportStmt = this.postProcess(
|
||||||
stmt,
|
stmt,
|
||||||
ts.createExportDeclaration(
|
ts.createExportDeclaration(
|
||||||
/*decorators*/ undefined, /*modifiers*/ undefined,
|
/*decorators*/ undefined, /*modifiers*/ undefined,
|
||||||
ts.createNamedExports([ts.createExportSpecifier(stmt.name, stmt.name)])));
|
ts.createNamedExports([ts.createExportSpecifier(stmt.name, stmt.name)])));
|
||||||
return [tsVarStmt, exportStmt];
|
return [tsVarStmt, exportStmt];
|
||||||
}
|
}
|
||||||
return this.record(stmt, ts.createVariableStatement(this.getModifiers(stmt), varDeclList));
|
return this.postProcess(stmt, ts.createVariableStatement(this.getModifiers(stmt), varDeclList));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitDeclareFunctionStmt(stmt: DeclareFunctionStmt) {
|
visitDeclareFunctionStmt(stmt: DeclareFunctionStmt) {
|
||||||
return this.record(
|
return this.postProcess(
|
||||||
stmt,
|
stmt,
|
||||||
ts.createFunctionDeclaration(
|
ts.createFunctionDeclaration(
|
||||||
/* decorators */ undefined, this.getModifiers(stmt),
|
/* decorators */ undefined, this.getModifiers(stmt),
|
||||||
@ -372,11 +370,11 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitExpressionStmt(stmt: ExpressionStatement) {
|
visitExpressionStmt(stmt: ExpressionStatement) {
|
||||||
return this.record(stmt, ts.createStatement(stmt.expr.visitExpression(this, null)));
|
return this.postProcess(stmt, ts.createStatement(stmt.expr.visitExpression(this, null)));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitReturnStmt(stmt: ReturnStatement) {
|
visitReturnStmt(stmt: ReturnStatement) {
|
||||||
return this.record(
|
return this.postProcess(
|
||||||
stmt, ts.createReturn(stmt.value ? stmt.value.visitExpression(this, null) : undefined));
|
stmt, ts.createReturn(stmt.value ? stmt.value.visitExpression(this, null) : undefined));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -434,7 +432,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor {
|
|||||||
/* decorators */ undefined, /* modifiers */ undefined,
|
/* decorators */ undefined, /* modifiers */ undefined,
|
||||||
/* dotDotDotToken */ undefined, p.name)),
|
/* dotDotDotToken */ undefined, p.name)),
|
||||||
/* type */ undefined, this._visitStatements(method.body)));
|
/* type */ undefined, this._visitStatements(method.body)));
|
||||||
return this.record(
|
return this.postProcess(
|
||||||
stmt,
|
stmt,
|
||||||
ts.createClassDeclaration(
|
ts.createClassDeclaration(
|
||||||
/* decorators */ undefined, modifiers, stmt.name, /* typeParameters*/ undefined,
|
/* decorators */ undefined, modifiers, stmt.name, /* typeParameters*/ undefined,
|
||||||
@ -446,7 +444,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitIfStmt(stmt: IfStmt) {
|
visitIfStmt(stmt: IfStmt) {
|
||||||
return this.record(
|
return this.postProcess(
|
||||||
stmt,
|
stmt,
|
||||||
ts.createIf(
|
ts.createIf(
|
||||||
stmt.condition.visitExpression(this, null), this._visitStatements(stmt.trueCase),
|
stmt.condition.visitExpression(this, null), this._visitStatements(stmt.trueCase),
|
||||||
@ -455,7 +453,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitTryCatchStmt(stmt: TryCatchStmt): RecordedNode<ts.TryStatement> {
|
visitTryCatchStmt(stmt: TryCatchStmt): RecordedNode<ts.TryStatement> {
|
||||||
return this.record(
|
return this.postProcess(
|
||||||
stmt,
|
stmt,
|
||||||
ts.createTry(
|
ts.createTry(
|
||||||
this._visitStatements(stmt.bodyStmts),
|
this._visitStatements(stmt.bodyStmts),
|
||||||
@ -474,64 +472,46 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitThrowStmt(stmt: ThrowStmt) {
|
visitThrowStmt(stmt: ThrowStmt) {
|
||||||
return this.record(stmt, ts.createThrow(stmt.error.visitExpression(this, null)));
|
return this.postProcess(stmt, ts.createThrow(stmt.error.visitExpression(this, null)));
|
||||||
}
|
|
||||||
|
|
||||||
visitCommentStmt(stmt: CommentStmt, sourceFile: ts.SourceFile) {
|
|
||||||
const text = stmt.multiline ? ` ${stmt.comment} ` : ` ${stmt.comment}`;
|
|
||||||
return this.createCommentStmt(text, stmt.multiline, sourceFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitJSDocCommentStmt(stmt: JSDocCommentStmt, sourceFile: ts.SourceFile) {
|
|
||||||
return this.createCommentStmt(stmt.toString(), true, sourceFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
private createCommentStmt(text: string, multiline: boolean, sourceFile: ts.SourceFile):
|
|
||||||
ts.NotEmittedStatement {
|
|
||||||
const commentStmt = ts.createNotEmittedStatement(sourceFile);
|
|
||||||
const kind =
|
|
||||||
multiline ? ts.SyntaxKind.MultiLineCommentTrivia : ts.SyntaxKind.SingleLineCommentTrivia;
|
|
||||||
ts.setSyntheticLeadingComments(commentStmt, [{kind, text, pos: -1, end: -1}]);
|
|
||||||
return commentStmt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExpressionVisitor
|
// ExpressionVisitor
|
||||||
visitWrappedNodeExpr(expr: WrappedNodeExpr<any>) {
|
visitWrappedNodeExpr(expr: WrappedNodeExpr<any>) {
|
||||||
return this.record(expr, expr.node);
|
return this.postProcess(expr, expr.node);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitTypeofExpr(expr: TypeofExpr) {
|
visitTypeofExpr(expr: TypeofExpr) {
|
||||||
const typeOf = ts.createTypeOf(expr.expr.visitExpression(this, null));
|
const typeOf = ts.createTypeOf(expr.expr.visitExpression(this, null));
|
||||||
return this.record(expr, typeOf);
|
return this.postProcess(expr, typeOf);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExpressionVisitor
|
// ExpressionVisitor
|
||||||
visitReadVarExpr(expr: ReadVarExpr) {
|
visitReadVarExpr(expr: ReadVarExpr) {
|
||||||
switch (expr.builtin) {
|
switch (expr.builtin) {
|
||||||
case BuiltinVar.This:
|
case BuiltinVar.This:
|
||||||
return this.record(expr, ts.createIdentifier(METHOD_THIS_NAME));
|
return this.postProcess(expr, ts.createIdentifier(METHOD_THIS_NAME));
|
||||||
case BuiltinVar.CatchError:
|
case BuiltinVar.CatchError:
|
||||||
return this.record(expr, ts.createIdentifier(CATCH_ERROR_NAME));
|
return this.postProcess(expr, ts.createIdentifier(CATCH_ERROR_NAME));
|
||||||
case BuiltinVar.CatchStack:
|
case BuiltinVar.CatchStack:
|
||||||
return this.record(expr, ts.createIdentifier(CATCH_STACK_NAME));
|
return this.postProcess(expr, ts.createIdentifier(CATCH_STACK_NAME));
|
||||||
case BuiltinVar.Super:
|
case BuiltinVar.Super:
|
||||||
return this.record(expr, ts.createSuper());
|
return this.postProcess(expr, ts.createSuper());
|
||||||
}
|
}
|
||||||
if (expr.name) {
|
if (expr.name) {
|
||||||
return this.record(expr, ts.createIdentifier(expr.name));
|
return this.postProcess(expr, ts.createIdentifier(expr.name));
|
||||||
}
|
}
|
||||||
throw Error(`Unexpected ReadVarExpr form`);
|
throw Error(`Unexpected ReadVarExpr form`);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitWriteVarExpr(expr: WriteVarExpr): RecordedNode<ts.BinaryExpression> {
|
visitWriteVarExpr(expr: WriteVarExpr): RecordedNode<ts.BinaryExpression> {
|
||||||
return this.record(
|
return this.postProcess(
|
||||||
expr,
|
expr,
|
||||||
ts.createAssignment(
|
ts.createAssignment(
|
||||||
ts.createIdentifier(expr.name), expr.value.visitExpression(this, null)));
|
ts.createIdentifier(expr.name), expr.value.visitExpression(this, null)));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitWriteKeyExpr(expr: WriteKeyExpr): RecordedNode<ts.BinaryExpression> {
|
visitWriteKeyExpr(expr: WriteKeyExpr): RecordedNode<ts.BinaryExpression> {
|
||||||
return this.record(
|
return this.postProcess(
|
||||||
expr,
|
expr,
|
||||||
ts.createAssignment(
|
ts.createAssignment(
|
||||||
ts.createElementAccess(
|
ts.createElementAccess(
|
||||||
@ -540,7 +520,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitWritePropExpr(expr: WritePropExpr): RecordedNode<ts.BinaryExpression> {
|
visitWritePropExpr(expr: WritePropExpr): RecordedNode<ts.BinaryExpression> {
|
||||||
return this.record(
|
return this.postProcess(
|
||||||
expr,
|
expr,
|
||||||
ts.createAssignment(
|
ts.createAssignment(
|
||||||
ts.createPropertyAccess(expr.receiver.visitExpression(this, null), expr.name),
|
ts.createPropertyAccess(expr.receiver.visitExpression(this, null), expr.name),
|
||||||
@ -549,7 +529,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor {
|
|||||||
|
|
||||||
visitInvokeMethodExpr(expr: InvokeMethodExpr): RecordedNode<ts.CallExpression> {
|
visitInvokeMethodExpr(expr: InvokeMethodExpr): RecordedNode<ts.CallExpression> {
|
||||||
const methodName = getMethodName(expr);
|
const methodName = getMethodName(expr);
|
||||||
return this.record(
|
return this.postProcess(
|
||||||
expr,
|
expr,
|
||||||
ts.createCall(
|
ts.createCall(
|
||||||
ts.createPropertyAccess(expr.receiver.visitExpression(this, null), methodName),
|
ts.createPropertyAccess(expr.receiver.visitExpression(this, null), methodName),
|
||||||
@ -557,7 +537,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitInvokeFunctionExpr(expr: InvokeFunctionExpr): RecordedNode<ts.CallExpression> {
|
visitInvokeFunctionExpr(expr: InvokeFunctionExpr): RecordedNode<ts.CallExpression> {
|
||||||
return this.record(
|
return this.postProcess(
|
||||||
expr,
|
expr,
|
||||||
ts.createCall(
|
ts.createCall(
|
||||||
expr.fn.visitExpression(this, null), /* typeArguments */ undefined,
|
expr.fn.visitExpression(this, null), /* typeArguments */ undefined,
|
||||||
@ -565,7 +545,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitInstantiateExpr(expr: InstantiateExpr): RecordedNode<ts.NewExpression> {
|
visitInstantiateExpr(expr: InstantiateExpr): RecordedNode<ts.NewExpression> {
|
||||||
return this.record(
|
return this.postProcess(
|
||||||
expr,
|
expr,
|
||||||
ts.createNew(
|
ts.createNew(
|
||||||
expr.classExpr.visitExpression(this, null), /* typeArguments */ undefined,
|
expr.classExpr.visitExpression(this, null), /* typeArguments */ undefined,
|
||||||
@ -573,7 +553,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitLiteralExpr(expr: LiteralExpr) {
|
visitLiteralExpr(expr: LiteralExpr) {
|
||||||
return this.record(expr, createLiteral(expr.value));
|
return this.postProcess(expr, createLiteral(expr.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitLocalizedString(expr: LocalizedString, context: any) {
|
visitLocalizedString(expr: LocalizedString, context: any) {
|
||||||
@ -581,12 +561,12 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitExternalExpr(expr: ExternalExpr) {
|
visitExternalExpr(expr: ExternalExpr) {
|
||||||
return this.record(expr, this._visitIdentifier(expr.value));
|
return this.postProcess(expr, this._visitIdentifier(expr.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitConditionalExpr(expr: ConditionalExpr): RecordedNode<ts.ParenthesizedExpression> {
|
visitConditionalExpr(expr: ConditionalExpr): RecordedNode<ts.ParenthesizedExpression> {
|
||||||
// TODO {chuckj}: Review use of ! on falseCase. Should it be non-nullable?
|
// TODO {chuckj}: Review use of ! on falseCase. Should it be non-nullable?
|
||||||
return this.record(
|
return this.postProcess(
|
||||||
expr,
|
expr,
|
||||||
ts.createParen(ts.createConditional(
|
ts.createParen(ts.createConditional(
|
||||||
expr.condition.visitExpression(this, null), expr.trueCase.visitExpression(this, null),
|
expr.condition.visitExpression(this, null), expr.trueCase.visitExpression(this, null),
|
||||||
@ -594,7 +574,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitNotExpr(expr: NotExpr): RecordedNode<ts.PrefixUnaryExpression> {
|
visitNotExpr(expr: NotExpr): RecordedNode<ts.PrefixUnaryExpression> {
|
||||||
return this.record(
|
return this.postProcess(
|
||||||
expr,
|
expr,
|
||||||
ts.createPrefix(
|
ts.createPrefix(
|
||||||
ts.SyntaxKind.ExclamationToken, expr.condition.visitExpression(this, null)));
|
ts.SyntaxKind.ExclamationToken, expr.condition.visitExpression(this, null)));
|
||||||
@ -609,7 +589,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitFunctionExpr(expr: FunctionExpr) {
|
visitFunctionExpr(expr: FunctionExpr) {
|
||||||
return this.record(
|
return this.postProcess(
|
||||||
expr,
|
expr,
|
||||||
ts.createFunctionExpression(
|
ts.createFunctionExpression(
|
||||||
/* modifiers */ undefined, /* astriskToken */ undefined,
|
/* modifiers */ undefined, /* astriskToken */ undefined,
|
||||||
@ -636,7 +616,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor {
|
|||||||
throw new Error(`Unknown operator: ${expr.operator}`);
|
throw new Error(`Unknown operator: ${expr.operator}`);
|
||||||
}
|
}
|
||||||
const binary = ts.createPrefix(unaryOperator, expr.expr.visitExpression(this, null));
|
const binary = ts.createPrefix(unaryOperator, expr.expr.visitExpression(this, null));
|
||||||
return this.record(expr, expr.parens ? ts.createParen(binary) : binary);
|
return this.postProcess(expr, expr.parens ? ts.createParen(binary) : binary);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitBinaryOperatorExpr(expr: BinaryOperatorExpr):
|
visitBinaryOperatorExpr(expr: BinaryOperatorExpr):
|
||||||
@ -696,28 +676,28 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor {
|
|||||||
}
|
}
|
||||||
const binary = ts.createBinary(
|
const binary = ts.createBinary(
|
||||||
expr.lhs.visitExpression(this, null), binaryOperator, expr.rhs.visitExpression(this, null));
|
expr.lhs.visitExpression(this, null), binaryOperator, expr.rhs.visitExpression(this, null));
|
||||||
return this.record(expr, expr.parens ? ts.createParen(binary) : binary);
|
return this.postProcess(expr, expr.parens ? ts.createParen(binary) : binary);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitReadPropExpr(expr: ReadPropExpr): RecordedNode<ts.PropertyAccessExpression> {
|
visitReadPropExpr(expr: ReadPropExpr): RecordedNode<ts.PropertyAccessExpression> {
|
||||||
return this.record(
|
return this.postProcess(
|
||||||
expr, ts.createPropertyAccess(expr.receiver.visitExpression(this, null), expr.name));
|
expr, ts.createPropertyAccess(expr.receiver.visitExpression(this, null), expr.name));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitReadKeyExpr(expr: ReadKeyExpr): RecordedNode<ts.ElementAccessExpression> {
|
visitReadKeyExpr(expr: ReadKeyExpr): RecordedNode<ts.ElementAccessExpression> {
|
||||||
return this.record(
|
return this.postProcess(
|
||||||
expr,
|
expr,
|
||||||
ts.createElementAccess(
|
ts.createElementAccess(
|
||||||
expr.receiver.visitExpression(this, null), expr.index.visitExpression(this, null)));
|
expr.receiver.visitExpression(this, null), expr.index.visitExpression(this, null)));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitLiteralArrayExpr(expr: LiteralArrayExpr): RecordedNode<ts.ArrayLiteralExpression> {
|
visitLiteralArrayExpr(expr: LiteralArrayExpr): RecordedNode<ts.ArrayLiteralExpression> {
|
||||||
return this.record(
|
return this.postProcess(
|
||||||
expr, ts.createArrayLiteral(expr.entries.map(entry => entry.visitExpression(this, null))));
|
expr, ts.createArrayLiteral(expr.entries.map(entry => entry.visitExpression(this, null))));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitLiteralMapExpr(expr: LiteralMapExpr): RecordedNode<ts.ObjectLiteralExpression> {
|
visitLiteralMapExpr(expr: LiteralMapExpr): RecordedNode<ts.ObjectLiteralExpression> {
|
||||||
return this.record(
|
return this.postProcess(
|
||||||
expr,
|
expr,
|
||||||
ts.createObjectLiteral(expr.entries.map(
|
ts.createObjectLiteral(expr.entries.map(
|
||||||
entry => ts.createPropertyAssignment(
|
entry => ts.createPropertyAssignment(
|
||||||
@ -728,7 +708,7 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitCommaExpr(expr: CommaExpr): RecordedNode<ts.Expression> {
|
visitCommaExpr(expr: CommaExpr): RecordedNode<ts.Expression> {
|
||||||
return this.record(
|
return this.postProcess(
|
||||||
expr,
|
expr,
|
||||||
expr.parts.map(e => e.visitExpression(this, null))
|
expr.parts.map(e => e.visitExpression(this, null))
|
||||||
.reduce<ts.Expression|null>(
|
.reduce<ts.Expression|null>(
|
||||||
@ -773,7 +753,6 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getMethodName(methodRef: {name: string|null; builtin: BuiltinMethod | null}): string {
|
function getMethodName(methodRef: {name: string|null; builtin: BuiltinMethod | null}): string {
|
||||||
if (methodRef.name) {
|
if (methodRef.name) {
|
||||||
return methodRef.name;
|
return methodRef.name;
|
||||||
|
@ -10,15 +10,15 @@ import {GeneratedFile} from '@angular/compiler';
|
|||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
import {TypeScriptNodeEmitter} from './node_emitter';
|
import {TypeScriptNodeEmitter} from './node_emitter';
|
||||||
import {GENERATED_FILES} from './util';
|
import {GENERATED_FILES, stripComment} from './util';
|
||||||
|
|
||||||
function getPreamble(original: string) {
|
function getPreamble(original: string) {
|
||||||
return `/**
|
return `*
|
||||||
* @fileoverview This file was generated by the Angular template compiler. Do not edit.
|
* @fileoverview This file was generated by the Angular template compiler. Do not edit.
|
||||||
* ${original}
|
* ${original}
|
||||||
* @suppress {suspiciousCode,uselessCode,missingProperties,missingOverride,checkTypes,extraRequire}
|
* @suppress {suspiciousCode,uselessCode,missingProperties,missingOverride,checkTypes,extraRequire}
|
||||||
* tslint:disable
|
* tslint:disable
|
||||||
*/`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,9 +41,6 @@ export function getAngularEmitterTransformFactory(
|
|||||||
if (orig) originalComment = getFileoverviewComment(orig);
|
if (orig) originalComment = getFileoverviewComment(orig);
|
||||||
const preamble = getPreamble(originalComment);
|
const preamble = getPreamble(originalComment);
|
||||||
if (g && g.stmts) {
|
if (g && g.stmts) {
|
||||||
const orig = program.getSourceFile(g.srcFileUrl);
|
|
||||||
let originalComment = '';
|
|
||||||
if (orig) originalComment = getFileoverviewComment(orig);
|
|
||||||
const [newSourceFile] = emitter.updateSourceFile(sourceFile, g.stmts, preamble);
|
const [newSourceFile] = emitter.updateSourceFile(sourceFile, g.stmts, preamble);
|
||||||
return newSourceFile;
|
return newSourceFile;
|
||||||
} else if (GENERATED_FILES.test(sourceFile.fileName)) {
|
} else if (GENERATED_FILES.test(sourceFile.fileName)) {
|
||||||
@ -51,8 +48,11 @@ export function getAngularEmitterTransformFactory(
|
|||||||
// and various minutiae.
|
// and various minutiae.
|
||||||
// Clear out the source file entirely, only including the preamble comment, so that
|
// Clear out the source file entirely, only including the preamble comment, so that
|
||||||
// ngc produces an empty .js file.
|
// ngc produces an empty .js file.
|
||||||
return ts.updateSourceFileNode(
|
const commentStmt = ts.createNotEmittedStatement(sourceFile);
|
||||||
sourceFile, [emitter.createCommentStatement(sourceFile, preamble)]);
|
ts.addSyntheticLeadingComment(
|
||||||
|
commentStmt, ts.SyntaxKind.MultiLineCommentTrivia, preamble,
|
||||||
|
/* hasTrailingNewline */ true);
|
||||||
|
return ts.updateSourceFileNode(sourceFile, [commentStmt]);
|
||||||
}
|
}
|
||||||
return sourceFile;
|
return sourceFile;
|
||||||
};
|
};
|
||||||
@ -75,5 +75,6 @@ function getFileoverviewComment(sourceFile: ts.SourceFile): string {
|
|||||||
const commentText = sourceFile.getFullText().substring(comment.pos, comment.end);
|
const commentText = sourceFile.getFullText().substring(comment.pos, comment.end);
|
||||||
// Closure Compiler ignores @suppress and similar if the comment contains @license.
|
// Closure Compiler ignores @suppress and similar if the comment contains @license.
|
||||||
if (commentText.indexOf('@license') !== -1) return '';
|
if (commentText.indexOf('@license') !== -1) return '';
|
||||||
return commentText.replace(/^\/\*\*/, '').replace(/ ?\*\/$/, '');
|
// Also remove any leading `* ` from the first line in case it was a JSDOC comment
|
||||||
|
return stripComment(commentText).replace(/^\*\s+/, '');
|
||||||
}
|
}
|
||||||
|
@ -94,3 +94,12 @@ export function ngToTsDiagnostic(ng: Diagnostic): ts.Diagnostic {
|
|||||||
length,
|
length,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strip multiline comment start and end markers from the `commentText` string.
|
||||||
|
*
|
||||||
|
* This will also strip the JSDOC comment start marker (`/**`).
|
||||||
|
*/
|
||||||
|
export function stripComment(commentText: string): string {
|
||||||
|
return commentText.replace(/^\/\*\*?/, '').replace(/\*\/$/, '').trim();
|
||||||
|
}
|
||||||
|
@ -141,7 +141,7 @@ describe('ngc transformer command-line', () => {
|
|||||||
write('mymodule.ts', `
|
write('mymodule.ts', `
|
||||||
import {NgModule} from '@angular/core';
|
import {NgModule} from '@angular/core';
|
||||||
import {AClass} from './aclass';
|
import {AClass} from './aclass';
|
||||||
|
|
||||||
@NgModule({declarations: []})
|
@NgModule({declarations: []})
|
||||||
export class MyModule {
|
export class MyModule {
|
||||||
constructor(importedClass: AClass) {}
|
constructor(importedClass: AClass) {}
|
||||||
@ -382,13 +382,13 @@ describe('ngc transformer command-line', () => {
|
|||||||
})
|
})
|
||||||
export class MyModule {}
|
export class MyModule {}
|
||||||
`);
|
`);
|
||||||
expect(contents).toContain('@fileoverview');
|
expect(contents).toContain(
|
||||||
expect(contents).toContain('generated by the Angular template compiler');
|
'/**\n * @fileoverview This file was generated by the Angular template compiler. Do not edit.');
|
||||||
expect(contents).toContain('@suppress {suspiciousCode');
|
expect(contents).toContain('\n * @suppress {suspiciousCode');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be merged with existing fileoverview comments', () => {
|
it('should be merged with existing fileoverview comments', () => {
|
||||||
const contents = compileAndRead(`/** Hello world. */
|
const contents = compileAndRead(`/**\n * @fileoverview Hello world.\n */
|
||||||
|
|
||||||
import {CommonModule} from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
import {NgModule} from '@angular/core';
|
import {NgModule} from '@angular/core';
|
||||||
@ -398,7 +398,7 @@ describe('ngc transformer command-line', () => {
|
|||||||
})
|
})
|
||||||
export class MyModule {}
|
export class MyModule {}
|
||||||
`);
|
`);
|
||||||
expect(contents).toContain('Hello world.');
|
expect(contents).toContain('\n * @fileoverview Hello world.\n');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should only pick file comments', () => {
|
it('should only pick file comments', () => {
|
||||||
|
@ -267,48 +267,69 @@ describe('TypeScriptNodeEmitter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('comments', () => {
|
describe('comments', () => {
|
||||||
it('should support a preamble', () => {
|
it('should support a preamble, which is wrapped as a multi-line comment with no trimming or padding',
|
||||||
expect(emitStmt(o.variable('a').toStmt(), Format.Flat, '/* SomePreamble */'))
|
() => {
|
||||||
.toBe('/* SomePreamble */ a;');
|
expect(emitStmt(o.variable('a').toStmt(), Format.Raw, '*\n * SomePreamble\n '))
|
||||||
});
|
.toBe('/**\n * SomePreamble\n */\na;');
|
||||||
|
});
|
||||||
|
|
||||||
it('should support singleline comments', () => {
|
it('should support singleline comments', () => {
|
||||||
expect(emitStmt(new o.CommentStmt('Simple comment'))).toBe('// Simple comment');
|
expect(emitStmt(
|
||||||
|
new o.ReturnStatement(o.literal(1), null, [o.leadingComment(' a\n b', false)]),
|
||||||
|
Format.Raw))
|
||||||
|
.toBe('// a\n// b\nreturn 1;');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support multiline comments', () => {
|
it('should support multiline comments', () => {
|
||||||
expect(emitStmt(new o.CommentStmt('Multiline comment', true)))
|
expect(emitStmt(
|
||||||
.toBe('/* Multiline comment */');
|
new o.ReturnStatement(
|
||||||
expect(emitStmt(new o.CommentStmt(`Multiline\ncomment`, true), Format.Raw))
|
o.literal(1), null, [o.leadingComment('Multiline comment', true)]),
|
||||||
.toBe(`/* Multiline\ncomment */`);
|
Format.Raw))
|
||||||
|
.toBe('/* Multiline comment */\nreturn 1;');
|
||||||
|
expect(emitStmt(
|
||||||
|
new o.ReturnStatement(
|
||||||
|
o.literal(1), null, [o.leadingComment(`Multiline\ncomment`, true)]),
|
||||||
|
Format.Raw))
|
||||||
|
.toBe(`/* Multiline\ncomment */\nreturn 1;`);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('JSDoc comments', () => {
|
describe('JSDoc comments', () => {
|
||||||
it('should be supported', () => {
|
it('should be supported', () => {
|
||||||
expect(emitStmt(new o.JSDocCommentStmt([{text: 'Intro comment'}]), Format.Raw))
|
|
||||||
.toBe(`/**\n * Intro comment\n */`);
|
|
||||||
expect(emitStmt(
|
expect(emitStmt(
|
||||||
new o.JSDocCommentStmt([{tagName: o.JSDocTagName.Desc, text: 'description'}]),
|
new o.ReturnStatement(
|
||||||
|
o.literal(1), null, [o.jsDocComment([{text: 'Intro comment'}])]),
|
||||||
Format.Raw))
|
Format.Raw))
|
||||||
.toBe(`/**\n * @desc description\n */`);
|
.toBe(`/**\n * Intro comment\n */\nreturn 1;`);
|
||||||
expect(emitStmt(
|
expect(emitStmt(
|
||||||
new o.JSDocCommentStmt([
|
new o.ReturnStatement(
|
||||||
{text: 'Intro comment'},
|
o.literal(1), null,
|
||||||
{tagName: o.JSDocTagName.Desc, text: 'description'},
|
[o.jsDocComment([{tagName: o.JSDocTagName.Desc, text: 'description'}])]),
|
||||||
{tagName: o.JSDocTagName.Id, text: '{number} identifier 123'},
|
Format.Raw))
|
||||||
]),
|
.toBe(`/**\n * @desc description\n */\nreturn 1;`);
|
||||||
|
expect(emitStmt(
|
||||||
|
new o.ReturnStatement(
|
||||||
|
o.literal(1), null, [o.jsDocComment([
|
||||||
|
{text: 'Intro comment'},
|
||||||
|
{tagName: o.JSDocTagName.Desc, text: 'description'},
|
||||||
|
{tagName: o.JSDocTagName.Id, text: '{number} identifier 123'},
|
||||||
|
])]),
|
||||||
Format.Raw))
|
Format.Raw))
|
||||||
.toBe(
|
.toBe(
|
||||||
`/**\n * Intro comment\n * @desc description\n * @id {number} identifier 123\n */`);
|
`/**\n * Intro comment\n * @desc description\n * @id {number} identifier 123\n */\nreturn 1;`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should escape @ in the text', () => {
|
it('should escape @ in the text', () => {
|
||||||
expect(emitStmt(new o.JSDocCommentStmt([{text: 'email@google.com'}]), Format.Raw))
|
expect(emitStmt(
|
||||||
.toBe(`/**\n * email\\@google.com\n */`);
|
new o.ReturnStatement(
|
||||||
|
o.literal(1), null, [o.jsDocComment([{text: 'email@google.com'}])]),
|
||||||
|
Format.Raw))
|
||||||
|
.toBe(`/**\n * email\\@google.com\n */\nreturn 1;`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not allow /* and */ in the text', () => {
|
it('should not allow /* and */ in the text', () => {
|
||||||
expect(() => emitStmt(new o.JSDocCommentStmt([{text: 'some text /* */'}]), Format.Raw))
|
expect(
|
||||||
|
() => emitStmt(new o.ReturnStatement(
|
||||||
|
o.literal(1), null, [o.jsDocComment([{text: 'some text /* */'}])])))
|
||||||
.toThrowError(`JSDoc text cannot contain "/*" and "*/"`);
|
.toThrowError(`JSDoc text cannot contain "/*" and "*/"`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -78,7 +78,7 @@ export * from './ml_parser/tags';
|
|||||||
export {LexerRange} from './ml_parser/lexer';
|
export {LexerRange} from './ml_parser/lexer';
|
||||||
export * from './ml_parser/xml_parser';
|
export * from './ml_parser/xml_parser';
|
||||||
export {NgModuleCompiler} from './ng_module_compiler';
|
export {NgModuleCompiler} from './ng_module_compiler';
|
||||||
export {ArrayType, AssertNotNull, DYNAMIC_TYPE, BinaryOperator, BinaryOperatorExpr, BuiltinMethod, BuiltinType, BuiltinTypeName, BuiltinVar, CastExpr, ClassField, ClassMethod, ClassStmt, CommaExpr, CommentStmt, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, Expression, ExpressionStatement, ExpressionType, ExpressionVisitor, ExternalExpr, ExternalReference, literalMap, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, JSDocCommentStmt, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, MapType, NotExpr, NONE_TYPE, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, StatementVisitor, ThrowStmt, TryCatchStmt, Type, TypeVisitor, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr, StmtModifier, Statement, STRING_TYPE, TypeofExpr, collectExternalReferences} from './output/output_ast';
|
export {ArrayType, AssertNotNull, DYNAMIC_TYPE, BinaryOperator, BinaryOperatorExpr, BuiltinMethod, BuiltinType, BuiltinTypeName, BuiltinVar, CastExpr, ClassField, ClassMethod, ClassStmt, CommaExpr, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, Expression, ExpressionStatement, ExpressionType, ExpressionVisitor, ExternalExpr, ExternalReference, literalMap, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, MapType, NotExpr, NONE_TYPE, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, StatementVisitor, ThrowStmt, TryCatchStmt, Type, TypeVisitor, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr, StmtModifier, Statement, STRING_TYPE, TypeofExpr, collectExternalReferences, jsDocComment, leadingComment, LeadingComment, JSDocComment, UnaryOperator, UnaryOperatorExpr, LocalizedString} from './output/output_ast';
|
||||||
export {EmitterVisitorContext} from './output/abstract_emitter';
|
export {EmitterVisitorContext} from './output/abstract_emitter';
|
||||||
export {JitEvaluator} from './output/output_jit';
|
export {JitEvaluator} from './output/output_jit';
|
||||||
export * from './output/ts_emitter';
|
export * from './output/ts_emitter';
|
||||||
|
@ -202,13 +202,34 @@ export class EmitterVisitorContext {
|
|||||||
export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.ExpressionVisitor {
|
export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.ExpressionVisitor {
|
||||||
constructor(private _escapeDollarInStrings: boolean) {}
|
constructor(private _escapeDollarInStrings: boolean) {}
|
||||||
|
|
||||||
|
protected printLeadingComments(stmt: o.Statement, ctx: EmitterVisitorContext): void {
|
||||||
|
if (stmt.leadingComments === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const comment of stmt.leadingComments) {
|
||||||
|
if (comment instanceof o.JSDocComment) {
|
||||||
|
ctx.print(stmt, `/*${comment.toString()}*/`, comment.trailingNewline);
|
||||||
|
} else {
|
||||||
|
if (comment.multiline) {
|
||||||
|
ctx.print(stmt, `/* ${comment.text} */`, comment.trailingNewline);
|
||||||
|
} else {
|
||||||
|
comment.text.split('\n').forEach((line) => {
|
||||||
|
ctx.println(stmt, `// ${line}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
visitExpressionStmt(stmt: o.ExpressionStatement, ctx: EmitterVisitorContext): any {
|
visitExpressionStmt(stmt: o.ExpressionStatement, ctx: EmitterVisitorContext): any {
|
||||||
|
this.printLeadingComments(stmt, ctx);
|
||||||
stmt.expr.visitExpression(this, ctx);
|
stmt.expr.visitExpression(this, ctx);
|
||||||
ctx.println(stmt, ';');
|
ctx.println(stmt, ';');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
visitReturnStmt(stmt: o.ReturnStatement, ctx: EmitterVisitorContext): any {
|
visitReturnStmt(stmt: o.ReturnStatement, ctx: EmitterVisitorContext): any {
|
||||||
|
this.printLeadingComments(stmt, ctx);
|
||||||
ctx.print(stmt, `return `);
|
ctx.print(stmt, `return `);
|
||||||
stmt.value.visitExpression(this, ctx);
|
stmt.value.visitExpression(this, ctx);
|
||||||
ctx.println(stmt, ';');
|
ctx.println(stmt, ';');
|
||||||
@ -220,6 +241,7 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
|
|||||||
abstract visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any;
|
abstract visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any;
|
||||||
|
|
||||||
visitIfStmt(stmt: o.IfStmt, ctx: EmitterVisitorContext): any {
|
visitIfStmt(stmt: o.IfStmt, ctx: EmitterVisitorContext): any {
|
||||||
|
this.printLeadingComments(stmt, ctx);
|
||||||
ctx.print(stmt, `if (`);
|
ctx.print(stmt, `if (`);
|
||||||
stmt.condition.visitExpression(this, ctx);
|
stmt.condition.visitExpression(this, ctx);
|
||||||
ctx.print(stmt, `) {`);
|
ctx.print(stmt, `) {`);
|
||||||
@ -248,25 +270,12 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
|
|||||||
abstract visitTryCatchStmt(stmt: o.TryCatchStmt, ctx: EmitterVisitorContext): any;
|
abstract visitTryCatchStmt(stmt: o.TryCatchStmt, ctx: EmitterVisitorContext): any;
|
||||||
|
|
||||||
visitThrowStmt(stmt: o.ThrowStmt, ctx: EmitterVisitorContext): any {
|
visitThrowStmt(stmt: o.ThrowStmt, ctx: EmitterVisitorContext): any {
|
||||||
|
this.printLeadingComments(stmt, ctx);
|
||||||
ctx.print(stmt, `throw `);
|
ctx.print(stmt, `throw `);
|
||||||
stmt.error.visitExpression(this, ctx);
|
stmt.error.visitExpression(this, ctx);
|
||||||
ctx.println(stmt, `;`);
|
ctx.println(stmt, `;`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
visitCommentStmt(stmt: o.CommentStmt, ctx: EmitterVisitorContext): any {
|
|
||||||
if (stmt.multiline) {
|
|
||||||
ctx.println(stmt, `/* ${stmt.comment} */`);
|
|
||||||
} else {
|
|
||||||
stmt.comment.split('\n').forEach((line) => {
|
|
||||||
ctx.println(stmt, `// ${line}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitJSDocCommentStmt(stmt: o.JSDocCommentStmt, ctx: EmitterVisitorContext) {
|
|
||||||
ctx.println(stmt, `/*${stmt.toString()}*/`);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any;
|
abstract visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any;
|
||||||
|
|
||||||
|
@ -16,15 +16,11 @@ export enum TypeModifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export abstract class Type {
|
export abstract class Type {
|
||||||
constructor(public modifiers: TypeModifier[]|null = null) {
|
constructor(public modifiers: TypeModifier[] = []) {}
|
||||||
if (!modifiers) {
|
|
||||||
this.modifiers = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
abstract visitType(visitor: TypeVisitor, context: any): any;
|
abstract visitType(visitor: TypeVisitor, context: any): any;
|
||||||
|
|
||||||
hasModifier(modifier: TypeModifier): boolean {
|
hasModifier(modifier: TypeModifier): boolean {
|
||||||
return this.modifiers!.indexOf(modifier) !== -1;
|
return this.modifiers.indexOf(modifier) !== -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +36,7 @@ export enum BuiltinTypeName {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class BuiltinType extends Type {
|
export class BuiltinType extends Type {
|
||||||
constructor(public name: BuiltinTypeName, modifiers: TypeModifier[]|null = null) {
|
constructor(public name: BuiltinTypeName, modifiers?: TypeModifier[]) {
|
||||||
super(modifiers);
|
super(modifiers);
|
||||||
}
|
}
|
||||||
visitType(visitor: TypeVisitor, context: any): any {
|
visitType(visitor: TypeVisitor, context: any): any {
|
||||||
@ -50,8 +46,7 @@ export class BuiltinType extends Type {
|
|||||||
|
|
||||||
export class ExpressionType extends Type {
|
export class ExpressionType extends Type {
|
||||||
constructor(
|
constructor(
|
||||||
public value: Expression, modifiers: TypeModifier[]|null = null,
|
public value: Expression, modifiers?: TypeModifier[], public typeParams: Type[]|null = null) {
|
||||||
public typeParams: Type[]|null = null) {
|
|
||||||
super(modifiers);
|
super(modifiers);
|
||||||
}
|
}
|
||||||
visitType(visitor: TypeVisitor, context: any): any {
|
visitType(visitor: TypeVisitor, context: any): any {
|
||||||
@ -61,7 +56,7 @@ export class ExpressionType extends Type {
|
|||||||
|
|
||||||
|
|
||||||
export class ArrayType extends Type {
|
export class ArrayType extends Type {
|
||||||
constructor(public of: Type, modifiers: TypeModifier[]|null = null) {
|
constructor(public of: Type, modifiers?: TypeModifier[]) {
|
||||||
super(modifiers);
|
super(modifiers);
|
||||||
}
|
}
|
||||||
visitType(visitor: TypeVisitor, context: any): any {
|
visitType(visitor: TypeVisitor, context: any): any {
|
||||||
@ -72,7 +67,7 @@ export class ArrayType extends Type {
|
|||||||
|
|
||||||
export class MapType extends Type {
|
export class MapType extends Type {
|
||||||
public valueType: Type|null;
|
public valueType: Type|null;
|
||||||
constructor(valueType: Type|null|undefined, modifiers: TypeModifier[]|null = null) {
|
constructor(valueType: Type|null|undefined, modifiers?: TypeModifier[]) {
|
||||||
super(modifiers);
|
super(modifiers);
|
||||||
this.valueType = valueType || null;
|
this.valueType = valueType || null;
|
||||||
}
|
}
|
||||||
@ -357,7 +352,7 @@ export class WriteVarExpr extends Expression {
|
|||||||
return visitor.visitWriteVarExpr(this, context);
|
return visitor.visitWriteVarExpr(this, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
toDeclStmt(type?: Type|null, modifiers?: StmtModifier[]|null): DeclareVarStmt {
|
toDeclStmt(type?: Type|null, modifiers?: StmtModifier[]): DeclareVarStmt {
|
||||||
return new DeclareVarStmt(this.name, this.value, type, modifiers, this.sourceSpan);
|
return new DeclareVarStmt(this.name, this.value, type, modifiers, this.sourceSpan);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -764,7 +759,7 @@ export class FunctionExpr extends Expression {
|
|||||||
return visitor.visitFunctionExpr(this, context);
|
return visitor.visitFunctionExpr(this, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
toDeclStmt(name: string, modifiers: StmtModifier[]|null = null): DeclareFunctionStmt {
|
toDeclStmt(name: string, modifiers?: StmtModifier[]): DeclareFunctionStmt {
|
||||||
return new DeclareFunctionStmt(
|
return new DeclareFunctionStmt(
|
||||||
name, this.params, this.statements, this.type, modifiers, this.sourceSpan);
|
name, this.params, this.statements, this.type, modifiers, this.sourceSpan);
|
||||||
}
|
}
|
||||||
@ -978,13 +973,25 @@ export enum StmtModifier {
|
|||||||
Static,
|
Static,
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class Statement {
|
export class LeadingComment {
|
||||||
public modifiers: StmtModifier[];
|
constructor(public text: string, public multiline: boolean, public trailingNewline: boolean) {}
|
||||||
public sourceSpan: ParseSourceSpan|null;
|
toString() {
|
||||||
constructor(modifiers?: StmtModifier[]|null, sourceSpan?: ParseSourceSpan|null) {
|
return this.multiline ? ` ${this.text} ` : this.text;
|
||||||
this.modifiers = modifiers || [];
|
|
||||||
this.sourceSpan = sourceSpan || null;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
export class JSDocComment extends LeadingComment {
|
||||||
|
constructor(public tags: JSDocTag[]) {
|
||||||
|
super('', /* multiline */ true, /* trailingNewline */ true);
|
||||||
|
}
|
||||||
|
toString(): string {
|
||||||
|
return serializeTags(this.tags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class Statement {
|
||||||
|
constructor(
|
||||||
|
public modifiers: StmtModifier[] = [], public sourceSpan: ParseSourceSpan|null = null,
|
||||||
|
public leadingComments?: LeadingComment[]) {}
|
||||||
/**
|
/**
|
||||||
* Calculates whether this statement produces the same value as the given statement.
|
* Calculates whether this statement produces the same value as the given statement.
|
||||||
* Note: We don't check Types nor ParseSourceSpans nor function arguments.
|
* Note: We don't check Types nor ParseSourceSpans nor function arguments.
|
||||||
@ -994,7 +1001,12 @@ export abstract class Statement {
|
|||||||
abstract visitStatement(visitor: StatementVisitor, context: any): any;
|
abstract visitStatement(visitor: StatementVisitor, context: any): any;
|
||||||
|
|
||||||
hasModifier(modifier: StmtModifier): boolean {
|
hasModifier(modifier: StmtModifier): boolean {
|
||||||
return this.modifiers!.indexOf(modifier) !== -1;
|
return this.modifiers.indexOf(modifier) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
addLeadingComment(leadingComment: LeadingComment): void {
|
||||||
|
this.leadingComments = this.leadingComments ?? [];
|
||||||
|
this.leadingComments.push(leadingComment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1002,9 +1014,9 @@ export abstract class Statement {
|
|||||||
export class DeclareVarStmt extends Statement {
|
export class DeclareVarStmt extends Statement {
|
||||||
public type: Type|null;
|
public type: Type|null;
|
||||||
constructor(
|
constructor(
|
||||||
public name: string, public value?: Expression, type?: Type|null,
|
public name: string, public value?: Expression, type?: Type|null, modifiers?: StmtModifier[],
|
||||||
modifiers: StmtModifier[]|null = null, sourceSpan?: ParseSourceSpan|null) {
|
sourceSpan?: ParseSourceSpan|null, leadingComments?: LeadingComment[]) {
|
||||||
super(modifiers, sourceSpan);
|
super(modifiers, sourceSpan, leadingComments);
|
||||||
this.type = type || (value && value.type) || null;
|
this.type = type || (value && value.type) || null;
|
||||||
}
|
}
|
||||||
isEquivalent(stmt: Statement): boolean {
|
isEquivalent(stmt: Statement): boolean {
|
||||||
@ -1020,28 +1032,29 @@ export class DeclareFunctionStmt extends Statement {
|
|||||||
public type: Type|null;
|
public type: Type|null;
|
||||||
constructor(
|
constructor(
|
||||||
public name: string, public params: FnParam[], public statements: Statement[],
|
public name: string, public params: FnParam[], public statements: Statement[],
|
||||||
type?: Type|null, modifiers: StmtModifier[]|null = null, sourceSpan?: ParseSourceSpan|null) {
|
type?: Type|null, modifiers?: StmtModifier[], sourceSpan?: ParseSourceSpan|null,
|
||||||
super(modifiers, sourceSpan);
|
leadingComments?: LeadingComment[]) {
|
||||||
|
super(modifiers, sourceSpan, leadingComments);
|
||||||
this.type = type || null;
|
this.type = type || null;
|
||||||
}
|
}
|
||||||
isEquivalent(stmt: Statement): boolean {
|
isEquivalent(stmt: Statement): boolean {
|
||||||
return stmt instanceof DeclareFunctionStmt && areAllEquivalent(this.params, stmt.params) &&
|
return stmt instanceof DeclareFunctionStmt && areAllEquivalent(this.params, stmt.params) &&
|
||||||
areAllEquivalent(this.statements, stmt.statements);
|
areAllEquivalent(this.statements, stmt.statements);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitStatement(visitor: StatementVisitor, context: any): any {
|
visitStatement(visitor: StatementVisitor, context: any): any {
|
||||||
return visitor.visitDeclareFunctionStmt(this, context);
|
return visitor.visitDeclareFunctionStmt(this, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ExpressionStatement extends Statement {
|
export class ExpressionStatement extends Statement {
|
||||||
constructor(public expr: Expression, sourceSpan?: ParseSourceSpan|null) {
|
constructor(
|
||||||
super(null, sourceSpan);
|
public expr: Expression, sourceSpan?: ParseSourceSpan|null,
|
||||||
|
leadingComments?: LeadingComment[]) {
|
||||||
|
super([], sourceSpan, leadingComments);
|
||||||
}
|
}
|
||||||
isEquivalent(stmt: Statement): boolean {
|
isEquivalent(stmt: Statement): boolean {
|
||||||
return stmt instanceof ExpressionStatement && this.expr.isEquivalent(stmt.expr);
|
return stmt instanceof ExpressionStatement && this.expr.isEquivalent(stmt.expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitStatement(visitor: StatementVisitor, context: any): any {
|
visitStatement(visitor: StatementVisitor, context: any): any {
|
||||||
return visitor.visitExpressionStmt(this, context);
|
return visitor.visitExpressionStmt(this, context);
|
||||||
}
|
}
|
||||||
@ -1049,8 +1062,10 @@ export class ExpressionStatement extends Statement {
|
|||||||
|
|
||||||
|
|
||||||
export class ReturnStatement extends Statement {
|
export class ReturnStatement extends Statement {
|
||||||
constructor(public value: Expression, sourceSpan?: ParseSourceSpan|null) {
|
constructor(
|
||||||
super(null, sourceSpan);
|
public value: Expression, sourceSpan: ParseSourceSpan|null = null,
|
||||||
|
leadingComments?: LeadingComment[]) {
|
||||||
|
super([], sourceSpan, leadingComments);
|
||||||
}
|
}
|
||||||
isEquivalent(stmt: Statement): boolean {
|
isEquivalent(stmt: Statement): boolean {
|
||||||
return stmt instanceof ReturnStatement && this.value.isEquivalent(stmt.value);
|
return stmt instanceof ReturnStatement && this.value.isEquivalent(stmt.value);
|
||||||
@ -1061,21 +1076,15 @@ export class ReturnStatement extends Statement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class AbstractClassPart {
|
export class AbstractClassPart {
|
||||||
public type: Type|null;
|
constructor(public type: Type|null = null, public modifiers: StmtModifier[] = []) {}
|
||||||
constructor(type: Type|null|undefined, public modifiers: StmtModifier[]|null) {
|
|
||||||
if (!modifiers) {
|
|
||||||
this.modifiers = [];
|
|
||||||
}
|
|
||||||
this.type = type || null;
|
|
||||||
}
|
|
||||||
hasModifier(modifier: StmtModifier): boolean {
|
hasModifier(modifier: StmtModifier): boolean {
|
||||||
return this.modifiers!.indexOf(modifier) !== -1;
|
return this.modifiers.indexOf(modifier) !== -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ClassField extends AbstractClassPart {
|
export class ClassField extends AbstractClassPart {
|
||||||
constructor(
|
constructor(
|
||||||
public name: string, type?: Type|null, modifiers: StmtModifier[]|null = null,
|
public name: string, type?: Type|null, modifiers?: StmtModifier[],
|
||||||
public initializer?: Expression) {
|
public initializer?: Expression) {
|
||||||
super(type, modifiers);
|
super(type, modifiers);
|
||||||
}
|
}
|
||||||
@ -1088,7 +1097,7 @@ export class ClassField extends AbstractClassPart {
|
|||||||
export class ClassMethod extends AbstractClassPart {
|
export class ClassMethod extends AbstractClassPart {
|
||||||
constructor(
|
constructor(
|
||||||
public name: string|null, public params: FnParam[], public body: Statement[],
|
public name: string|null, public params: FnParam[], public body: Statement[],
|
||||||
type?: Type|null, modifiers: StmtModifier[]|null = null) {
|
type?: Type|null, modifiers?: StmtModifier[]) {
|
||||||
super(type, modifiers);
|
super(type, modifiers);
|
||||||
}
|
}
|
||||||
isEquivalent(m: ClassMethod) {
|
isEquivalent(m: ClassMethod) {
|
||||||
@ -1099,8 +1108,7 @@ export class ClassMethod extends AbstractClassPart {
|
|||||||
|
|
||||||
export class ClassGetter extends AbstractClassPart {
|
export class ClassGetter extends AbstractClassPart {
|
||||||
constructor(
|
constructor(
|
||||||
public name: string, public body: Statement[], type?: Type|null,
|
public name: string, public body: Statement[], type?: Type|null, modifiers?: StmtModifier[]) {
|
||||||
modifiers: StmtModifier[]|null = null) {
|
|
||||||
super(type, modifiers);
|
super(type, modifiers);
|
||||||
}
|
}
|
||||||
isEquivalent(m: ClassGetter) {
|
isEquivalent(m: ClassGetter) {
|
||||||
@ -1113,9 +1121,9 @@ export class ClassStmt extends Statement {
|
|||||||
constructor(
|
constructor(
|
||||||
public name: string, public parent: Expression|null, public fields: ClassField[],
|
public name: string, public parent: Expression|null, public fields: ClassField[],
|
||||||
public getters: ClassGetter[], public constructorMethod: ClassMethod,
|
public getters: ClassGetter[], public constructorMethod: ClassMethod,
|
||||||
public methods: ClassMethod[], modifiers: StmtModifier[]|null = null,
|
public methods: ClassMethod[], modifiers?: StmtModifier[], sourceSpan?: ParseSourceSpan|null,
|
||||||
sourceSpan?: ParseSourceSpan|null) {
|
leadingComments?: LeadingComment[]) {
|
||||||
super(modifiers, sourceSpan);
|
super(modifiers, sourceSpan, leadingComments);
|
||||||
}
|
}
|
||||||
isEquivalent(stmt: Statement): boolean {
|
isEquivalent(stmt: Statement): boolean {
|
||||||
return stmt instanceof ClassStmt && this.name === stmt.name &&
|
return stmt instanceof ClassStmt && this.name === stmt.name &&
|
||||||
@ -1134,8 +1142,9 @@ export class ClassStmt extends Statement {
|
|||||||
export class IfStmt extends Statement {
|
export class IfStmt extends Statement {
|
||||||
constructor(
|
constructor(
|
||||||
public condition: Expression, public trueCase: Statement[],
|
public condition: Expression, public trueCase: Statement[],
|
||||||
public falseCase: Statement[] = [], sourceSpan?: ParseSourceSpan|null) {
|
public falseCase: Statement[] = [], sourceSpan?: ParseSourceSpan|null,
|
||||||
super(null, sourceSpan);
|
leadingComments?: LeadingComment[]) {
|
||||||
|
super([], sourceSpan, leadingComments);
|
||||||
}
|
}
|
||||||
isEquivalent(stmt: Statement): boolean {
|
isEquivalent(stmt: Statement): boolean {
|
||||||
return stmt instanceof IfStmt && this.condition.isEquivalent(stmt.condition) &&
|
return stmt instanceof IfStmt && this.condition.isEquivalent(stmt.condition) &&
|
||||||
@ -1147,38 +1156,11 @@ export class IfStmt extends Statement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CommentStmt extends Statement {
|
|
||||||
constructor(public comment: string, public multiline = false, sourceSpan?: ParseSourceSpan|null) {
|
|
||||||
super(null, sourceSpan);
|
|
||||||
}
|
|
||||||
isEquivalent(stmt: Statement): boolean {
|
|
||||||
return stmt instanceof CommentStmt;
|
|
||||||
}
|
|
||||||
visitStatement(visitor: StatementVisitor, context: any): any {
|
|
||||||
return visitor.visitCommentStmt(this, context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class JSDocCommentStmt extends Statement {
|
|
||||||
constructor(public tags: JSDocTag[] = [], sourceSpan?: ParseSourceSpan|null) {
|
|
||||||
super(null, sourceSpan);
|
|
||||||
}
|
|
||||||
isEquivalent(stmt: Statement): boolean {
|
|
||||||
return stmt instanceof JSDocCommentStmt && this.toString() === stmt.toString();
|
|
||||||
}
|
|
||||||
visitStatement(visitor: StatementVisitor, context: any): any {
|
|
||||||
return visitor.visitJSDocCommentStmt(this, context);
|
|
||||||
}
|
|
||||||
toString(): string {
|
|
||||||
return serializeTags(this.tags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class TryCatchStmt extends Statement {
|
export class TryCatchStmt extends Statement {
|
||||||
constructor(
|
constructor(
|
||||||
public bodyStmts: Statement[], public catchStmts: Statement[],
|
public bodyStmts: Statement[], public catchStmts: Statement[],
|
||||||
sourceSpan?: ParseSourceSpan|null) {
|
sourceSpan: ParseSourceSpan|null = null, leadingComments?: LeadingComment[]) {
|
||||||
super(null, sourceSpan);
|
super([], sourceSpan, leadingComments);
|
||||||
}
|
}
|
||||||
isEquivalent(stmt: Statement): boolean {
|
isEquivalent(stmt: Statement): boolean {
|
||||||
return stmt instanceof TryCatchStmt && areAllEquivalent(this.bodyStmts, stmt.bodyStmts) &&
|
return stmt instanceof TryCatchStmt && areAllEquivalent(this.bodyStmts, stmt.bodyStmts) &&
|
||||||
@ -1191,8 +1173,10 @@ export class TryCatchStmt extends Statement {
|
|||||||
|
|
||||||
|
|
||||||
export class ThrowStmt extends Statement {
|
export class ThrowStmt extends Statement {
|
||||||
constructor(public error: Expression, sourceSpan?: ParseSourceSpan|null) {
|
constructor(
|
||||||
super(null, sourceSpan);
|
public error: Expression, sourceSpan: ParseSourceSpan|null = null,
|
||||||
|
leadingComments?: LeadingComment[]) {
|
||||||
|
super([], sourceSpan, leadingComments);
|
||||||
}
|
}
|
||||||
isEquivalent(stmt: ThrowStmt): boolean {
|
isEquivalent(stmt: ThrowStmt): boolean {
|
||||||
return stmt instanceof TryCatchStmt && this.error.isEquivalent(stmt.error);
|
return stmt instanceof TryCatchStmt && this.error.isEquivalent(stmt.error);
|
||||||
@ -1211,8 +1195,6 @@ export interface StatementVisitor {
|
|||||||
visitIfStmt(stmt: IfStmt, context: any): any;
|
visitIfStmt(stmt: IfStmt, context: any): any;
|
||||||
visitTryCatchStmt(stmt: TryCatchStmt, context: any): any;
|
visitTryCatchStmt(stmt: TryCatchStmt, context: any): any;
|
||||||
visitThrowStmt(stmt: ThrowStmt, context: any): any;
|
visitThrowStmt(stmt: ThrowStmt, context: any): any;
|
||||||
visitCommentStmt(stmt: CommentStmt, context: any): any;
|
|
||||||
visitJSDocCommentStmt(stmt: JSDocCommentStmt, context: any): any;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AstTransformer implements StatementVisitor, ExpressionVisitor {
|
export class AstTransformer implements StatementVisitor, ExpressionVisitor {
|
||||||
@ -1374,7 +1356,7 @@ export class AstTransformer implements StatementVisitor, ExpressionVisitor {
|
|||||||
const entries = ast.entries.map(
|
const entries = ast.entries.map(
|
||||||
(entry): LiteralMapEntry => new LiteralMapEntry(
|
(entry): LiteralMapEntry => new LiteralMapEntry(
|
||||||
entry.key, entry.value.visitExpression(this, context), entry.quoted));
|
entry.key, entry.value.visitExpression(this, context), entry.quoted));
|
||||||
const mapType = new MapType(ast.valueType, null);
|
const mapType = new MapType(ast.valueType);
|
||||||
return this.transformExpr(new LiteralMapExpr(entries, mapType, ast.sourceSpan), context);
|
return this.transformExpr(new LiteralMapExpr(entries, mapType, ast.sourceSpan), context);
|
||||||
}
|
}
|
||||||
visitCommaExpr(ast: CommaExpr, context: any): any {
|
visitCommaExpr(ast: CommaExpr, context: any): any {
|
||||||
@ -1388,25 +1370,30 @@ export class AstTransformer implements StatementVisitor, ExpressionVisitor {
|
|||||||
visitDeclareVarStmt(stmt: DeclareVarStmt, context: any): any {
|
visitDeclareVarStmt(stmt: DeclareVarStmt, context: any): any {
|
||||||
const value = stmt.value && stmt.value.visitExpression(this, context);
|
const value = stmt.value && stmt.value.visitExpression(this, context);
|
||||||
return this.transformStmt(
|
return this.transformStmt(
|
||||||
new DeclareVarStmt(stmt.name, value, stmt.type, stmt.modifiers, stmt.sourceSpan), context);
|
new DeclareVarStmt(
|
||||||
|
stmt.name, value, stmt.type, stmt.modifiers, stmt.sourceSpan, stmt.leadingComments),
|
||||||
|
context);
|
||||||
}
|
}
|
||||||
visitDeclareFunctionStmt(stmt: DeclareFunctionStmt, context: any): any {
|
visitDeclareFunctionStmt(stmt: DeclareFunctionStmt, context: any): any {
|
||||||
return this.transformStmt(
|
return this.transformStmt(
|
||||||
new DeclareFunctionStmt(
|
new DeclareFunctionStmt(
|
||||||
stmt.name, stmt.params, this.visitAllStatements(stmt.statements, context), stmt.type,
|
stmt.name, stmt.params, this.visitAllStatements(stmt.statements, context), stmt.type,
|
||||||
stmt.modifiers, stmt.sourceSpan),
|
stmt.modifiers, stmt.sourceSpan, stmt.leadingComments),
|
||||||
context);
|
context);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitExpressionStmt(stmt: ExpressionStatement, context: any): any {
|
visitExpressionStmt(stmt: ExpressionStatement, context: any): any {
|
||||||
return this.transformStmt(
|
return this.transformStmt(
|
||||||
new ExpressionStatement(stmt.expr.visitExpression(this, context), stmt.sourceSpan),
|
new ExpressionStatement(
|
||||||
|
stmt.expr.visitExpression(this, context), stmt.sourceSpan, stmt.leadingComments),
|
||||||
context);
|
context);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitReturnStmt(stmt: ReturnStatement, context: any): any {
|
visitReturnStmt(stmt: ReturnStatement, context: any): any {
|
||||||
return this.transformStmt(
|
return this.transformStmt(
|
||||||
new ReturnStatement(stmt.value.visitExpression(this, context), stmt.sourceSpan), context);
|
new ReturnStatement(
|
||||||
|
stmt.value.visitExpression(this, context), stmt.sourceSpan, stmt.leadingComments),
|
||||||
|
context);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitDeclareClassStmt(stmt: ClassStmt, context: any): any {
|
visitDeclareClassStmt(stmt: ClassStmt, context: any): any {
|
||||||
@ -1435,7 +1422,8 @@ export class AstTransformer implements StatementVisitor, ExpressionVisitor {
|
|||||||
new IfStmt(
|
new IfStmt(
|
||||||
stmt.condition.visitExpression(this, context),
|
stmt.condition.visitExpression(this, context),
|
||||||
this.visitAllStatements(stmt.trueCase, context),
|
this.visitAllStatements(stmt.trueCase, context),
|
||||||
this.visitAllStatements(stmt.falseCase, context), stmt.sourceSpan),
|
this.visitAllStatements(stmt.falseCase, context), stmt.sourceSpan,
|
||||||
|
stmt.leadingComments),
|
||||||
context);
|
context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1443,21 +1431,16 @@ export class AstTransformer implements StatementVisitor, ExpressionVisitor {
|
|||||||
return this.transformStmt(
|
return this.transformStmt(
|
||||||
new TryCatchStmt(
|
new TryCatchStmt(
|
||||||
this.visitAllStatements(stmt.bodyStmts, context),
|
this.visitAllStatements(stmt.bodyStmts, context),
|
||||||
this.visitAllStatements(stmt.catchStmts, context), stmt.sourceSpan),
|
this.visitAllStatements(stmt.catchStmts, context), stmt.sourceSpan,
|
||||||
|
stmt.leadingComments),
|
||||||
context);
|
context);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitThrowStmt(stmt: ThrowStmt, context: any): any {
|
visitThrowStmt(stmt: ThrowStmt, context: any): any {
|
||||||
return this.transformStmt(
|
return this.transformStmt(
|
||||||
new ThrowStmt(stmt.error.visitExpression(this, context), stmt.sourceSpan), context);
|
new ThrowStmt(
|
||||||
}
|
stmt.error.visitExpression(this, context), stmt.sourceSpan, stmt.leadingComments),
|
||||||
|
context);
|
||||||
visitCommentStmt(stmt: CommentStmt, context: any): any {
|
|
||||||
return this.transformStmt(stmt, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitJSDocCommentStmt(stmt: JSDocCommentStmt, context: any): any {
|
|
||||||
return this.transformStmt(stmt, context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
visitAllStatements(stmts: Statement[], context: any): Statement[] {
|
visitAllStatements(stmts: Statement[], context: any): Statement[] {
|
||||||
@ -1647,12 +1630,6 @@ export class RecursiveAstVisitor implements StatementVisitor, ExpressionVisitor
|
|||||||
stmt.error.visitExpression(this, context);
|
stmt.error.visitExpression(this, context);
|
||||||
return stmt;
|
return stmt;
|
||||||
}
|
}
|
||||||
visitCommentStmt(stmt: CommentStmt, context: any): any {
|
|
||||||
return stmt;
|
|
||||||
}
|
|
||||||
visitJSDocCommentStmt(stmt: JSDocCommentStmt, context: any): any {
|
|
||||||
return stmt;
|
|
||||||
}
|
|
||||||
visitAllStatements(stmts: Statement[], context: any): void {
|
visitAllStatements(stmts: Statement[], context: any): void {
|
||||||
stmts.forEach(stmt => stmt.visitStatement(this, context));
|
stmts.forEach(stmt => stmt.visitStatement(this, context));
|
||||||
}
|
}
|
||||||
@ -1743,6 +1720,15 @@ class _ApplySourceSpanTransformer extends AstTransformer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function leadingComment(
|
||||||
|
text: string, multiline: boolean = false, trailingNewline: boolean = true): LeadingComment {
|
||||||
|
return new LeadingComment(text, multiline, trailingNewline);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function jsDocComment(tags: JSDocTag[] = []): JSDocComment {
|
||||||
|
return new JSDocComment(tags);
|
||||||
|
}
|
||||||
|
|
||||||
export function variable(
|
export function variable(
|
||||||
name: string, type?: Type|null, sourceSpan?: ParseSourceSpan|null): ReadVarExpr {
|
name: string, type?: Type|null, sourceSpan?: ParseSourceSpan|null): ReadVarExpr {
|
||||||
return new ReadVarExpr(name, type, sourceSpan);
|
return new ReadVarExpr(name, type, sourceSpan);
|
||||||
@ -1755,14 +1741,13 @@ export function importExpr(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function importType(
|
export function importType(
|
||||||
id: ExternalReference, typeParams: Type[]|null = null,
|
id: ExternalReference, typeParams?: Type[]|null,
|
||||||
typeModifiers: TypeModifier[]|null = null): ExpressionType|null {
|
typeModifiers?: TypeModifier[]): ExpressionType|null {
|
||||||
return id != null ? expressionType(importExpr(id, typeParams, null), typeModifiers) : null;
|
return id != null ? expressionType(importExpr(id, typeParams, null), typeModifiers) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function expressionType(
|
export function expressionType(
|
||||||
expr: Expression, typeModifiers: TypeModifier[]|null = null,
|
expr: Expression, typeModifiers?: TypeModifier[], typeParams?: Type[]|null): ExpressionType {
|
||||||
typeParams: Type[]|null = null): ExpressionType {
|
|
||||||
return new ExpressionType(expr, typeModifiers, typeParams);
|
return new ExpressionType(expr, typeModifiers, typeParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1802,8 +1787,10 @@ export function fn(
|
|||||||
return new FunctionExpr(params, body, type, sourceSpan, name);
|
return new FunctionExpr(params, body, type, sourceSpan, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ifStmt(condition: Expression, thenClause: Statement[], elseClause?: Statement[]) {
|
export function ifStmt(
|
||||||
return new IfStmt(condition, thenClause, elseClause);
|
condition: Expression, thenClause: Statement[], elseClause?: Statement[],
|
||||||
|
sourceSpan?: ParseSourceSpan, leadingComments?: LeadingComment[]) {
|
||||||
|
return new IfStmt(condition, thenClause, elseClause, sourceSpan, leadingComments);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function literal(
|
export function literal(
|
||||||
@ -1865,10 +1852,15 @@ function tagToString(tag: JSDocTag): string {
|
|||||||
function serializeTags(tags: JSDocTag[]): string {
|
function serializeTags(tags: JSDocTag[]): string {
|
||||||
if (tags.length === 0) return '';
|
if (tags.length === 0) return '';
|
||||||
|
|
||||||
|
if (tags.length === 1 && tags[0].tagName && !tags[0].text) {
|
||||||
|
// The JSDOC comment is a single simple tag: e.g `/** @tagname */`.
|
||||||
|
return `*${tagToString(tags[0])} `;
|
||||||
|
}
|
||||||
|
|
||||||
let out = '*\n';
|
let out = '*\n';
|
||||||
for (const tag of tags) {
|
for (const tag of tags) {
|
||||||
out += ' *';
|
out += ' *';
|
||||||
// If the tagToString is multi-line, insert " * " prefixes on subsequent lines.
|
// If the tagToString is multi-line, insert " * " prefixes on lines.
|
||||||
out += tagToString(tag).replace(/\n/g, '\n * ');
|
out += tagToString(tag).replace(/\n/g, '\n * ');
|
||||||
out += '\n';
|
out += '\n';
|
||||||
}
|
}
|
||||||
|
@ -233,12 +233,6 @@ class StatementInterpreter implements o.StatementVisitor, o.ExpressionVisitor {
|
|||||||
visitThrowStmt(stmt: o.ThrowStmt, ctx: _ExecutionContext): any {
|
visitThrowStmt(stmt: o.ThrowStmt, ctx: _ExecutionContext): any {
|
||||||
throw stmt.error.visitExpression(this, ctx);
|
throw stmt.error.visitExpression(this, ctx);
|
||||||
}
|
}
|
||||||
visitCommentStmt(stmt: o.CommentStmt, context?: any): any {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitJSDocCommentStmt(stmt: o.JSDocCommentStmt, context?: any): any {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitInstantiateExpr(ast: o.InstantiateExpr, ctx: _ExecutionContext): any {
|
visitInstantiateExpr(ast: o.InstantiateExpr, ctx: _ExecutionContext): any {
|
||||||
const args = this.visitAllExpressions(ast.args, ctx);
|
const args = this.visitAllExpressions(ast.args, ctx);
|
||||||
const clazz = ast.classExpr.visitExpression(this, ctx);
|
const clazz = ast.classExpr.visitExpression(this, ctx);
|
||||||
|
@ -45,14 +45,14 @@ export function convertMetaToOutput(meta: any, ctx: OutputContext): o.Expression
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function typeWithParameters(type: o.Expression, numParams: number): o.ExpressionType {
|
export function typeWithParameters(type: o.Expression, numParams: number): o.ExpressionType {
|
||||||
let params: o.Type[]|null = null;
|
if (numParams === 0) {
|
||||||
if (numParams > 0) {
|
return o.expressionType(type);
|
||||||
params = [];
|
|
||||||
for (let i = 0; i < numParams; i++) {
|
|
||||||
params.push(o.DYNAMIC_TYPE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return o.expressionType(type, null, params);
|
const params: o.Type[] = [];
|
||||||
|
for (let i = 0; i < numParams; i++) {
|
||||||
|
params.push(o.DYNAMIC_TYPE);
|
||||||
|
}
|
||||||
|
return o.expressionType(type, undefined, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface R3Reference {
|
export interface R3Reference {
|
||||||
|
@ -10,7 +10,7 @@ import {mapLiteral} from '../../../output/map_util';
|
|||||||
import * as o from '../../../output/output_ast';
|
import * as o from '../../../output/output_ast';
|
||||||
|
|
||||||
import {serializeIcuNode} from './icu_serializer';
|
import {serializeIcuNode} from './icu_serializer';
|
||||||
import {i18nMetaToDocStmt} from './meta';
|
import {i18nMetaToJSDoc} from './meta';
|
||||||
import {formatI18nPlaceholderName} from './util';
|
import {formatI18nPlaceholderName} from './util';
|
||||||
|
|
||||||
/** Closure uses `goog.getMsg(message)` to lookup translations */
|
/** Closure uses `goog.getMsg(message)` to lookup translations */
|
||||||
@ -31,15 +31,13 @@ export function createGoogleGetMsgStatements(
|
|||||||
// */
|
// */
|
||||||
// const MSG_... = goog.getMsg(..);
|
// const MSG_... = goog.getMsg(..);
|
||||||
// I18N_X = MSG_...;
|
// I18N_X = MSG_...;
|
||||||
const statements = [];
|
const googGetMsgStmt = closureVar.set(o.variable(GOOG_GET_MSG).callFn(args)).toConstDecl();
|
||||||
const jsdocComment = i18nMetaToDocStmt(message);
|
const metaComment = i18nMetaToJSDoc(message);
|
||||||
if (jsdocComment !== null) {
|
if (metaComment !== null) {
|
||||||
statements.push(jsdocComment);
|
googGetMsgStmt.addLeadingComment(metaComment);
|
||||||
}
|
}
|
||||||
statements.push(closureVar.set(o.variable(GOOG_GET_MSG).callFn(args)).toConstDecl());
|
const i18nAssignmentStmt = new o.ExpressionStatement(variable.set(closureVar));
|
||||||
statements.push(new o.ExpressionStatement(variable.set(closureVar)));
|
return [googGetMsgStmt, i18nAssignmentStmt];
|
||||||
|
|
||||||
return statements;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -231,7 +231,7 @@ export function parseI18nMeta(meta: string = ''): I18nMeta {
|
|||||||
|
|
||||||
// Converts i18n meta information for a message (id, description, meaning)
|
// Converts i18n meta information for a message (id, description, meaning)
|
||||||
// to a JsDoc statement formatted as expected by the Closure compiler.
|
// to a JsDoc statement formatted as expected by the Closure compiler.
|
||||||
export function i18nMetaToDocStmt(meta: I18nMeta): o.JSDocCommentStmt|null {
|
export function i18nMetaToJSDoc(meta: I18nMeta): o.JSDocComment|null {
|
||||||
const tags: o.JSDocTag[] = [];
|
const tags: o.JSDocTag[] = [];
|
||||||
if (meta.description) {
|
if (meta.description) {
|
||||||
tags.push({tagName: o.JSDocTagName.Desc, text: meta.description});
|
tags.push({tagName: o.JSDocTagName.Desc, text: meta.description});
|
||||||
@ -239,5 +239,5 @@ export function i18nMetaToDocStmt(meta: I18nMeta): o.JSDocCommentStmt|null {
|
|||||||
if (meta.meaning) {
|
if (meta.meaning) {
|
||||||
tags.push({tagName: o.JSDocTagName.Meaning, text: meta.meaning});
|
tags.push({tagName: o.JSDocTagName.Meaning, text: meta.meaning});
|
||||||
}
|
}
|
||||||
return tags.length == 0 ? null : new o.JSDocCommentStmt(tags);
|
return tags.length == 0 ? null : o.jsDocComment(tags);
|
||||||
}
|
}
|
||||||
|
@ -179,5 +179,5 @@ export function getTranslationConstPrefix(extra: string): string {
|
|||||||
*/
|
*/
|
||||||
export function declareI18nVariable(variable: o.ReadVarExpr): o.Statement {
|
export function declareI18nVariable(variable: o.ReadVarExpr): o.Statement {
|
||||||
return new o.DeclareVarStmt(
|
return new o.DeclareVarStmt(
|
||||||
variable.name!, undefined, o.INFERRED_TYPE, null, variable.sourceSpan);
|
variable.name!, undefined, o.INFERRED_TYPE, undefined, variable.sourceSpan);
|
||||||
}
|
}
|
||||||
|
@ -893,7 +893,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
const templateFunctionExpr = templateVisitor.buildTemplateFunction(
|
const templateFunctionExpr = templateVisitor.buildTemplateFunction(
|
||||||
template.children, template.variables,
|
template.children, template.variables,
|
||||||
this._ngContentReservedSlots.length + this._ngContentSelectorsOffset, template.i18n);
|
this._ngContentReservedSlots.length + this._ngContentSelectorsOffset, template.i18n);
|
||||||
this.constantPool.statements.push(templateFunctionExpr.toDeclStmt(templateName, null));
|
this.constantPool.statements.push(templateFunctionExpr.toDeclStmt(templateName));
|
||||||
if (templateVisitor._ngContentReservedSlots.length) {
|
if (templateVisitor._ngContentReservedSlots.length) {
|
||||||
this._ngContentReservedSlots.push(...templateVisitor._ngContentReservedSlots);
|
this._ngContentReservedSlots.push(...templateVisitor._ngContentReservedSlots);
|
||||||
}
|
}
|
||||||
|
@ -189,8 +189,49 @@ const externalModuleIdentifier = new o.ExternalReference(anotherModuleUrl, 'some
|
|||||||
].join('\n'));
|
].join('\n'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support comments', () => {
|
describe('comments', () => {
|
||||||
expect(emitStmt(new o.CommentStmt('a\nb'))).toEqual(['// a', '// b'].join('\n'));
|
it('should support a preamble', () => {
|
||||||
|
expect(emitStmt(o.variable('a').toStmt(), '/* SomePreamble */')).toBe([
|
||||||
|
'/* SomePreamble */', 'a;'
|
||||||
|
].join('\n'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support singleline comments', () => {
|
||||||
|
expect(emitStmt(new o.ReturnStatement(o.literal(1), null, [o.leadingComment('a\nb')])))
|
||||||
|
.toBe('// a\n// b\nreturn 1;');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support multiline comments', () => {
|
||||||
|
expect(emitStmt(new o.ReturnStatement(o.literal(1), null, [
|
||||||
|
o.leadingComment('Multiline comment', true)
|
||||||
|
]))).toBe('/* Multiline comment */\nreturn 1;');
|
||||||
|
expect(emitStmt(new o.ReturnStatement(o.literal(1), null, [
|
||||||
|
o.leadingComment(`Multiline\ncomment`, true)
|
||||||
|
]))).toBe(`/* Multiline\ncomment */\nreturn 1;`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support inline multiline comments', () => {
|
||||||
|
expect(emitStmt(new o.ReturnStatement(o.literal(1), null, [
|
||||||
|
o.leadingComment('inline comment', true, false)
|
||||||
|
]))).toBe('/* inline comment */return 1;');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support JSDoc comments', () => {
|
||||||
|
expect(emitStmt(new o.ReturnStatement(o.literal(1), null, [
|
||||||
|
o.jsDocComment([{text: 'Intro comment'}])
|
||||||
|
]))).toBe(`/**\n * Intro comment\n */\nreturn 1;`);
|
||||||
|
expect(emitStmt(new o.ReturnStatement(o.literal(1), null, [
|
||||||
|
o.jsDocComment([{tagName: o.JSDocTagName.Desc, text: 'description'}])
|
||||||
|
]))).toBe(`/**\n * @desc description\n */\nreturn 1;`);
|
||||||
|
expect(emitStmt(new o.ReturnStatement(
|
||||||
|
o.literal(1), null, [o.jsDocComment([
|
||||||
|
{text: 'Intro comment'},
|
||||||
|
{tagName: o.JSDocTagName.Desc, text: 'description'},
|
||||||
|
{tagName: o.JSDocTagName.Id, text: '{number} identifier 123'},
|
||||||
|
])])))
|
||||||
|
.toBe(
|
||||||
|
`/**\n * Intro comment\n * @desc description\n * @id {number} identifier 123\n */\nreturn 1;`);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support if stmt', () => {
|
it('should support if stmt', () => {
|
||||||
|
@ -21,22 +21,5 @@ import * as o from '../../src/output/output_ast';
|
|||||||
expect(o.collectExternalReferences([stmt])).toEqual([ref1, ref2]);
|
expect(o.collectExternalReferences([stmt])).toEqual([ref1, ref2]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('comments', () => {
|
|
||||||
it('different JSDocCommentStmt should not be equivalent', () => {
|
|
||||||
const comment1 = new o.JSDocCommentStmt([{text: 'text'}]);
|
|
||||||
const comment2 = new o.JSDocCommentStmt([{text: 'text2'}]);
|
|
||||||
const comment3 = new o.JSDocCommentStmt([{tagName: o.JSDocTagName.Desc, text: 'text2'}]);
|
|
||||||
const comment4 = new o.JSDocCommentStmt([{text: 'text2'}, {text: 'text3'}]);
|
|
||||||
|
|
||||||
expect(comment1.isEquivalent(comment2)).toBeFalsy();
|
|
||||||
expect(comment1.isEquivalent(comment3)).toBeFalsy();
|
|
||||||
expect(comment1.isEquivalent(comment4)).toBeFalsy();
|
|
||||||
expect(comment2.isEquivalent(comment3)).toBeFalsy();
|
|
||||||
expect(comment2.isEquivalent(comment4)).toBeFalsy();
|
|
||||||
expect(comment3.isEquivalent(comment4)).toBeFalsy();
|
|
||||||
expect(comment1.isEquivalent(comment1)).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -239,7 +239,8 @@ const externalModuleIdentifier = new o.ExternalReference(anotherModuleUrl, 'some
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should support comments', () => {
|
it('should support comments', () => {
|
||||||
expect(emitStmt(new o.CommentStmt('a\nb'))).toEqual(['// a', '// b'].join('\n'));
|
expect(emitStmt(new o.ReturnStatement(o.literal(1), null, [o.leadingComment('a\nb')])))
|
||||||
|
.toEqual('// a\n// b\nreturn 1;');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support if stmt', () => {
|
it('should support if stmt', () => {
|
||||||
@ -441,29 +442,40 @@ const externalModuleIdentifier = new o.ExternalReference(anotherModuleUrl, 'some
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should support singleline comments', () => {
|
it('should support singleline comments', () => {
|
||||||
expect(emitStmt(new o.CommentStmt('Simple comment'))).toBe('// Simple comment');
|
expect(emitStmt(new o.ReturnStatement(o.literal(1), null, [o.leadingComment('a\nb')])))
|
||||||
|
.toBe('// a\n// b\nreturn 1;');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support multiline comments', () => {
|
it('should support multiline comments', () => {
|
||||||
expect(emitStmt(new o.CommentStmt('Multiline comment', true)))
|
expect(emitStmt(new o.ReturnStatement(o.literal(1), null, [
|
||||||
.toBe('/* Multiline comment */');
|
o.leadingComment('Multiline comment', true)
|
||||||
expect(emitStmt(new o.CommentStmt(`Multiline\ncomment`, true)))
|
]))).toBe('/* Multiline comment */\nreturn 1;');
|
||||||
.toBe(`/* Multiline\ncomment */`);
|
expect(emitStmt(new o.ReturnStatement(o.literal(1), null, [
|
||||||
|
o.leadingComment(`Multiline\ncomment`, true)
|
||||||
|
]))).toBe(`/* Multiline\ncomment */\nreturn 1;`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support inline multiline comments', () => {
|
||||||
|
expect(emitStmt(new o.ReturnStatement(o.literal(1), null, [
|
||||||
|
o.leadingComment('inline comment', true, false)
|
||||||
|
]))).toBe('/* inline comment */return 1;');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support JSDoc comments', () => {
|
it('should support JSDoc comments', () => {
|
||||||
expect(emitStmt(new o.JSDocCommentStmt([{text: 'Intro comment'}])))
|
expect(emitStmt(new o.ReturnStatement(o.literal(1), null, [
|
||||||
.toBe(`/**\n * Intro comment\n */`);
|
o.jsDocComment([{text: 'Intro comment'}])
|
||||||
expect(emitStmt(new o.JSDocCommentStmt([
|
]))).toBe(`/**\n * Intro comment\n */\nreturn 1;`);
|
||||||
{tagName: o.JSDocTagName.Desc, text: 'description'}
|
expect(emitStmt(new o.ReturnStatement(o.literal(1), null, [
|
||||||
]))).toBe(`/**\n * @desc description\n */`);
|
o.jsDocComment([{tagName: o.JSDocTagName.Desc, text: 'description'}])
|
||||||
expect(emitStmt(new o.JSDocCommentStmt([
|
]))).toBe(`/**\n * @desc description\n */\nreturn 1;`);
|
||||||
{text: 'Intro comment'},
|
expect(emitStmt(new o.ReturnStatement(
|
||||||
{tagName: o.JSDocTagName.Desc, text: 'description'},
|
o.literal(1), null, [o.jsDocComment([
|
||||||
{tagName: o.JSDocTagName.Id, text: '{number} identifier 123'},
|
{text: 'Intro comment'},
|
||||||
])))
|
{tagName: o.JSDocTagName.Desc, text: 'description'},
|
||||||
|
{tagName: o.JSDocTagName.Id, text: '{number} identifier 123'},
|
||||||
|
])])))
|
||||||
.toBe(
|
.toBe(
|
||||||
`/**\n * Intro comment\n * @desc description\n * @id {number} identifier 123\n */`);
|
`/**\n * Intro comment\n * @desc description\n * @id {number} identifier 123\n */\nreturn 1;`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user