OLD | NEW |
| (Empty) |
1 import * as ts from 'typescript'; | |
2 import * as base from './base'; | |
3 import {Transpiler} from './main'; | |
4 import {FacadeConverter} from './facade_converter'; | |
5 | |
6 export default class LiteralTranspiler extends base.TranspilerBase { | |
7 constructor(tr: Transpiler, private fc: FacadeConverter) { super(tr); } | |
8 | |
9 visitNode(node: ts.Node): boolean { | |
10 switch (node.kind) { | |
11 // Literals. | |
12 case ts.SyntaxKind.NumericLiteral: | |
13 let nLit = <ts.LiteralExpression>node; | |
14 this.emit(nLit.getText()); | |
15 break; | |
16 case ts.SyntaxKind.StringLiteral: | |
17 let sLit = <ts.LiteralExpression>node; | |
18 let text = JSON.stringify(sLit.text); | |
19 // Escape dollar sign since dart will interpolate in double quoted liter
al | |
20 text = text.replace(/\$/, '\\$'); | |
21 this.emit(text); | |
22 break; | |
23 case ts.SyntaxKind.NoSubstitutionTemplateLiteral: | |
24 this.emit(`'''${this.escapeTextForTemplateString(node)}'''`); | |
25 break; | |
26 case ts.SyntaxKind.TemplateMiddle: | |
27 this.emitNoSpace(this.escapeTextForTemplateString(node)); | |
28 break; | |
29 case ts.SyntaxKind.TemplateExpression: | |
30 let tmpl = <ts.TemplateExpression>node; | |
31 if (tmpl.head) this.visit(tmpl.head); | |
32 if (tmpl.templateSpans) this.visitEach(tmpl.templateSpans); | |
33 break; | |
34 case ts.SyntaxKind.TemplateHead: | |
35 this.emit(`'''${this.escapeTextForTemplateString(node)}`); // highlight
ing bug:' | |
36 break; | |
37 case ts.SyntaxKind.TemplateTail: | |
38 this.emitNoSpace(this.escapeTextForTemplateString(node)); | |
39 this.emitNoSpace(`'''`); | |
40 break; | |
41 case ts.SyntaxKind.TemplateSpan: | |
42 let span = <ts.TemplateSpan>node; | |
43 if (span.expression) { | |
44 // Do not emit extra whitespace inside the string template | |
45 this.emitNoSpace('${'); | |
46 this.visit(span.expression); | |
47 this.emitNoSpace('}'); | |
48 } | |
49 if (span.literal) this.visit(span.literal); | |
50 break; | |
51 case ts.SyntaxKind.ArrayLiteralExpression: | |
52 if (this.shouldBeConst(node)) this.emit('const'); | |
53 let ale = <ts.ArrayLiteralExpression>node; | |
54 this.handleReifiedArray(ale); | |
55 this.emit('['); | |
56 this.visitList(ale.elements); | |
57 this.emit(']'); | |
58 break; | |
59 case ts.SyntaxKind.ObjectLiteralExpression: | |
60 if (this.shouldBeConst(node)) this.emit('const'); | |
61 let ole = <ts.ObjectLiteralExpression>node; | |
62 this.handleReifiedMap(ole); | |
63 this.emit('{'); | |
64 this.visitList(ole.properties); | |
65 this.emit('}'); | |
66 break; | |
67 case ts.SyntaxKind.PropertyAssignment: | |
68 let propAssign = <ts.PropertyAssignment>node; | |
69 if (propAssign.name.kind === ts.SyntaxKind.Identifier) { | |
70 // Dart identifiers in Map literals need quoting. | |
71 this.emitNoSpace(' "'); | |
72 this.emitNoSpace((<ts.Identifier>propAssign.name).text); | |
73 this.emitNoSpace('"'); | |
74 } else { | |
75 this.visit(propAssign.name); | |
76 } | |
77 this.emit(':'); | |
78 this.visit(propAssign.initializer); | |
79 break; | |
80 case ts.SyntaxKind.ShorthandPropertyAssignment: | |
81 let shorthand = <ts.ShorthandPropertyAssignment>node; | |
82 this.emitNoSpace(' "'); | |
83 this.emitNoSpace(shorthand.name.text); | |
84 this.emitNoSpace('"'); | |
85 this.emit(':'); | |
86 this.visit(shorthand.name); | |
87 break; | |
88 | |
89 case ts.SyntaxKind.TrueKeyword: | |
90 this.emit('true'); | |
91 break; | |
92 case ts.SyntaxKind.FalseKeyword: | |
93 this.emit('false'); | |
94 break; | |
95 case ts.SyntaxKind.NullKeyword: | |
96 this.emit('null'); | |
97 break; | |
98 case ts.SyntaxKind.RegularExpressionLiteral: | |
99 this.emit('new RegExp ('); | |
100 this.emit('r\''); | |
101 let regExp = (<ts.LiteralExpression>node).text; | |
102 let slashIdx = regExp.lastIndexOf('/'); | |
103 let flags = regExp.substring(slashIdx + 1); | |
104 regExp = regExp.substring(1, slashIdx); // cut off /.../ char
s. | |
105 regExp = regExp.replace(/'/g, '\' + "\'" + r\''); // handle nested quot
es by concatenation. | |
106 this.emitNoSpace(regExp); | |
107 this.emitNoSpace('\''); | |
108 if (flags.indexOf('g') === -1) { | |
109 // Dart RegExps are always global, so JS regexps must use 'g' so that
semantics match. | |
110 this.reportError(node, 'Regular Expressions must use the //g flag'); | |
111 } | |
112 if (flags.indexOf('m') !== -1) { | |
113 this.emit(', multiLine: true'); | |
114 } | |
115 if (flags.indexOf('i') !== -1) { | |
116 this.emit(', caseSensitive: false'); | |
117 } | |
118 this.emit(')'); | |
119 break; | |
120 case ts.SyntaxKind.ThisKeyword: | |
121 this.emit('this'); | |
122 break; | |
123 | |
124 default: | |
125 return false; | |
126 } | |
127 return true; | |
128 } | |
129 | |
130 private shouldBeConst(n: ts.Node): boolean { | |
131 return this.hasAncestor(n, ts.SyntaxKind.Decorator) || this.fc.isInsideConst
Expr(n); | |
132 } | |
133 | |
134 private escapeTextForTemplateString(n: ts.Node): string { | |
135 return (<ts.StringLiteral>n).text.replace(/\\/g, '\\\\').replace(/([$'])/g,
'\\$1'); | |
136 } | |
137 | |
138 private handleReifiedArray(node: ts.ArrayLiteralExpression) { | |
139 if (node.parent.kind !== ts.SyntaxKind.TypeAssertionExpression) return; | |
140 let ta = <ts.TypeAssertion>node.parent; | |
141 if (ta.type.kind !== ts.SyntaxKind.ArrayType) return; | |
142 this.emit('<'); | |
143 this.visit((<ts.ArrayTypeNode>ta.type).elementType); | |
144 this.emit('>'); | |
145 return true; | |
146 } | |
147 | |
148 | |
149 private handleReifiedMap(node: ts.ObjectLiteralExpression) { | |
150 if (node.parent.kind !== ts.SyntaxKind.TypeAssertionExpression) return; | |
151 let ta = <ts.TypeAssertion>node.parent; | |
152 if (ta.type.kind !== ts.SyntaxKind.TypeLiteral) return; | |
153 let it = this.maybeDestructureIndexType(<ts.TypeLiteralNode>ta.type); | |
154 if (!it) { | |
155 this.reportError(node, 'expected {[k]: v} type on object literal'); | |
156 return; | |
157 } | |
158 this.emit('<'); | |
159 this.visit(it[0]); | |
160 this.emit(','); | |
161 this.visit(it[1]); | |
162 this.emit('>'); | |
163 } | |
164 } | |
OLD | NEW |