Skip to content

Commit 052d27f

Browse files
committed
refactor(compiler): Move the attribute comment to the HTML AST
This is to help the support for comment formating by third-party tools like prettier.
1 parent aa7d9fc commit 052d27f

6 files changed

Lines changed: 62 additions & 8 deletions

File tree

packages/compiler/src/ml_parser/ast.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ export type Node =
2626
| Block
2727
| BlockParameter
2828
| Component
29-
| Directive;
29+
| Directive
30+
| StartTagComment;
3031

3132
export abstract class NodeWithI18n implements BaseNode {
3233
constructor(
@@ -97,6 +98,17 @@ export class Attribute extends NodeWithI18n {
9798
}
9899
}
99100

101+
export class StartTagComment implements BaseNode {
102+
constructor(
103+
public value: string,
104+
public type: 'single' | 'multi',
105+
public sourceSpan: ParseSourceSpan,
106+
) {}
107+
visit(visitor: Visitor, context: any): any {
108+
return visitor.visitAttributeComment ? visitor.visitAttributeComment(this, context) : undefined;
109+
}
110+
}
111+
100112
export class Element extends NodeWithI18n {
101113
constructor(
102114
public name: string,
@@ -109,6 +121,7 @@ export class Element extends NodeWithI18n {
109121
public endSourceSpan: ParseSourceSpan | null = null,
110122
readonly isVoid: boolean,
111123
i18n?: I18nMeta,
124+
public comments: StartTagComment[] = [],
112125
) {
113126
super(sourceSpan, i18n);
114127
}
@@ -159,6 +172,7 @@ export class Component extends NodeWithI18n {
159172
readonly startSourceSpan: ParseSourceSpan,
160173
public endSourceSpan: ParseSourceSpan | null = null,
161174
i18n?: I18nMeta,
175+
public comments: StartTagComment[] = [],
162176
) {
163177
super(sourceSpan, i18n);
164178
}
@@ -223,6 +237,7 @@ export interface Visitor {
223237
visitLetDeclaration(decl: LetDeclaration, context: any): any;
224238
visitComponent(component: Component, context: any): any;
225239
visitDirective(directive: Directive, context: any): any;
240+
visitAttributeComment?(comment: StartTagComment, context: any): any;
226241
}
227242

228243
export function visitAll(visitor: Visitor, nodes: Node[], context: any = null): any[] {
@@ -247,11 +262,13 @@ export class RecursiveVisitor implements Visitor {
247262
this.visitChildren(context, (visit) => {
248263
visit(ast.attrs);
249264
visit(ast.directives);
265+
visit(ast.comments);
250266
visit(ast.children);
251267
});
252268
}
253269

254270
visitAttribute(ast: Attribute, context: any): any {}
271+
visitAttributeComment(ast: StartTagComment, context: any): any {}
255272
visitText(ast: Text, context: any): any {}
256273
visitComment(ast: Comment, context: any): any {}
257274

@@ -277,6 +294,7 @@ export class RecursiveVisitor implements Visitor {
277294
visitComponent(component: Component, context: any) {
278295
this.visitChildren(context, (visit) => {
279296
visit(component.attrs);
297+
visit(component.comments);
280298
visit(component.children);
281299
});
282300
}

packages/compiler/src/ml_parser/html_whitespaces.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ export class WhitespaceVisitor implements html.Visitor {
8181
element.endSourceSpan,
8282
element.isVoid,
8383
element.i18n,
84+
element.comments,
8485
);
8586
this.originalNodeMap?.set(newElement, element);
8687
return newElement;
@@ -97,6 +98,7 @@ export class WhitespaceVisitor implements html.Visitor {
9798
element.endSourceSpan,
9899
element.isVoid,
99100
element.i18n,
101+
element.comments,
100102
);
101103
this.originalNodeMap?.set(newElement, element);
102104
return newElement;
@@ -229,6 +231,7 @@ export class WhitespaceVisitor implements html.Visitor {
229231
node.startSourceSpan,
230232
node.endSourceSpan,
231233
node.i18n,
234+
node.comments,
232235
);
233236
this.originalNodeMap?.set(newElement, node);
234237
return newElement;
@@ -246,6 +249,7 @@ export class WhitespaceVisitor implements html.Visitor {
246249
node.startSourceSpan,
247250
node.endSourceSpan,
248251
node.i18n,
252+
node.comments,
249253
);
250254
this.originalNodeMap?.set(newElement, node);
251255
return newElement;

packages/compiler/src/ml_parser/lexer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -813,7 +813,7 @@ class _Tokenizer {
813813
const content = spanEnd.getChars(contentStart);
814814

815815
this._beginToken(TokenType.IN_ELEMENT_COMMENT, start);
816-
this._endToken([content], spanEnd);
816+
this._endToken([content, 'single'], spanEnd);
817817

818818
this._attemptCharCodeUntilFn(isNotWhitespace);
819819
}
@@ -842,7 +842,7 @@ class _Tokenizer {
842842
}
843843

844844
this._beginToken(TokenType.IN_ELEMENT_COMMENT, start);
845-
this._endToken([content], spanEnd);
845+
this._endToken([content, 'multi'], spanEnd);
846846
}
847847

848848
private _consumeTagOpen(start: CharacterCursor) {

packages/compiler/src/ml_parser/parser.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,8 @@ class _TreeBuilder {
393393
private _consumeElementStartTag(startTagToken: TagOpenStartToken | IncompleteTagOpenToken) {
394394
const attrs: html.Attribute[] = [];
395395
const directives: html.Directive[] = [];
396-
this._consumeAttributesAndDirectives(attrs, directives);
396+
const comments: html.StartTagComment[] = [];
397+
this._consumeAttributesAndDirectives(attrs, directives, comments);
397398

398399
const fullName = this._getElementFullName(startTagToken, this._getClosestElementLikeParent());
399400
const tagDef = this._getTagDefinition(fullName);
@@ -438,6 +439,8 @@ class _TreeBuilder {
438439
startSpan,
439440
undefined,
440441
tagDef?.isVoid ?? false,
442+
undefined,
443+
comments,
441444
);
442445
const parent = this._getContainer();
443446
const isClosedByChild =
@@ -464,7 +467,8 @@ class _TreeBuilder {
464467
const componentName = startToken.parts[0];
465468
const attrs: html.Attribute[] = [];
466469
const directives: html.Directive[] = [];
467-
this._consumeAttributesAndDirectives(attrs, directives);
470+
const comments: html.StartTagComment[] = [];
471+
this._consumeAttributesAndDirectives(attrs, directives, comments);
468472

469473
const closestElement = this._getClosestElementLikeParent();
470474
const tagName = this._getComponentTagName(startToken, closestElement);
@@ -494,6 +498,8 @@ class _TreeBuilder {
494498
span,
495499
startSpan,
496500
undefined,
501+
undefined,
502+
comments,
497503
);
498504
const parent = this._getContainer();
499505
const isClosedByChild =
@@ -515,6 +521,7 @@ class _TreeBuilder {
515521
private _consumeAttributesAndDirectives(
516522
attributesResult: html.Attribute[],
517523
directivesResult: html.Directive[],
524+
commentsResult: html.StartTagComment[],
518525
) {
519526
while (
520527
this._peek.type === TokenType.ATTR_NAME ||
@@ -527,7 +534,13 @@ class _TreeBuilder {
527534
attributesResult.push(this._consumeAttr(this._advance<AttributeNameToken>()));
528535
} else {
529536
const commentToken = this._advance<InElementCommentToken>();
530-
this._addToParent(new html.Comment(commentToken.parts[0], commentToken.sourceSpan));
537+
commentsResult.push(
538+
new html.StartTagComment(
539+
commentToken.parts[0],
540+
commentToken.parts[1],
541+
commentToken.sourceSpan,
542+
),
543+
);
531544
}
532545
}
533546
}

packages/compiler/src/ml_parser/tokens.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ export interface CommentEndToken extends TokenBase {
164164

165165
export interface InElementCommentToken extends TokenBase {
166166
type: TokenType.IN_ELEMENT_COMMENT;
167-
parts: [content: string];
167+
parts: [content: string, type: 'single' | 'multi'];
168168
}
169169

170170
export interface CdataStartToken extends TokenBase {

packages/compiler/src/render3/r3_template_transform.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {BindingParser} from '../template_parser/binding_parser';
2525
import {PreparsedElementType, preparseElement} from '../template_parser/template_preparser';
2626

2727
import * as t from './r3_ast';
28+
import {createContentBlock} from './r3_content_blocks';
2829
import {
2930
createForLoop,
3031
createIfBlock,
@@ -33,7 +34,6 @@ import {
3334
isConnectedIfLoopBlock,
3435
} from './r3_control_flow';
3536
import {createDeferredBlock, isConnectedDeferLoopBlock} from './r3_deferred_blocks';
36-
import {createContentBlock} from './r3_content_blocks';
3737
import {I18N_ICU_VAR_PREFIX} from './view/i18n/util';
3838

3939
const BIND_NAME_REGEXP = /^(?:(bind-)|(let-)|(ref-|#)|(on-)|(bindon-)|(@))(.*)$/;
@@ -264,6 +264,12 @@ class HtmlAstToIvyAst implements html.Visitor {
264264
);
265265
}
266266

267+
if (this.options.collectCommentNodes) {
268+
element.comments.forEach((comment) => {
269+
this.commentNodes.push(new t.Comment(comment.value || '', comment.sourceSpan));
270+
});
271+
}
272+
267273
if (elementHasInlineTemplate) {
268274
// If this node is an inline-template (e.g. has *ngFor) then we need to create a template
269275
// node that contains this node.
@@ -349,6 +355,13 @@ class HtmlAstToIvyAst implements html.Visitor {
349355
return null;
350356
}
351357

358+
visitAttributeComment(comment: html.StartTagComment): null {
359+
if (this.options.collectCommentNodes) {
360+
this.commentNodes.push(new t.Comment(comment.value || '', comment.sourceSpan));
361+
}
362+
return null;
363+
}
364+
352365
visitLetDeclaration(decl: html.LetDeclaration, context: any) {
353366
const value = this.bindingParser.parseBinding(
354367
decl.value,
@@ -432,6 +445,12 @@ class HtmlAstToIvyAst implements html.Visitor {
432445
component.i18n,
433446
);
434447

448+
if (this.options.collectCommentNodes) {
449+
component.comments.forEach((comment) => {
450+
this.commentNodes.push(new t.Comment(comment.value || '', comment.sourceSpan));
451+
});
452+
}
453+
435454
if (elementHasInlineTemplate) {
436455
node = this.wrapInTemplate(
437456
node,

0 commit comments

Comments
 (0)