hermes-parser 0.4.8 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,383 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ * @format
9
+ */
10
+
11
+ /*
12
+ This class does some very "javascripty" things in the name of
13
+ performance which are ultimately impossible to soundly type.
14
+
15
+ So instead of adding strict types and a large number of suppression
16
+ comments, instead it is left untyped and subclasses are strictly
17
+ typed via a separate flow declaration file.
18
+ */
19
+
20
+ import type {HermesNode} from './HermesAST';
21
+
22
+ import HermesASTAdapter from './HermesASTAdapter';
23
+
24
+ export default class HermesToBabelAdapter extends HermesASTAdapter {
25
+ fixSourceLocation(node: HermesNode): void {
26
+ const loc = node.loc;
27
+ if (loc == null) {
28
+ return;
29
+ }
30
+
31
+ node.loc = {
32
+ source: this.sourceFilename ?? null,
33
+ start: loc.start,
34
+ end: loc.end,
35
+ };
36
+
37
+ node.start = loc.rangeStart;
38
+ node.end = loc.rangeEnd;
39
+ }
40
+
41
+ mapNode(node: HermesNode): HermesNode {
42
+ this.fixSourceLocation(node);
43
+ switch (node.type) {
44
+ case 'Program':
45
+ return this.mapProgram(node);
46
+ case 'BlockStatement':
47
+ return this.mapNodeWithDirectives(node);
48
+ case 'Empty':
49
+ return this.mapEmpty(node);
50
+ case 'Identifier':
51
+ return this.mapIdentifier(node);
52
+ case 'TemplateElement':
53
+ return this.mapTemplateElement(node);
54
+ case 'GenericTypeAnnotation':
55
+ return this.mapGenericTypeAnnotation(node);
56
+ case 'SymbolTypeAnnotation':
57
+ return this.mapSymbolTypeAnnotation(node);
58
+ case 'Property':
59
+ return this.mapProperty(node);
60
+ case 'MethodDefinition':
61
+ return this.mapMethodDefinition(node);
62
+ case 'ImportDeclaration':
63
+ return this.mapImportDeclaration(node);
64
+ case 'ImportSpecifier':
65
+ return this.mapImportSpecifier(node);
66
+ case 'ExportDefaultDeclaration':
67
+ return this.mapExportDefaultDeclaration(node);
68
+ case 'ExportNamedDeclaration':
69
+ return this.mapExportNamedDeclaration(node);
70
+ case 'ExportAllDeclaration':
71
+ return this.mapExportAllDeclaration(node);
72
+ case 'RestElement':
73
+ return this.mapRestElement(node);
74
+ case 'ImportExpression':
75
+ return this.mapImportExpression(node);
76
+ case 'JSXStringLiteral':
77
+ return this.mapJSXStringLiteral(node);
78
+ case 'PrivateName':
79
+ case 'ClassPrivateProperty':
80
+ return this.mapPrivateProperty(node);
81
+ case 'FunctionDeclaration':
82
+ case 'FunctionExpression':
83
+ return this.mapFunction(node);
84
+ case 'IndexedAccessType':
85
+ case 'OptionalIndexedAccessType':
86
+ return this.mapUnsupportedTypeAnnotation(node);
87
+ default:
88
+ return this.mapNodeDefault(node);
89
+ }
90
+ }
91
+
92
+ mapProgram(node: HermesNode): HermesNode {
93
+ // Visit child nodes and convert to directives
94
+ const {comments, ...program} = this.mapNodeWithDirectives(node);
95
+
96
+ program.sourceType = this.getSourceType();
97
+
98
+ // Adjust start loc to beginning of file
99
+ program.loc.start = {line: 1, column: 0};
100
+ program.start = 0;
101
+
102
+ // Adjust end loc to include last comment if program ends with a comment
103
+ if (comments.length > 0) {
104
+ const lastComment = comments[comments.length - 1];
105
+ if (lastComment.end > program.end) {
106
+ program.loc.end = lastComment.loc.end;
107
+ program.end = lastComment.end;
108
+ }
109
+ }
110
+
111
+ // Rename root node to File node and move Program node under program property
112
+ return {
113
+ type: 'File',
114
+ loc: program.loc,
115
+ start: program.start,
116
+ end: program.end,
117
+ program,
118
+ comments,
119
+ };
120
+ }
121
+
122
+ mapNodeWithDirectives(node: HermesNode): HermesNode {
123
+ const directives = [];
124
+ for (const child of node.body) {
125
+ if (child.type === 'ExpressionStatement' && child.directive != null) {
126
+ // Visit directive children
127
+ const directiveChild = this.mapNode(child);
128
+
129
+ // Modify string literal node to be DirectiveLiteral node
130
+ directiveChild.expression.type = 'DirectiveLiteral';
131
+
132
+ // Construct Directive node with DirectiveLiteral value
133
+ directives.push({
134
+ type: 'Directive',
135
+ loc: directiveChild.loc,
136
+ start: directiveChild.start,
137
+ end: directiveChild.end,
138
+ value: directiveChild.expression,
139
+ });
140
+ } else {
141
+ // Once we have found the first non-directive node we know there cannot be any more directives
142
+ break;
143
+ }
144
+ }
145
+
146
+ // Move directives from body to new directives array
147
+ node.directives = directives;
148
+ if (directives.length !== 0) {
149
+ node.body = node.body.slice(directives.length);
150
+ }
151
+
152
+ // Visit expression statement children
153
+ const body = node.body;
154
+ for (let i = 0; i < body.length; i++) {
155
+ const child = body[i];
156
+ if (child != null) {
157
+ body[i] = this.mapNode(child);
158
+ }
159
+ }
160
+
161
+ return node;
162
+ }
163
+
164
+ mapIdentifier(node: HermesNode): HermesNode {
165
+ node.loc.identifierName = node.name;
166
+ return this.mapNodeDefault(node);
167
+ }
168
+
169
+ mapTemplateElement(node: HermesNode): HermesNode {
170
+ // Adjust start loc to exclude "`" at beginning of template literal if this is the first quasi,
171
+ // otherwise exclude "}" from previous expression.
172
+ const startCharsToExclude = 1;
173
+
174
+ // Adjust end loc to exclude "`" at end of template literal if this is the last quasi,
175
+ // otherwise exclude "${" from next expression.
176
+ const endCharsToExclude = node.tail ? 1 : 2;
177
+
178
+ return {
179
+ type: 'TemplateElement',
180
+ loc: {
181
+ start: {
182
+ line: node.loc.start.line,
183
+ column: node.loc.start.column + startCharsToExclude,
184
+ },
185
+ end: {
186
+ line: node.loc.end.line,
187
+ column: node.loc.end.column - endCharsToExclude,
188
+ },
189
+ },
190
+ start: node.start + startCharsToExclude,
191
+ end: node.end - endCharsToExclude,
192
+ tail: node.tail,
193
+ value: {
194
+ cooked: node.cooked,
195
+ raw: node.raw,
196
+ },
197
+ };
198
+ }
199
+
200
+ mapGenericTypeAnnotation(node: HermesNode): HermesNode {
201
+ // Convert simple `this` generic type to ThisTypeAnnotation
202
+ if (
203
+ node.typeParameters == null &&
204
+ node.id.type === 'Identifier' &&
205
+ node.id.name === 'this'
206
+ ) {
207
+ return {
208
+ type: 'ThisTypeAnnotation',
209
+ loc: node.loc,
210
+ start: node.start,
211
+ end: node.end,
212
+ };
213
+ }
214
+
215
+ return this.mapNodeDefault(node);
216
+ }
217
+
218
+ mapSymbolTypeAnnotation(node: HermesNode): HermesNode {
219
+ return {
220
+ type: 'GenericTypeAnnotation',
221
+ loc: node.loc,
222
+ start: node.start,
223
+ end: node.end,
224
+ id: {
225
+ type: 'Identifier',
226
+ loc: node.loc,
227
+ start: node.start,
228
+ end: node.end,
229
+ name: 'symbol',
230
+ },
231
+ typeParameters: null,
232
+ };
233
+ }
234
+
235
+ mapProperty(node: HermesNode): HermesNode {
236
+ const key = this.mapNode(node.key);
237
+ const value = this.mapNode(node.value);
238
+
239
+ // Convert methods, getters, and setters to ObjectMethod nodes
240
+ if (node.method || node.kind !== 'init') {
241
+ // Properties under the FunctionExpression value that should be moved
242
+ // to the ObjectMethod node itself.
243
+ const {
244
+ id,
245
+ params,
246
+ body,
247
+ async,
248
+ generator,
249
+ returnType,
250
+ typeParameters,
251
+ predicate,
252
+ } = value;
253
+
254
+ return {
255
+ type: 'ObjectMethod',
256
+ loc: node.loc,
257
+ start: node.start,
258
+ end: node.end,
259
+ // Non getter or setter methods have `kind = method`
260
+ kind: node.kind === 'init' ? 'method' : node.kind,
261
+ computed: node.computed,
262
+ key,
263
+ id,
264
+ params,
265
+ body,
266
+ async,
267
+ generator,
268
+ returnType,
269
+ typeParameters,
270
+ predicate,
271
+ };
272
+ } else {
273
+ // Non-method property nodes should be renamed to ObjectProperty
274
+ node.type = 'ObjectProperty';
275
+ return node;
276
+ }
277
+ }
278
+
279
+ mapMethodDefinition(node: HermesNode): HermesNode {
280
+ const key = this.mapNode(node.key);
281
+ const value = this.mapNode(node.value);
282
+
283
+ // Properties under the FunctionExpression value that should be moved
284
+ // to the ClassMethod node itself.
285
+ const {
286
+ id,
287
+ params,
288
+ body,
289
+ async,
290
+ generator,
291
+ returnType,
292
+ typeParameters,
293
+ predicate,
294
+ } = value;
295
+
296
+ return {
297
+ type: 'ClassMethod',
298
+ loc: node.loc,
299
+ start: node.start,
300
+ end: node.end,
301
+ kind: node.kind,
302
+ computed: node.computed,
303
+ static: node.static,
304
+ key,
305
+ id,
306
+ params,
307
+ body,
308
+ async,
309
+ generator,
310
+ returnType,
311
+ typeParameters,
312
+ predicate,
313
+ };
314
+ }
315
+
316
+ mapRestElement(node: HermesNode): HermesNode {
317
+ const restElement = this.mapNodeDefault(node);
318
+
319
+ // Hermes puts type annotations on rest elements on the argument node,
320
+ // but Babel expects type annotations on the rest element node itself.
321
+ const annotation = restElement.argument.typeAnnotation;
322
+ if (annotation != null) {
323
+ restElement.typeAnnotation = annotation;
324
+ restElement.argument.typeAnnotation = null;
325
+ }
326
+
327
+ return restElement;
328
+ }
329
+
330
+ mapImportExpression(node: HermesNode): HermesNode {
331
+ // Babel expects ImportExpression to be structued as a regular
332
+ // CallExpression where the callee is an Import node.
333
+ return {
334
+ type: 'CallExpression',
335
+ loc: node.loc,
336
+ start: node.start,
337
+ end: node.end,
338
+ callee: {
339
+ type: 'Import',
340
+ loc: node.loc,
341
+ start: node.start,
342
+ end: node.end,
343
+ },
344
+ arguments: [this.mapNode(node.source)],
345
+ };
346
+ }
347
+
348
+ mapJSXStringLiteral(node: HermesNode): HermesNode {
349
+ // Babel expects StringLiterals in JSX,
350
+ // but Hermes uses JSXStringLiteral to attach the raw value without
351
+ // having to internally attach it to every single string literal.
352
+ return {
353
+ type: 'StringLiteral',
354
+ loc: node.loc,
355
+ start: node.start,
356
+ end: node.end,
357
+ value: node.value,
358
+ };
359
+ }
360
+
361
+ mapFunction(node: HermesNode): HermesNode {
362
+ // Remove the first parameter if it is a this-type annotation,
363
+ // which is not recognized by Babel.
364
+ if (node.params.length !== 0 && node.params[0].name === 'this') {
365
+ node.params.shift();
366
+ }
367
+
368
+ return this.mapNodeDefault(node);
369
+ }
370
+
371
+ /**
372
+ * If Babel (the version we target) does not support a type annotation we
373
+ * parse, we need to return some other valid type annotation in its place.
374
+ */
375
+ mapUnsupportedTypeAnnotation(node: HermesNode): HermesNode {
376
+ return {
377
+ type: 'AnyTypeAnnotation',
378
+ loc: node.loc,
379
+ start: node.start,
380
+ end: node.end,
381
+ };
382
+ }
383
+ }