source: frontend/node_modules/sucrase/dist/transformers/RootTransformer.js

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

Fix frontend appearance

  • Property mode set to 100644
File size: 16.9 KB
Line 
1"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
2
3
4var _keywords = require('../parser/tokenizer/keywords');
5var _types = require('../parser/tokenizer/types');
6
7var _getClassInfo = require('../util/getClassInfo'); var _getClassInfo2 = _interopRequireDefault(_getClassInfo);
8var _CJSImportTransformer = require('./CJSImportTransformer'); var _CJSImportTransformer2 = _interopRequireDefault(_CJSImportTransformer);
9var _ESMImportTransformer = require('./ESMImportTransformer'); var _ESMImportTransformer2 = _interopRequireDefault(_ESMImportTransformer);
10var _FlowTransformer = require('./FlowTransformer'); var _FlowTransformer2 = _interopRequireDefault(_FlowTransformer);
11var _JestHoistTransformer = require('./JestHoistTransformer'); var _JestHoistTransformer2 = _interopRequireDefault(_JestHoistTransformer);
12var _JSXTransformer = require('./JSXTransformer'); var _JSXTransformer2 = _interopRequireDefault(_JSXTransformer);
13var _NumericSeparatorTransformer = require('./NumericSeparatorTransformer'); var _NumericSeparatorTransformer2 = _interopRequireDefault(_NumericSeparatorTransformer);
14var _OptionalCatchBindingTransformer = require('./OptionalCatchBindingTransformer'); var _OptionalCatchBindingTransformer2 = _interopRequireDefault(_OptionalCatchBindingTransformer);
15var _OptionalChainingNullishTransformer = require('./OptionalChainingNullishTransformer'); var _OptionalChainingNullishTransformer2 = _interopRequireDefault(_OptionalChainingNullishTransformer);
16var _ReactDisplayNameTransformer = require('./ReactDisplayNameTransformer'); var _ReactDisplayNameTransformer2 = _interopRequireDefault(_ReactDisplayNameTransformer);
17var _ReactHotLoaderTransformer = require('./ReactHotLoaderTransformer'); var _ReactHotLoaderTransformer2 = _interopRequireDefault(_ReactHotLoaderTransformer);
18
19var _TypeScriptTransformer = require('./TypeScriptTransformer'); var _TypeScriptTransformer2 = _interopRequireDefault(_TypeScriptTransformer);
20
21
22
23
24
25
26
27
28 class RootTransformer {
29 __init() {this.transformers = []}
30
31
32 __init2() {this.generatedVariables = []}
33
34
35
36
37
38 constructor(
39 sucraseContext,
40 transforms,
41 enableLegacyBabel5ModuleInterop,
42 options,
43 ) {;RootTransformer.prototype.__init.call(this);RootTransformer.prototype.__init2.call(this);
44 this.nameManager = sucraseContext.nameManager;
45 this.helperManager = sucraseContext.helperManager;
46 const {tokenProcessor, importProcessor} = sucraseContext;
47 this.tokens = tokenProcessor;
48 this.isImportsTransformEnabled = transforms.includes("imports");
49 this.isReactHotLoaderTransformEnabled = transforms.includes("react-hot-loader");
50 this.disableESTransforms = Boolean(options.disableESTransforms);
51
52 if (!options.disableESTransforms) {
53 this.transformers.push(
54 new (0, _OptionalChainingNullishTransformer2.default)(tokenProcessor, this.nameManager),
55 );
56 this.transformers.push(new (0, _NumericSeparatorTransformer2.default)(tokenProcessor));
57 this.transformers.push(new (0, _OptionalCatchBindingTransformer2.default)(tokenProcessor, this.nameManager));
58 }
59
60 if (transforms.includes("jsx")) {
61 if (options.jsxRuntime !== "preserve") {
62 this.transformers.push(
63 new (0, _JSXTransformer2.default)(this, tokenProcessor, importProcessor, this.nameManager, options),
64 );
65 }
66 this.transformers.push(
67 new (0, _ReactDisplayNameTransformer2.default)(this, tokenProcessor, importProcessor, options),
68 );
69 }
70
71 let reactHotLoaderTransformer = null;
72 if (transforms.includes("react-hot-loader")) {
73 if (!options.filePath) {
74 throw new Error("filePath is required when using the react-hot-loader transform.");
75 }
76 reactHotLoaderTransformer = new (0, _ReactHotLoaderTransformer2.default)(tokenProcessor, options.filePath);
77 this.transformers.push(reactHotLoaderTransformer);
78 }
79
80 // Note that we always want to enable the imports transformer, even when the import transform
81 // itself isn't enabled, since we need to do type-only import pruning for both Flow and
82 // TypeScript.
83 if (transforms.includes("imports")) {
84 if (importProcessor === null) {
85 throw new Error("Expected non-null importProcessor with imports transform enabled.");
86 }
87 this.transformers.push(
88 new (0, _CJSImportTransformer2.default)(
89 this,
90 tokenProcessor,
91 importProcessor,
92 this.nameManager,
93 this.helperManager,
94 reactHotLoaderTransformer,
95 enableLegacyBabel5ModuleInterop,
96 Boolean(options.enableLegacyTypeScriptModuleInterop),
97 transforms.includes("typescript"),
98 transforms.includes("flow"),
99 Boolean(options.preserveDynamicImport),
100 Boolean(options.keepUnusedImports),
101 ),
102 );
103 } else {
104 this.transformers.push(
105 new (0, _ESMImportTransformer2.default)(
106 tokenProcessor,
107 this.nameManager,
108 this.helperManager,
109 reactHotLoaderTransformer,
110 transforms.includes("typescript"),
111 transforms.includes("flow"),
112 Boolean(options.keepUnusedImports),
113 options,
114 ),
115 );
116 }
117
118 if (transforms.includes("flow")) {
119 this.transformers.push(
120 new (0, _FlowTransformer2.default)(this, tokenProcessor, transforms.includes("imports")),
121 );
122 }
123 if (transforms.includes("typescript")) {
124 this.transformers.push(
125 new (0, _TypeScriptTransformer2.default)(this, tokenProcessor, transforms.includes("imports")),
126 );
127 }
128 if (transforms.includes("jest")) {
129 this.transformers.push(
130 new (0, _JestHoistTransformer2.default)(this, tokenProcessor, this.nameManager, importProcessor),
131 );
132 }
133 }
134
135 transform() {
136 this.tokens.reset();
137 this.processBalancedCode();
138 const shouldAddUseStrict = this.isImportsTransformEnabled;
139 // "use strict" always needs to be first, so override the normal transformer order.
140 let prefix = shouldAddUseStrict ? '"use strict";' : "";
141 for (const transformer of this.transformers) {
142 prefix += transformer.getPrefixCode();
143 }
144 prefix += this.helperManager.emitHelpers();
145 prefix += this.generatedVariables.map((v) => ` var ${v};`).join("");
146 for (const transformer of this.transformers) {
147 prefix += transformer.getHoistedCode();
148 }
149 let suffix = "";
150 for (const transformer of this.transformers) {
151 suffix += transformer.getSuffixCode();
152 }
153 const result = this.tokens.finish();
154 let {code} = result;
155 if (code.startsWith("#!")) {
156 let newlineIndex = code.indexOf("\n");
157 if (newlineIndex === -1) {
158 newlineIndex = code.length;
159 code += "\n";
160 }
161 return {
162 code: code.slice(0, newlineIndex + 1) + prefix + code.slice(newlineIndex + 1) + suffix,
163 // The hashbang line has no tokens, so shifting the tokens to account
164 // for prefix can happen normally.
165 mappings: this.shiftMappings(result.mappings, prefix.length),
166 };
167 } else {
168 return {
169 code: prefix + code + suffix,
170 mappings: this.shiftMappings(result.mappings, prefix.length),
171 };
172 }
173 }
174
175 processBalancedCode() {
176 let braceDepth = 0;
177 let parenDepth = 0;
178 while (!this.tokens.isAtEnd()) {
179 if (this.tokens.matches1(_types.TokenType.braceL) || this.tokens.matches1(_types.TokenType.dollarBraceL)) {
180 braceDepth++;
181 } else if (this.tokens.matches1(_types.TokenType.braceR)) {
182 if (braceDepth === 0) {
183 return;
184 }
185 braceDepth--;
186 }
187 if (this.tokens.matches1(_types.TokenType.parenL)) {
188 parenDepth++;
189 } else if (this.tokens.matches1(_types.TokenType.parenR)) {
190 if (parenDepth === 0) {
191 return;
192 }
193 parenDepth--;
194 }
195 this.processToken();
196 }
197 }
198
199 processToken() {
200 if (this.tokens.matches1(_types.TokenType._class)) {
201 this.processClass();
202 return;
203 }
204 for (const transformer of this.transformers) {
205 const wasProcessed = transformer.process();
206 if (wasProcessed) {
207 return;
208 }
209 }
210 this.tokens.copyToken();
211 }
212
213 /**
214 * Skip past a class with a name and return that name.
215 */
216 processNamedClass() {
217 if (!this.tokens.matches2(_types.TokenType._class, _types.TokenType.name)) {
218 throw new Error("Expected identifier for exported class name.");
219 }
220 const name = this.tokens.identifierNameAtIndex(this.tokens.currentIndex() + 1);
221 this.processClass();
222 return name;
223 }
224
225 processClass() {
226 const classInfo = _getClassInfo2.default.call(void 0, this, this.tokens, this.nameManager, this.disableESTransforms);
227
228 // Both static and instance initializers need a class name to use to invoke the initializer, so
229 // assign to one if necessary.
230 const needsCommaExpression =
231 (classInfo.headerInfo.isExpression || !classInfo.headerInfo.className) &&
232 classInfo.staticInitializerNames.length + classInfo.instanceInitializerNames.length > 0;
233
234 let className = classInfo.headerInfo.className;
235 if (needsCommaExpression) {
236 className = this.nameManager.claimFreeName("_class");
237 this.generatedVariables.push(className);
238 this.tokens.appendCode(` (${className} =`);
239 }
240
241 const classToken = this.tokens.currentToken();
242 const contextId = classToken.contextId;
243 if (contextId == null) {
244 throw new Error("Expected class to have a context ID.");
245 }
246 this.tokens.copyExpectedToken(_types.TokenType._class);
247 while (!this.tokens.matchesContextIdAndLabel(_types.TokenType.braceL, contextId)) {
248 this.processToken();
249 }
250
251 this.processClassBody(classInfo, className);
252
253 const staticInitializerStatements = classInfo.staticInitializerNames.map(
254 (name) => `${className}.${name}()`,
255 );
256 if (needsCommaExpression) {
257 this.tokens.appendCode(
258 `, ${staticInitializerStatements.map((s) => `${s}, `).join("")}${className})`,
259 );
260 } else if (classInfo.staticInitializerNames.length > 0) {
261 this.tokens.appendCode(` ${staticInitializerStatements.map((s) => `${s};`).join(" ")}`);
262 }
263 }
264
265 /**
266 * We want to just handle class fields in all contexts, since TypeScript supports them. Later,
267 * when some JS implementations support class fields, this should be made optional.
268 */
269 processClassBody(classInfo, className) {
270 const {
271 headerInfo,
272 constructorInsertPos,
273 constructorInitializerStatements,
274 fields,
275 instanceInitializerNames,
276 rangesToRemove,
277 } = classInfo;
278 let fieldIndex = 0;
279 let rangeToRemoveIndex = 0;
280 const classContextId = this.tokens.currentToken().contextId;
281 if (classContextId == null) {
282 throw new Error("Expected non-null context ID on class.");
283 }
284 this.tokens.copyExpectedToken(_types.TokenType.braceL);
285 if (this.isReactHotLoaderTransformEnabled) {
286 this.tokens.appendCode(
287 "__reactstandin__regenerateByEval(key, code) {this[key] = eval(code);}",
288 );
289 }
290
291 const needsConstructorInit =
292 constructorInitializerStatements.length + instanceInitializerNames.length > 0;
293
294 if (constructorInsertPos === null && needsConstructorInit) {
295 const constructorInitializersCode = this.makeConstructorInitCode(
296 constructorInitializerStatements,
297 instanceInitializerNames,
298 className,
299 );
300 if (headerInfo.hasSuperclass) {
301 const argsName = this.nameManager.claimFreeName("args");
302 this.tokens.appendCode(
303 `constructor(...${argsName}) { super(...${argsName}); ${constructorInitializersCode}; }`,
304 );
305 } else {
306 this.tokens.appendCode(`constructor() { ${constructorInitializersCode}; }`);
307 }
308 }
309
310 while (!this.tokens.matchesContextIdAndLabel(_types.TokenType.braceR, classContextId)) {
311 if (fieldIndex < fields.length && this.tokens.currentIndex() === fields[fieldIndex].start) {
312 let needsCloseBrace = false;
313 if (this.tokens.matches1(_types.TokenType.bracketL)) {
314 this.tokens.copyTokenWithPrefix(`${fields[fieldIndex].initializerName}() {this`);
315 } else if (this.tokens.matches1(_types.TokenType.string) || this.tokens.matches1(_types.TokenType.num)) {
316 this.tokens.copyTokenWithPrefix(`${fields[fieldIndex].initializerName}() {this[`);
317 needsCloseBrace = true;
318 } else {
319 this.tokens.copyTokenWithPrefix(`${fields[fieldIndex].initializerName}() {this.`);
320 }
321 while (this.tokens.currentIndex() < fields[fieldIndex].end) {
322 if (needsCloseBrace && this.tokens.currentIndex() === fields[fieldIndex].equalsIndex) {
323 this.tokens.appendCode("]");
324 }
325 this.processToken();
326 }
327 this.tokens.appendCode("}");
328 fieldIndex++;
329 } else if (
330 rangeToRemoveIndex < rangesToRemove.length &&
331 this.tokens.currentIndex() >= rangesToRemove[rangeToRemoveIndex].start
332 ) {
333 if (this.tokens.currentIndex() < rangesToRemove[rangeToRemoveIndex].end) {
334 this.tokens.removeInitialToken();
335 }
336 while (this.tokens.currentIndex() < rangesToRemove[rangeToRemoveIndex].end) {
337 this.tokens.removeToken();
338 }
339 rangeToRemoveIndex++;
340 } else if (this.tokens.currentIndex() === constructorInsertPos) {
341 this.tokens.copyToken();
342 if (needsConstructorInit) {
343 this.tokens.appendCode(
344 `;${this.makeConstructorInitCode(
345 constructorInitializerStatements,
346 instanceInitializerNames,
347 className,
348 )};`,
349 );
350 }
351 this.processToken();
352 } else {
353 this.processToken();
354 }
355 }
356 this.tokens.copyExpectedToken(_types.TokenType.braceR);
357 }
358
359 makeConstructorInitCode(
360 constructorInitializerStatements,
361 instanceInitializerNames,
362 className,
363 ) {
364 return [
365 ...constructorInitializerStatements,
366 ...instanceInitializerNames.map((name) => `${className}.prototype.${name}.call(this)`),
367 ].join(";");
368 }
369
370 /**
371 * Normally it's ok to simply remove type tokens, but we need to be more careful when dealing with
372 * arrow function return types since they can confuse the parser. In that case, we want to move
373 * the close-paren to the same line as the arrow.
374 *
375 * See https://github.com/alangpierce/sucrase/issues/391 for more details.
376 */
377 processPossibleArrowParamEnd() {
378 if (this.tokens.matches2(_types.TokenType.parenR, _types.TokenType.colon) && this.tokens.tokenAtRelativeIndex(1).isType) {
379 let nextNonTypeIndex = this.tokens.currentIndex() + 1;
380 // Look ahead to see if this is an arrow function or something else.
381 while (this.tokens.tokens[nextNonTypeIndex].isType) {
382 nextNonTypeIndex++;
383 }
384 if (this.tokens.matches1AtIndex(nextNonTypeIndex, _types.TokenType.arrow)) {
385 this.tokens.removeInitialToken();
386 while (this.tokens.currentIndex() < nextNonTypeIndex) {
387 this.tokens.removeToken();
388 }
389 this.tokens.replaceTokenTrimmingLeftWhitespace(") =>");
390 return true;
391 }
392 }
393 return false;
394 }
395
396 /**
397 * An async arrow function might be of the form:
398 *
399 * async <
400 * T
401 * >() => {}
402 *
403 * in which case, removing the type parameters will cause a syntax error. Detect this case and
404 * move the open-paren earlier.
405 */
406 processPossibleAsyncArrowWithTypeParams() {
407 if (
408 !this.tokens.matchesContextual(_keywords.ContextualKeyword._async) &&
409 !this.tokens.matches1(_types.TokenType._async)
410 ) {
411 return false;
412 }
413 const nextToken = this.tokens.tokenAtRelativeIndex(1);
414 if (nextToken.type !== _types.TokenType.lessThan || !nextToken.isType) {
415 return false;
416 }
417
418 let nextNonTypeIndex = this.tokens.currentIndex() + 1;
419 // Look ahead to see if this is an arrow function or something else.
420 while (this.tokens.tokens[nextNonTypeIndex].isType) {
421 nextNonTypeIndex++;
422 }
423 if (this.tokens.matches1AtIndex(nextNonTypeIndex, _types.TokenType.parenL)) {
424 this.tokens.replaceToken("async (");
425 this.tokens.removeInitialToken();
426 while (this.tokens.currentIndex() < nextNonTypeIndex) {
427 this.tokens.removeToken();
428 }
429 this.tokens.removeToken();
430 // We ate a ( token, so we need to process the tokens in between and then the ) token so that
431 // we remain balanced.
432 this.processBalancedCode();
433 this.processToken();
434 return true;
435 }
436 return false;
437 }
438
439 processPossibleTypeRange() {
440 if (this.tokens.currentToken().isType) {
441 this.tokens.removeInitialToken();
442 while (this.tokens.currentToken().isType) {
443 this.tokens.removeToken();
444 }
445 return true;
446 }
447 return false;
448 }
449
450 shiftMappings(
451 mappings,
452 prefixLength,
453 ) {
454 for (let i = 0; i < mappings.length; i++) {
455 const mapping = mappings[i];
456 if (mapping !== undefined) {
457 mappings[i] = mapping + prefixLength;
458 }
459 }
460 return mappings;
461 }
462} exports.default = RootTransformer;
Note: See TracBrowser for help on using the repository browser.