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:

committed by
Misko Hevery

parent
7fb388f929
commit
d795a00137
@ -141,7 +141,7 @@ describe('ngc transformer command-line', () => {
|
||||
write('mymodule.ts', `
|
||||
import {NgModule} from '@angular/core';
|
||||
import {AClass} from './aclass';
|
||||
|
||||
|
||||
@NgModule({declarations: []})
|
||||
export class MyModule {
|
||||
constructor(importedClass: AClass) {}
|
||||
@ -382,13 +382,13 @@ describe('ngc transformer command-line', () => {
|
||||
})
|
||||
export class MyModule {}
|
||||
`);
|
||||
expect(contents).toContain('@fileoverview');
|
||||
expect(contents).toContain('generated by the Angular template compiler');
|
||||
expect(contents).toContain('@suppress {suspiciousCode');
|
||||
expect(contents).toContain(
|
||||
'/**\n * @fileoverview This file was generated by the Angular template compiler. Do not edit.');
|
||||
expect(contents).toContain('\n * @suppress {suspiciousCode');
|
||||
});
|
||||
|
||||
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 {NgModule} from '@angular/core';
|
||||
@ -398,7 +398,7 @@ describe('ngc transformer command-line', () => {
|
||||
})
|
||||
export class MyModule {}
|
||||
`);
|
||||
expect(contents).toContain('Hello world.');
|
||||
expect(contents).toContain('\n * @fileoverview Hello world.\n');
|
||||
});
|
||||
|
||||
it('should only pick file comments', () => {
|
||||
|
@ -267,48 +267,69 @@ describe('TypeScriptNodeEmitter', () => {
|
||||
});
|
||||
|
||||
describe('comments', () => {
|
||||
it('should support a preamble', () => {
|
||||
expect(emitStmt(o.variable('a').toStmt(), Format.Flat, '/* SomePreamble */'))
|
||||
.toBe('/* SomePreamble */ a;');
|
||||
});
|
||||
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.Raw, '*\n * SomePreamble\n '))
|
||||
.toBe('/**\n * SomePreamble\n */\na;');
|
||||
});
|
||||
|
||||
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', () => {
|
||||
expect(emitStmt(new o.CommentStmt('Multiline comment', true)))
|
||||
.toBe('/* Multiline comment */');
|
||||
expect(emitStmt(new o.CommentStmt(`Multiline\ncomment`, true), Format.Raw))
|
||||
.toBe(`/* Multiline\ncomment */`);
|
||||
expect(emitStmt(
|
||||
new o.ReturnStatement(
|
||||
o.literal(1), null, [o.leadingComment('Multiline comment', true)]),
|
||||
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', () => {
|
||||
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'}]),
|
||||
new o.ReturnStatement(
|
||||
o.literal(1), null, [o.jsDocComment([{text: 'Intro comment'}])]),
|
||||
Format.Raw))
|
||||
.toBe(`/**\n * @desc description\n */`);
|
||||
.toBe(`/**\n * Intro comment\n */\nreturn 1;`);
|
||||
expect(emitStmt(
|
||||
new o.JSDocCommentStmt([
|
||||
{text: 'Intro comment'},
|
||||
{tagName: o.JSDocTagName.Desc, text: 'description'},
|
||||
{tagName: o.JSDocTagName.Id, text: '{number} identifier 123'},
|
||||
]),
|
||||
new o.ReturnStatement(
|
||||
o.literal(1), null,
|
||||
[o.jsDocComment([{tagName: o.JSDocTagName.Desc, text: 'description'}])]),
|
||||
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))
|
||||
.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', () => {
|
||||
expect(emitStmt(new o.JSDocCommentStmt([{text: 'email@google.com'}]), Format.Raw))
|
||||
.toBe(`/**\n * email\\@google.com\n */`);
|
||||
expect(emitStmt(
|
||||
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', () => {
|
||||
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 "*/"`);
|
||||
});
|
||||
});
|
||||
|
Reference in New Issue
Block a user