X Tutup
Skip to content

Commit 9e44dd8

Browse files
vicbIgorMinar
authored andcommitted
feat(camelCase Angular): legacy template transformer
1 parent da9b46a commit 9e44dd8

File tree

2 files changed

+503
-0
lines changed

2 files changed

+503
-0
lines changed
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
import {Injectable, Provider, provide} from 'angular2/src/core/di';
2+
3+
import {
4+
StringWrapper,
5+
RegExpWrapper,
6+
CONST_EXPR,
7+
isBlank,
8+
isPresent
9+
} from 'angular2/src/facade/lang';
10+
11+
import {HtmlAstVisitor, HtmlAttrAst, HtmlElementAst, HtmlTextAst, HtmlAst} from './html_ast';
12+
import {HtmlParser, HtmlParseTreeResult} from './html_parser';
13+
14+
import {dashCaseToCamelCase, camelCaseToDashCase} from './util';
15+
16+
var LONG_SYNTAX_REGEXP = /^(?:on-(.*)|bindon-(.*)|bind-(.*)|var-(.*))$/ig;
17+
var SHORT_SYNTAX_REGEXP = /^(?:\((.*)\)|\[\((.*)\)\]|\[(.*)\]|#(.*))$/ig;
18+
var VARIABLE_TPL_BINDING_REGEXP = /(\bvar\s+|#)(\S+)/ig;
19+
var TEMPLATE_SELECTOR_REGEXP = /^(\S+)/g;
20+
var SPECIAL_PREFIXES_REGEXP = /^(class|style|attr)\./ig;
21+
var INTERPOLATION_REGEXP = /\{\{.*?\}\}/g;
22+
23+
const SPECIAL_CASES = CONST_EXPR([
24+
'ng-non-bindable',
25+
'ng-default-control',
26+
'ng-no-form',
27+
]);
28+
29+
/**
30+
* Convert templates to the case sensitive syntax
31+
*
32+
* @internal
33+
*/
34+
export class LegacyHtmlAstTransformer implements HtmlAstVisitor {
35+
rewrittenAst: HtmlAst[] = [];
36+
visitingTemplateEl: boolean = false;
37+
38+
constructor(private dashCaseSelectors?: string[]) {}
39+
40+
visitElement(ast: HtmlElementAst, context: any): HtmlElementAst {
41+
this.visitingTemplateEl = ast.name.toLowerCase() == 'template';
42+
let attrs = ast.attrs.map(attr => attr.visit(this, null));
43+
let children = ast.children.map(child => child.visit(this, null));
44+
return new HtmlElementAst(ast.name, attrs, children, ast.sourceSpan);
45+
}
46+
47+
visitAttr(originalAst: HtmlAttrAst, context: any): HtmlAttrAst {
48+
let ast = originalAst;
49+
50+
if (this.visitingTemplateEl) {
51+
if (isPresent(RegExpWrapper.firstMatch(LONG_SYNTAX_REGEXP, ast.name))) {
52+
// preserve the "-" in the prefix for the long syntax
53+
ast = this._rewriteLongSyntax(ast);
54+
} else {
55+
// rewrite any other attribute
56+
let name = dashCaseToCamelCase(ast.name);
57+
ast = name == ast.name ? ast : new HtmlAttrAst(name, ast.value, ast.sourceSpan);
58+
}
59+
} else {
60+
ast = this._rewriteTemplateAttribute(ast);
61+
ast = this._rewriteLongSyntax(ast);
62+
ast = this._rewriteShortSyntax(ast);
63+
ast = this._rewriteStar(ast);
64+
ast = this._rewriteInterpolation(ast);
65+
ast = this._rewriteSpecialCases(ast);
66+
}
67+
68+
if (ast !== originalAst) {
69+
this.rewrittenAst.push(ast);
70+
}
71+
72+
return ast;
73+
}
74+
75+
visitText(ast: HtmlTextAst, context: any): HtmlTextAst { return ast; }
76+
77+
private _rewriteLongSyntax(ast: HtmlAttrAst): HtmlAttrAst {
78+
let m = RegExpWrapper.firstMatch(LONG_SYNTAX_REGEXP, ast.name);
79+
let attrName = ast.name;
80+
let attrValue = ast.value;
81+
82+
if (isPresent(m)) {
83+
if (isPresent(m[1])) {
84+
attrName = `on-${dashCaseToCamelCase(m[1])}`;
85+
} else if (isPresent(m[2])) {
86+
attrName = `bindon-${dashCaseToCamelCase(m[2])}`;
87+
} else if (isPresent(m[3])) {
88+
attrName = `bind-${dashCaseToCamelCase(m[3])}`;
89+
} else if (isPresent(m[4])) {
90+
attrName = `var-${dashCaseToCamelCase(m[4])}`;
91+
attrValue = dashCaseToCamelCase(attrValue);
92+
}
93+
}
94+
95+
return attrName == ast.name && attrValue == ast.value ?
96+
ast :
97+
new HtmlAttrAst(attrName, attrValue, ast.sourceSpan);
98+
}
99+
100+
private _rewriteTemplateAttribute(ast: HtmlAttrAst): HtmlAttrAst {
101+
let name = ast.name;
102+
let value = ast.value;
103+
104+
if (name.toLowerCase() == 'template') {
105+
name = 'template';
106+
107+
// rewrite the directive selector
108+
value = StringWrapper.replaceAllMapped(value, TEMPLATE_SELECTOR_REGEXP,
109+
(m) => { return dashCaseToCamelCase(m[1]); });
110+
111+
// rewrite the var declarations
112+
value = StringWrapper.replaceAllMapped(value, VARIABLE_TPL_BINDING_REGEXP, m => {
113+
return `${m[1].toLowerCase()}${dashCaseToCamelCase(m[2])}`;
114+
});
115+
}
116+
117+
if (name == ast.name && value == ast.value) {
118+
return ast;
119+
}
120+
121+
return new HtmlAttrAst(name, value, ast.sourceSpan);
122+
}
123+
124+
private _rewriteShortSyntax(ast: HtmlAttrAst): HtmlAttrAst {
125+
let m = RegExpWrapper.firstMatch(SHORT_SYNTAX_REGEXP, ast.name);
126+
let attrName = ast.name;
127+
let attrValue = ast.value;
128+
129+
if (isPresent(m)) {
130+
if (isPresent(m[1])) {
131+
attrName = `(${dashCaseToCamelCase(m[1])})`;
132+
} else if (isPresent(m[2])) {
133+
attrName = `[(${dashCaseToCamelCase(m[2])})]`;
134+
} else if (isPresent(m[3])) {
135+
let prop = StringWrapper.replaceAllMapped(m[3], SPECIAL_PREFIXES_REGEXP,
136+
(m) => { return m[1].toLowerCase() + '.'; });
137+
138+
if (prop.startsWith('class.') || prop.startsWith('attr.') || prop.startsWith('style.')) {
139+
attrName = `[${prop}]`;
140+
} else {
141+
attrName = `[${dashCaseToCamelCase(prop)}]`;
142+
}
143+
} else if (isPresent(m[4])) {
144+
attrName = `#${dashCaseToCamelCase(m[4])}`;
145+
attrValue = dashCaseToCamelCase(attrValue);
146+
}
147+
}
148+
149+
return attrName == ast.name && attrValue == ast.value ?
150+
ast :
151+
new HtmlAttrAst(attrName, attrValue, ast.sourceSpan);
152+
}
153+
154+
private _rewriteStar(ast: HtmlAttrAst): HtmlAttrAst {
155+
let attrName = ast.name;
156+
let attrValue = ast.value;
157+
158+
if (attrName[0] == '*') {
159+
attrName = dashCaseToCamelCase(attrName);
160+
// rewrite the var declarations
161+
attrValue = StringWrapper.replaceAllMapped(attrValue, VARIABLE_TPL_BINDING_REGEXP, m => {
162+
return `${m[1].toLowerCase()}${dashCaseToCamelCase(m[2])}`;
163+
});
164+
}
165+
166+
return attrName == ast.name && attrValue == ast.value ?
167+
ast :
168+
new HtmlAttrAst(attrName, attrValue, ast.sourceSpan);
169+
}
170+
171+
private _rewriteInterpolation(ast: HtmlAttrAst): HtmlAttrAst {
172+
let hasInterpolation = RegExpWrapper.test(INTERPOLATION_REGEXP, ast.value);
173+
174+
if (!hasInterpolation) {
175+
return ast;
176+
}
177+
178+
let name = ast.name;
179+
180+
if (!(name.startsWith('attr.') || name.startsWith('class.') || name.startsWith('style.'))) {
181+
name = dashCaseToCamelCase(ast.name);
182+
}
183+
184+
return name == ast.name ? ast : new HtmlAttrAst(name, ast.value, ast.sourceSpan);
185+
}
186+
187+
private _rewriteSpecialCases(ast: HtmlAttrAst): HtmlAttrAst {
188+
let attrName = ast.name;
189+
190+
if (SPECIAL_CASES.indexOf(attrName) > -1) {
191+
return new HtmlAttrAst(dashCaseToCamelCase(attrName), ast.value, ast.sourceSpan);
192+
}
193+
194+
if (isPresent(this.dashCaseSelectors) && this.dashCaseSelectors.indexOf(attrName) > -1) {
195+
return new HtmlAttrAst(dashCaseToCamelCase(attrName), ast.value, ast.sourceSpan);
196+
}
197+
198+
return ast;
199+
}
200+
}
201+
202+
@Injectable()
203+
export class LegacyHtmlParser extends HtmlParser {
204+
parse(sourceContent: string, sourceUrl: string): HtmlParseTreeResult {
205+
let transformer = new LegacyHtmlAstTransformer();
206+
let htmlParseTreeResult = super.parse(sourceContent, sourceUrl);
207+
208+
let rootNodes = htmlParseTreeResult.rootNodes.map(node => node.visit(transformer, null));
209+
210+
return transformer.rewrittenAst.length > 0 ?
211+
new HtmlParseTreeResult(rootNodes, htmlParseTreeResult.errors) :
212+
htmlParseTreeResult;
213+
}
214+
}

0 commit comments

Comments
 (0)
X Tutup