source: frontend/node_modules/sucrase/dist/util/getClassInfo.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 13 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 11.2 KB
Line 
1"use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
3var _keywords = require('../parser/tokenizer/keywords');
4var _types = require('../parser/tokenizer/types');
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44/**
45 * Get information about the class fields for this class, given a token processor pointing to the
46 * open-brace at the start of the class.
47 */
48 function getClassInfo(
49 rootTransformer,
50 tokens,
51 nameManager,
52 disableESTransforms,
53) {
54 const snapshot = tokens.snapshot();
55
56 const headerInfo = processClassHeader(tokens);
57
58 let constructorInitializerStatements = [];
59 const instanceInitializerNames = [];
60 const staticInitializerNames = [];
61 let constructorInsertPos = null;
62 const fields = [];
63 const rangesToRemove = [];
64
65 const classContextId = tokens.currentToken().contextId;
66 if (classContextId == null) {
67 throw new Error("Expected non-null class context ID on class open-brace.");
68 }
69
70 tokens.nextToken();
71 while (!tokens.matchesContextIdAndLabel(_types.TokenType.braceR, classContextId)) {
72 if (tokens.matchesContextual(_keywords.ContextualKeyword._constructor) && !tokens.currentToken().isType) {
73 ({constructorInitializerStatements, constructorInsertPos} = processConstructor(tokens));
74 } else if (tokens.matches1(_types.TokenType.semi)) {
75 if (!disableESTransforms) {
76 rangesToRemove.push({start: tokens.currentIndex(), end: tokens.currentIndex() + 1});
77 }
78 tokens.nextToken();
79 } else if (tokens.currentToken().isType) {
80 tokens.nextToken();
81 } else {
82 // Either a method or a field. Skip to the identifier part.
83 const statementStartIndex = tokens.currentIndex();
84 let isStatic = false;
85 let isESPrivate = false;
86 let isDeclareOrAbstract = false;
87 while (isAccessModifier(tokens.currentToken())) {
88 if (tokens.matches1(_types.TokenType._static)) {
89 isStatic = true;
90 }
91 if (tokens.matches1(_types.TokenType.hash)) {
92 isESPrivate = true;
93 }
94 if (tokens.matches1(_types.TokenType._declare) || tokens.matches1(_types.TokenType._abstract)) {
95 isDeclareOrAbstract = true;
96 }
97 tokens.nextToken();
98 }
99 if (isStatic && tokens.matches1(_types.TokenType.braceL)) {
100 // This is a static block, so don't process it in any special way.
101 skipToNextClassElement(tokens, classContextId);
102 continue;
103 }
104 if (isESPrivate) {
105 // Sucrase doesn't attempt to transpile private fields; just leave them as-is.
106 skipToNextClassElement(tokens, classContextId);
107 continue;
108 }
109 if (
110 tokens.matchesContextual(_keywords.ContextualKeyword._constructor) &&
111 !tokens.currentToken().isType
112 ) {
113 ({constructorInitializerStatements, constructorInsertPos} = processConstructor(tokens));
114 continue;
115 }
116
117 const nameStartIndex = tokens.currentIndex();
118 skipFieldName(tokens);
119 if (tokens.matches1(_types.TokenType.lessThan) || tokens.matches1(_types.TokenType.parenL)) {
120 // This is a method, so nothing to process.
121 skipToNextClassElement(tokens, classContextId);
122 continue;
123 }
124 // There might be a type annotation that we need to skip.
125 while (tokens.currentToken().isType) {
126 tokens.nextToken();
127 }
128 if (tokens.matches1(_types.TokenType.eq)) {
129 const equalsIndex = tokens.currentIndex();
130 // This is an initializer, so we need to wrap in an initializer method.
131 const valueEnd = tokens.currentToken().rhsEndIndex;
132 if (valueEnd == null) {
133 throw new Error("Expected rhsEndIndex on class field assignment.");
134 }
135 tokens.nextToken();
136 while (tokens.currentIndex() < valueEnd) {
137 rootTransformer.processToken();
138 }
139 let initializerName;
140 if (isStatic) {
141 initializerName = nameManager.claimFreeName("__initStatic");
142 staticInitializerNames.push(initializerName);
143 } else {
144 initializerName = nameManager.claimFreeName("__init");
145 instanceInitializerNames.push(initializerName);
146 }
147 // Fields start at the name, so `static x = 1;` has a field range of `x = 1;`.
148 fields.push({
149 initializerName,
150 equalsIndex,
151 start: nameStartIndex,
152 end: tokens.currentIndex(),
153 });
154 } else if (!disableESTransforms || isDeclareOrAbstract) {
155 // This is a regular field declaration, like `x;`. With the class transform enabled, we just
156 // remove the line so that no output is produced. With the class transform disabled, we
157 // usually want to preserve the declaration (but still strip types), but if the `declare`
158 // or `abstract` keyword is specified, we should remove the line to avoid initializing the
159 // value to undefined.
160 rangesToRemove.push({start: statementStartIndex, end: tokens.currentIndex()});
161 }
162 }
163 }
164
165 tokens.restoreToSnapshot(snapshot);
166 if (disableESTransforms) {
167 // With ES transforms disabled, we don't want to transform regular class
168 // field declarations, and we don't need to do any additional tricks to
169 // reference the constructor for static init, but we still need to transform
170 // TypeScript field initializers defined as constructor parameters and we
171 // still need to remove `declare` fields. For now, we run the same code
172 // path but omit any field information, as if the class had no field
173 // declarations. In the future, when we fully drop the class fields
174 // transform, we can simplify this code significantly.
175 return {
176 headerInfo,
177 constructorInitializerStatements,
178 instanceInitializerNames: [],
179 staticInitializerNames: [],
180 constructorInsertPos,
181 fields: [],
182 rangesToRemove,
183 };
184 } else {
185 return {
186 headerInfo,
187 constructorInitializerStatements,
188 instanceInitializerNames,
189 staticInitializerNames,
190 constructorInsertPos,
191 fields,
192 rangesToRemove,
193 };
194 }
195} exports.default = getClassInfo;
196
197/**
198 * Move the token processor to the next method/field in the class.
199 *
200 * To do that, we seek forward to the next start of a class name (either an open
201 * bracket or an identifier, or the closing curly brace), then seek backward to
202 * include any access modifiers.
203 */
204function skipToNextClassElement(tokens, classContextId) {
205 tokens.nextToken();
206 while (tokens.currentToken().contextId !== classContextId) {
207 tokens.nextToken();
208 }
209 while (isAccessModifier(tokens.tokenAtRelativeIndex(-1))) {
210 tokens.previousToken();
211 }
212}
213
214function processClassHeader(tokens) {
215 const classToken = tokens.currentToken();
216 const contextId = classToken.contextId;
217 if (contextId == null) {
218 throw new Error("Expected context ID on class token.");
219 }
220 const isExpression = classToken.isExpression;
221 if (isExpression == null) {
222 throw new Error("Expected isExpression on class token.");
223 }
224 let className = null;
225 let hasSuperclass = false;
226 tokens.nextToken();
227 if (tokens.matches1(_types.TokenType.name)) {
228 className = tokens.identifierName();
229 }
230 while (!tokens.matchesContextIdAndLabel(_types.TokenType.braceL, contextId)) {
231 // If this has a superclass, there will always be an `extends` token. If it doesn't have a
232 // superclass, only type parameters and `implements` clauses can show up here, all of which
233 // consist only of type tokens. A declaration like `class A<B extends C> {` should *not* count
234 // as having a superclass.
235 if (tokens.matches1(_types.TokenType._extends) && !tokens.currentToken().isType) {
236 hasSuperclass = true;
237 }
238 tokens.nextToken();
239 }
240 return {isExpression, className, hasSuperclass};
241}
242
243/**
244 * Extract useful information out of a constructor, starting at the "constructor" name.
245 */
246function processConstructor(tokens)
247
248
249 {
250 const constructorInitializerStatements = [];
251
252 tokens.nextToken();
253 const constructorContextId = tokens.currentToken().contextId;
254 if (constructorContextId == null) {
255 throw new Error("Expected context ID on open-paren starting constructor params.");
256 }
257 // Advance through parameters looking for access modifiers.
258 while (!tokens.matchesContextIdAndLabel(_types.TokenType.parenR, constructorContextId)) {
259 if (tokens.currentToken().contextId === constructorContextId) {
260 // Current token is an open paren or comma just before a param, so check
261 // that param for access modifiers.
262 tokens.nextToken();
263 if (isAccessModifier(tokens.currentToken())) {
264 tokens.nextToken();
265 while (isAccessModifier(tokens.currentToken())) {
266 tokens.nextToken();
267 }
268 const token = tokens.currentToken();
269 if (token.type !== _types.TokenType.name) {
270 throw new Error("Expected identifier after access modifiers in constructor arg.");
271 }
272 const name = tokens.identifierNameForToken(token);
273 constructorInitializerStatements.push(`this.${name} = ${name}`);
274 }
275 } else {
276 tokens.nextToken();
277 }
278 }
279 // )
280 tokens.nextToken();
281 // Constructor type annotations are invalid, but skip them anyway since
282 // they're easy to skip.
283 while (tokens.currentToken().isType) {
284 tokens.nextToken();
285 }
286 let constructorInsertPos = tokens.currentIndex();
287
288 // Advance through body looking for a super call.
289 let foundSuperCall = false;
290 while (!tokens.matchesContextIdAndLabel(_types.TokenType.braceR, constructorContextId)) {
291 if (!foundSuperCall && tokens.matches2(_types.TokenType._super, _types.TokenType.parenL)) {
292 tokens.nextToken();
293 const superCallContextId = tokens.currentToken().contextId;
294 if (superCallContextId == null) {
295 throw new Error("Expected a context ID on the super call");
296 }
297 while (!tokens.matchesContextIdAndLabel(_types.TokenType.parenR, superCallContextId)) {
298 tokens.nextToken();
299 }
300 constructorInsertPos = tokens.currentIndex();
301 foundSuperCall = true;
302 }
303 tokens.nextToken();
304 }
305 // }
306 tokens.nextToken();
307
308 return {constructorInitializerStatements, constructorInsertPos};
309}
310
311/**
312 * Determine if this is any token that can go before the name in a method/field.
313 */
314function isAccessModifier(token) {
315 return [
316 _types.TokenType._async,
317 _types.TokenType._get,
318 _types.TokenType._set,
319 _types.TokenType.plus,
320 _types.TokenType.minus,
321 _types.TokenType._readonly,
322 _types.TokenType._static,
323 _types.TokenType._public,
324 _types.TokenType._private,
325 _types.TokenType._protected,
326 _types.TokenType._override,
327 _types.TokenType._abstract,
328 _types.TokenType.star,
329 _types.TokenType._declare,
330 _types.TokenType.hash,
331 ].includes(token.type);
332}
333
334/**
335 * The next token or set of tokens is either an identifier or an expression in square brackets, for
336 * a method or field name.
337 */
338function skipFieldName(tokens) {
339 if (tokens.matches1(_types.TokenType.bracketL)) {
340 const startToken = tokens.currentToken();
341 const classContextId = startToken.contextId;
342 if (classContextId == null) {
343 throw new Error("Expected class context ID on computed name open bracket.");
344 }
345 while (!tokens.matchesContextIdAndLabel(_types.TokenType.bracketR, classContextId)) {
346 tokens.nextToken();
347 }
348 tokens.nextToken();
349 } else {
350 tokens.nextToken();
351 }
352}
Note: See TracBrowser for help on using the repository browser.