UNPKG

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