ts-simple-ast
Version:
TypeScript compiler wrapper for AST navigation and code generation.
204 lines (160 loc) • 16.5 kB
HTML
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>ts-simple-ast - Performance</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous">
<link rel="stylesheet" href="/assets/css/style.css?v=502728dc7b09041e36b06c50f79a13adbfb6f1b2">
<!--
<link rel="stylesheet" href="/assets/css/style.css?v=502728dc7b09041e36b06c50f79a13adbfb6f1b2">
<link rel="stylesheet" href="/assets/css/custom-style.css?v=502728dc7b09041e36b06c50f79a13adbfb6f1b2">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src="/assets/js/main.js"></script>-->
</head>
<body>
<div class="main">
<header class="container">
<div class="row">
<h1 onclick="document.location.href = '/'" class="headerLink">ts-simple-ast</h1>
<!--<p class="subText">Simple way to navigate and manipulate the TypeScript AST.</p>-->
</div>
</header>
<div class="container">
<div class="row">
<div class="col-md-3">
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<nav class="sidebar-nav" id="navbarSupportedContent">
<ul class="navbar-nav navbar-default">
<li class="nav-item">
<a class="nav-link" href="/">Overview</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/setup">Setup</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/navigation">Navigation</a>
</li>
<li class="nav-item active">
<a class="nav-link" href="/manipulation">Manipulation</a>
<ul>
<li class=""><a href="/manipulation/settings">Settings</a></li>
<li class=""><a href="/manipulation/renaming">Renaming</a></li>
<li class=""><a href="/manipulation/removing">Removing</a></li>
<li class=""><a href="/manipulation/order">Order</a></li>
<li class=""><a href="/manipulation/code-writer">Code Writer</a></li>
<li class="active"><a href="/manipulation/performance">Performance</a></li>
</ul>
</li>
<li class="nav-item">
<a class="nav-link" href="/emitting">Emitting</a>
</li>
<li class="">
<a class="nav-link" href="/details/index">Details</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/utilities">Utilities</a>
</li>
<li class="nav-item">
<a class="nav-link" href="http://github.com/dsherret/ts-simple-ast">View on GitHub</a>
</li>
</ul>
</nav>
</div>
<section class="container-fluid col-md-9">
<h2 id="performance">Performance</h2>
<p>There’s a lot of opportunity for performance improvements. The library originally started off favouring correctness, but it’s now starting to switch to improving performance.</p>
<p>Issues: <a href="https://github.com/dsherret/ts-simple-ast/issues/224">#224</a>, <a href="https://github.com/dsherret/ts-simple-ast/issues/141">#141</a>, <a href="https://github.com/dsherret/ts-simple-ast/issues/142">#142</a></p>
<h2 id="overview">Overview</h2>
<p>This library makes manipulations easy for you by keeping track of how the underlying syntax tree changes between manipulations.</p>
<p>Behind the scenes, when you manipulate the AST:</p>
<ol>
<li>A new source file is created using the TypeScript compiler.</li>
<li>The previously navigated nodes have their underlying compiler nodes replaced with the new compiler nodes.</li>
</ol>
<p>It’s why you can do this:</p>
<div class="language-typescript highlighter-rouge"><pre class="highlight"><code><span class="c1">// sourcefile contains: interface Person { name: string; }</span>
<span class="kd">const</span> <span class="nx">personInterface</span> <span class="o">=</span> <span class="nx">sourceFile</span><span class="p">.</span><span class="nx">getInterfaceOrThrow</span><span class="p">(</span><span class="s2">"Person"</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">nameProperty</span> <span class="o">=</span> <span class="nx">personInterface</span><span class="p">.</span><span class="nx">getPropertyOrThrow</span><span class="p">(</span><span class="s2">"name"</span><span class="p">);</span>
<span class="nx">nameProperty</span><span class="p">.</span><span class="nx">setType</span><span class="p">(</span><span class="s2">"number"</span><span class="p">);</span>
<span class="nx">nameProperty</span><span class="p">.</span><span class="nx">getText</span><span class="p">();</span> <span class="c1">// "name: number;"</span>
</code></pre>
</div>
<p>Instead of having to renavigate the tree after each manipulation:</p>
<div class="language-typescript highlighter-rouge"><pre class="highlight"><code><span class="c1">// thankfully the library does not work this way</span>
<span class="kd">let</span> <span class="nx">personInterface</span> <span class="o">=</span> <span class="nx">sourceFile</span><span class="p">.</span><span class="nx">getInterfaceOrThrow</span><span class="p">(</span><span class="s2">"Person"</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">nameProperty</span> <span class="o">=</span> <span class="nx">personInterface</span><span class="p">.</span><span class="nx">getPropertyOrThrow</span><span class="p">(</span><span class="s2">"name"</span><span class="p">);</span>
<span class="nx">nameProperty</span><span class="p">.</span><span class="nx">setType</span><span class="p">(</span><span class="s2">"number"</span><span class="p">);</span>
<span class="nx">nameProperty</span><span class="p">.</span><span class="nx">getText</span><span class="p">();</span> <span class="c1">// "name: string;"</span>
<span class="nx">personInterface</span> <span class="o">=</span> <span class="nx">sourceFile</span><span class="p">.</span><span class="nx">getInterfaceOrThrow</span><span class="p">(</span><span class="s2">"Person"</span><span class="p">);</span>
<span class="nx">nameProperty</span> <span class="o">=</span> <span class="nx">personInterface</span><span class="p">.</span><span class="nx">getPropertyOrThrow</span><span class="p">(</span><span class="s2">"name"</span><span class="p">);</span>
<span class="nx">nameProperty</span><span class="p">.</span><span class="nx">getText</span><span class="p">();</span> <span class="c1">// "name: number;"</span>
</code></pre>
</div>
<p>When thinking about performance, the key point here is that if you have a lot of previously navigated nodes and a very large file, then manipulation might start to become sluggish.</p>
<h3 id="forgetting-nodes-advanced">Forgetting Nodes (Advanced)</h3>
<p>The main way to improve performance when manipulating, is to “forget” a node when you’re done with it.</p>
<div class="language-typescript highlighter-rouge"><pre class="highlight"><code><span class="nx">personInterface</span><span class="p">.</span><span class="nx">forget</span><span class="p">();</span>
</code></pre>
</div>
<p>That will stop tracking the node and all its previously navigated descendants (ex. in this case, <code class="highlighter-rouge">nameProperty</code> as well).
It won’t be updated when manipulation happens again. Note that after doing this, the node will throw an error if one of its properties or methods is accessed.</p>
<h3 id="forget-blocks-advanced">Forget Blocks (Advanced)</h3>
<p>It’s possible to make sure all created nodes within a block are forgotten:</p>
<div class="language-typescript highlighter-rouge"><pre class="highlight"><code><span class="k">import</span> <span class="p">{</span><span class="nx">Ast</span><span class="p">,</span> <span class="nx">NamespaceDeclaration</span><span class="p">,</span> <span class="nx">InterfaceDeclaration</span><span class="p">,</span> <span class="nx">ClassDeclaration</span><span class="p">}</span> <span class="k">from</span> <span class="s2">"ts-simple-ast"</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">ast</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Ast</span><span class="p">();</span>
<span class="kd">const</span> <span class="nx">text</span> <span class="o">=</span> <span class="s2">"namespace Namespace { interface Interface {} class Class {} }"</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">sourceFile</span> <span class="o">=</span> <span class="nx">ast</span><span class="p">.</span><span class="nx">createSourceFile</span><span class="p">(</span><span class="s2">"file.ts"</span><span class="p">,</span> <span class="nx">text</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">namespaceDeclaration</span><span class="err">:</span> <span class="nx">NamespaceDeclaration</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">interfaceDeclaration</span><span class="err">:</span> <span class="nx">InterfaceDeclaration</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">classDeclaration</span><span class="err">:</span> <span class="nx">ClassDeclaration</span><span class="p">;</span>
<span class="nx">ast</span><span class="p">.</span><span class="nx">forgetNodesCreatedInBlock</span><span class="p">(</span><span class="nx">remember</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">namespaceDeclaration</span> <span class="o">=</span> <span class="nx">sourceFile</span><span class="p">.</span><span class="nx">getNamespaceOrThrow</span><span class="p">(</span><span class="s2">"Namespace"</span><span class="p">);</span>
<span class="nx">interfaceDeclaration</span> <span class="o">=</span> <span class="nx">namespaceDeclaration</span><span class="p">.</span><span class="nx">getInterfaceOrThrow</span><span class="p">(</span><span class="s2">"Interface"</span><span class="p">);</span>
<span class="nx">classDeclaration</span> <span class="o">=</span> <span class="nx">namespaceDeclaration</span><span class="p">.</span><span class="nx">getClassDeclarationOrThrow</span><span class="p">(</span><span class="s2">"Class"</span><span class="p">);</span>
<span class="c1">// you can mark nodes to remember outside the scope of this block...</span>
<span class="c1">// this will remember the specified node and all its ancestors</span>
<span class="nx">remember</span><span class="p">(</span><span class="nx">interfaceDeclaration</span><span class="p">);</span> <span class="c1">// or pass in multiple nodes</span>
<span class="p">});</span>
<span class="nx">namespaceDeclaration</span><span class="p">.</span><span class="nx">getText</span><span class="p">();</span> <span class="c1">// ok, child was marked to remember</span>
<span class="nx">interfaceDeclaration</span><span class="p">.</span><span class="nx">getText</span><span class="p">();</span> <span class="c1">// ok, was explicitly marked to remember</span>
<span class="nx">classDeclaration</span><span class="p">.</span><span class="nx">getText</span><span class="p">();</span> <span class="c1">// throws, was forgotten</span>
</code></pre>
</div>
<p>Also, do not be concerned about nesting forget blocks. That is perfectly fine to do:</p>
<div class="language-typescript highlighter-rouge"><pre class="highlight"><code><span class="nx">ast</span><span class="p">.</span><span class="nx">forgetNodesCreatedInBlock</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">namespaceDeclaration</span> <span class="o">=</span> <span class="nx">sourceFile</span><span class="p">.</span><span class="nx">getNamespaceOrThrow</span><span class="p">(</span><span class="s2">"Namespace"</span><span class="p">);</span>
<span class="nx">interfaceDeclaration</span> <span class="o">=</span> <span class="nx">namespaceDeclaration</span><span class="p">.</span><span class="nx">getInterfaceOrThrow</span><span class="p">(</span><span class="s2">"Interface"</span><span class="p">);</span>
<span class="nx">ast</span><span class="p">.</span><span class="nx">forgetNodesCreatedInBlock</span><span class="p">(</span><span class="nx">remember</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">classDeclaration</span> <span class="o">=</span> <span class="nx">namespaceDeclaration</span><span class="p">.</span><span class="nx">getClassDeclarationOrThrow</span><span class="p">(</span><span class="s2">"Class"</span><span class="p">);</span>
<span class="nx">remember</span><span class="p">(</span><span class="nx">namespaceDeclaration</span><span class="p">);</span>
<span class="p">});</span>
<span class="nx">classDeclaration</span><span class="p">.</span><span class="nx">getText</span><span class="p">();</span> <span class="c1">// throws, was forgotten outside the block above</span>
<span class="nx">interfaceDeclaration</span><span class="p">.</span><span class="nx">getText</span><span class="p">();</span> <span class="c1">// ok, hasn't been forgotten yet</span>
<span class="p">});</span>
<span class="nx">namespaceDeclaration</span><span class="p">.</span><span class="nx">getText</span><span class="p">();</span> <span class="c1">// ok, was marked to remember in one of the blocks</span>
<span class="nx">interfaceDeclaration</span><span class="p">.</span><span class="nx">getText</span><span class="p">();</span> <span class="c1">// throws, was forgotten</span>
<span class="nx">classDeclaration</span><span class="p">.</span><span class="nx">getText</span><span class="p">();</span> <span class="c1">// throws, was forgotten</span>
</code></pre>
</div>
<h4 id="async">Async</h4>
<p>This method supports async and await:</p>
<div class="language-ts highlighter-rouge"><pre class="highlight"><code><span class="k">await</span> <span class="nx">ast</span><span class="p">.</span><span class="nx">forgetNodesCreatedInBlock</span><span class="p">(</span><span class="k">async</span> <span class="nx">remember</span> <span class="o">=></span> <span class="p">{</span>
<span class="c1">// do stuff</span>
<span class="p">});</span>
</code></pre>
</div>
</section>
</div>
</div>
<footer>
</footer>
</div>
<script src="https://code.jquery.com/jquery-3.1.1.slim.min.js" integrity="sha384-A7FZj7v+d/sdmMqp/nOQwliLvUsJfDHW+k9Omg/a/EheAdgtzNs3hpfag6Ed950n" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js" integrity="sha384-DztdAPBWPRXSA/3eYEEUWrWCy7G5KFbe8fFjk5JAIxUYHKkDx6Qin1DkWx51bBrb" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js" integrity="sha384-vBWWzlZJ8ea9aCX4pEW3rVHjgjt7zpkNpZk+02D9phzyeVkE+jo0ieGizqPLForn" crossorigin="anonymous"></script>
<!--[if !IE]><script>fixScale(document);</script><![endif]-->
</body>
</html>