-
Notifications
You must be signed in to change notification settings - Fork 6.7k
/
tree-traversal.ts
118 lines (111 loc) · 4.1 KB
/
tree-traversal.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* 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
*/
import * as compiler from '@angular/compiler';
/**
* Traverses the given tree of nodes and runs the given callbacks for each Element node encountered.
*
* Note that updates to the start tags of html element should be done in the postorder callback,
* and updates to the end tags of html elements should be done in the preorder callback to avoid
* issues with line collisions.
*
* @param nodes The nodes of the ast from a parsed template.
* @param preorderCallback A function that gets run for each Element node in a preorder traversal.
* @param postorderCallback A function that gets run for each Element node in a postorder traversal.
*/
export function visitElements(
nodes: compiler.TmplAstNode[],
preorderCallback: (node: compiler.TmplAstElement) => void = () => {},
postorderCallback: (node: compiler.TmplAstElement) => void = () => {},
): void {
nodes.reverse();
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (node instanceof compiler.TmplAstElement) {
preorderCallback(node);
visitElements(node.children, preorderCallback, postorderCallback);
postorderCallback(node);
}
}
}
/**
* A wrapper for the Angular compilers parseTemplate, which passes the correct options to ensure
* the parsed template is accurate.
*
* For more details, see https://github.com/angular/angular/blob/4332897baa2226ef246ee054fdd5254e3c129109/packages/compiler-cli/src/ngtsc/annotations/component/src/resources.ts#L230.
*
* @param html text of the template to parse
* @param filePath URL to use for source mapping of the parsed template
* @returns the updated template html.
*/
export function parseTemplate(template: string, templateUrl: string = ''): compiler.ParsedTemplate {
return compiler.parseTemplate(template, templateUrl, {
preserveWhitespaces: true,
preserveLineEndings: true,
leadingTriviaChars: [],
});
}
/**
* Replaces the start tag of the given Element node inside of the html document with a new tag name.
*
* @param html The full html document.
* @param node The Element node to be updated.
* @param tag A new tag name.
* @returns an updated html document.
*/
export function replaceStartTag(html: string, node: compiler.TmplAstElement, tag: string): string {
return replaceAt(html, node.startSourceSpan.start.offset + 1, node.name, tag);
}
/**
* Replaces the end tag of the given Element node inside of the html document with a new tag name.
*
* @param html The full html document.
* @param node The Element node to be updated.
* @param tag A new tag name.
* @returns an updated html document.
*/
export function replaceEndTag(html: string, node: compiler.TmplAstElement, tag: string): string {
if (!node.endSourceSpan) {
return html;
}
return replaceAt(html, node.endSourceSpan.start.offset + 2, node.name, tag);
}
/**
* Appends an attribute to the given node of the template html.
*
* @param html The template html to be updated.
* @param node The node to be updated.
* @param name The name of the attribute.
* @param value The value of the attribute.
* @returns The updated template html.
*/
export function addAttribute(
html: string,
node: compiler.TmplAstElement,
name: string,
value: string,
): string {
const index = node.startSourceSpan.start.offset + node.name.length + 1;
const prefix = html.slice(0, index);
const suffix = html.slice(index);
return prefix + ` ${name}="${value}"` + suffix;
}
/**
* Replaces a substring of a given string starting at some offset index.
*
* @param str A string to be updated.
* @param offset An offset index to start at.
* @param oldSubstr The old substring to be replaced.
* @param newSubstr A new substring.
* @returns the updated string.
*/
function replaceAt(str: string, offset: number, oldSubstr: string, newSubstr: string): string {
const index = offset;
const prefix = str.slice(0, index);
const suffix = str.slice(index + oldSubstr.length);
return prefix + newSubstr + suffix;
}