203 lines
5.1 KiB
JavaScript
203 lines
5.1 KiB
JavaScript
/* eslint max-len: 0 */
|
|
|
|
import type Hub from "../hub";
|
|
import type TraversalContext from "../context";
|
|
import * as virtualTypes from "./lib/virtual-types";
|
|
import buildDebug from "debug";
|
|
import invariant from "invariant";
|
|
import traverse from "../index";
|
|
import assign from "lodash/object/assign";
|
|
import Scope from "../scope";
|
|
import * as t from "babel-types";
|
|
import { path as pathCache } from "../cache";
|
|
|
|
let debug = buildDebug("babel");
|
|
|
|
export default class NodePath {
|
|
constructor(hub: Hub, parent: Object) {
|
|
this.parent = parent;
|
|
this.hub = hub;
|
|
this.contexts = [];
|
|
this.data = {};
|
|
this.shouldSkip = false;
|
|
this.shouldStop = false;
|
|
this.removed = false;
|
|
this.state = null;
|
|
this.opts = null;
|
|
this.skipKeys = null;
|
|
this.parentPath = null;
|
|
this.context = null;
|
|
this.container = null;
|
|
this.listKey = null;
|
|
this.inList = false;
|
|
this.parentKey = null;
|
|
this.key = null;
|
|
this.node = null;
|
|
this.scope = null;
|
|
this.type = null;
|
|
this.typeAnnotation = null;
|
|
}
|
|
|
|
parent: Object;
|
|
hub: Hub;
|
|
contexts: Array<TraversalContext>;
|
|
data: Object;
|
|
shouldSkip: boolean;
|
|
shouldStop: boolean;
|
|
removed: boolean;
|
|
state: any;
|
|
opts: ?Object;
|
|
skipKeys: ?Object;
|
|
parentPath: ?NodePath;
|
|
context: TraversalContext;
|
|
container: ?Object | Array<Object>;
|
|
listKey: ?string;
|
|
inList: boolean;
|
|
parentKey: ?string;
|
|
key: ?string;
|
|
node: ?Object;
|
|
scope: Scope;
|
|
type: ?string;
|
|
typeAnnotation: ?Object;
|
|
|
|
static get({ hub, parentPath, parent, container, listKey, key }): NodePath {
|
|
if (!hub && parentPath) {
|
|
hub = parentPath.hub;
|
|
}
|
|
|
|
invariant(parent, "To get a node path the parent needs to exist");
|
|
|
|
let targetNode = container[key];
|
|
|
|
let paths = pathCache.get(parent) || [];
|
|
if (!pathCache.has(parent)) {
|
|
pathCache.set(parent, paths);
|
|
}
|
|
|
|
let path;
|
|
|
|
for (let i = 0; i < paths.length; i++) {
|
|
let pathCheck = paths[i];
|
|
if (pathCheck.node === targetNode) {
|
|
path = pathCheck;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (path && !(path instanceof NodePath)) {
|
|
if (path.constructor.name === "NodePath") {
|
|
// we're going to absolutley thrash the tree and allocate way too many node paths
|
|
// than is necessary but there's no way around this as the node module resolution
|
|
// algorithm is ridiculous
|
|
path = null;
|
|
} else {
|
|
// badly deserialised probably
|
|
throw new Error("We found a path that isn't a NodePath instance. Possibly due to bad serialisation.");
|
|
}
|
|
}
|
|
|
|
if (!path) {
|
|
path = new NodePath(hub, parent);
|
|
paths.push(path);
|
|
}
|
|
|
|
path.setup(parentPath, container, listKey, key);
|
|
|
|
return path;
|
|
}
|
|
|
|
getScope(scope: Scope) {
|
|
let ourScope = scope;
|
|
|
|
// we're entering a new scope so let's construct it!
|
|
if (this.isScope()) {
|
|
ourScope = new Scope(this, scope);
|
|
}
|
|
|
|
return ourScope;
|
|
}
|
|
|
|
setData(key: string, val: any): any {
|
|
return this.data[key] = val;
|
|
}
|
|
|
|
getData(key: string, def?: any): any {
|
|
let val = this.data[key];
|
|
if (!val && def) val = this.data[key] = def;
|
|
return val;
|
|
}
|
|
|
|
buildCodeFrameError(msg: string, Error: typeof Error = SyntaxError): Error {
|
|
return this.hub.file.buildCodeFrameError(this.node, msg, Error);
|
|
}
|
|
|
|
traverse(visitor: Object, state?: any) {
|
|
traverse(this.node, visitor, this.scope, state, this);
|
|
}
|
|
|
|
mark(type: string, message: string) {
|
|
this.hub.file.metadata.marked.push({
|
|
type,
|
|
message,
|
|
loc: this.node.loc
|
|
});
|
|
}
|
|
|
|
set(key: string, node: Object) {
|
|
t.validate(this.node, key, node);
|
|
this.node[key] = node;
|
|
}
|
|
|
|
getPathLocation(): string {
|
|
let parts = [];
|
|
let path = this;
|
|
do {
|
|
let key = path.key;
|
|
if (path.inList) key = `${path.listKey}[${key}]`;
|
|
parts.unshift(key);
|
|
} while (path = path.parentPath);
|
|
return parts.join(".");
|
|
}
|
|
|
|
debug(buildMessage: Function) {
|
|
if (!debug.enabled) return;
|
|
debug(`${this.getPathLocation()} ${this.type}: ${buildMessage()}`);
|
|
}
|
|
}
|
|
|
|
assign(NodePath.prototype, require("./ancestry"));
|
|
assign(NodePath.prototype, require("./inference"));
|
|
assign(NodePath.prototype, require("./replacement"));
|
|
assign(NodePath.prototype, require("./evaluation"));
|
|
assign(NodePath.prototype, require("./conversion"));
|
|
assign(NodePath.prototype, require("./introspection"));
|
|
assign(NodePath.prototype, require("./context"));
|
|
assign(NodePath.prototype, require("./removal"));
|
|
assign(NodePath.prototype, require("./modification"));
|
|
assign(NodePath.prototype, require("./family"));
|
|
assign(NodePath.prototype, require("./comments"));
|
|
|
|
for (let type of (t.TYPES: Array<string>)) {
|
|
let typeKey = `is${type}`;
|
|
NodePath.prototype[typeKey] = function (opts) {
|
|
return t[typeKey](this.node, opts);
|
|
};
|
|
|
|
NodePath.prototype[`assert${type}`] = function (opts) {
|
|
if (!this[typeKey](opts)) {
|
|
throw new TypeError(`Expected node path of type ${type}`);
|
|
}
|
|
};
|
|
}
|
|
|
|
for (let type in virtualTypes) {
|
|
if (type[0] === "_") continue;
|
|
if (t.TYPES.indexOf(type) < 0) t.TYPES.push(type);
|
|
|
|
let virtualType = virtualTypes[type];
|
|
|
|
NodePath.prototype[`is${type}`] = function (opts) {
|
|
return virtualType.checkPath(this, opts);
|
|
};
|
|
}
|