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,109 @@
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
+
15
+ import {getVisitorKeys, isNode} from '../getVisitorKeys';
16
+
17
+ export type TraverserCallback = (node: ESNode, parent: ?ESNode) => void;
18
+ export type TraverserOptions = $ReadOnly<{
19
+ /** The callback function which is called on entering each node. */
20
+ enter: TraverserCallback,
21
+ /** The callback function which is called on leaving each node. */
22
+ leave: TraverserCallback,
23
+ }>;
24
+
25
+ /**
26
+ * Can be thrown within the traversal "enter" function to prevent the traverser
27
+ * from traversing the node any further, essentially culling the remainder of the
28
+ * AST branch
29
+ */
30
+ export const SimpleTraverserSkip: Error = new Error();
31
+ /**
32
+ * Can be thrown at any point during the traversal to immediately stop traversal
33
+ * entirely.
34
+ */
35
+ export const SimpleTraverserBreak: Error = new Error();
36
+
37
+ /**
38
+ * A very simple traverser class to traverse AST trees.
39
+ */
40
+ export class SimpleTraverser {
41
+ /**
42
+ * Traverse the given AST tree.
43
+ * @param node The root node to traverse.
44
+ * @param options The option object.
45
+ */
46
+ traverse(node: ESNode, options: TraverserOptions): void {
47
+ try {
48
+ this._traverse(node, null, options);
49
+ } catch (ex) {
50
+ if (ex === SimpleTraverserBreak) {
51
+ return;
52
+ }
53
+ throw ex;
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Traverse the given AST tree recursively.
59
+ * @param node The current node.
60
+ * @param parent The parent node.
61
+ * @private
62
+ */
63
+ _traverse(node: ESNode, parent: ?ESNode, options: TraverserOptions): void {
64
+ if (!isNode(node)) {
65
+ return;
66
+ }
67
+
68
+ try {
69
+ options.enter(node, parent);
70
+ } catch (ex) {
71
+ if (ex === SimpleTraverserSkip) {
72
+ return;
73
+ }
74
+ throw ex;
75
+ }
76
+
77
+ const keys = getVisitorKeys(node);
78
+ for (const key of keys) {
79
+ // $FlowExpectedError[prop-missing]
80
+ const child = node[key];
81
+
82
+ if (Array.isArray(child)) {
83
+ for (let j = 0; j < child.length; ++j) {
84
+ this._traverse(child[j], node, options);
85
+ }
86
+ } else {
87
+ this._traverse(child, node, options);
88
+ }
89
+ }
90
+
91
+ try {
92
+ options.leave(node, parent);
93
+ } catch (ex) {
94
+ if (ex === SimpleTraverserSkip) {
95
+ return;
96
+ }
97
+ throw ex;
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Traverse the given AST tree.
103
+ * @param node The root node to traverse.
104
+ * @param options The option object.
105
+ */
106
+ static traverse(node: ESNode, options: TraverserOptions) {
107
+ new SimpleTraverser().traverse(node, options);
108
+ }
109
+ }
@@ -0,0 +1,37 @@
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.query = exports.parse = exports.matches = exports.match = void 0;
16
+
17
+ var _esquery = _interopRequireDefault(require("esquery"));
18
+
19
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
20
+
21
+ // $FlowExpectedError[cannot-resolve-module]
22
+
23
+ /** Parse a selector and return its AST. */
24
+ var parse = _esquery["default"].parse;
25
+ /** From a JS AST and a selector AST, collect all JS AST nodes that match the selector. */
26
+
27
+ exports.parse = parse;
28
+ var match = _esquery["default"].match;
29
+ /** Given a `node` and its ancestors, determine if `node` is matched by `selector`. */
30
+
31
+ exports.match = match;
32
+ var matches = _esquery["default"].matches;
33
+ /** Query the code AST using the selector string. */
34
+
35
+ exports.matches = matches;
36
+ var query = _esquery["default"].query;
37
+ exports.query = query;
@@ -0,0 +1,173 @@
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
+ // $FlowExpectedError[cannot-resolve-module]
15
+ import esquery from 'esquery';
16
+
17
+ //
18
+ // Base Atoms
19
+ //
20
+ interface Atom {
21
+ // type: string;
22
+ }
23
+ interface SubjectSelectorAtom extends Atom {
24
+ subject?: ?boolean;
25
+ }
26
+ interface NthSelectorAtom extends SubjectSelectorAtom {
27
+ index: NumericLiteralAtom;
28
+ }
29
+ interface BinarySelectorAtom extends SubjectSelectorAtom {
30
+ // type: 'child' | 'sibling' | 'adjacent' | 'descendant';
31
+ left: SubjectSelector;
32
+ right: SubjectSelector;
33
+ }
34
+ interface MultiSelectorAtom extends SubjectSelectorAtom {
35
+ selectors: Array<SubjectSelector>;
36
+ }
37
+ interface LiteralAtom extends Atom {
38
+ type: 'literal';
39
+ // value: string | number;
40
+ }
41
+
42
+ //
43
+ // Literals
44
+ //
45
+ interface StringLiteralAtom extends LiteralAtom {
46
+ value: string;
47
+ }
48
+ interface NumericLiteralAtom extends LiteralAtom {
49
+ value: number;
50
+ }
51
+ interface RegExpLiteralAtom extends Atom {
52
+ type: 'regexp';
53
+ value: RegExp;
54
+ }
55
+
56
+ //
57
+ // Atoms
58
+ //
59
+ interface FieldAtom extends Atom {
60
+ type: 'field';
61
+ name: string;
62
+ }
63
+ interface TypeAtom extends Atom {
64
+ type: 'type';
65
+ value: string;
66
+ }
67
+ interface SequenceAtom extends MultiSelectorAtom {
68
+ type: 'compound';
69
+ }
70
+ interface IdentifierAtom extends SubjectSelectorAtom {
71
+ type: 'identifier';
72
+ value: string;
73
+ }
74
+ interface WildcardAtom extends SubjectSelectorAtom {
75
+ type: 'wildcard';
76
+ value: '*';
77
+ }
78
+ interface AttributeAtom extends SubjectSelectorAtom {
79
+ type: 'attribute';
80
+ name: string;
81
+ operator?: ?('=' | '!=' | '>' | '<' | '>=' | '<=');
82
+ value?: ?(Literal | RegExpLiteralAtom | TypeAtom);
83
+ }
84
+ interface NthChildAtom extends NthSelectorAtom {
85
+ type: 'nth-child';
86
+ }
87
+ interface NthLastChildAtom extends NthSelectorAtom {
88
+ type: 'nth-last-child';
89
+ }
90
+ interface DescendantAtom extends BinarySelectorAtom {
91
+ type: 'descendant';
92
+ }
93
+ interface ChildAtom extends BinarySelectorAtom {
94
+ type: 'child';
95
+ }
96
+ interface SiblingAtom extends BinarySelectorAtom {
97
+ type: 'sibling';
98
+ }
99
+ interface AdjacentAtom extends BinarySelectorAtom {
100
+ type: 'adjacent';
101
+ }
102
+ interface NegationAtom extends MultiSelectorAtom {
103
+ type: 'not';
104
+ }
105
+ interface MatchesAtom extends MultiSelectorAtom {
106
+ type: 'matches';
107
+ }
108
+ interface HasAtom extends MultiSelectorAtom {
109
+ type: 'has';
110
+ }
111
+ interface ClassAtom extends Atom {
112
+ type: 'class';
113
+ name: 'declaration' | 'expression' | 'function' | 'pattern' | 'statement';
114
+ }
115
+
116
+ //
117
+ // Unions
118
+ //
119
+ export type Selector =
120
+ | FieldAtom
121
+ | TypeAtom
122
+ | SequenceAtom
123
+ | IdentifierAtom
124
+ | WildcardAtom
125
+ | AttributeAtom
126
+ | NthChildAtom
127
+ | NthLastChildAtom
128
+ | DescendantAtom
129
+ | ChildAtom
130
+ | SiblingAtom
131
+ | AdjacentAtom
132
+ | NegationAtom
133
+ | MatchesAtom
134
+ | HasAtom
135
+ | ClassAtom;
136
+ type MultiSelector = SequenceAtom | NegationAtom | MatchesAtom | HasAtom;
137
+ type BinarySelector = DescendantAtom | ChildAtom | SiblingAtom | AdjacentAtom;
138
+ type NthSelector = NthChildAtom | NthLastChildAtom;
139
+ type SubjectSelector =
140
+ | NthSelector
141
+ | BinarySelector
142
+ | MultiSelector
143
+ | IdentifierAtom
144
+ | WildcardAtom
145
+ | AttributeAtom;
146
+ type Literal = StringLiteralAtom | NumericLiteralAtom;
147
+
148
+ export type ESQueryOptions = $ReadOnly<{
149
+ visitorKeys?: $ReadOnly<{[string]: $ReadOnlyArray<string>}>,
150
+ fallback?: (node: ESNode) => $ReadOnlyArray<string>,
151
+ }>;
152
+
153
+ /** Parse a selector and return its AST. */
154
+ export const parse = (esquery.parse: (selector: string) => Selector);
155
+ /** From a JS AST and a selector AST, collect all JS AST nodes that match the selector. */
156
+ export const match = (esquery.match: (
157
+ ast: ESNode,
158
+ selector: ?Selector,
159
+ options?: ESQueryOptions,
160
+ ) => Array<ESNode>);
161
+ /** Given a `node` and its ancestors, determine if `node` is matched by `selector`. */
162
+ export const matches = (esquery.matches: (
163
+ node: ?ESNode,
164
+ selector: ?Selector,
165
+ ancestry?: Array<ESNode>,
166
+ options?: ESQueryOptions,
167
+ ) => boolean);
168
+ /** Query the code AST using the selector string. */
169
+ export const query = (esquery.query: (
170
+ ast: ESNode,
171
+ selector: string,
172
+ options?: ESQueryOptions,
173
+ ) => Array<ESNode>);
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.traverse = traverse;
7
+ exports.traverseWithContext = traverseWithContext;
8
+
9
+ var _NodeEventGenerator = require("./NodeEventGenerator");
10
+
11
+ var _SafeEmitter = require("./SafeEmitter");
12
+
13
+ var _SimpleTraverser = require("./SimpleTraverser");
14
+
15
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
16
+
17
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
18
+
19
+ 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; }
20
+
21
+ 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; } } }; }
22
+
23
+ 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); }
24
+
25
+ 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; }
26
+
27
+ /**
28
+ * Traverse the AST with additional context members provided by `additionalContext`.
29
+ * @param ast the ESTree AST to traverse
30
+ * @param scopeManager the eslint-scope compatible scope manager instance calculated using the ast
31
+ * @param additionalContext a callback function which returns additional context members to add to the context provided to the visitor
32
+ */
33
+ function traverseWithContext(ast, scopeManager, additionalContext, visitor) {
34
+ var emitter = new _SafeEmitter.SafeEmitter();
35
+ var nodeQueue = [];
36
+ var currentNode = ast; // set parent pointers and build up the traversal queue
37
+
38
+ _SimpleTraverser.SimpleTraverser.traverse(ast, {
39
+ enter: function enter(node, parent) {
40
+ // $FlowExpectedError[cannot-write] - hermes doesn't set this
41
+ node.parent = parent;
42
+ nodeQueue.push({
43
+ isEntering: true,
44
+ node: node
45
+ });
46
+ },
47
+ leave: function leave(node) {
48
+ nodeQueue.push({
49
+ isEntering: false,
50
+ node: node
51
+ });
52
+ }
53
+ });
54
+
55
+ var getScope = function getScope() {
56
+ var givenNode = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : currentNode;
57
+ // On Program node, get the outermost scope to avoid return Node.js special function scope or ES modules scope.
58
+ var inner = givenNode.type !== 'Program';
59
+
60
+ for (var _node = givenNode; _node; _node = _node.parent) {
61
+ var scope = scopeManager.acquire(_node, inner);
62
+
63
+ if (scope) {
64
+ if (scope.type === 'function-expression-name') {
65
+ return scope.childScopes[0];
66
+ }
67
+
68
+ return scope;
69
+ }
70
+ }
71
+
72
+ return scopeManager.scopes[0];
73
+ };
74
+
75
+ var traversalContextBase = Object.freeze({
76
+ getDeclaredVariables: function getDeclaredVariables(node) {
77
+ return scopeManager.getDeclaredVariables(node);
78
+ },
79
+ getBinding: function getBinding(name) {
80
+ var currentScope = getScope();
81
+
82
+ while (currentScope != null) {
83
+ var _iterator = _createForOfIteratorHelper(currentScope.variables),
84
+ _step;
85
+
86
+ try {
87
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
88
+ var variable = _step.value;
89
+
90
+ if (variable.defs.length && variable.name === name) {
91
+ return variable;
92
+ }
93
+ }
94
+ } catch (err) {
95
+ _iterator.e(err);
96
+ } finally {
97
+ _iterator.f();
98
+ }
99
+
100
+ currentScope = currentScope.upper;
101
+ }
102
+
103
+ return null;
104
+ },
105
+ getScope: getScope
106
+ });
107
+ var traversalContext = Object.freeze(_objectSpread(_objectSpread({}, traversalContextBase), additionalContext(traversalContextBase)));
108
+ var selectors = visitor(traversalContext); // add all the selectors from the visitor as listeners
109
+
110
+ Object.keys(selectors).forEach(function (selector) {
111
+ // flow doesn't want us to be general here - but it's safe
112
+ // $FlowExpectedError[incompatible-type]
113
+ // $FlowExpectedError[prop-missing]
114
+ var listener = selectors[selector];
115
+
116
+ if (listener) {
117
+ emitter.on(selector, listener);
118
+ }
119
+ });
120
+ var eventGenerator = new _NodeEventGenerator.NodeEventGenerator(emitter);
121
+ nodeQueue.forEach(function (traversalInfo) {
122
+ currentNode = traversalInfo.node;
123
+
124
+ try {
125
+ if (traversalInfo.isEntering) {
126
+ eventGenerator.enterNode(currentNode);
127
+ } else {
128
+ eventGenerator.leaveNode(currentNode);
129
+ }
130
+ } catch (err) {
131
+ err.currentNode = currentNode;
132
+ throw err;
133
+ }
134
+ });
135
+ }
136
+
137
+ function traverse(ast, scopeManager, visitor) {
138
+ traverseWithContext(ast, scopeManager, function () {}, visitor);
139
+ }
@@ -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
+ * @flow strict-local
8
+ * @format
9
+ */
10
+
11
+ import type {ESNode, ESQueryNodeSelectors, Program} from 'hermes-estree';
12
+ import type {ScopeManager, Scope, Variable} from 'hermes-eslint';
13
+ import type {EmitterListener} from './SafeEmitter';
14
+
15
+ import {NodeEventGenerator} from './NodeEventGenerator';
16
+ import {SafeEmitter} from './SafeEmitter';
17
+ import {SimpleTraverser} from './SimpleTraverser';
18
+
19
+ export type TraversalContext<T = void> = $ReadOnly<{
20
+ /**
21
+ * Gets the variables that were declared by the given node.
22
+ */
23
+ getDeclaredVariables: (node: ESNode) => $ReadOnlyArray<Variable>,
24
+ /**
25
+ * Gets the nearest variable declared for the given name declare in the
26
+ * current or an upper scope.
27
+ */
28
+ getBinding: (name: string) => ?Variable,
29
+ /**
30
+ * Gets the scope for the given node.
31
+ * Defaults to the currently traversed node.
32
+ */
33
+ getScope: (node?: ESNode) => Scope,
34
+
35
+ ...T,
36
+ }>;
37
+
38
+ export type Visitor<T = void> = (
39
+ context: TraversalContext<T>,
40
+ ) => ESQueryNodeSelectors;
41
+
42
+ /**
43
+ * Traverse the AST with additional context members provided by `additionalContext`.
44
+ * @param ast the ESTree AST to traverse
45
+ * @param scopeManager the eslint-scope compatible scope manager instance calculated using the ast
46
+ * @param additionalContext a callback function which returns additional context members to add to the context provided to the visitor
47
+ */
48
+ export function traverseWithContext<T = void>(
49
+ ast: Program,
50
+ scopeManager: ScopeManager,
51
+ additionalContext: (TraversalContext<void>) => T,
52
+ visitor: Visitor<T>,
53
+ ): void {
54
+ const emitter = new SafeEmitter();
55
+ const nodeQueue: Array<{isEntering: boolean, node: ESNode}> = [];
56
+
57
+ let currentNode: ESNode = ast;
58
+
59
+ // set parent pointers and build up the traversal queue
60
+ SimpleTraverser.traverse(ast, {
61
+ enter(node, parent) {
62
+ // $FlowExpectedError[cannot-write] - hermes doesn't set this
63
+ node.parent = parent;
64
+ nodeQueue.push({isEntering: true, node});
65
+ },
66
+ leave(node) {
67
+ nodeQueue.push({isEntering: false, node});
68
+ },
69
+ });
70
+
71
+ const getScope = (givenNode: ESNode = currentNode) => {
72
+ // On Program node, get the outermost scope to avoid return Node.js special function scope or ES modules scope.
73
+ const inner = givenNode.type !== 'Program';
74
+
75
+ for (let node = givenNode; node; node = node.parent) {
76
+ const scope = scopeManager.acquire(node, inner);
77
+
78
+ if (scope) {
79
+ if (scope.type === 'function-expression-name') {
80
+ return scope.childScopes[0];
81
+ }
82
+ return scope;
83
+ }
84
+ }
85
+
86
+ return scopeManager.scopes[0];
87
+ };
88
+
89
+ const traversalContextBase: TraversalContext<void> = Object.freeze({
90
+ getDeclaredVariables: (node: ESNode) =>
91
+ scopeManager.getDeclaredVariables(node),
92
+ getBinding: (name: string) => {
93
+ let currentScope = getScope();
94
+
95
+ while (currentScope != null) {
96
+ for (const variable of currentScope.variables) {
97
+ if (variable.defs.length && variable.name === name) {
98
+ return variable;
99
+ }
100
+ }
101
+ currentScope = currentScope.upper;
102
+ }
103
+
104
+ return null;
105
+ },
106
+ getScope,
107
+ });
108
+ const traversalContext: TraversalContext<T> = Object.freeze({
109
+ ...traversalContextBase,
110
+ ...additionalContext(traversalContextBase),
111
+ });
112
+
113
+ const selectors = visitor(traversalContext);
114
+
115
+ // add all the selectors from the visitor as listeners
116
+ Object.keys(selectors).forEach(selector => {
117
+ // flow doesn't want us to be general here - but it's safe
118
+ // $FlowExpectedError[incompatible-type]
119
+ // $FlowExpectedError[prop-missing]
120
+ const listener: ?EmitterListener = selectors[selector];
121
+ if (listener) {
122
+ emitter.on(selector, listener);
123
+ }
124
+ });
125
+
126
+ const eventGenerator = new NodeEventGenerator(emitter);
127
+ nodeQueue.forEach(traversalInfo => {
128
+ currentNode = traversalInfo.node;
129
+
130
+ try {
131
+ if (traversalInfo.isEntering) {
132
+ eventGenerator.enterNode(currentNode);
133
+ } else {
134
+ eventGenerator.leaveNode(currentNode);
135
+ }
136
+ } catch (err) {
137
+ err.currentNode = currentNode;
138
+ throw err;
139
+ }
140
+ });
141
+ }
142
+
143
+ export function traverse(
144
+ ast: Program,
145
+ scopeManager: ScopeManager,
146
+ visitor: Visitor<void>,
147
+ ): void {
148
+ traverseWithContext(ast, scopeManager, () => {}, visitor);
149
+ }
package/package.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "hermes-transform",
3
+ "version": "0.5.0",
4
+ "description": "Tools built on top of Hermes-ESTree to enable codebase transformation",
5
+ "main": "dist/index.js",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git@github.com:facebook/hermes.git"
10
+ },
11
+ "dependencies": {
12
+ "esquery": "^1.4.0",
13
+ "hermes-eslint": "0.5.0",
14
+ "hermes-estree": "0.5.0"
15
+ },
16
+ "peerDependencies": {
17
+ "prettier": "^2.4.1"
18
+ },
19
+ "files": [
20
+ "dist"
21
+ ]
22
+ }