typescript-node-ids
Version:
TypeScript support for naming Nodes uniquely and query them per SourceFile. This really make sense in libraries like ts-simple-ast when modifying AST in a loop. Implementations for native typescript compiler API and ts-simple-ast library
90 lines (89 loc) • 3.58 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const ts_simple_ast_1 = require("ts-simple-ast");
const ts_simple_ast_extra_1 = require("ts-simple-ast-extra");
class TsSimpleAstImpl {
/**
* Installs ids on each descendants of given node. Use `this.getId` on any node to obtain a node's id or
* `getNodeById` to query a node from a parent one using its id.
*
* In general users will provide a SourceFile so the entire AST Nodes filled with ids, but it could also pass
* a fragment hierarchy, for example, to be faster in case only that fragment is invalidated / modified (see
* uninstall method)
*
* If given node's parent doesn't' have an id, the whole source file containing given node will be used as parent.
*
*/
install(node) {
let startingId;
if (ts_simple_ast_1.TypeGuards.isSourceFile(node)) {
startingId = '0';
}
else if (node.getParent() && this.getId(node.getParent())) {
startingId = this.getId(node.getParent());
}
else {
// installing ids on whole source tree since given node's parent doesn't have id
node = node.getSourceFile();
startingId = '0';
}
this.setId(node, startingId);
ts_simple_ast_extra_1.visitChildrenRecursiveDeepFirst(node, (child, index) => this.setId(child, `${child.getParent() ? this.getId(child.getParent()) : '0'}.${index}`));
return node;
}
/**
* User could choose to put ids only to certain nodes and don't use install at all which name all AST's nodes
*/
setId(node, id) {
node.__$node$_$id$__ = id;
}
/**
* Returns given node's id or throws exception if node has't one
*/
getIdOrThrow(node) {
if (!this.getId(node)) {
throw new Error(`Expected node ${node.getKindName()} to have an Id`);
}
return this.getId(node);
}
/**
* Returns given node's id or undefined if it hasn't one
*/
getId(node) {
return node.__$node$_$id$__;
}
/**
* removes given node's id and returns it.
*/
deleteId(node) {
const id = node.__$node$_$id$__;
delete node.__$node$_$id$__;
return id;
}
/**
* Remove given node and all its descendants ids.
*
* This could be useful when user modify the ast and wants to invalidate that AST so further getNodeById calls won't find anything
*/
uninstall(node) {
node.forEachDescendant(c => this.deleteId(c));
// visitChildrenRecursiveDeepFirst(node, c => (this.deleteId(c), undefined))
// throw new Error('Not implemented')
}
/**
* Return the first descendant of given parent node with given id or undefined if none is found
*/
getNodeById(parent, id) {
// TODO very very slow implementation we should use next getChildMatchingId declaration or similar - get indexes and kinds from ids, never iterate1
// return visitChildrenRecursiveDeepFirst(parent, child => this.getId(child) === id ? child : undefined, undefined, undefined, true)
return parent.getFirstDescendant(child => this.getId(child) === id);
// return parent.getDescendants().find(child => this.getId(child) === id)
}
}
exports.TsSimpleAstImpl = TsSimpleAstImpl;
// export TsSimpleAst
function createTsSimpleAstImpl() {
return new TsSimpleAstImpl();
}
exports.createTsSimpleAstImpl = createTsSimpleAstImpl;
exports.tsSimpleAstImpl = createTsSimpleAstImpl();