UNPKG

8.41 kBJavaScriptView Raw
1
2
3
4import { TokenType as tt} from "./parser/tokenizer/types";
5import isAsyncOperation from "./util/isAsyncOperation";
6
7
8
9
10
11
12export default class TokenProcessor {
13 __init() {this.resultCode = ""}
14 __init2() {this.tokenIndex = 0}
15
16 constructor(
17 code,
18 tokens,
19 isFlowEnabled,
20 disableESTransforms,
21 helperManager,
22 ) {;this.code = code;this.tokens = tokens;this.isFlowEnabled = isFlowEnabled;this.disableESTransforms = disableESTransforms;this.helperManager = helperManager;TokenProcessor.prototype.__init.call(this);TokenProcessor.prototype.__init2.call(this);}
23
24 /**
25 * Make a new TokenProcessor for things like lookahead.
26 */
27 snapshot() {
28 return {resultCode: this.resultCode, tokenIndex: this.tokenIndex};
29 }
30
31 restoreToSnapshot(snapshot) {
32 this.resultCode = snapshot.resultCode;
33 this.tokenIndex = snapshot.tokenIndex;
34 }
35
36 getResultCodeIndex() {
37 return this.resultCode.length;
38 }
39
40 reset() {
41 this.resultCode = "";
42 this.tokenIndex = 0;
43 }
44
45 matchesContextualAtIndex(index, contextualKeyword) {
46 return (
47 this.matches1AtIndex(index, tt.name) &&
48 this.tokens[index].contextualKeyword === contextualKeyword
49 );
50 }
51
52 identifierNameAtIndex(index) {
53 // TODO: We need to process escapes since technically you can have unicode escapes in variable
54 // names.
55 return this.identifierNameForToken(this.tokens[index]);
56 }
57
58 identifierName() {
59 return this.identifierNameForToken(this.currentToken());
60 }
61
62 identifierNameForToken(token) {
63 return this.code.slice(token.start, token.end);
64 }
65
66 rawCodeForToken(token) {
67 return this.code.slice(token.start, token.end);
68 }
69
70 stringValueAtIndex(index) {
71 return this.stringValueForToken(this.tokens[index]);
72 }
73
74 stringValue() {
75 return this.stringValueForToken(this.currentToken());
76 }
77
78 stringValueForToken(token) {
79 // This is used to identify when two imports are the same and to resolve TypeScript enum keys.
80 // Ideally we'd process escapes within the strings, but for now we pretty much take the raw
81 // code.
82 return this.code.slice(token.start + 1, token.end - 1);
83 }
84
85 matches1AtIndex(index, t1) {
86 return this.tokens[index].type === t1;
87 }
88
89 matches2AtIndex(index, t1, t2) {
90 return this.tokens[index].type === t1 && this.tokens[index + 1].type === t2;
91 }
92
93 matches3AtIndex(index, t1, t2, t3) {
94 return (
95 this.tokens[index].type === t1 &&
96 this.tokens[index + 1].type === t2 &&
97 this.tokens[index + 2].type === t3
98 );
99 }
100
101 matches1(t1) {
102 return this.tokens[this.tokenIndex].type === t1;
103 }
104
105 matches2(t1, t2) {
106 return this.tokens[this.tokenIndex].type === t1 && this.tokens[this.tokenIndex + 1].type === t2;
107 }
108
109 matches3(t1, t2, t3) {
110 return (
111 this.tokens[this.tokenIndex].type === t1 &&
112 this.tokens[this.tokenIndex + 1].type === t2 &&
113 this.tokens[this.tokenIndex + 2].type === t3
114 );
115 }
116
117 matches4(t1, t2, t3, t4) {
118 return (
119 this.tokens[this.tokenIndex].type === t1 &&
120 this.tokens[this.tokenIndex + 1].type === t2 &&
121 this.tokens[this.tokenIndex + 2].type === t3 &&
122 this.tokens[this.tokenIndex + 3].type === t4
123 );
124 }
125
126 matches5(t1, t2, t3, t4, t5) {
127 return (
128 this.tokens[this.tokenIndex].type === t1 &&
129 this.tokens[this.tokenIndex + 1].type === t2 &&
130 this.tokens[this.tokenIndex + 2].type === t3 &&
131 this.tokens[this.tokenIndex + 3].type === t4 &&
132 this.tokens[this.tokenIndex + 4].type === t5
133 );
134 }
135
136 matchesContextual(contextualKeyword) {
137 return this.matchesContextualAtIndex(this.tokenIndex, contextualKeyword);
138 }
139
140 matchesContextIdAndLabel(type, contextId) {
141 return this.matches1(type) && this.currentToken().contextId === contextId;
142 }
143
144 previousWhitespaceAndComments() {
145 let whitespaceAndComments = this.code.slice(
146 this.tokenIndex > 0 ? this.tokens[this.tokenIndex - 1].end : 0,
147 this.tokenIndex < this.tokens.length ? this.tokens[this.tokenIndex].start : this.code.length,
148 );
149 if (this.isFlowEnabled) {
150 whitespaceAndComments = whitespaceAndComments.replace(/@flow/g, "");
151 }
152 return whitespaceAndComments;
153 }
154
155 replaceToken(newCode) {
156 this.resultCode += this.previousWhitespaceAndComments();
157 this.appendTokenPrefix();
158 this.resultCode += newCode;
159 this.appendTokenSuffix();
160 this.tokenIndex++;
161 }
162
163 replaceTokenTrimmingLeftWhitespace(newCode) {
164 this.resultCode += this.previousWhitespaceAndComments().replace(/[^\r\n]/g, "");
165 this.appendTokenPrefix();
166 this.resultCode += newCode;
167 this.appendTokenSuffix();
168 this.tokenIndex++;
169 }
170
171 removeInitialToken() {
172 this.replaceToken("");
173 }
174
175 removeToken() {
176 this.replaceTokenTrimmingLeftWhitespace("");
177 }
178
179 copyExpectedToken(tokenType) {
180 if (this.tokens[this.tokenIndex].type !== tokenType) {
181 throw new Error(`Expected token ${tokenType}`);
182 }
183 this.copyToken();
184 }
185
186 copyToken() {
187 this.resultCode += this.previousWhitespaceAndComments();
188 this.appendTokenPrefix();
189 this.resultCode += this.code.slice(
190 this.tokens[this.tokenIndex].start,
191 this.tokens[this.tokenIndex].end,
192 );
193 this.appendTokenSuffix();
194 this.tokenIndex++;
195 }
196
197 copyTokenWithPrefix(prefix) {
198 this.resultCode += this.previousWhitespaceAndComments();
199 this.appendTokenPrefix();
200 this.resultCode += prefix;
201 this.resultCode += this.code.slice(
202 this.tokens[this.tokenIndex].start,
203 this.tokens[this.tokenIndex].end,
204 );
205 this.appendTokenSuffix();
206 this.tokenIndex++;
207 }
208
209 appendTokenPrefix() {
210 const token = this.currentToken();
211 if (token.numNullishCoalesceStarts || token.isOptionalChainStart) {
212 token.isAsyncOperation = isAsyncOperation(this);
213 }
214 if (this.disableESTransforms) {
215 return;
216 }
217 if (token.numNullishCoalesceStarts) {
218 for (let i = 0; i < token.numNullishCoalesceStarts; i++) {
219 if (token.isAsyncOperation) {
220 this.resultCode += "await ";
221 this.resultCode += this.helperManager.getHelperName("asyncNullishCoalesce");
222 } else {
223 this.resultCode += this.helperManager.getHelperName("nullishCoalesce");
224 }
225 this.resultCode += "(";
226 }
227 }
228 if (token.isOptionalChainStart) {
229 if (token.isAsyncOperation) {
230 this.resultCode += "await ";
231 }
232 if (this.tokenIndex > 0 && this.tokenAtRelativeIndex(-1).type === tt._delete) {
233 if (token.isAsyncOperation) {
234 this.resultCode += this.helperManager.getHelperName("asyncOptionalChainDelete");
235 } else {
236 this.resultCode += this.helperManager.getHelperName("optionalChainDelete");
237 }
238 } else if (token.isAsyncOperation) {
239 this.resultCode += this.helperManager.getHelperName("asyncOptionalChain");
240 } else {
241 this.resultCode += this.helperManager.getHelperName("optionalChain");
242 }
243 this.resultCode += "([";
244 }
245 }
246
247 appendTokenSuffix() {
248 const token = this.currentToken();
249 if (token.isOptionalChainEnd && !this.disableESTransforms) {
250 this.resultCode += "])";
251 }
252 if (token.numNullishCoalesceEnds && !this.disableESTransforms) {
253 for (let i = 0; i < token.numNullishCoalesceEnds; i++) {
254 this.resultCode += "))";
255 }
256 }
257 }
258
259 appendCode(code) {
260 this.resultCode += code;
261 }
262
263 currentToken() {
264 return this.tokens[this.tokenIndex];
265 }
266
267 currentTokenCode() {
268 const token = this.currentToken();
269 return this.code.slice(token.start, token.end);
270 }
271
272 tokenAtRelativeIndex(relativeIndex) {
273 return this.tokens[this.tokenIndex + relativeIndex];
274 }
275
276 currentIndex() {
277 return this.tokenIndex;
278 }
279
280 /**
281 * Move to the next token. Only suitable in preprocessing steps. When
282 * generating new code, you should use copyToken or removeToken.
283 */
284 nextToken() {
285 if (this.tokenIndex === this.tokens.length) {
286 throw new Error("Unexpectedly reached end of input.");
287 }
288 this.tokenIndex++;
289 }
290
291 previousToken() {
292 this.tokenIndex--;
293 }
294
295 finish() {
296 if (this.tokenIndex !== this.tokens.length) {
297 throw new Error("Tried to finish processing tokens before reaching the end.");
298 }
299 this.resultCode += this.previousWhitespaceAndComments();
300 return this.resultCode;
301 }
302
303 isAtEnd() {
304 return this.tokenIndex === this.tokens.length;
305 }
306}