forked from firefox-devtools/debugger
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsimple-path.js
More file actions
153 lines (121 loc) · 3.52 KB
/
Copy pathsimple-path.js
File metadata and controls
153 lines (121 loc) · 3.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
// @flow
import type { Node, TraversalAncestors } from "@babel/types";
export type { Node, TraversalAncestors };
export default function createSimplePath(ancestors: TraversalAncestors) {
if (ancestors.length === 0) {
return null;
}
// Slice the array because babel-types traverse may continue mutating
// the ancestors array in later traversal logic.
return new SimplePath(ancestors.slice());
}
export type { SimplePath };
/**
* Mimics @babel/traverse's NodePath API in a simpler fashion that isn't as
* heavy, but still allows the ease of passing paths around to process nested
* AST structures.
*/
class SimplePath {
_index: number;
_ancestors: TraversalAncestors;
_ancestor: $ElementType<TraversalAncestors, number>;
_parentPath: SimplePath | null | void;
constructor(
ancestors: TraversalAncestors,
index: number = ancestors.length - 1
) {
if (index < 0 || index >= ancestors.length) {
console.error(ancestors);
throw new Error("Created invalid path");
}
this._ancestors = ancestors;
this._ancestor = ancestors[index];
this._index = index;
}
get parentPath(): SimplePath | null {
let path = this._parentPath;
if (path === undefined) {
if (this._index === 0) {
path = null;
} else {
path = new SimplePath(this._ancestors, this._index - 1);
}
this._parentPath = path;
}
return path;
}
get parent(): Node {
return this._ancestor.node;
}
get node(): Node {
const { node, key, index } = this._ancestor;
if (typeof index === "number") {
return node[key][index];
}
return node[key];
}
set node(replacement: Node): void {
if (this.type !== "Identifier") {
throw new Error(
"Replacing anything other than leaf nodes is undefined behavior " +
"in t.traverse()"
);
}
const { node, key, index } = this._ancestor;
if (typeof index === "number") {
node[key][index] = replacement;
} else {
node[key] = replacement;
}
}
get type(): string {
return this.node.type;
}
get inList(): boolean {
return typeof this._ancestor.index === "number";
}
get containerIndex(): number {
const { index } = this._ancestor;
if (typeof index !== "number") {
throw new Error("Cannot get index of non-array node");
}
return index;
}
get depth(): number {
return this._index;
}
replace(node: Node) {
this.node = node;
}
find(predicate: SimplePath => boolean): SimplePath | null {
for (let path = this; path; path = path.parentPath) {
if (predicate(path)) {
return path;
}
}
return null;
}
findParent(predicate: SimplePath => boolean): SimplePath | null {
if (!this.parentPath) {
throw new Error("Cannot use findParent on root path");
}
return this.parentPath.find(predicate);
}
getSibling(offset: number) {
const { node, key, index } = this._ancestor;
if (typeof index !== "number") {
throw new Error("Non-array nodes do not have siblings");
}
const container = node[key];
const siblingIndex = index + offset;
if (siblingIndex < 0 || siblingIndex >= container.length) {
return null;
}
return new SimplePath(
this._ancestors.slice(0, -1).concat([{ node, key, index: siblingIndex }])
);
}
}