hermes-transform 0.5.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.
Files changed (74) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +8 -0
  3. package/dist/detachedNode.js +128 -0
  4. package/dist/detachedNode.js.flow +113 -0
  5. package/dist/generated/TransformCloneSignatures.js.flow +19 -0
  6. package/dist/generated/TransformReplaceSignatures.js.flow +943 -0
  7. package/dist/generated/node-types.js +2071 -0
  8. package/dist/generated/node-types.js.flow +3149 -0
  9. package/dist/generated/special-case-node-types.js +178 -0
  10. package/dist/generated/special-case-node-types.js.flow +248 -0
  11. package/dist/getVisitorKeys.js +35 -0
  12. package/dist/getVisitorKeys.js.flow +31 -0
  13. package/dist/index.js +41 -0
  14. package/dist/index.js.flow +15 -0
  15. package/dist/transform/Errors.js +151 -0
  16. package/dist/transform/Errors.js.flow +17 -0
  17. package/dist/transform/MutationContext.js +94 -0
  18. package/dist/transform/MutationContext.js.flow +80 -0
  19. package/dist/transform/TransformContext.js +136 -0
  20. package/dist/transform/TransformContext.js.flow +378 -0
  21. package/dist/transform/comments/comments.js +140 -0
  22. package/dist/transform/comments/comments.js.flow +145 -0
  23. package/dist/transform/comments/prettier/README.md +6 -0
  24. package/dist/transform/comments/prettier/common/util.js +365 -0
  25. package/dist/transform/comments/prettier/common/util.js.flow +349 -0
  26. package/dist/transform/comments/prettier/language-js/comments.js +777 -0
  27. package/dist/transform/comments/prettier/language-js/comments.js.flow +950 -0
  28. package/dist/transform/comments/prettier/language-js/loc.js +41 -0
  29. package/dist/transform/comments/prettier/language-js/loc.js.flow +41 -0
  30. package/dist/transform/comments/prettier/language-js/printer-estree.js +31 -0
  31. package/dist/transform/comments/prettier/language-js/printer-estree.js.flow +37 -0
  32. package/dist/transform/comments/prettier/language-js/utils.js +131 -0
  33. package/dist/transform/comments/prettier/language-js/utils.js.flow +135 -0
  34. package/dist/transform/comments/prettier/main/comments.js +513 -0
  35. package/dist/transform/comments/prettier/main/comments.js.flow +436 -0
  36. package/dist/transform/comments/prettier/utils/get-last.js +15 -0
  37. package/dist/transform/comments/prettier/utils/get-last.js.flow +14 -0
  38. package/dist/transform/getTransformedAST.js +159 -0
  39. package/dist/transform/getTransformedAST.js.flow +128 -0
  40. package/dist/transform/mutations/AddLeadingComments.js +47 -0
  41. package/dist/transform/mutations/AddLeadingComments.js.flow +49 -0
  42. package/dist/transform/mutations/AddTrailingComments.js +47 -0
  43. package/dist/transform/mutations/AddTrailingComments.js.flow +49 -0
  44. package/dist/transform/mutations/CloneCommentsTo.js +46 -0
  45. package/dist/transform/mutations/CloneCommentsTo.js.flow +51 -0
  46. package/dist/transform/mutations/InsertStatement.js +92 -0
  47. package/dist/transform/mutations/InsertStatement.js.flow +113 -0
  48. package/dist/transform/mutations/RemoveComment.js +96 -0
  49. package/dist/transform/mutations/RemoveComment.js.flow +80 -0
  50. package/dist/transform/mutations/RemoveStatement.js +61 -0
  51. package/dist/transform/mutations/RemoveStatement.js.flow +68 -0
  52. package/dist/transform/mutations/ReplaceNode.js +96 -0
  53. package/dist/transform/mutations/ReplaceNode.js.flow +113 -0
  54. package/dist/transform/mutations/ReplaceStatementWithMany.js +81 -0
  55. package/dist/transform/mutations/ReplaceStatementWithMany.js.flow +102 -0
  56. package/dist/transform/mutations/utils/arrayUtils.js +41 -0
  57. package/dist/transform/mutations/utils/arrayUtils.js.flow +35 -0
  58. package/dist/transform/mutations/utils/getStatementParent.js +147 -0
  59. package/dist/transform/mutations/utils/getStatementParent.js.flow +143 -0
  60. package/dist/transform/mutations/utils/isValidModuleDeclarationParent.js +53 -0
  61. package/dist/transform/mutations/utils/isValidModuleDeclarationParent.js.flow +50 -0
  62. package/dist/transform/transform.js +69 -0
  63. package/dist/transform/transform.js.flow +60 -0
  64. package/dist/traverse/NodeEventGenerator.js +427 -0
  65. package/dist/traverse/NodeEventGenerator.js.flow +406 -0
  66. package/dist/traverse/SafeEmitter.js +70 -0
  67. package/dist/traverse/SafeEmitter.js.flow +46 -0
  68. package/dist/traverse/SimpleTraverser.js +149 -0
  69. package/dist/traverse/SimpleTraverser.js.flow +109 -0
  70. package/dist/traverse/esquery.js +37 -0
  71. package/dist/traverse/esquery.js.flow +173 -0
  72. package/dist/traverse/traverse.js +139 -0
  73. package/dist/traverse/traverse.js.flow +149 -0
  74. package/package.json +22 -0
