UNPKG

5.11 kBJavaScriptView Raw
1import {TokenType as tt} from "../parser/tokenizer/types";
2
3import isIdentifier from "../util/isIdentifier";
4
5import Transformer from "./Transformer";
6
7export default class TypeScriptTransformer extends Transformer {
8 constructor(
9 rootTransformer,
10 tokens,
11 isImportsTransformEnabled,
12 ) {
13 super();this.rootTransformer = rootTransformer;this.tokens = tokens;this.isImportsTransformEnabled = isImportsTransformEnabled;;
14 }
15
16 process() {
17 if (
18 this.rootTransformer.processPossibleArrowParamEnd() ||
19 this.rootTransformer.processPossibleTypeRange()
20 ) {
21 return true;
22 }
23 if (
24 this.tokens.matches1(tt._public) ||
25 this.tokens.matches1(tt._protected) ||
26 this.tokens.matches1(tt._private) ||
27 this.tokens.matches1(tt._abstract) ||
28 this.tokens.matches1(tt._readonly) ||
29 this.tokens.matches1(tt.nonNullAssertion)
30 ) {
31 this.tokens.removeInitialToken();
32 return true;
33 }
34 if (this.tokens.matches1(tt._enum) || this.tokens.matches2(tt._const, tt._enum)) {
35 this.processEnum();
36 return true;
37 }
38 if (
39 this.tokens.matches2(tt._export, tt._enum) ||
40 this.tokens.matches3(tt._export, tt._const, tt._enum)
41 ) {
42 this.processEnum(true);
43 return true;
44 }
45 return false;
46 }
47
48 processEnum(isExport = false) {
49 // We might have "export const enum", so just remove all relevant tokens.
50 this.tokens.removeInitialToken();
51 while (this.tokens.matches1(tt._const) || this.tokens.matches1(tt._enum)) {
52 this.tokens.removeToken();
53 }
54 const enumName = this.tokens.identifierName();
55 this.tokens.removeToken();
56 if (isExport && !this.isImportsTransformEnabled) {
57 this.tokens.appendCode("export ");
58 }
59 this.tokens.appendCode(`var ${enumName}; (function (${enumName})`);
60 this.tokens.copyExpectedToken(tt.braceL);
61 this.processEnumBody(enumName);
62 this.tokens.copyExpectedToken(tt.braceR);
63 if (isExport && this.isImportsTransformEnabled) {
64 this.tokens.appendCode(`)(${enumName} || (exports.${enumName} = ${enumName} = {}));`);
65 } else {
66 this.tokens.appendCode(`)(${enumName} || (${enumName} = {}));`);
67 }
68 }
69
70 /**
71 * Rather than try to compute the actual enum values at compile time, we just create variables for
72 * each one and let everything evaluate at runtime. There's some additional complexity due to
73 * handling string literal names, including ones that happen to be valid identifiers.
74 */
75 processEnumBody(enumName) {
76 let isPreviousValidIdentifier = false;
77 let lastValueReference = null;
78 while (true) {
79 if (this.tokens.matches1(tt.braceR)) {
80 break;
81 }
82 const nameToken = this.tokens.currentToken();
83 let name;
84 let nameStringCode;
85 if (nameToken.type === tt.name) {
86 name = this.tokens.identifierNameForToken(nameToken);
87 nameStringCode = `"${name}"`;
88 } else if (nameToken.type === tt.string) {
89 name = this.tokens.stringValueForToken(nameToken);
90 nameStringCode = this.tokens.code.slice(nameToken.start, nameToken.end);
91 } else {
92 throw new Error("Expected name or string at beginning of enum element.");
93 }
94 const isValidIdentifier = isIdentifier(name);
95 this.tokens.removeInitialToken();
96
97 let valueIsString;
98 let valueCode;
99
100 if (this.tokens.matches1(tt.eq)) {
101 const rhsEndIndex = this.tokens.currentToken().rhsEndIndex;
102 if (rhsEndIndex == null) {
103 throw new Error("Expected rhsEndIndex on enum assign.");
104 }
105 this.tokens.removeToken();
106 if (
107 this.tokens.matches2(tt.string, tt.comma) ||
108 this.tokens.matches2(tt.string, tt.braceR)
109 ) {
110 valueIsString = true;
111 }
112 const startToken = this.tokens.currentToken();
113 while (this.tokens.currentIndex() < rhsEndIndex) {
114 this.tokens.removeToken();
115 }
116 valueCode = this.tokens.code.slice(
117 startToken.start,
118 this.tokens.tokenAtRelativeIndex(-1).end,
119 );
120 } else {
121 valueIsString = false;
122 if (lastValueReference != null) {
123 if (isPreviousValidIdentifier) {
124 valueCode = `${lastValueReference} + 1`;
125 } else {
126 valueCode = `(${lastValueReference}) + 1`;
127 }
128 } else {
129 valueCode = "0";
130 }
131 }
132 if (this.tokens.matches1(tt.comma)) {
133 this.tokens.removeToken();
134 }
135
136 let valueReference;
137 if (isValidIdentifier) {
138 this.tokens.appendCode(`const ${name} = ${valueCode}; `);
139 valueReference = name;
140 } else {
141 valueReference = valueCode;
142 }
143
144 if (valueIsString) {
145 this.tokens.appendCode(`${enumName}[${nameStringCode}] = ${valueReference};`);
146 } else {
147 this.tokens.appendCode(
148 `${enumName}[${enumName}[${nameStringCode}] = ${valueReference}] = ${nameStringCode};`,
149 );
150 }
151 lastValueReference = valueReference;
152 isPreviousValidIdentifier = isValidIdentifier;
153 }
154 }
155}