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.
- package/LICENSE +21 -0
- package/README.md +8 -0
- package/dist/detachedNode.js +128 -0
- package/dist/detachedNode.js.flow +113 -0
- package/dist/generated/TransformCloneSignatures.js.flow +19 -0
- package/dist/generated/TransformReplaceSignatures.js.flow +943 -0
- package/dist/generated/node-types.js +2071 -0
- package/dist/generated/node-types.js.flow +3149 -0
- package/dist/generated/special-case-node-types.js +178 -0
- package/dist/generated/special-case-node-types.js.flow +248 -0
- package/dist/getVisitorKeys.js +35 -0
- package/dist/getVisitorKeys.js.flow +31 -0
- package/dist/index.js +41 -0
- package/dist/index.js.flow +15 -0
- package/dist/transform/Errors.js +151 -0
- package/dist/transform/Errors.js.flow +17 -0
- package/dist/transform/MutationContext.js +94 -0
- package/dist/transform/MutationContext.js.flow +80 -0
- package/dist/transform/TransformContext.js +136 -0
- package/dist/transform/TransformContext.js.flow +378 -0
- package/dist/transform/comments/comments.js +140 -0
- package/dist/transform/comments/comments.js.flow +145 -0
- package/dist/transform/comments/prettier/README.md +6 -0
- package/dist/transform/comments/prettier/common/util.js +365 -0
- package/dist/transform/comments/prettier/common/util.js.flow +349 -0
- package/dist/transform/comments/prettier/language-js/comments.js +777 -0
- package/dist/transform/comments/prettier/language-js/comments.js.flow +950 -0
- package/dist/transform/comments/prettier/language-js/loc.js +41 -0
- package/dist/transform/comments/prettier/language-js/loc.js.flow +41 -0
- package/dist/transform/comments/prettier/language-js/printer-estree.js +31 -0
- package/dist/transform/comments/prettier/language-js/printer-estree.js.flow +37 -0
- package/dist/transform/comments/prettier/language-js/utils.js +131 -0
- package/dist/transform/comments/prettier/language-js/utils.js.flow +135 -0
- package/dist/transform/comments/prettier/main/comments.js +513 -0
- package/dist/transform/comments/prettier/main/comments.js.flow +436 -0
- package/dist/transform/comments/prettier/utils/get-last.js +15 -0
- package/dist/transform/comments/prettier/utils/get-last.js.flow +14 -0
- package/dist/transform/getTransformedAST.js +159 -0
- package/dist/transform/getTransformedAST.js.flow +128 -0
- package/dist/transform/mutations/AddLeadingComments.js +47 -0
- package/dist/transform/mutations/AddLeadingComments.js.flow +49 -0
- package/dist/transform/mutations/AddTrailingComments.js +47 -0
- package/dist/transform/mutations/AddTrailingComments.js.flow +49 -0
- package/dist/transform/mutations/CloneCommentsTo.js +46 -0
- package/dist/transform/mutations/CloneCommentsTo.js.flow +51 -0
- package/dist/transform/mutations/InsertStatement.js +92 -0
- package/dist/transform/mutations/InsertStatement.js.flow +113 -0
- package/dist/transform/mutations/RemoveComment.js +96 -0
- package/dist/transform/mutations/RemoveComment.js.flow +80 -0
- package/dist/transform/mutations/RemoveStatement.js +61 -0
- package/dist/transform/mutations/RemoveStatement.js.flow +68 -0
- package/dist/transform/mutations/ReplaceNode.js +96 -0
- package/dist/transform/mutations/ReplaceNode.js.flow +113 -0
- package/dist/transform/mutations/ReplaceStatementWithMany.js +81 -0
- package/dist/transform/mutations/ReplaceStatementWithMany.js.flow +102 -0
- package/dist/transform/mutations/utils/arrayUtils.js +41 -0
- package/dist/transform/mutations/utils/arrayUtils.js.flow +35 -0
- package/dist/transform/mutations/utils/getStatementParent.js +147 -0
- package/dist/transform/mutations/utils/getStatementParent.js.flow +143 -0
- package/dist/transform/mutations/utils/isValidModuleDeclarationParent.js +53 -0
- package/dist/transform/mutations/utils/isValidModuleDeclarationParent.js.flow +50 -0
- package/dist/transform/transform.js +69 -0
- package/dist/transform/transform.js.flow +60 -0
- package/dist/traverse/NodeEventGenerator.js +427 -0
- package/dist/traverse/NodeEventGenerator.js.flow +406 -0
- package/dist/traverse/SafeEmitter.js +70 -0
- package/dist/traverse/SafeEmitter.js.flow +46 -0
- package/dist/traverse/SimpleTraverser.js +149 -0
- package/dist/traverse/SimpleTraverser.js.flow +109 -0
- package/dist/traverse/esquery.js +37 -0
- package/dist/traverse/esquery.js.flow +173 -0
- package/dist/traverse/traverse.js +139 -0
- package/dist/traverse/traverse.js.flow +149 -0
- 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
|
+
}
|