@@ -0,0 +1,406 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its 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 strict-local
8
+ * @format
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ import type {ESNode} from 'hermes-estree';
14
+ import type {ESQueryOptions, Selector} from './esquery';
15
+ import type {SafeEmitter} from './SafeEmitter';
16
+
17
+ import {VisitorKeys} from 'hermes-eslint';
18
+ import * as esquery from './esquery';
19
+
20
+ type ParsedSelector = $ReadOnly<{
21
+ /** The string that was parsed into this selector */
22
+ rawSelector: string,
23
+ /** `true` if this should be emitted when exiting the node rather than when entering */
24
+ isExit: boolean,
25
+ /** An object (from esquery) describing the matching behavior of the selector */
26
+ parsedSelector: Selector,
27
+ /** A list of node types that could possibly cause the selector to match, or `null` if all node types could cause a match */
28
+ listenerTypes: ?Array<ESNode['type']>,
29
+ /** The total number of classes, pseudo-classes, and attribute queries in this selector */
30
+ attributeCount: number,
31
+ /** The total number of identifier queries in this selector */
32
+ identifierCount: number,
33
+ }>;
34
+
35
+ const ESQUERY_OPTIONS: ESQueryOptions = Object.freeze({
36
+ visitorKeys: VisitorKeys,
37
+ fallback: node => {
38
+ throw new Error(`No visitor keys found for node type "${node.type}".`);
39
+ },
40
+ });
41
+
42
+ /**
43
+ * Computes the union of one or more arrays
44
+ * @param arrays One or more arrays to union
45
+ * @returns The union of the input arrays
46
+ */
47
+ function union<T>(...arrays: Array<Array<T>>): Array<T> {
48
+ return [...new Set(arrays.flat())];
49
+ }
50
+
51
+ /**
52
+ * Computes the intersection of one or more arrays
53
+ * @param arrays One or more arrays to intersect
54
+ * @returns The intersection of the input arrays
55
+ */
56
+ function intersection<T>(...arrays: Array<Array<T>>): Array<T> {
57
+ if (arrays.length === 0) {
58
+ return [];
59
+ }
60
+
61
+ let result = [...new Set(arrays[0])];
62
+
63
+ for (const array of arrays.slice(1)) {
64
+ result = result.filter(x => array.includes(x));
65
+ }
66
+ return result;
67
+ }
68
+
69
+ /**
70
+ * Gets the possible types of a selector
71
+ * @param parsedSelector An object (from esquery) describing the matching behavior of the selector
72
+ * @returns The node types that could possibly trigger this selector, or `null` if all node types could trigger it
73
+ */
74
+ function getPossibleTypes(parsedSelector: Selector): ?Array<ESNode['type']> {
75
+ switch (parsedSelector.type) {
76
+ case 'identifier':
77
+ if (!(parsedSelector.value in VisitorKeys)) {
78
+ throw new Error(`Unexpected selector ${parsedSelector.value}`);
79
+ }
80
+ // $FlowExpectedError[incompatible-return]
81
+ return [parsedSelector.value];
82
+
83
+ case 'matches': {
84
+ const typesForComponents = parsedSelector.selectors.map(getPossibleTypes);
85
+ const typesForComponentsNonNull = typesForComponents.filter(Boolean);
86
+
87
+ if (typesForComponents.length === typesForComponentsNonNull.length) {
88
+ return union(...typesForComponentsNonNull);
89
+ }
90
+ return null;
91
+ }
92
+
93
+ case 'compound': {
94
+ const typesForComponents = parsedSelector.selectors
95
+ .map(getPossibleTypes)
96
+ .filter(Boolean);
97
+
98
+ // If all of the components could match any type, then the compound could also match any type.
99
+ if (!typesForComponents.length) {
100
+ return null;
101
+ }
102
+
103
+ /*
104
+ * If at least one of the components could only match a particular type, the compound could only match
105
+ * the intersection of those types.
106
+ */
107
+ return intersection(...typesForComponents);
108
+ }
109
+
110
+ case 'child':
111
+ case 'descendant':
112
+ case 'sibling':
113
+ case 'adjacent':
114
+ return getPossibleTypes(parsedSelector.right);
115
+
116
+ case 'class':
117
+ if (parsedSelector.name === 'function') {
118
+ return [
119
+ 'FunctionDeclaration',
120
+ 'FunctionExpression',
121
+ 'ArrowFunctionExpression',
122
+ ];
123
+ }
124
+
125
+ return null;
126
+
127
+ default:
128
+ return null;
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Counts the number of class, pseudo-class, and attribute queries in this selector
134
+ * @param parsedSelector An object (from esquery) describing the selector's matching behavior
135
+ * @returns The number of class, pseudo-class, and attribute queries in this selector
136
+ */
137
+ function countClassAttributes(parsedSelector: Selector): number {
138
+ switch (parsedSelector.type) {
139
+ case 'child':
140
+ case 'descendant':
141
+ case 'sibling':
142
+ case 'adjacent':
143
+ return (
144
+ countClassAttributes(parsedSelector.left) +
145
+ countClassAttributes(parsedSelector.right)
146
+ );
147
+
148
+ case 'compound':
149
+ case 'not':
150
+ case 'matches':
151
+ return parsedSelector.selectors.reduce(
152
+ (sum, childSelector) => sum + countClassAttributes(childSelector),
153
+ 0,
154
+ );
155
+
156
+ case 'attribute':
157
+ case 'field':
158
+ case 'nth-child':
159
+ case 'nth-last-child':
160
+ return 1;
161
+
162
+ default:
163
+ return 0;
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Counts the number of identifier queries in this selector
169
+ * @param parsedSelector An object (from esquery) describing the selector's matching behavior
170
+ * @returns The number of identifier queries
171
+ */
172
+ function countIdentifiers(parsedSelector: Selector): number {
173
+ switch (parsedSelector.type) {
174
+ case 'child':
175
+ case 'descendant':
176
+ case 'sibling':
177
+ case 'adjacent':
178
+ return (
179
+ countIdentifiers(parsedSelector.left) +
180
+ countIdentifiers(parsedSelector.right)
181
+ );
182
+
183
+ case 'compound':
184
+ case 'not':
185
+ case 'matches':
186
+ return parsedSelector.selectors.reduce(
187
+ (sum, childSelector) => sum + countIdentifiers(childSelector),
188
+ 0,
189
+ );
190
+
191
+ case 'identifier':
192
+ return 1;
193
+
194
+ default:
195
+ return 0;
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Compares the specificity of two selector objects, with CSS-like rules.
201
+ * @param selectorA An AST selector descriptor
202
+ * @param selectorB Another AST selector descriptor
203
+ * @returns
204
+ * a value less than 0 if selectorA is less specific than selectorB
205
+ * a value greater than 0 if selectorA is more specific than selectorB
206
+ * a value less than 0 if selectorA and selectorB have the same specificity, and selectorA <= selectorB alphabetically
207
+ * a value greater than 0 if selectorA and selectorB have the same specificity, and selectorA > selectorB alphabetically
208
+ */
209
+ function compareSpecificity(
210
+ selectorA: ParsedSelector,
211
+ selectorB: ParsedSelector,
212
+ ): number {
213
+ return (
214
+ selectorA.attributeCount - selectorB.attributeCount ||
215
+ selectorA.identifierCount - selectorB.identifierCount ||
216
+ (selectorA.rawSelector <= selectorB.rawSelector ? -1 : 1)
217
+ );
218
+ }
219
+
220
+ /**
221
+ * Parses a raw selector string, and throws a useful error if parsing fails.
222
+ * @param rawSelector A raw AST selector
223
+ * @returns An object (from esquery) describing the matching behavior of this selector
224
+ * @throws An error if the selector is invalid
225
+ */
226
+ function tryParseSelector(rawSelector: string): Selector {
227
+ try {
228
+ return esquery.parse(rawSelector.replace(/:exit$/u, ''));
229
+ } catch (err) {
230
+ if (
231
+ err.location &&
232
+ err.location.start &&
233
+ typeof err.location.start.offset === 'number'
234
+ ) {
235
+ throw new SyntaxError(
236
+ `Syntax error in selector "${rawSelector}" at position ${err.location.start.offset}: ${err.message}`,
237
+ );
238
+ }
239
+ throw err;
240
+ }
241
+ }
242
+
243
+ const selectorCache = new Map<string, ParsedSelector>();
244
+
245
+ /**
246
+ * Parses a raw selector string, and returns the parsed selector along with specificity and type information.
247
+ * @param rawSelector A raw AST selector
248
+ * @returns A selector descriptor
249
+ */
250
+ function parseSelector(rawSelector: string): ParsedSelector {
251
+ const cachedSelector = selectorCache.get(rawSelector);
252
+ if (cachedSelector) {
253
+ return cachedSelector;
254
+ }
255
+
256
+ const parsedSelector = tryParseSelector(rawSelector);
257
+
258
+ const result: ParsedSelector = {
259
+ rawSelector,
260
+ isExit: rawSelector.endsWith(':exit'),
261
+ parsedSelector,
262
+ listenerTypes: getPossibleTypes(parsedSelector),
263
+ attributeCount: countClassAttributes(parsedSelector),
264
+ identifierCount: countIdentifiers(parsedSelector),
265
+ };
266
+
267
+ selectorCache.set(rawSelector, result);
268
+ return result;
269
+ }
270
+
271
+ /**
272
+ * The event generator for AST nodes.
273
+ */
274
+ export class NodeEventGenerator {
275
+ +emitter: SafeEmitter;
276
+ +_currentAncestry: Array<ESNode> = [];
277
+ +_enterSelectorsByNodeType: Map<ESNode['type'], Array<ParsedSelector>> =
278
+ new Map();
279
+ +_exitSelectorsByNodeType: Map<ESNode['type'], Array<ParsedSelector>> =
280
+ new Map();
281
+ +_anyTypeEnterSelectors: Array<ParsedSelector> = [];
282
+ +_anyTypeExitSelectors: Array<ParsedSelector> = [];
283
+
284
+ /**
285
+ * @param emitter
286
+ * An SafeEmitter which is the destination of events. This emitter must already
287
+ * have registered listeners for all of the events that it needs to listen for.
288
+ * (See lib/linter/safe-emitter.js for more details on `SafeEmitter`.)
289
+ */
290
+ constructor(emitter: SafeEmitter) {
291
+ this.emitter = emitter;
292
+
293
+ emitter.eventNames().forEach(rawSelector => {
294
+ const selector = parseSelector(rawSelector);
295
+
296
+ if (selector.listenerTypes) {
297
+ const typeMap = selector.isExit
298
+ ? this._exitSelectorsByNodeType
299
+ : this._enterSelectorsByNodeType;
300
+
301
+ selector.listenerTypes.forEach(nodeType => {
302
+ const selectors = typeMap.get(nodeType);
303
+ if (!selectors) {
304
+ typeMap.set(nodeType, [selector]);
305
+ } else {
306
+ selectors.push(selector);
307
+ }
308
+ });
309
+ } else {
310
+ const selectors = selector.isExit
311
+ ? this._anyTypeExitSelectors
312
+ : this._anyTypeEnterSelectors;
313
+
314
+ selectors.push(selector);
315
+ }
316
+ });
317
+
318
+ this._anyTypeEnterSelectors.sort(compareSpecificity);
319
+ this._anyTypeExitSelectors.sort(compareSpecificity);
320
+ this._enterSelectorsByNodeType.forEach(selectorList =>
321
+ selectorList.sort(compareSpecificity),
322
+ );
323
+ this._exitSelectorsByNodeType.forEach(selectorList =>
324
+ selectorList.sort(compareSpecificity),
325
+ );
326
+ }
327
+
328
+ /**
329
+ * Checks a selector against a node, and emits it if it matches
330
+ * @param node The node to check
331
+ * @param selector An AST selector descriptor
332
+ * @private
333
+ */
334
+ _applySelector(node: ESNode, selector: ParsedSelector): void {
335
+ if (
336
+ esquery.matches(
337
+ node,
338
+ selector.parsedSelector,
339
+ this._currentAncestry,
340
+ ESQUERY_OPTIONS,
341
+ )
342
+ ) {
343
+ this.emitter.emit(selector.rawSelector, node);
344
+ }
345
+ }
346
+
347
+ /**
348
+ * Applies all appropriate selectors to a node, in specificity order
349
+ * @param node The node to check
350
+ * @param isExit `false` if the node is currently being entered, `true` if it's currently being exited
351
+ * @private
352
+ */
353
+ _applySelectors(node: ESNode, isExit: boolean): void {
354
+ const selectorsByNodeType =
355
+ (isExit
356
+ ? this._exitSelectorsByNodeType
357
+ : this._enterSelectorsByNodeType
358
+ ).get(node.type) || [];
359
+ const anyTypeSelectors = isExit
360
+ ? this._anyTypeExitSelectors
361
+ : this._anyTypeEnterSelectors;
362
+
363
+ /*
364
+ * selectorsByNodeType and anyTypeSelectors were already sorted by specificity in the constructor.
365
+ * Iterate through each of them, applying selectors in the right order.
366
+ */
367
+ let selectorsByTypeIndex = 0;
368
+ let anyTypeSelectorsIndex = 0;
369
+
370
+ while (
371
+ selectorsByTypeIndex < selectorsByNodeType.length ||
372
+ anyTypeSelectorsIndex < anyTypeSelectors.length
373
+ ) {
374
+ if (
375
+ selectorsByTypeIndex >= selectorsByNodeType.length ||
376
+ (anyTypeSelectorsIndex < anyTypeSelectors.length &&
377
+ compareSpecificity(
378
+ anyTypeSelectors[anyTypeSelectorsIndex],
379
+ selectorsByNodeType[selectorsByTypeIndex],
380
+ ) < 0)
381
+ ) {
382
+ this._applySelector(node, anyTypeSelectors[anyTypeSelectorsIndex++]);
383
+ } else {
384
+ this._applySelector(node, selectorsByNodeType[selectorsByTypeIndex++]);
385
+ }
386
+ }
387
+ }
388
+
389
+ /**
390
+ * Emits an event of entering AST node.
391
+ * @param node A node which was entered.
392
+ */
393
+ enterNode(node: ESNode): void {
394
+ this._applySelectors(node, false);
395
+ this._currentAncestry.unshift(node);
396
+ }
397
+
398
+ /**
399
+ * Emits an event of leaving AST node.
400
+ * @param node A node which was left.
401
+ */
402
+ leaveNode(node: ESNode): void {
403
+ this._currentAncestry.shift();
404
+ this._applySelectors(node, true);
405
+ }
406
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its 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
+ *
8
+ * @format
9
+ */
10
+ 'use strict';
11
+
12
+ Object.defineProperty(exports, "__esModule", {
13
+ value: true
14
+ });
15
+ exports.SafeEmitter = void 0;
16
+
17
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
18
+
19
+ function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
20
+
21
+ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
22
+
23
+ function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
24
+
25
+ /**
26
+ * Creates an object which can listen for and emit events.
27
+ * This is similar to the EventEmitter API in Node's standard library, but it has a few differences.
28
+ * The goal is to allow multiple modules to attach arbitrary listeners to the same emitter, without
29
+ * letting the modules know about each other at all.
30
+ * 1. It has no special keys like `error` and `newListener`, which would allow modules to detect when
31
+ * another module throws an error or registers a listener.
32
+ * 2. It calls listener functions without any `this` value. (`EventEmitter` calls listeners with a
33
+ * `this` value of the emitter instance, which would give listeners access to other listeners.)
34
+ */
35
+ var SafeEmitter = /*#__PURE__*/function () {
36
+ function SafeEmitter() {
37
+ _classCallCheck(this, SafeEmitter);
38
+
39
+ _defineProperty(this, "listeners", Object.create(null));
40
+ }
41
+
42
+ _createClass(SafeEmitter, [{
43
+ key: "on",
44
+ value: function on(eventName, listener) {
45
+ if (eventName in this.listeners) {
46
+ this.listeners[eventName].push(listener);
47
+ } else {
48
+ this.listeners[eventName] = [listener];
49
+ }
50
+ }
51
+ }, {
52
+ key: "emit",
53
+ value: function emit(eventName, node) {
54
+ if (eventName in this.listeners) {
55
+ this.listeners[eventName].forEach(function (listener) {
56
+ return listener(node);
57
+ });
58
+ }
59
+ }
60
+ }, {
61
+ key: "eventNames",
62
+ value: function eventNames() {
63
+ return Object.keys(this.listeners);
64
+ }
65
+ }]);
66
+
67
+ return SafeEmitter;
68
+ }();
69
+
70
+ exports.SafeEmitter = SafeEmitter;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its 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 strict
8
+ * @format
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ import type {ESNode} from 'hermes-estree';
14
+
15
+ export type EmitterListener = (node: ESNode) => void;
16
+
17
+ /**
18
+ * Creates an object which can listen for and emit events.
19
+ * This is similar to the EventEmitter API in Node's standard library, but it has a few differences.
20
+ * The goal is to allow multiple modules to attach arbitrary listeners to the same emitter, without
21
+ * letting the modules know about each other at all.
22
+ * 1. It has no special keys like `error` and `newListener`, which would allow modules to detect when
23
+ * another module throws an error or registers a listener.
24
+ * 2. It calls listener functions without any `this` value. (`EventEmitter` calls listeners with a
25
+ * `this` value of the emitter instance, which would give listeners access to other listeners.)
26
+ */
27
+ export class SafeEmitter {
28
+ // $FlowExpectedError[incompatible-type] - Object.create is always typed as returning `mixed`
29
+ +listeners: {[string]: Array<EmitterListener>} = Object.create(null);
30
+
31
+ on(eventName: string, listener: EmitterListener): void {
32
+ if (eventName in this.listeners) {
33
+ this.listeners[eventName].push(listener);
34
+ } else {
35
+ this.listeners[eventName] = [listener];
36
+ }
37
+ }
38
+ emit(eventName: string, node: ESNode): void {
39
+ if (eventName in this.listeners) {
40
+ this.listeners[eventName].forEach(listener => listener(node));
41
+ }
42
+ }
43
+ eventNames(): Array<string> {
44
+ return Object.keys(this.listeners);
45
+ }
46
+ }
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its 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
+ *
8
+ * @format
9
+ */
10
+ 'use strict';
11
+
12
+ Object.defineProperty(exports, "__esModule", {
13
+ value: true
14
+ });
15
+ exports.SimpleTraverserSkip = exports.SimpleTraverserBreak = exports.SimpleTraverser = void 0;
16
+
17
+ var _getVisitorKeys = require("../getVisitorKeys");
18
+
19
+ function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
20
+
21
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
22
+
23
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
24
+
25
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
26
+
27
+ function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
28
+
29
+ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
30
+
31
+ /**
32
+ * Can be thrown within the traversal "enter" function to prevent the traverser
33
+ * from traversing the node any further, essentially culling the remainder of the
34
+ * AST branch
35
+ */
36
+ var SimpleTraverserSkip = new Error();
37
+ /**
38
+ * Can be thrown at any point during the traversal to immediately stop traversal
39
+ * entirely.
40
+ */
41
+
42
+ exports.SimpleTraverserSkip = SimpleTraverserSkip;
43
+ var SimpleTraverserBreak = new Error();
44
+ /**
45
+ * A very simple traverser class to traverse AST trees.
46
+ */
47
+
48
+ exports.SimpleTraverserBreak = SimpleTraverserBreak;
49
+
50
+ var SimpleTraverser = /*#__PURE__*/function () {
51
+ function SimpleTraverser() {
52
+ _classCallCheck(this, SimpleTraverser);
53
+ }
54
+
55
+ _createClass(SimpleTraverser, [{
56
+ key: "traverse",
57
+ value:
58
+ /**
59
+ * Traverse the given AST tree.
60
+ * @param node The root node to traverse.
61
+ * @param options The option object.
62
+ */
63
+ function traverse(node, options) {
64
+ try {
65
+ this._traverse(node, null, options);
66
+ } catch (ex) {
67
+ if (ex === SimpleTraverserBreak) {
68
+ return;
69
+ }
70
+
71
+ throw ex;
72
+ }
73
+ }
74
+ /**
75
+ * Traverse the given AST tree recursively.
76
+ * @param node The current node.
77
+ * @param parent The parent node.
78
+ * @private
79
+ */
80
+
81
+ }, {
82
+ key: "_traverse",
83
+ value: function _traverse(node, parent, options) {
84
+ if (!(0, _getVisitorKeys.isNode)(node)) {
85
+ return;
86
+ }
87
+
88
+ try {
89
+ options.enter(node, parent);
90
+ } catch (ex) {
91
+ if (ex === SimpleTraverserSkip) {
92
+ return;
93
+ }
94
+
95
+ throw ex;
96
+ }
97
+
98
+ var keys = (0, _getVisitorKeys.getVisitorKeys)(node);
99
+
100
+ var _iterator = _createForOfIteratorHelper(keys),
101
+ _step;
102
+
103
+ try {
104
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
105
+ var key = _step.value;
106
+ // $FlowExpectedError[prop-missing]
107
+ var child = node[key];
108
+
109
+ if (Array.isArray(child)) {
110
+ for (var j = 0; j < child.length; ++j) {
111
+ this._traverse(child[j], node, options);
112
+ }
113
+ } else {
114
+ this._traverse(child, node, options);
115
+ }
116
+ }
117
+ } catch (err) {
118
+ _iterator.e(err);
119
+ } finally {
120
+ _iterator.f();
121
+ }
122
+
123
+ try {
124
+ options.leave(node, parent);
125
+ } catch (ex) {
126
+ if (ex === SimpleTraverserSkip) {
127
+ return;
128
+ }
129
+
130
+ throw ex;
131
+ }
132
+ }
133
+ /**
134
+ * Traverse the given AST tree.
135
+ * @param node The root node to traverse.
136
+ * @param options The option object.
137
+ */
138
+
139
+ }], [{
140
+ key: "traverse",
141
+ value: function traverse(node, options) {
142
+ new SimpleTraverser().traverse(node, options);
143
+ }
144
+ }]);
145
+
146
+ return SimpleTraverser;
147
+ }();
148
+
149
+ exports.SimpleTraverser = SimpleTraverser;