UNPKG

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
"use strict"; 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();