feat(compiler): support for singleline, multiline & jsdoc comments (#22715)
PR Close #22715
This commit is contained in:

committed by
Miško Hevery

parent
02e6ac2117
commit
3b167be069
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AssertNotNull, BinaryOperator, BinaryOperatorExpr, BuiltinMethod, BuiltinVar, CastExpr, ClassMethod, ClassStmt, CommaExpr, CommentStmt, CompileIdentifierMetadata, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, ExpressionStatement, ExpressionVisitor, ExternalExpr, ExternalReference, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, NotExpr, ParseSourceFile, ParseSourceSpan, PartialModule, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, Statement, StatementVisitor, StaticSymbol, StmtModifier, ThrowStmt, TryCatchStmt, WriteKeyExpr, WritePropExpr, WriteVarExpr} from '@angular/compiler';
|
||||
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, WriteKeyExpr, WritePropExpr, WriteVarExpr} from '@angular/compiler';
|
||||
import * as ts from 'typescript';
|
||||
import {error} from './util';
|
||||
|
||||
@ -72,8 +72,8 @@ export function updateSourceFile(
|
||||
classes.map<[string, ClassStmt]>(classStatement => [classStatement.name, classStatement]));
|
||||
const classNames = new Set(classes.map(classStatement => classStatement.name));
|
||||
|
||||
const prefix =
|
||||
<ts.Statement[]>prefixStatements.map(statement => statement.visitStatement(converter, null));
|
||||
const prefix: ts.Statement[] =
|
||||
prefixStatements.map(statement => statement.visitStatement(converter, sourceFile));
|
||||
|
||||
// Add static methods to all the classes referenced in module.
|
||||
let newStatements = sourceFile.statements.map(node => {
|
||||
@ -342,7 +342,7 @@ class _NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor {
|
||||
return this.record(stmt, ts.createVariableStatement(this.getModifiers(stmt), varDeclList));
|
||||
}
|
||||
|
||||
visitDeclareFunctionStmt(stmt: DeclareFunctionStmt, context: any) {
|
||||
visitDeclareFunctionStmt(stmt: DeclareFunctionStmt) {
|
||||
return this.record(
|
||||
stmt, ts.createFunctionDeclaration(
|
||||
/* decorators */ undefined, this.getModifiers(stmt),
|
||||
@ -443,7 +443,23 @@ class _NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor {
|
||||
return this.record(stmt, ts.createThrow(stmt.error.visitExpression(this, null)));
|
||||
}
|
||||
|
||||
visitCommentStmt(stmt: CommentStmt) { return 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
|
||||
visitReadVarExpr(expr: ReadVarExpr) {
|
||||
@ -712,4 +728,4 @@ function modifierFromModifier(modifier: StmtModifier): ts.Modifier {
|
||||
|
||||
function translateModifiers(modifiers: StmtModifier[] | null): ts.Modifier[]|undefined {
|
||||
return modifiers == null ? undefined : modifiers !.map(modifierFromModifier);
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,8 @@ describe('TypeScriptNodeEmitter', () => {
|
||||
someVar = o.variable('someVar', null, null);
|
||||
});
|
||||
|
||||
function emitStmt(stmt: o.Statement | o.Statement[], preamble?: string): string {
|
||||
function emitStmt(
|
||||
stmt: o.Statement | o.Statement[], format: Format = Format.Flat, preamble?: string): string {
|
||||
const stmts = Array.isArray(stmt) ? stmt : [stmt];
|
||||
|
||||
const program = ts.createProgram(
|
||||
@ -57,7 +58,7 @@ describe('TypeScriptNodeEmitter', () => {
|
||||
result = data;
|
||||
}
|
||||
}, undefined, undefined, transformers);
|
||||
return normalizeResult(result);
|
||||
return normalizeResult(result, format);
|
||||
}
|
||||
|
||||
it('should declare variables', () => {
|
||||
@ -248,7 +249,52 @@ describe('TypeScriptNodeEmitter', () => {
|
||||
]))).toEqual(`function someFn(param1) { }`);
|
||||
});
|
||||
|
||||
it('should support comments', () => { expect(emitStmt(new o.CommentStmt('a\nb'))).toEqual(''); });
|
||||
describe('comments', () => {
|
||||
it('should support a preamble', () => {
|
||||
expect(emitStmt(o.variable('a').toStmt(), Format.Flat, '/* SomePreamble */'))
|
||||
.toBe('/* SomePreamble */ a;');
|
||||
});
|
||||
|
||||
it('should support singleline comments',
|
||||
() => { expect(emitStmt(new o.CommentStmt('Simple comment'))).toBe('// Simple comment'); });
|
||||
|
||||
it('should support multiline comments', () => {
|
||||
expect(emitStmt(new o.CommentStmt('Multiline comment', true)))
|
||||
.toBe('/* Multiline comment */');
|
||||
expect(emitStmt(new o.CommentStmt(`Multiline\ncomment`, true), Format.Raw))
|
||||
.toBe(`/* Multiline\ncomment */`);
|
||||
});
|
||||
|
||||
describe('JSDoc comments', () => {
|
||||
it('should be supported', () => {
|
||||
expect(emitStmt(new o.JSDocCommentStmt([{text: 'Intro comment'}]), Format.Raw))
|
||||
.toBe(`/**\n * Intro comment\n */`);
|
||||
expect(emitStmt(
|
||||
new o.JSDocCommentStmt([{tagName: o.JSDocTagName.Desc, text: 'description'}]),
|
||||
Format.Raw))
|
||||
.toBe(`/**\n * @desc description\n */`);
|
||||
expect(emitStmt(
|
||||
new o.JSDocCommentStmt([
|
||||
{text: 'Intro comment'},
|
||||
{tagName: o.JSDocTagName.Desc, text: 'description'},
|
||||
{tagName: o.JSDocTagName.Id, text: '{number} identifier 123'},
|
||||
]),
|
||||
Format.Raw))
|
||||
.toBe(
|
||||
`/**\n * Intro comment\n * @desc description\n * @id {number} identifier 123\n */`);
|
||||
});
|
||||
|
||||
it('should escape @ in the text', () => {
|
||||
expect(emitStmt(new o.JSDocCommentStmt([{text: 'email@google.com'}]), Format.Raw))
|
||||
.toBe(`/**\n * email\\@google.com\n */`);
|
||||
});
|
||||
|
||||
it('should not allow /* and */ in the text', () => {
|
||||
expect(() => emitStmt(new o.JSDocCommentStmt([{text: 'some text /* */'}]), Format.Raw))
|
||||
.toThrowError(`JSDoc text cannot contain "/*" and "*/"`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should support if stmt', () => {
|
||||
const trueCase = o.variable('trueCase').callFn([]).toStmt();
|
||||
@ -391,10 +437,6 @@ describe('TypeScriptNodeEmitter', () => {
|
||||
expect(emitStmt(writeVarExpr.toDeclStmt(new o.MapType(o.INT_TYPE)))).toEqual('var a = null;');
|
||||
});
|
||||
|
||||
it('should support a preamble', () => {
|
||||
expect(emitStmt(o.variable('a').toStmt(), '/* SomePreamble */')).toBe('/* SomePreamble */ a;');
|
||||
});
|
||||
|
||||
describe('source maps', () => {
|
||||
function emitStmt(stmt: o.Statement | o.Statement[], preamble?: string): string {
|
||||
const stmts = Array.isArray(stmt) ? stmt : [stmt];
|
||||
@ -515,16 +557,20 @@ const FILES: Directory = {
|
||||
somePackage: {'someGenFile.ts': `export var a: number;`}
|
||||
};
|
||||
|
||||
function normalizeResult(result: string): string {
|
||||
const enum Format { Raw, Flat }
|
||||
|
||||
function normalizeResult(result: string, format: Format): string {
|
||||
// Remove TypeScript prefixes
|
||||
let res = result.replace('"use strict";', ' ')
|
||||
.replace('exports.__esModule = true;', ' ')
|
||||
.replace('Object.defineProperty(exports, "__esModule", { value: true });', ' ');
|
||||
|
||||
// Remove new lines
|
||||
// Squish adjacent spaces
|
||||
if (format === Format.Flat) {
|
||||
return res.replace(/\n/g, ' ').replace(/ +/g, ' ').replace(/^ /g, '').replace(/ $/g, '');
|
||||
}
|
||||
|
||||
// Remove prefix and postfix spaces
|
||||
return result.replace('"use strict";', ' ')
|
||||
.replace('exports.__esModule = true;', ' ')
|
||||
.replace('Object.defineProperty(exports, "__esModule", { value: true });', ' ')
|
||||
.replace(/\n/g, ' ')
|
||||
.replace(/ +/g, ' ')
|
||||
.replace(/^ /g, '')
|
||||
.replace(/ $/g, '');
|
||||
return res.trim();
|
||||
}
|
||||
|
Reference in New Issue
Block a user