{
  "version": "https://jsonfeed.org/version/1.1",
  "title": "Andrew Branch",
  "language": "en",
  "home_page_url": "https://blog.andrewbran.ch/",
  "feed_url": "https://blog.andrewbran.ch/feed.json",
  "description": "A blog about TypeScript, mostly.",
  "author": {
    "name": "Andrew Branch"
  },
  "items": [
    {
      "id": "https://blog.andrewbran.ch/default-exports-in-commonjs-libraries/",
      "url": "https://blog.andrewbran.ch/default-exports-in-commonjs-libraries/",
      "title": "Default exports in CommonJS libraries",
      "content_html": "<p>Time for another Tweet analysis:</p>\n<blockquote class=\"rounded-lg bg-[var(--color-fg05)] p2 md:p4\">\n<div class=\"font-grotesk\">\n<p><strong>Brandon McConnell</strong><br />\n<a class=\"text-textSecondary\" href=\"https://twitter.com/branmcconnell\">@branmcconnell</a></p>\n</div>\n<p>Apparently, I've been doing NPM package exports in TSC wrong my entire life. I usually use the ESM <code>export default _</code> if I'm doing a default export.</p>\n<p>This generated CJS output appears to break when require'd in a CJS environment, requiring a property ref of <code>packageName.default</code> instead of simply <code>packageName</code>.</p>\n<p>After much research, it appears the generally recommended fix is to use <code>module.exports =</code> in your main ESM file that TSC processes. WHAT. That's not a typo.</p>\n<p>You can use any ESM imports or syntax you need in that file, but for the CJS output file to use <code>module.exports</code>, the ESM needs to use it too. Why can't TSC convert <code>export default</code> to <code>module.exports =</code> for the CJS target?</p>\n<p>Doing this also appears to mess with the export types, so the <code>.d.ts</code> files sometimes get emptied out unless you export your exports with <strong><em>both</em></strong> ESM and CJS syntax together, even like this:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-js\"><span class=\"line\"><span style=\"color:#C586C0\">export</span><span style=\"color:#C586C0\"> default</span><span style=\"color:#9CDCFE\"> myFunction</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"><span style=\"color:#4EC9B0\">module</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#4EC9B0\">exports</span><span style=\"color:#D4D4D4\"> = </span><span style=\"color:#9CDCFE\">myFunction</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"></span></code></pre>\n<p>Has anyone else run into this? Twilight Zone moment with a simple fix? 🤞🏼</p>\n<small class=\"font-grotesk text-textSecondary\">\n<a href=\"https://twitter.com/mattpocockuk/status/1724495021745860793\">10:30 AM · Nov 14, 2023</a>\n</small>\n</blockquote>\n<p>This is a great breakdown bringing up some interesting points. Everything in it is factually correct, but the “fix” Brandon heard recommended is wrong. Let’s get into the details.</p>\n<h2 id=\"background%3A-commonjs-semantics\" tabindex=\"-1\"><a class=\"no-underline\" href=\"https://blog.andrewbran.ch/default-exports-in-commonjs-libraries/#background%3A-commonjs-semantics\">Background: CommonJS semantics</a></h2>\n<p>A CommonJS module can export any single value by assigning to <code>module.exports</code>:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-js\"><span class=\"line\"><span style=\"color:#4EC9B0\">module</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#4EC9B0\">exports</span><span style=\"color:#D4D4D4\"> = </span><span style=\"color:#CE9178\">\"Hello, world\"</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"></span></code></pre>\n<p>Other CommonJS modules can access that value as the result of a <code>require</code> call of that module:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-js\"><span class=\"line\"><span style=\"color:#569CD6\">const</span><span style=\"color:#4FC1FF\"> greetings</span><span style=\"color:#D4D4D4\"> = </span><span style=\"color:#DCDCAA\">require</span><span style=\"color:#D4D4D4\">(</span><span style=\"color:#CE9178\">\"./greetings.cjs\"</span><span style=\"color:#D4D4D4\">);</span></span>\n<span class=\"line\"><span style=\"color:#9CDCFE\">console</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#DCDCAA\">log</span><span style=\"color:#D4D4D4\">(</span><span style=\"color:#9CDCFE\">greetings</span><span style=\"color:#D4D4D4\">); </span><span style=\"color:#6A9955\">// \"Hello, world\"</span></span>\n<span class=\"line\"></span></code></pre>\n<p><code>module.exports</code> is initialized to an empty object, and a free variable <code>exports</code> points to that same object. It’s common to simulate named exports by mutating that object with property assignments:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-js\"><span class=\"line\"><span style=\"color:#4EC9B0\">exports</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#9CDCFE\">message</span><span style=\"color:#D4D4D4\"> = </span><span style=\"color:#CE9178\">\"Hello, world\"</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"><span style=\"color:#4EC9B0\">exports</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#DCDCAA\">sayHello</span><span style=\"color:#D4D4D4\"> = </span><span style=\"color:#9CDCFE\">name</span><span style=\"color:#569CD6\"> =></span><span style=\"color:#9CDCFE\"> console</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#DCDCAA\">log</span><span style=\"color:#D4D4D4\">(</span><span style=\"color:#CE9178\">`Hello, </span><span style=\"color:#569CD6\">${</span><span style=\"color:#9CDCFE\">name</span><span style=\"color:#569CD6\">}</span><span style=\"color:#CE9178\">`</span><span style=\"color:#D4D4D4\">);</span></span>\n<span class=\"line\"></span></code></pre>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-js\"><span class=\"line\"><span style=\"color:#569CD6\">const</span><span style=\"color:#4FC1FF\"> greetings</span><span style=\"color:#D4D4D4\"> = </span><span style=\"color:#DCDCAA\">require</span><span style=\"color:#D4D4D4\">(</span><span style=\"color:#CE9178\">\"./greetings.cjs\"</span><span style=\"color:#D4D4D4\">);</span></span>\n<span class=\"line\"><span style=\"color:#9CDCFE\">console</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#DCDCAA\">log</span><span style=\"color:#D4D4D4\">(</span><span style=\"color:#9CDCFE\">greetings</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#9CDCFE\">message</span><span style=\"color:#D4D4D4\">); </span><span style=\"color:#6A9955\">// \"Hello, world\";</span></span>\n<span class=\"line\"><span style=\"color:#9CDCFE\">greetings</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#DCDCAA\">sayHello</span><span style=\"color:#D4D4D4\">(</span><span style=\"color:#CE9178\">\"Andrew\"</span><span style=\"color:#D4D4D4\">);   </span><span style=\"color:#6A9955\">// \"Hello, Andrew\";</span></span>\n<span class=\"line\"></span></code></pre>\n<h2 id=\"background%3A-transforming-esm-to-commonjs\" tabindex=\"-1\"><a class=\"no-underline\" href=\"https://blog.andrewbran.ch/default-exports-in-commonjs-libraries/#background%3A-transforming-esm-to-commonjs\">Background: Transforming ESM to CommonJS</a></h2>\n<p>ECMAScript modules are fundamentally different from CommonJS modules. Where CommonJS modules expose exactly one nameless value of any type, ES modules always expose a <a href=\"https://tc39.es/ecma262/#sec-module-namespace-objects\">Module Namespace Object</a>—a collection of named exports. This creates something of a paradox for translating between module systems. If you have an existing CommonJS module that exports a string:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-js\"><span class=\"line\"><span style=\"color:#4EC9B0\">module</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#4EC9B0\">exports</span><span style=\"color:#D4D4D4\"> = </span><span style=\"color:#CE9178\">\"Hello, world\"</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"></span></code></pre>\n<p>and you want to translate it to ESM, then transform it back to equivalent CommonJS output, what input can you write? The only plausible answer is to use a default export:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-js\"><span class=\"line\"><span style=\"color:#C586C0\">export</span><span style=\"color:#C586C0\"> default</span><span style=\"color:#CE9178\"> \"Hello, world\"</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"></span></code></pre>\n<p>But if you ever add another named export to this module:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-js\"><span class=\"line\"><span style=\"color:#C586C0\">export</span><span style=\"color:#C586C0\"> default</span><span style=\"color:#CE9178\"> \"Hello, world\"</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"><span style=\"color:#C586C0\">export</span><span style=\"color:#569CD6\"> const</span><span style=\"color:#4FC1FF\"> anotherExport</span><span style=\"color:#D4D4D4\"> = </span><span style=\"color:#CE9178\">\"uh oh\"</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"></span></code></pre>\n<p>you now have a problem. The transformed output can’t be</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-js\"><span class=\"line\"><span style=\"color:#4EC9B0\">module</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#4EC9B0\">exports</span><span style=\"color:#D4D4D4\"> = </span><span style=\"color:#CE9178\">\"Hello, world\"</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"><span style=\"color:#4EC9B0\">module</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#4EC9B0\">exports</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#9CDCFE\">anotherExport</span><span style=\"color:#D4D4D4\"> = </span><span style=\"color:#CE9178\">\"uh oh\"</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"></span></code></pre>\n<p>because attempting to assign the property <code>anotherExport</code> on a primitive won’t do anything useful. Remembering that default exports are actually just named exports with special syntax, you’d probably conclude that the output has to be:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-js\"><span class=\"line\"><span style=\"color:#4EC9B0\">exports</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#9CDCFE\">default</span><span style=\"color:#D4D4D4\"> = </span><span style=\"color:#CE9178\">\"Hello, world\"</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"><span style=\"color:#4EC9B0\">exports</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#9CDCFE\">anotherExport</span><span style=\"color:#D4D4D4\"> = </span><span style=\"color:#CE9178\">\"uh oh\"</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"></span></code></pre>\n<p>So does an <code>export default</code> become <code>module.exports</code> or <code>module.exports.default</code>? Should it really flip flop between them based on whether there are <em>other</em> named exports? Unfortunately, different compilers have landed on different answers to this question over the years. <code>tsc</code> took the approach of always assigning ESM default exports to <code>exports.default</code>. So <code>export default &quot;Hello, world&quot;</code> becomes:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-js\"><span class=\"line\"><span style=\"color:#9CDCFE\">Object</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#DCDCAA\">defineProperty</span><span style=\"color:#D4D4D4\">(</span><span style=\"color:#4EC9B0\">exports</span><span style=\"color:#D4D4D4\">, </span><span style=\"color:#CE9178\">\"__esModule\"</span><span style=\"color:#D4D4D4\">, { </span><span style=\"color:#9CDCFE\">value:</span><span style=\"color:#569CD6\"> true</span><span style=\"color:#D4D4D4\"> });</span></span>\n<span class=\"line\"><span style=\"color:#4EC9B0\">exports</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#9CDCFE\">default</span><span style=\"color:#D4D4D4\"> = </span><span style=\"color:#CE9178\">\"Hello, world\"</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"></span></code></pre>\n<p>(The <code>__esModule</code> marker, an ecosystem standard that first appeared in Traceur, is used by transformed default <em>imports</em>, but isn’t relevant for the rest of this post. A more complete discussion of ESM/CJS transforms and interoperability can be found in the <a href=\"https://www.typescriptlang.org/docs/handbook/modules/appendices/esm-cjs-interop.html\">TypeScript modules documentation</a>.)</p>\n<p>This answers one of the questions posed in the tweet:</p>\n<blockquote>\n<p>Why can't TSC convert <code>export default</code> to <code>module.exports =</code> for the CJS target?</p>\n</blockquote>\n<p>There’s an argument for doing that—some compilers do, or expose options to do that—but every approach has tradeoffs, and <code>tsc</code> chose a lane extremely early in the life of ESM and stuck with it.</p>\n<h2 id=\"understanding-the-behavior-with-export-default-alone\" tabindex=\"-1\"><a class=\"no-underline\" href=\"https://blog.andrewbran.ch/default-exports-in-commonjs-libraries/#understanding-the-behavior-with-export-default-alone\">Understanding the behavior with <code>export default</code> alone</a></h2>\n<p>With that background, it should be easy to interpret the behavior Brandon observed:</p>\n<blockquote>\n<p>I usually use the ESM <code>export default _</code> if I'm doing a default export... This generated CJS output appears to break when require'd in a CJS environment, requiring a property ref of <code>packageName.default</code> instead of simply <code>packageName</code>.</p>\n</blockquote>\n<p><code>export default _</code> turns into <code>exports.default = _</code>, so if someone writing a <code>require</code> wants to access <code>_</code>, of course they need to write a property access like <code>require(&quot;pkg&quot;).default</code> to get it.</p>\n<p>This is only a “break” if you have a specific expectation about how to translate from ESM to CJS, and as I argued in the previous section, all such expectations are fraught with inconsistencies and incompatibilities. The ESM specification didn’t say anything about interoperability with CommonJS, so tools that wanted to support both module formats kind of just made it up as they went. This is not to say that the outcome was <em>good</em> or even that <code>tsc</code>’s decision to always assign to <code>exports.default</code> looks like the best decision with the benefit of nine years of hindsight. It wasn’t mentioned in the tweet, but the <code>exports.default</code> output is also problematic when imported by true ES modules in Node.js:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-js\"><span class=\"line\"><span style=\"color:#6A9955\">// node_modules/hello/index.js</span></span>\n<span class=\"line\"><span style=\"color:#9CDCFE\">Object</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#DCDCAA\">defineProperty</span><span style=\"color:#D4D4D4\">(</span><span style=\"color:#4EC9B0\">exports</span><span style=\"color:#D4D4D4\">, </span><span style=\"color:#CE9178\">\"__esModule\"</span><span style=\"color:#D4D4D4\">, { </span><span style=\"color:#9CDCFE\">value:</span><span style=\"color:#569CD6\"> true</span><span style=\"color:#D4D4D4\"> });</span></span>\n<span class=\"line\"><span style=\"color:#4EC9B0\">exports</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#9CDCFE\">default</span><span style=\"color:#D4D4D4\"> = </span><span style=\"color:#CE9178\">\"Hello, world\"</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#6A9955\">// main.mjs</span></span>\n<span class=\"line\"><span style=\"color:#C586C0\">import</span><span style=\"color:#9CDCFE\"> hello</span><span style=\"color:#C586C0\"> from</span><span style=\"color:#CE9178\"> \"hello\"</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"><span style=\"color:#9CDCFE\">console</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#DCDCAA\">log</span><span style=\"color:#D4D4D4\">(</span><span style=\"color:#9CDCFE\">hello</span><span style=\"color:#D4D4D4\">);         </span><span style=\"color:#6A9955\">// { default: \"Hello, world\" }</span></span>\n<span class=\"line\"><span style=\"color:#9CDCFE\">console</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#DCDCAA\">log</span><span style=\"color:#D4D4D4\">(</span><span style=\"color:#9CDCFE\">hello</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#9CDCFE\">default</span><span style=\"color:#D4D4D4\">); </span><span style=\"color:#6A9955\">// \"Hello, world\"</span></span>\n<span class=\"line\"></span></code></pre>\n<p>Our <em>real</em> default import fails to bind to our <em>transformed</em> default export—we still need to access the <code>.default</code> property! This can make it nearly impossible to write code that works both in Node.js and in all bundlers. So, while the behavior with transformed <code>export default</code> in <code>tsc</code> is understandable and predictable, it is indeed problematic for libraries.</p>\n<h2 id=\"the-problem-with-adding-module.exports-%3D-to-the-input\" tabindex=\"-1\"><a class=\"no-underline\" href=\"https://blog.andrewbran.ch/default-exports-in-commonjs-libraries/#the-problem-with-adding-module.exports-%3D-to-the-input\">The problem with adding <code>module.exports =</code> to the input</a></h2>\n<p>Apparently, there is some conventional wisdom floating around that the way to solve this problem is to add a <code>module.exports =</code> assignment to the TypeScript module, along with the <code>export default</code> statement. This is a Bad Idea, and Brandon included a clue as to why:</p>\n<blockquote>\n<p>Doing this also appears to mess with the export types, so the <code>.d.ts</code> files sometimes get emptied out unless you export your exports with <strong>both</strong> ESM and CJS syntax together, even like this:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-js\"><span class=\"line\"><span style=\"color:#C586C0\">export</span><span style=\"color:#C586C0\"> default</span><span style=\"color:#9CDCFE\"> myFunction</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"><span style=\"color:#4EC9B0\">module</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#4EC9B0\">exports</span><span style=\"color:#D4D4D4\"> = </span><span style=\"color:#9CDCFE\">myFunction</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"></span></code></pre>\n</blockquote>\n<p>The key is that TypeScript doesn’t understand <code>module.exports</code> in TypeScript files, so it passes that expression through verbatim to the output JavaScript file, while emitting nothing for it into the output <code>.d.ts</code> file. That’s why writing <code>module.exports</code> without <code>export default</code> emits an empty declaration file. Including them both, the resulting output files look like:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-ts\"><span class=\"line\"><span style=\"color:#6A9955\">// myFunction.js</span></span>\n<span class=\"line\"><span style=\"color:#CE9178\">\"use strict\"</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"><span style=\"color:#9CDCFE\">Object</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#DCDCAA\">defineProperty</span><span style=\"color:#D4D4D4\">(</span><span style=\"color:#4EC9B0\">exports</span><span style=\"color:#D4D4D4\">, </span><span style=\"color:#CE9178\">\"__esModule\"</span><span style=\"color:#D4D4D4\">, { </span><span style=\"color:#9CDCFE\">value:</span><span style=\"color:#569CD6\"> true</span><span style=\"color:#D4D4D4\"> });</span></span>\n<span class=\"line\"><span style=\"color:#569CD6\">function</span><span style=\"color:#DCDCAA\"> myFunction</span><span style=\"color:#D4D4D4\">() { </span><span style=\"color:#6A9955\">/* ... */</span><span style=\"color:#D4D4D4\"> }</span></span>\n<span class=\"line\"><span style=\"color:#4EC9B0\">exports</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#9CDCFE\">default</span><span style=\"color:#D4D4D4\"> = </span><span style=\"color:#9CDCFE\">myFunction</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"><span style=\"color:#4EC9B0\">module</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#4EC9B0\">exports</span><span style=\"color:#D4D4D4\"> = </span><span style=\"color:#9CDCFE\">myFunction</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"></span></code></pre>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-ts\"><span class=\"line\"><span style=\"color:#6A9955\">// myFunction.d.ts</span></span>\n<span class=\"line\"><span style=\"color:#569CD6\">declare</span><span style=\"color:#569CD6\"> function</span><span style=\"color:#DCDCAA\"> myFunction</span><span style=\"color:#D4D4D4\">(): </span><span style=\"color:#4EC9B0\">void</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"><span style=\"color:#C586C0\">export</span><span style=\"color:#C586C0\"> default</span><span style=\"color:#9CDCFE\"> myFunction</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"></span></code></pre>\n<p>In the JavaScript, the <code>module.exports = myFunction</code> completely overwrites the effect of <code>exports.default = myFunction</code>. But the compiler doesn’t know that; all it sees (from a type perspective) is the default export, so that’s what it includes in the declaration file. So the <a href=\"https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseExportDefault.md\">JavaScript and TypeScript are out of sync</a>: the types assert that the shape of the module is <code>{ default: () =&gt; void }</code>, when in reality, it’s <code>() =&gt; void</code>. If a JavaScript user were to <code>require</code> this module, IntelliSense would guide them to write:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-js\"><span class=\"line\"><span style=\"color:#DCDCAA\">require</span><span style=\"color:#D4D4D4\">(</span><span style=\"color:#CE9178\">\"pkg/myFunction\"</span><span style=\"color:#D4D4D4\">).</span><span style=\"color:#DCDCAA\">default</span><span style=\"color:#D4D4D4\">();</span></span>\n<span class=\"line\"></span></code></pre>\n<p>when they actually need to write</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-js\"><span class=\"line\"><span style=\"color:#DCDCAA\">require</span><span style=\"color:#D4D4D4\">(</span><span style=\"color:#CE9178\">\"pkg/myFunction\"</span><span style=\"color:#D4D4D4\">)();</span></span>\n<span class=\"line\"></span></code></pre>\n<p>💥</p>\n<h2 id=\"the-simplest-solution\" tabindex=\"-1\"><a class=\"no-underline\" href=\"https://blog.andrewbran.ch/default-exports-in-commonjs-libraries/#the-simplest-solution\">The simplest solution</a></h2>\n<p>The idea that the output JavaScript should use <code>module.exports =</code> instead of <code>exports.default =</code> is a good one; we just need to accomplish that in a way TypeScript understands, so the JavaScript and declaration output stay in sync with each other. Fortunately, there is a TypeScript-specific syntax for <code>module.exports =</code>:</p>\n<pre class=\"shiki dark-modern has-diff\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-js\"><span class=\"line\"><span style=\"color:#569CD6\">function</span><span style=\"color:#DCDCAA\"> myFunction</span><span style=\"color:#D4D4D4\">() { </span><span style=\"color:#6A9955\">/* ... */</span><span style=\"color:#D4D4D4\"> }</span></span>\n<span class=\"line diff remove\"><span style=\"color:#C586C0\">export</span><span style=\"color:#C586C0\"> default</span><span style=\"color:#9CDCFE\"> myFunction</span><span style=\"color:#D4D4D4\">; </span></span>\n<span class=\"line diff add\"><span style=\"color:#C586C0\">export</span><span style=\"color:#D4D4D4\"> = </span><span style=\"color:#9CDCFE\">myFunction</span><span style=\"color:#D4D4D4\">;       </span></span>\n<span class=\"line\"></span></code></pre>\n<p>This creates the output pair:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-js\"><span class=\"line\"><span style=\"color:#6A9955\">// myFunction.js</span></span>\n<span class=\"line\"><span style=\"color:#CE9178\">\"use strict\"</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"><span style=\"color:#569CD6\">function</span><span style=\"color:#DCDCAA\"> myFunction</span><span style=\"color:#D4D4D4\">() { </span><span style=\"color:#6A9955\">/* ... */</span><span style=\"color:#D4D4D4\"> }</span></span>\n<span class=\"line\"><span style=\"color:#4EC9B0\">module</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#4EC9B0\">exports</span><span style=\"color:#D4D4D4\"> = </span><span style=\"color:#9CDCFE\">myFunction</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"></span></code></pre>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-ts\"><span class=\"line\"><span style=\"color:#6A9955\">// myFunction.d.ts</span></span>\n<span class=\"line\"><span style=\"color:#569CD6\">declare</span><span style=\"color:#569CD6\"> function</span><span style=\"color:#DCDCAA\"> myFunction</span><span style=\"color:#D4D4D4\">(): </span><span style=\"color:#4EC9B0\">void</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"><span style=\"color:#C586C0\">export</span><span style=\"color:#D4D4D4\"> = </span><span style=\"color:#9CDCFE\">myFunction</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"></span></code></pre>\n<p>The JavaScript uses <code>module.exports</code> and the types agree. Success!</p>\n<p>One inconvenience to this approach is that you’re not allowed to include other top-level named exports alongside the <code>export =</code>:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-ts\"><span class=\"line\"><span style=\"color:#C586C0\">export</span><span style=\"color:#569CD6\"> interface</span><span style=\"color:#4EC9B0\"> MyFunctionOptions</span><span style=\"color:#D4D4D4\"> { </span><span style=\"color:#6A9955\">/* ... */</span><span style=\"color:#D4D4D4\"> }</span></span>\n<span class=\"line\"><span style=\"color:#C586C0\">export</span><span style=\"color:#D4D4D4\"> = </span><span style=\"color:#9CDCFE\">myFunction</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"><span style=\"color:#6A9955\">// ^^^^^^^^^^^^^^^^</span></span>\n<span class=\"line\"><span style=\"color:#6A9955\">// ts2309: An export assignment cannot be used in a module with</span></span>\n<span class=\"line\"><span style=\"color:#6A9955\">//         other exported elements.</span></span>\n<span class=\"line\"></span></code></pre>\n<p>Doing this requires a somewhat unintuitive workaround:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-ts\"><span class=\"line\"><span style=\"color:#569CD6\">function</span><span style=\"color:#DCDCAA\"> myFunction</span><span style=\"color:#D4D4D4\">() { </span><span style=\"color:#6A9955\">/* ... */</span><span style=\"color:#D4D4D4\"> }</span></span>\n<span class=\"line\"><span style=\"color:#569CD6\">namespace</span><span style=\"color:#4EC9B0\"> myFunction</span><span style=\"color:#D4D4D4\"> {</span></span>\n<span class=\"line\"><span style=\"color:#C586C0\">  export</span><span style=\"color:#569CD6\"> interface</span><span style=\"color:#4EC9B0\"> MyFunctionOptions</span><span style=\"color:#D4D4D4\"> { </span><span style=\"color:#6A9955\">/* ... */</span><span style=\"color:#D4D4D4\"> }</span></span>\n<span class=\"line\"><span style=\"color:#D4D4D4\">}</span></span>\n<span class=\"line\"><span style=\"color:#C586C0\">export</span><span style=\"color:#D4D4D4\"> = </span><span style=\"color:#9CDCFE\">myFunction</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"></span></code></pre>\n<h2 id=\"why-is-such-a-footgun-allowed-to-exist%3F\" tabindex=\"-1\"><a class=\"no-underline\" href=\"https://blog.andrewbran.ch/default-exports-in-commonjs-libraries/#why-is-such-a-footgun-allowed-to-exist%3F\">Why is such a footgun allowed to exist?</a></h2>\n<p>Default exports, even transformed to CommonJS, are not a problem as long as the only person importing them is <em>you</em>, within an app where all your files are written in ESM syntax and compiled with the same compiler with an internally consistent set of transformations. Tools to compile ESM to CommonJS started emerging in 2014, before the specification was even finalized. I’m not sure anyone thought very hard about the possible long-term consequences of pushing a huge amount of ESM-transformed-to-CommonJS code to the npm registry.</p>\n<p>TypeScript library authors are now encouraged to compile with the <code>verbatimModuleSyntax</code> option, which prevents writing ESM syntax when generating CommonJS output (i.e., the compiler forces you to use <code>export =</code> instead of <code>export default</code>), completely sidestepping this compatibility confusion.</p>\n<h2 id=\"looking-ahead\" tabindex=\"-1\"><a class=\"no-underline\" href=\"https://blog.andrewbran.ch/default-exports-in-commonjs-libraries/#looking-ahead\">Looking ahead</a></h2>\n<p>I see two reasons to be hopeful for the future.</p>\n<p><a href=\"https://devblogs.microsoft.com/typescript/announcing-typescript-5-5-beta/\">TypeScript 5.5</a>, just released in beta, introduces an <code>--isolatedDeclarations</code> option, which lays the groundwork for third-party tools to implement their own fast, syntax-based declaration emit. I mentioned earlier that some existing third-party tools have options that solve this problem for JavaScript emit, but don’t generate declaration files, potentially creating a worse problem. Hopefully, the next generation of JavaScript and TypeScript tooling will generate declaration files that accurately represent the transformations they do to generate their JavaScript output.</p>\n<p>Secondly, <a href=\"https://nodejs.org/en/blog/announcements/v22-release-announce\">Node.js v22</a> includes experimental support for being able to <code>require</code> ESM graphs. If that feature lands unflagged in the future, there will be <em>much</em> less of a reason for library authors to continue shipping CommonJS to npm.</p>\n",
      "date_published": "2024-04-28T00:00:00Z"
    }
    ,
    {
      "id": "https://blog.andrewbran.ch/is-nodenext-right-for-libraries-that-dont-target-node-js/",
      "url": "https://blog.andrewbran.ch/is-nodenext-right-for-libraries-that-dont-target-node-js/",
      "title": "Is `nodenext` right for libraries that don’t target Node.js?",
      "content_html": "<p>I started to reply to <a href=\"https://twitter.com/mattpocockuk/status/1724495021745860793\">this tweet</a>, but I think it deserves more than a hasty string of 240-character concatenations:</p>\n<blockquote class=\"rounded-lg bg-[var(--color-fg05)] p2 md:p4\">\n<div class=\"font-grotesk\">\n<p><strong>Matt Pocock</strong><br />\n<a class=\"text-textSecondary\" href=\"https://twitter.com/mattpocockuk\">@mattpocockuk</a></p>\n</div>\n<p>cc <a href=\"https://twitter.com/atcb\">@atcb</a> in case I've got something drastically wrong.</p>\n<p>Summary:</p>\n<p>I think that if you transpile TS code with <code>NodeNext</code>, that will be compatible with any modern bundler (any <code>moduleResolution: Bundler</code> environment).</p>\n<p><a href=\"https://twitter.com/tpillard\">@tpillard</a> thinks that there are issues which mean you should transpile one export with <code>moduleResolution: Bundler</code> and another with <code>moduleResolution: NodeNext</code> if you plan to support both.</p>\n<small class=\"font-grotesk text-textSecondary\">\n<a href=\"https://twitter.com/mattpocockuk/status/1724495021745860793\">10:30 AM · Nov 14, 2023</a>\n</small>\n</blockquote>\n<p>I <em>mostly</em> agree with Matt, and his advice earlier in the thread more or less matches what I published in TypeScript’s <a href=\"https://www.typescriptlang.org/docs/handbook/modules/guides/choosing-compiler-options.html#im-writing-a-library\">Choosing Compiler Options</a> guide for modules.</p>\n<p>It’s worth unpacking some of the nuances here. Something important about Tim’s position, and about how <code>tsc</code> works, is lost to the Twitter shorthand in Matt’s characterization above. <code>moduleResolution</code> doesn’t affect <code>tsc</code>’s emit, so producing two different outputs toggling that option alone would do nothing for consumers. But as Matt and Tim both know, it’s not possible to toggle <em>just</em> <code>moduleResolution</code> between <code>bundler</code> and <code>nodenext</code>, because each of these enforces a corresponding <code>module</code> option, which <em>does</em> affect emit:</p>\n<ul>\n<li><code>moduleResolution: bundler</code> requires <code>module: esnext</code></li>\n<li><code>moduleResolution: nodenext</code> requires <code>module: nodenext</code></li>\n</ul>\n<p>So at face value, the real question posed in Matt’s tweet is whether there are differences in emit between <code>module: esnext</code> and <code>module: nodenext</code> that might cause bundlers to trip over <code>nodenext</code> code.</p>\n<h2 id=\"emit-differences\" tabindex=\"-1\"><a class=\"no-underline\" href=\"https://blog.andrewbran.ch/is-nodenext-right-for-libraries-that-dont-target-node-js/#emit-differences\">Emit differences</a></h2>\n<p>The most important thing to understand about <code>module: nodenext</code> is it doesn’t just emit ESM; it emits whatever format it <em>has</em> to in order for Node.js not to crash; each output filename is inherently either ESM or CommonJS according to <a href=\"https://www.typescriptlang.org/docs/handbook/modules/theory.html#module-format-detection\">Node.js’s rules</a> and <code>tsc</code> chooses its emit format for each file based on those rules. So depending on the context, we might be asking be asking about the difference between CommonJS and ESM, or the difference between <code>module: esnext</code> and the particular flavor of ESM produced by <code>module: nodenext</code>.</p>\n<p>Bundlers are capable of processing (and typically willing to process) both CommonJS and ESM constructs wherever they appear (unlike Node.js, which <em>parses</em> ES modules and CommonJS scripts differently), so it’s fair to say that the potential difference in module format between <code>module: esnext</code> and <code>module: nodenext</code> will not itself break bundlers (although most bundlers have more difficulty tree-shaking CommonJS than ESM).</p>\n<p>There is, however, one specifically Node.js-flavored emit construct that could derail a bundler. In <code>module: nodenext</code>, a TypeScript <code>import</code>/<code>require</code> inside an ESM-format file:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-ts\"><span class=\"line\"><span style=\"color:#6A9955\">// @Filename: index.mts</span></span>\n<span class=\"line\"><span style=\"color:#C586C0\">import</span><span style=\"color:#9CDCFE\"> fs</span><span style=\"color:#D4D4D4\"> = </span><span style=\"color:#C586C0\">require</span><span style=\"color:#D4D4D4\">(</span><span style=\"color:#CE9178\">\"dep\"</span><span style=\"color:#D4D4D4\">);</span></span>\n<span class=\"line\"></span></code></pre>\n<p>has a special Node.js-specific emit:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-js\"><span class=\"line\"><span style=\"color:#6A9955\">// @Filename: index.mjs</span></span>\n<span class=\"line\"><span style=\"color:#C586C0\">import</span><span style=\"color:#D4D4D4\"> { </span><span style=\"color:#9CDCFE\">createRequire</span><span style=\"color:#C586C0\"> as</span><span style=\"color:#9CDCFE\"> _createRequire</span><span style=\"color:#D4D4D4\"> } </span><span style=\"color:#C586C0\">from</span><span style=\"color:#CE9178\"> \"module\"</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"><span style=\"color:#569CD6\">const</span><span style=\"color:#4FC1FF\"> __require</span><span style=\"color:#D4D4D4\"> = </span><span style=\"color:#DCDCAA\">_createRequire</span><span style=\"color:#D4D4D4\">(</span><span style=\"color:#C586C0\">import</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#9CDCFE\">meta</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#9CDCFE\">url</span><span style=\"color:#D4D4D4\">);</span></span>\n<span class=\"line\"><span style=\"color:#569CD6\">const</span><span style=\"color:#4FC1FF\"> fs</span><span style=\"color:#D4D4D4\"> = </span><span style=\"color:#DCDCAA\">__require</span><span style=\"color:#D4D4D4\">(</span><span style=\"color:#CE9178\">\"dep\"</span><span style=\"color:#D4D4D4\">);</span></span>\n<span class=\"line\"></span></code></pre>\n<p>I <em>assume</em> this won’t work in most bundlers, though some have built-in Node.js shims, so I could be proven wrong. But this is a clear case where <code>module: nodenext</code> would allow Node.js-specific code to be emitted; it’s just not something someone is likely to write by mistake.</p>\n<h2 id=\"module-resolution\" tabindex=\"-1\"><a class=\"no-underline\" href=\"https://blog.andrewbran.ch/is-nodenext-right-for-libraries-that-dont-target-node-js/#module-resolution\">Module resolution</a></h2>\n<p>Continuing <code>import</code>/<code>require</code> shows another way to frame the question. Suppose we had our input code:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-ts\"><span class=\"line\"><span style=\"color:#6A9955\">// @Filename: index.mts</span></span>\n<span class=\"line\"><span style=\"color:#C586C0\">import</span><span style=\"color:#9CDCFE\"> fs</span><span style=\"color:#D4D4D4\"> = </span><span style=\"color:#C586C0\">require</span><span style=\"color:#D4D4D4\">(</span><span style=\"color:#CE9178\">\"dep\"</span><span style=\"color:#D4D4D4\">);</span></span>\n<span class=\"line\"></span></code></pre>\n<p>and in order to avoid the potential for Node.js-specific emit to crash a user’s bundler, we decided to produce two outputs—one for Node.js and one for bundlers. When switching to <code>module: esnext</code> and <code>moduleResolution: bundler</code>, our input code errors:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color: #1F1F1F; white-space: normal\"><code style=\"color: #D4D4D4\">\nts1202: Import assignment cannot be used when targeting ECMAScript modules. Consider using 'import * as ns from \"dep\"', 'import {a} from \"dep\"', 'import d from \"dep\"', or another module format instead.\n</code></pre>\n<p>So the question isn’t fully answered just by looking at the transforms applied by these <code>module</code> modes; we also need to ask whether valid input code in <code>module: nodenext</code> is also valid in <code>module: esnext</code> and <code>moduleResolution: bundler</code>. If there are compilation errors, that’s a strong signal that the output code will be a problem.</p>\n<p>Beyond this one emit incompatibility, what other compilation errors are possible? Earlier in the Twitter thread, the theory was that module resolution is the problem. Let’s define exactly what that would mean. Imagine we have an output file like:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-js\"><span class=\"line\"><span style=\"color:#6A9955\">// @Filename: utils.mjs</span></span>\n<span class=\"line\"><span style=\"color:#C586C0\">import</span><span style=\"color:#D4D4D4\"> { </span><span style=\"color:#9CDCFE\">sayHello</span><span style=\"color:#D4D4D4\"> } </span><span style=\"color:#C586C0\">from</span><span style=\"color:#CE9178\"> \"greetings\"</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"><span style=\"color:#DCDCAA\">sayHello</span><span style=\"color:#D4D4D4\">([</span><span style=\"color:#CE9178\">\"Matt\"</span><span style=\"color:#D4D4D4\">, </span><span style=\"color:#CE9178\">\"Tim\"</span><span style=\"color:#D4D4D4\">]);</span></span>\n<span class=\"line\"></span></code></pre>\n<p>Suppose we generated this file from a TypeScript input that compiled error-free under <code>module: nodenext</code>, meaning that TypeScript did its best to model how Node.js will resolve the specifier <code>&quot;greetings&quot;</code>, found type declarations for the resolved module, and verified that the usage of the API was correct. The theory that this analysis does not provide a similar level of confidence that this code will work in a bundler due to module resolution differences presupposes that at least one of these outcomes is possible:</p>\n<ol>\n<li>A bundler will not be able to resolve <code>&quot;greetings&quot;</code></li>\n<li>A bundler will resolve <code>&quot;greetings&quot;</code> to a module that has a sufficiently different shape, such that, if the types for that module were known, TypeScript would report an error for the usage of the API</li>\n</ol>\n<p>These are both <strong>absolutely possible</strong> via conditional package.json <code>&quot;exports&quot;</code>. For example, <code>&quot;greetings&quot;</code> could define <code>&quot;exports&quot;</code> like:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-json\"><span class=\"line\"><span style=\"color:#D4D4D4\">{</span></span>\n<span class=\"line\"><span style=\"color:#9CDCFE\">\t\"exports\"</span><span style=\"color:#D4D4D4\">: {</span></span>\n<span class=\"line\"><span style=\"color:#9CDCFE\">\t\t\"module\"</span><span style=\"color:#D4D4D4\">: </span><span style=\"color:#CE9178\">\"./contains-only-say-goodbye.js\"</span><span style=\"color:#D4D4D4\">,</span></span>\n<span class=\"line\"><span style=\"color:#9CDCFE\">\t\t\"node\"</span><span style=\"color:#D4D4D4\">: </span><span style=\"color:#CE9178\">\"./contains-only-say-hello.js\"</span></span>\n<span class=\"line\"><span style=\"color:#D4D4D4\">\t}</span></span>\n<span class=\"line\"><span style=\"color:#D4D4D4\">}</span></span>\n<span class=\"line\"></span></code></pre>\n<p>This would direct bundlers to one module and Node.js to another, each having a completely different API. In this case, it would be impossible to write a single import declaration that works in both contexts, and TypeScript could be used to catch an error like this by type checking the input code under multiple <code>module</code>/<code>moduleResolution</code>/<code>customConditions</code> settings. But this is terrible, terrible practice! I don’t think I’ve ever seen this in a real npm package (and I have looked at a <em>lot</em> of package.jsons in the last year).</p>\n<p>Indeed, the <em>only</em> difference between <code>moduleResolution: bundler</code> and the CommonJS <code>moduleResolution: nodenext</code> algorithm in TypeScript is import conditions, and every resolution that can be made in the ESM <code>moduleResolution: nodenext</code> algorithm will be made the same way in <code>moduleResolution: bundler</code>, with the exception of where import conditions cause them to diverge. (If there are other differences in the <em>real</em> resolution algorithms of bundlers and Node.js, they are not reflected in TypeScript’s algorithm, so additional checking with TypeScript won’t help.) Keep in mind that bundlers came about in large part so that modules on npm, written under the assumption that only Node.js would be able to use them, could be used in the browser, which lacked a module system at the time. Bundlers would not be doing their job if their module resolution algorithms choked on Node.js code. Conditional exports <em>can</em> be used to redirect a bundler, but doing this in a way that breaks the contract of the module that Node.js would see is arguably a bug.</p>\n<p>In other words, module resolution is not an issue unless a package is doing something extra terrible with <code>&quot;exports&quot;</code>.</p>\n<h2 id=\"module-interop\" tabindex=\"-1\"><a class=\"no-underline\" href=\"https://blog.andrewbran.ch/is-nodenext-right-for-libraries-that-dont-target-node-js/#module-interop\">Module interop</a></h2>\n<p>The last possible incompatibility (that TypeScript can catch) is how modules of different formats interoperate with each other. I’ve written about this <a href=\"https://www.typescriptlang.org/docs/handbook/modules/appendices/esm-cjs-interop.html\">extensively elsewhere</a>, so suffice it to say that it is possible to have a default import:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-ts\"><span class=\"line\"><span style=\"color:#C586C0\">import</span><span style=\"color:#9CDCFE\"> sayHello</span><span style=\"color:#C586C0\"> from</span><span style=\"color:#CE9178\"> \"greetings\"</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"></span></code></pre>\n<p>that <em>must</em> be called one way in a bundler:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-ts\"><span class=\"line\"><span style=\"color:#DCDCAA\">sayHello</span><span style=\"color:#D4D4D4\">();</span></span>\n<span class=\"line\"></span></code></pre>\n<p>and another, unintentionally different way in Node.js:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-ts\"><span class=\"line\"><span style=\"color:#9CDCFE\">sayHello</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#DCDCAA\">default</span><span style=\"color:#D4D4D4\">();</span></span>\n<span class=\"line\"></span></code></pre>\n<p>even though both module systems resolved to the same module. If you are writing a library, compiling to ESM, checking with <code>module: nodenext</code>, and find yourself needing to write <code>.default()</code> on something you default-imported, there is a possibility that your output code <a href=\"https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/CJSOnlyExportsDefault.md\">will not work in a bundler</a>. (There is also a possibility that the type declarations for the library are incorrectly telling TypeScript that <code>.default</code> is needed, when in fact it is either not needed or <a href=\"https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseExportDefault.md\">not present</a>!)</p>\n<h2 id=\"conclusion\" tabindex=\"-1\"><a class=\"no-underline\" href=\"https://blog.andrewbran.ch/is-nodenext-right-for-libraries-that-dont-target-node-js/#conclusion\">Conclusion</a></h2>\n<p>Matt’s advice, my usual advice, and the <a href=\"https://www.typescriptlang.org/docs/handbook/modules/guides/choosing-compiler-options.html#im-writing-a-library\">TypeScript documentation’s advice</a> is that you’re <em>usually basically</em> fine to use <code>nodenext</code> for all libraries:</p>\n<blockquote>\n<p>Choosing compilation settings as a library author is a fundamentally different process from choosing settings as an app author. When writing an app, settings are chosen that reflect the runtime environment or bundler—typically a single entity with known behavior. When writing a library, you would ideally check your code under <em>all possible</em> library consumer compilation settings. Since this is impractical, you can instead use the strictest possible settings, since satisfying those tends to satisfy all others.</p>\n</blockquote>\n<p>But Tim is right that this is imperfect. It’s definitely possible to construct examples that break. In reality, breaks only occur in the face of packages that are behaving badly. (Unfortunately, a fair number of packages behave badly.)</p>\n<p>I think I would summarize as:</p>\n<ul>\n<li><code>nodenext</code> is the right option for authoring libraries, because it prevents you from emitting ESM with module specifiers that <em>only</em> work in bundlers but will crash in Node.js. When writing conventional code, using common sense, and relying on high-quality dependencies, its output is usually highly compatible with bundlers and other runtimes.</li>\n<li>There is no TypeScript option specifically meant to enforce patterns that result in maximum cross-environment portability.</li>\n<li>When stronger guarantees of portability are needed, there is no substitute for runtime testing your output.</li>\n<li>However, a lower effort and reasonably good confidence booster would be to run <code>tsc --noEmit</code> under different <code>module</code>/<code>moduleResolution</code> options.</li>\n</ul>\n",
      "date_published": "2023-11-14T00:00:00Z"
    }
    ,
    {
      "id": "https://blog.andrewbran.ch/new-blog/",
      "url": "https://blog.andrewbran.ch/new-blog/",
      "title": "New Blog",
      "content_html": "<p>When I first created this blog in 2019, its inaugural post was titled <a href=\"https://blog.andrewbran.ch/overengineering-a-blog/\"><em>Overengineering a Blog</em></a>, which is precisely what I did. I wanted every code block to be a tiny TypeScript playground of sorts. Unsurprisingly, this was a huge pain to maintain, and I lost interest not only in using the interactive features I worked so hard to create but also in writing altogether. I put a colossal amount of effort into a few of the early posts, which made me hesitant to publish anything less polished. Shifting to working from home during COVID brought about a general technology fatigue during which I couldn’t imagine thinking about TypeScript or even being on a computer outside of work hours. The combination of these factors led to the three-and-a-half-year hiatus in posts preceding this one.</p>\n<h2 id=\"lowering-the-bar(rier)\" tabindex=\"-1\"><a class=\"no-underline\" href=\"https://blog.andrewbran.ch/new-blog/#lowering-the-bar(rier)\">Lowering the bar(rier)</a></h2>\n<p>In considering relaunching the blog and beginning to write again, I didn’t just want to make the infrastructure easier to maintain; I also wanted to give myself permission to publish posts that don’t take so much work that I burn out on writing for years afterward. Years ago, I was hoping to write mostly opinionated educational content, so I was highly concerned with correctness and credibility, and only dared to write about topics on which I thought I could present as an expert. Now, there’s only <a href=\"https://www.typescriptlang.org/docs/handbook/modules/introduction.html\">one topic</a> I can plausibly claim to be an expert on, so I don’t plan to write a lot of posts with the aim of convincing anyone to do anything. I feel more comfortable now with simply presenting information I find interesting, which makes it much easier to publish.</p>\n<h2 id=\"minimal-design-changes\" tabindex=\"-1\"><a class=\"no-underline\" href=\"https://blog.andrewbran.ch/new-blog/#minimal-design-changes\">Minimal design changes</a></h2>\n<p>I was tempted to redesign everything, but in the spirit of trying to actually write again instead of spending forever just working on the site, decided not to. I made a few small changes that I’m really happy with:</p>\n<ul>\n<li>Posts on the <a href=\"https://blog.andrewbran.ch/\">index page</a> now have an intentional description, rendered in a sans-serif, instead of an arbitrary truncation of the post content. I bought the regular weight of <a href=\"https://klim.co.nz/retail-fonts/founders-grotesk/\">Founders Grotesk</a> for this purpose, which I previously had only in medium for photo captions, footer text, and the About page link. I replaced some of these uses with the regular weight and like the way it lightened things up a bit.</li>\n<li>I removed the light/dark switcher in favor of using the system preference unconditionally.</li>\n<li>Code blocks always use a dark theme instead of switching between light and dark with the rest of the site. I am not anti-light-theme for code but I didn’t like the way it looked in this context. I also updated the dark theme colors to match VS Code’s new Dark Modern, which is the best syntax theme, having recently taken the crown over the previous best, VS Code’s Default Dark+.</li>\n<li>Links in post bodies are now permanently underlined with no hover effect, and have the same color as normal body text. They used to be blue, with an underline on hover.</li>\n<li>I added “newer” and “older” links at the bottom of posts, finding another place to use Founders Grotesk.</li>\n<li>The site footer is simplified, with icons removed. (The Twitter link is gone; the feed icon was replaced with text; the GitHub icon moved to the top to replace the theme switcher.)</li>\n<li>I also stole <a href=\"https://antfu.me/posts/sliding-enter-animation\">Anthony Fu’s sliding enter animation</a>. Historically, I have been very opposed to animation that doesn’t serve an important functional purpose, but upon seeing this on Anthony’s blog, I was instantly enamored with it. I’m not sure how I’ll feel once the novelty wears off, but I decided to take a chance on it for now.</li>\n</ul>\n<p>After making these changes, I found I was still quite happy with the design. A part of me wanted to switch to a humanist sans-serif body font for technical posts, which I expect to make up the majority of future posts, but I couldn’t give up <a href=\"https://klim.co.nz/collections/heldane/\">Heldane</a> for <a href=\"https://blog.andrewbran.ch/one-does-not-simply-walk-into-mordor/\">prose</a>. I may experiment with using different typefaces for posts of different categories, but this is fine for now.</p>\n<h2 id=\"choosing-the-new-stack\" tabindex=\"-1\"><a class=\"no-underline\" href=\"https://blog.andrewbran.ch/new-blog/#choosing-the-new-stack\">Choosing the new stack</a></h2>\n<p>The old blog was built with Gatsby. Since I decided to remove all the interactive components, there was no longer any reason to use React (or to have any client-side JavaScript at all) as part of the site generator. (I was not opposed to using React/JSX as my templating language, but without the need to hydrate any statically rendered components, it was not a necessity.) I considered <a href=\"https://gohugo.io/\">Hugo</a> for its unmatched speed, but decided that <a href=\"https://www.11ty.dev/\">Eleventy</a> would be faster for me to configure and customize since I know JavaScript about a million times better than I know Go.</p>\n<p>For styling, the old blog used <a href=\"https://github.com/emotion-js/emotion\">Emotion</a> and <a href=\"https://kyleamathews.github.io/typography.js/\">Typography.js</a>, which I actually liked quite a bit, but didn’t make sense to use without React or JavaScript. I ported my typography styles to plain CSS and used <a href=\"https://tailwindcss.com/\">Tailwind</a> via <a href=\"https://unocss.dev/\">UnoCSS</a> for the layout.</p>\n<p>I also decided to drop Google Analytics. I don’t look at the metrics, and don’t want to be complicit in any privacy harm it may be doing. I might try to self-host a more privacy-focused analytics platform in the future, but I’m ok not knowing anything about the traffic for now.</p>\n<h3 id=\"what-i-like\" tabindex=\"-1\"><a class=\"no-underline\" href=\"https://blog.andrewbran.ch/new-blog/#what-i-like\">What I like</a></h3>\n<ul>\n<li>Eleventy is fast and simple. It was extremely easy to get running, learn how to use, and recreate the structure of the blog. The vast majority of my time was spent on porting styles, not on configuring Eleventy.</li>\n<li>Eleventy’s primitive bundler plugin was sufficient to bundle all my vendor CSS (like a reset and Katex styles) into one asset-hashed file and to bundle my generated Tailwind utilities and other site CSS into another asset-hashed file, and write the <code>link</code> tags pointing to those generated URLs. More than good enough without taking a dependency on a full bundler.</li>\n<li>The selection of official and community <a href=\"https://www.11ty.dev/docs/plugins/\">Eleventy plugins</a> is decent, and where there were gaps, I found it easy enough to hack what I needed into a shortcode. We’re just generating HTML from data, in contrast to Gatsby, which had so many different contribution points and whose overall architectural model eluded me for the entire time I used it.</li>\n<li>Tailwind’s documentation is absolutely top-notch.</li>\n<li>Netlify is still great. After not touching it for 3.5 years, all I had to do was tell it to use a newer Ubuntu, and it picked up the new build system like nothing ever happened.</li>\n</ul>\n<h3 id=\"what-i-dislike\" tabindex=\"-1\"><a class=\"no-underline\" href=\"https://blog.andrewbran.ch/new-blog/#what-i-dislike\">What I dislike</a></h3>\n<ul>\n<li>Types (whether generated or hand-written, JSDoc or <code>.d.ts</code>) for Eleventy configuration and plugins were almost universally incorrect, an incomplete afterthought, or completely absent. Writing any amount of JavaScript that interacts with the generator is a really poor experience for those who are accustomed to being able to rely on IntelliSense (which feels like, pretty much everyone using any modern JS tools now?).</li>\n<li>It drives me crazy that the search on Eleventy’s docs is its own page, which requires scrolling to the top and clicking on a link, with no auto-complete or live results filtering. It’s a small thing, but so many developer tooling documentation sites have spoiled us with ⌘K Algolia-powered search that not having it feels like a significant paper cut now.</li>\n<li>I’m not sure what I got out of using UnoCSS over Tailwind CSS directly, even after reading the <a href=\"https://unocss.dev/guide/why\">page that was supposed to explain it to me</a>. It talks a lot about customization, but everything I customized in the config can be done with Tailwind’s config too. Honestly, I used UnoCSS only because I heard positive buzz about it, but I found its documentation a little lacking (for example, it doesn’t explain that <code>theme.breakpoints</code> is just a renaming of Tailwind’s <code>theme.screens</code>, while seemingly every other theme option keeps the same name?), and I really missed Tailwind’s excellent VS Code extension. UnoCSS has a VS Code extension too, but it doesn’t activate on Liquid templates, and seemed not to be reading my customizations from my config file (perhaps because I named it <code>uno.config.mjs</code> instead of <code>uno.config.js</code>?). All it did was underline every occurrence of the word “my” in my Markdown files. After discovering the extension has no documentation whatsoever that I could use to attempt to debug these problems, I uninstalled it.</li>\n</ul>\n<h2 id=\"content-on-deck\" tabindex=\"-1\"><a class=\"no-underline\" href=\"https://blog.andrewbran.ch/new-blog/#content-on-deck\">Content on deck</a></h2>\n<p>I recently outlined a post detailing the history and design considerations of type-only imports, a TypeScript feature I worked on. I think that will be interesting for, you know, a very niche audience, but it would benefit from being able to lean on a separate technical explainer of TypeScript declarations and symbols, which is something I wish someone had already written when I was learning the compiler API. So I plan to start working on those two soon.</p>\n",
      "date_published": "2023-11-12T00:00:00Z"
    }
    ,
    {
      "id": "https://blog.andrewbran.ch/marketing-web-rules/",
      "url": "https://blog.andrewbran.ch/marketing-web-rules/",
      "title": "Marketing Web Rules",
      "content_html": "<p><span class=\"small-caps\">In early 2016</span>, I was working as a web developer on the marketing team at Xamarin. We had just built the event site for the third Xamarin Evolve, the company’s developer conference, to be held in Orlando in the spring. The event afforded an opportunity to our designers to push the Xamarin brand and palette into more adventurous territory, so the site materialized at a larger-than-life scale, with massive all-caps headings, hundreds of pixels between sections, and neon SVG animations, all set atop a vertical gradient background rotating through every color of a Florida sunset.</p>\n<p>My teammates and I also used it as an opportunity to try out some newer tech than what we had inherited in the monolithic .NET company site. React, Webpack, and CSS modules were becoming dominant forces in the frontend web world, and we were excited to bring a modern toolset into into our daily work. The result was infrastructural overkill, but it was also rapid iteration and developer happiness.</p>\n<p>In April, less than a month before the conference, we received two independent bug reports that the registration button—the single most important link on the site—was <em>missing</em> on mobile Safari. When we couldn’t reproduce the bug on our own devices or on any iOS simulator device, it was difficult to determine next steps—it’s tough to fix a bug you can’t observe. Eventually, after broadcasting a plea across the company Slack, we found a coworker whose iPhone exhibited the bug, and my teammate, <a href=\"https://www.smockle.com/\">Clay</a>, determined that the content blocker <a href=\"https://www.purify-app.com/\">Purify</a> was hiding the button.</p>\n<p>But… why? The content blocker wasn’t using any suspicious custom configuration; it was just supposed to block ads. What was unique about the registration button, when other buttons on the site were displaying correctly? Plugging the phone into a Mac and inspecting the button with Safari’s developer tools eliminated the guesswork. Purify was injecting a large number of CSS rules into the page, including this one:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-css\"><span class=\"line\"><span style=\"color:#D4D4D4\">[</span><span style=\"color:#9CDCFE\">class</span><span style=\"color:#D4D4D4\">$=</span><span style=\"color:#CE9178\">\"Ad\"</span><span style=\"color:#D4D4D4\">] {</span></span>\n<span class=\"line\"><span style=\"color:#9CDCFE\">\tdisplay</span><span style=\"color:#D4D4D4\">: </span><span style=\"color:#CE9178\">none</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"><span style=\"color:#D4D4D4\">}</span></span>\n<span class=\"line\"></span></code></pre>\n<p>That attribute selector syntax means <em>hide everything with a class name ending in “Ad.”</em> <sup class=\"footnote-ref\"><a href=\"https://blog.andrewbran.ch/marketing-web-rules/#fn1\" id=\"fnref1\">[1]</a></sup></p>\n<p>And sure enough, that applied to our button. We didn’t author a class ending in “Ad”; rather, we were having Webpack append a base64 content hash to the end of each selector to guarantee uniqueness across CSS files and prevent naming collisions, as is the common practice with CSS modules:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-\"><span class=\"line\"><span>localIdentName=[name]__[local]___[hash:base64:5]</span></span>\n<span class=\"line\"><span></span></span></code></pre>\n<p>This meant that for a class name <code>register-button</code> in a file <code>styles.css</code>, the emitted class name would be something like <code>styles__register-button__fQ2Ad</code>, where the five-character suffix is a hash of the file content and the original class name—effectively a random set of letters and numbers. And it just happened that after an innocuous update of other CSS in that file, the hash for the single most important element on the page changed to end in <code>Ad</code>.<sup class=\"footnote-ref\"><a href=\"https://blog.andrewbran.ch/marketing-web-rules/#fn2\" id=\"fnref2\">[2]</a></sup></p>\n<p>A wacky solution to a wacky problem—if you don’t want a random string to end in <code>Ad</code>, make sure it ends in something else:</p>\n<p><picture><source type=\"image/webp\" srcset=\"https://blog.andrewbran.ch/img/rpA_KDxObd-2052.webp 2052w\" /><img alt=\"A commit displayed in GitHub showing a one-line change to the Webpack config from Clay, with the commit message “Purify blocks class names that end in 'Ad'. The auto-generated class name for the conference button ended in 'Ad' :facepalm:. Now it ends in 'Adc'.”\" class=\"\" loading=\"lazy\" decoding=\"async\" src=\"https://blog.andrewbran.ch/img/rpA_KDxObd-2052.png\" width=\"2052\" height=\"1030\" /></picture></p>\n<p>Clay appended the letter <code>c</code> to every generated class name. Problem solved.</p>\n<p>Interacting with buggy websites is a near daily occurrence, and as a software engineer who, I like to think, generally did a good job with the material I created when I worked in web, I instinctively feel incredulous when I encounter glaring errors on high-traffic production sites. <em>This login form had one job. Who did something dumb?</em> When I think about our vanishing button, I’m inclined to look more generously at mistakes that appear to reflect incompetency. The simplicity of the bugs we observe often belies the complexity of the systems that created them.</p>\n<p class=\"asterism\">⁂</p>\n<p>A year later, I was writing the Webpack configuration for <a href=\"https://appcenter.ms/\">App Center</a> with the same teammates who worked with me on the Evolve site, and recalling the Purify incident, I preemptively guarded our loader configuration against it. In the period after Xamarin was acquired by Microsoft, we were still officially on the Xamarin marketing team, but as Xamarin work was winding down, we were starting to spend much of our time helping App Center get off the ground. We had a sense of pride, I think, that we were working on rather fundamental pieces of infrastructure as a rag-tag band of outsiders from a <em>marketing</em> team. And so, with the atmosphere of team spirit and all the creative maturity of a nine-year-old, I chose to suffix the content hash with the letters <code>mwr</code>, a secret acronym for “Marketing Web rules.” It had a practical justification as well, I told myself, since choosing <em>multiple</em> letters that are phonetically incongruous and never appear at the end of any word, I supposed, would have even lower chances of triggering overzealous ad-blocking rules. I committed the configuration, and then completely forgot about it for the next three years, until starting to write this post.</p>\n<p>In those intervening years, App Center has moved repositories twice, gone through at least two major version upgrades of Webpack, formed an official core frontend team, and turned over the majority of that original team, myself included. So there was no way our little easter egg survived all that churn, I thought. It must have been lost, or someone must have removed it after noticing it in the source, in the browser developer tools, or in all the times it’s come up in Slack when someone pastes a Webpack error message originating from css-loader.</p>\n<p><picture><source type=\"image/webp\" srcset=\"https://blog.andrewbran.ch/img/68x15pp-f--2618.webp 2618w\" /><img alt=\"A screenshot of the App Center login page with Safari developer tools open, showing many search results in the elements panel for the string “mwr.”\" class=\"dark:hidden\" loading=\"lazy\" decoding=\"async\" src=\"https://blog.andrewbran.ch/img/68x15pp-f--2618.png\" width=\"2618\" height=\"1984\" /></picture></p>\n<p><picture><source type=\"image/webp\" srcset=\"https://blog.andrewbran.ch/img/0otL5JTYqy-2618.webp 2618w\" /><img alt=\"A screenshot of the App Center login page with Safari developer tools open, showing many search results in the elements panel for the string “mwr.”\" class=\"light:hidden\" loading=\"lazy\" decoding=\"async\" src=\"https://blog.andrewbran.ch/img/0otL5JTYqy-2618.png\" width=\"2618\" height=\"1984\" /></picture></p>\n<p>Nope. To this day<sup class=\"footnote-ref\"><a href=\"https://blog.andrewbran.ch/marketing-web-rules/#fn3\" id=\"fnref3\">[3]</a></sup>, the string <code>mwr</code> appears 167 times in the DOM on the login page alone. Woops. I’m grateful for gzip.</p>\n<hr class=\"footnotes-sep\" />\n<section class=\"footnotes\">\n<ol class=\"footnotes-list\">\n<li id=\"fn1\" class=\"footnote-item\"><p>It appears that Purify has since removed wildcard attribute selectors like this one. <a href=\"https://blog.andrewbran.ch/marketing-web-rules/#fnref1\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n<li id=\"fn2\" class=\"footnote-item\"><p>A little back-of-the-napkin math: assuming a hash algorithm with a perfectly uniform distribution, the odds of a base64 string having a particular <em>N</em>-character suffix is <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mn>1</mn><mi mathvariant=\"normal\">/</mi><mn>6</mn><msup><mn>4</mn><mi>N</mi></msup></mrow><annotation encoding=\"application/x-tex\">1 / 64^N</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:1.0913em;vertical-align:-0.25em;\"></span><span class=\"mord\">1/6</span><span class=\"mord\"><span class=\"mord\">4</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.8413em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mathnormal mtight\" style=\"margin-right:0.10903em;\">N</span></span></span></span></span></span></span></span></span></span></span>. So, the chances of any one of our hashes ending in “Ad” was 1 in 4096. I had always assumed this event was a statistical anomaly, but given the number of CSS classes we generated (not just per-build, but over the course of dozens of updates where hashes were regenerated), it starts to look less surprising. <a href=\"https://blog.andrewbran.ch/marketing-web-rules/#fnref2\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n<li id=\"fn3\" class=\"footnote-item\"><p>Before publishing this piece, I gave the App Center team a heads up, and they promptly shortened class names in production down to just an 8-character hash (plus the letter <code>x</code>, for good measure), citing a 1.4% reduction in post-compression asset weight on the login page. In non-production environments? Still <code>mwr</code>. 😄 <a href=\"https://blog.andrewbran.ch/marketing-web-rules/#fnref3\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n</ol>\n</section>\n",
      "date_published": "2020-03-13T00:00:00Z"
    }
    ,
    {
      "id": "https://blog.andrewbran.ch/debugging-the-typescript-codebase/",
      "url": "https://blog.andrewbran.ch/debugging-the-typescript-codebase/",
      "title": "Debugging the TypeScript Codebase",
      "content_html": "<p><span class=\"small-caps\">When I joined the TypeScript team</span>, debugging quickly became my most valuable skill, and by the same token, the debugability of the compiler became one of the codebase’s most valuable assets. The TypeScript compiler is just a Node app so it’s pretty easy to debug, but I’ve found a few useful tricks specific to the TypeScript codebase. Thanks to a <a href=\"https://twitter.com/JoshuaKGoldberg/status/1174770454743179265\">request over Twitter</a>, here they are.</p>\n<p>The <a href=\"https://blog.andrewbran.ch/debugging-the-typescript-codebase/#quick-start-three-ways-to-attach\">first section</a> of this post is like a quick-start: I’ll get you a debug session running as quickly as possible. If you’re already somewhat familiar with the compiler, this might be all you need. But if you find yourself struggling to navigate that debug session or figure out what part of the code you want to debug, hang in there! The <a href=\"https://blog.andrewbran.ch/debugging-the-typescript-codebase/#going-deeper-debugging-strategies\">second section</a> provides tips for finding strategic breakpoint locations and inspecting the compiler state while paused.</p>\n<h2 id=\"quick-start%3A-three-ways-to-attach\" tabindex=\"-1\"><a class=\"no-underline\" href=\"https://blog.andrewbran.ch/debugging-the-typescript-codebase/#quick-start%3A-three-ways-to-attach\">Quick-Start: Three Ways to Attach</a></h2>\n<p>The first step for any method of debugging is to clone and build TypeScript from source so we can step through the original TypeScript source instead of the bundled JavaScript files.</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#DCDCAA\">git</span><span style=\"color:#CE9178\"> clone</span><span style=\"color:#CE9178\"> git@github.com:microsoft/TypeScript.git</span></span>\n<span class=\"line\"><span style=\"color:#DCDCAA\">cd</span><span style=\"color:#CE9178\"> TypeScript</span></span>\n<span class=\"line\"><span style=\"color:#DCDCAA\">npm</span><span style=\"color:#CE9178\"> install</span></span>\n<span class=\"line\"><span style=\"color:#DCDCAA\">npm</span><span style=\"color:#CE9178\"> run</span><span style=\"color:#CE9178\"> build</span></span>\n<span class=\"line\"></span></code></pre>\n<h3 id=\"debugging-from-tsc\" tabindex=\"-1\"><a class=\"no-underline\" href=\"https://blog.andrewbran.ch/debugging-the-typescript-codebase/#debugging-from-tsc\">Debugging from tsc</a></h3>\n<p>The built compiler and source maps are now in <code>built/local</code>, including a file called <code>tsc.js</code>. Anywhere you would normally use <code>tsc</code>, you can now use <code>node built/local/tsc.js</code>. For example:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#DCDCAA\">$</span><span style=\"color:#CE9178\"> node</span><span style=\"color:#569CD6\"> --inspect-brk</span><span style=\"color:#CE9178\"> built/local/tsc.js</span><span style=\"color:#569CD6\"> -p</span><span style=\"color:#CE9178\"> ../MyBuggyProject</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#DCDCAA\">Debugger</span><span style=\"color:#CE9178\"> listening</span><span style=\"color:#CE9178\"> on</span><span style=\"color:#CE9178\"> ws://127.0.0.1:9229/60b1b25a-f29d-4568-8619-b5e29b6dee25</span></span>\n<span class=\"line\"><span style=\"color:#DCDCAA\">For</span><span style=\"color:#CE9178\"> help,</span><span style=\"color:#CE9178\"> see:</span><span style=\"color:#CE9178\"> https://nodejs.org/en/docs/inspector</span></span>\n<span class=\"line\"></span></code></pre>\n<p>Node is paused at the beginning of tsc.js and waiting for you to attach the debugger of your choice. I’ll be demonstrating use of VS Code’s built-in debugger<sup class=\"footnote-ref\"><a href=\"https://blog.andrewbran.ch/debugging-the-typescript-codebase/#fn1\" id=\"fnref1\">[1]</a></sup>, but any Node debugger that can attach to a listening debug port will work.</p>\n<p>If you haven’t already, open the TypeScript codebase in VS Code. Open the command palette and select “Debug: Attach to Node Process,” then select the process you just started (on port 9229 by default).</p>\n<p><picture><source type=\"image/webp\" srcset=\"https://blog.andrewbran.ch/img/QtVjOMjl2P-1592.webp 1592w\" /><img alt=\"A screenshot of VS Code with the command palette open, searching “debug.” A list of results is shown with “Debug: Attach to Node Process” focused.\" class=\"\" loading=\"lazy\" decoding=\"async\" src=\"https://blog.andrewbran.ch/img/QtVjOMjl2P-1592.png\" width=\"1592\" height=\"1088\" /></picture></p>\n<p>VS Code will open tsc.js and show that the debugger is paused on the first line. From here, you can continue or step the debugger and hit breakpoints in the TypeScript source files.<sup class=\"footnote-ref\"><a href=\"https://blog.andrewbran.ch/debugging-the-typescript-codebase/#fn2\" id=\"fnref2\">[2]</a></sup></p>\n<h3 id=\"debugging-from-ts-server\" tabindex=\"-1\"><a class=\"no-underline\" href=\"https://blog.andrewbran.ch/debugging-the-typescript-codebase/#debugging-from-ts-server\">Debugging from TS Server</a></h3>\n<p>If you need to debug a language service feature (like a refactor, a code fix, the formatter, or code completion), debugging VS Code’s TS Server instance is often the most convenient approach. Again, you’ll need the TypeScript codebase cloned, built, and opened in one VS Code window. You’ll also need <em>another</em> VS Code window opened to a project of your choice. (I have a dedicated project filled with nonsense TypeScript and JavaScript files for this purpose.) We’ll use the former VS Code window to debug the latter. (Impressively, a single VS Code instance <em>can</em> debug its own TS Server process, but TypeScript-powered editor features like go-to-definition don’t work while the process is paused, so it’s much easier to use two windows.)</p>\n<p>The window you want to debug needs to be opened with the environment variable <code>TSS_DEBUG</code> set to a port number. If you have the <code>code</code> CLI tool, you can do this from the command line:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#DCDCAA\">cd</span><span style=\"color:#CE9178\"> example-project</span></span>\n<span class=\"line\"><span style=\"color:#9CDCFE\">TSS_DEBUG</span><span style=\"color:#D4D4D4\">=</span><span style=\"color:#B5CEA8\">9559</span><span style=\"color:#DCDCAA\"> code</span><span style=\"color:#CE9178\"> .</span></span>\n<span class=\"line\"></span></code></pre>\n<p><em><strong>January 2020 update:</strong> I recently published a <a href=\"https://marketplace.visualstudio.com/items?itemName=andrewbranch.vscode-tsserver-debug\">VS Code extension</a> that can restart an open window’s TS Server process with a debug port listening. Could save you a step!</em></p>\n<p>Next, you need to tell that VS Code window where to find the version of TypeScript that you built locally so it can be used for TS Server. Create or modify your example project’s <code>.vscode/settings.json</code> file with the following setting:</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-json\"><span class=\"line\"><span style=\"color:#D4D4D4\">{</span></span>\n<span class=\"line\"><span style=\"color:#9CDCFE\">\t\"typescript.tsdk\"</span><span style=\"color:#D4D4D4\">: </span><span style=\"color:#CE9178\">\"../path/to/TypeScript/built/local\"</span></span>\n<span class=\"line\"><span style=\"color:#D4D4D4\">}</span></span>\n<span class=\"line\"></span></code></pre>\n<p>Now, back in the window with the TypeScript codebase, open the command palette and select “Debug: Attach to Node Process,” then select the process running on the port you selected for <code>TSS_DEBUG</code>.</p>\n<p>This time, you’re connected to a long-running process that’s not paused. To pause on something useful, you’ll need to set a breakpoint in an interesting function and trigger that function from your example project window. A good place to start is <a href=\"https://github.com/microsoft/TypeScript/blob/10e3f11c0d88b991eaca600ff71d01a603a769a3/src/services/services.ts#L2121-L2176\">services.ts</a>. As an example, to step through quick info generation, set a breakpoint in the function called <code>getQuickInfoAtPosition</code>, then in the example project window, hover a variable in a TypeScript or JavaScript file. The debugger in the other window should pause on that breakpoint.</p>\n<p><picture><source type=\"image/gif\" srcset=\"https://blog.andrewbran.ch/img/ZAQw7-bdzB-1224.gif 1224w\" /><img alt=\"A screen capture showing one VS Code window attach to another’s TS Server process by using the “Debug: Attach to Node Process command.”\" class=\"\" loading=\"lazy\" decoding=\"async\" src=\"https://blog.andrewbran.ch/img/ZAQw7-bdzB-1224.webp\" width=\"1224\" height=\"602\" /></picture></p>\n<h3 id=\"debugging-tests\" tabindex=\"-1\"><a class=\"no-underline\" href=\"https://blog.andrewbran.ch/debugging-the-typescript-codebase/#debugging-tests\">Debugging tests</a></h3>\n<p>The last method of debugging I’ll cover is perhaps the most expedient of all, especially if you’re working on actually contributing a bug fix or feature to the compiler. To do so, you’ll of course want to write tests, and it turns out that debugging those tests is really easy.</p>\n<p>All the files in <code>tests/cases/compiler</code> and <code>tests/cases/conformance</code> are just snippets of TypeScript (or JavaScript) that the compiler runs against. You won’t find any assertions in them; instead, info about how the compiler runs on these snippets is saved to <code>tests/baselines/reference</code>, and the assertion is that future compilations always match the info saved there. (This is exactly the same concept as snapshot testing, which you might be familiar with in Jest.)</p>\n<p>The TypeScript codebase includes a VS Code debugger configuration file for working with these files. To use it, simply copy <code>.vscode/launch.template.json</code> to <code>.vscode/launch.json</code>. Set breakpoints in the part of the compiler you’re interested in (I’ll cover some tips on how to find this shortly), then, open any test file in <code>tests/cases/compiler</code> or <code>tests/cases/conformance</code> (or <code>tests/cases/fourslash</code>, but those can be a bit trickier). Open VS Code’s debug panel, and click the play button. After a few moments, you should hit your first breakpoint.</p>\n<h2 id=\"going-deeper%3A-debugging-strategies\" tabindex=\"-1\"><a class=\"no-underline\" href=\"https://blog.andrewbran.ch/debugging-the-typescript-codebase/#going-deeper%3A-debugging-strategies\">Going Deeper: Debugging Strategies</a></h2>\n<p>Now you know the mechanics of how to start debugging, but how do you debug productively? Stepping through every line of a typical TypeScript compiler run would take… tens of hours, perhaps? How do you determine the relevant parts?</p>\n<h3 id=\"brief-architecture-review\" tabindex=\"-1\"><a class=\"no-underline\" href=\"https://blog.andrewbran.ch/debugging-the-typescript-codebase/#brief-architecture-review\">Brief Architecture Review</a></h3>\n<p>The answer is that it comes with time spent in the codebase, but having a high-level understanding of the organization of the compiler helps.</p>\n<p>Earlier, we saw how to debug <a href=\"https://github.com/microsoft/TypeScript/blob/master/src/tsc/tsc.ts\"><code>tsc</code></a> and the language service. These are the two most common entry points into the compiler. In a code editor scenario, as we saw before, the language service sits behind <a href=\"https://github.com/microsoft/TypeScript/blob/10e3f11c0d88b991eaca600ff71d01a603a769a3/src/tsserver/server.ts#L479\">TS Server</a>, which <a href=\"https://github.com/microsoft/TypeScript/blob/10e3f11c0d88b991eaca600ff71d01a603a769a3/src/server/session.ts#L2120-L2469\">translates messages into language service calls</a>. If you want to debug an editor-related feature like a <a href=\"https://github.com/microsoft/TypeScript/tree/master/src/services/codefixes\">code fix</a> (e.g., <a href=\"https://github.com/microsoft/TypeScript/pull/32356\">inserting a missing <code>await</code></a>), the place to start is in the functions returned by <code>createLanguageService</code> in <a href=\"https://github.com/microsoft/TypeScript/blob/10e3f11c0d88b991eaca600ff71d01a603a769a3/src/services/services.ts#L2121-L2176\">services.ts</a>. Otherwise, you’re probably interested in the <a href=\"https://github.com/microsoft/TypeScript/tree/master/src/compiler\">core compiler</a>, which is invoked both by the language service and by <code>tsc</code>.</p>\n<p>Both <code>tsc</code> and the language service use the <a href=\"https://github.com/microsoft/TypeScript/blob/10e3f11c0d88b991eaca600ff71d01a603a769a3/src/compiler/program.ts#L710\">Program</a> object as the entryway into the core of the compiler. It takes some <a href=\"https://github.com/microsoft/TypeScript/blob/10e3f11c0d88b991eaca600ff71d01a603a769a3/src/compiler/types.ts#L4703\">configuration options</a> (usually from a <code>tsconfig.json</code>) and a bunch of file paths and pipe them through the compiler to answer important questions like “<a href=\"https://github.com/microsoft/TypeScript/blob/10e3f11c0d88b991eaca600ff71d01a603a769a3/src/compiler/program.ts#L1625-L1631\">do my files have any errors</a>” and “<a href=\"https://github.com/microsoft/TypeScript/blob/e8782aef226fc2631f0e7f872e3bea7fbc3d1b9f/src/compiler/program.ts#L1574\">what’s the JavaScript equivalent of all this TypeScript</a>.” It does that by using the following major components:</p>\n<ol>\n<li>The <a href=\"https://github.com/microsoft/TypeScript/blob/master/src/compiler/scanner.ts\">scanner</a> converts the string text into <a href=\"https://github.com/microsoft/TypeScript/blob/10e3f11c0d88b991eaca600ff71d01a603a769a3/src/compiler/types.ts#L120-L280\">syntax tokens</a> (read: groups characters into meaningful chunks, like <code>if</code>, <code>&amp;&amp;</code>, <code>&quot;doggo&quot;</code>), and the <a href=\"https://github.com/microsoft/TypeScript/blob/10e3f11c0d88b991eaca600ff71d01a603a769a3/src/compiler/parser.ts#L584\">parser</a> converts those tokens into a tree structure of <a href=\"https://github.com/microsoft/TypeScript/blob/10e3f11c0d88b991eaca600ff71d01a603a769a3/src/compiler/types.ts#L622-L2561\">nodes</a>. (The root node is called a <a href=\"https://github.com/microsoft/TypeScript/blob/10e3f11c0d88b991eaca600ff71d01a603a769a3/src/compiler/types.ts#L2682\">SourceFile</a>.)</li>\n<li>The <a href=\"https://github.com/microsoft/TypeScript/blob/master/src/compiler/binder.ts\">binder</a> walks through the parse tree and finds declarations (read: places where names of stuff get introduced into some scope, e.g. variable declarations), creating <a href=\"https://github.com/microsoft/TypeScript/blob/10e3f11c0d88b991eaca600ff71d01a603a769a3/src/compiler/types.ts#L3776\">symbols</a> for them, links subsequent usages of those names to existing symbols, and builds a control flow graph.</li>\n<li>The <a href=\"https://github.com/microsoft/TypeScript/blob/master/src/compiler/checker.ts\">checker</a> gathers all the SourceFiles and walks their parse trees, creating <a href=\"https://github.com/microsoft/TypeScript/blob/10e3f11c0d88b991eaca600ff71d01a603a769a3/src/compiler/types.ts#L4074\">types</a> for symbols and ensuring that the relationships between them make sense.</li>\n<li>The <a href=\"https://github.com/microsoft/TypeScript/blob/master/src/compiler/transformer.ts\">transformer</a> transforms the TypeScript parse tree to a plain JavaScript syntax tree and/or a declaration file syntax tree (stripping away type annotations, converting fancy new syntax ES5-compatible syntax, etc.), and the <a href=\"https://github.com/microsoft/TypeScript/blob/master/src/compiler/emitter.ts\">emitter</a> writes those trees to text.</li>\n</ol>\n<p><picture><source type=\"image/webp\" srcset=\"https://blog.andrewbran.ch/img/CgduxQ2grN-2047.webp 2047w\" /><img alt=\"A rough, hand-drawn architecture diagram of the TypeScript codebase\" class=\"dark:hidden\" loading=\"lazy\" decoding=\"async\" src=\"https://blog.andrewbran.ch/img/CgduxQ2grN-2047.png\" width=\"2047\" height=\"1667\" /></picture></p>\n<p><picture><source type=\"image/webp\" srcset=\"https://blog.andrewbran.ch/img/zFMy1A9c7Y-2047.webp 2047w\" /><img alt=\"A rough, hand-drawn architecture diagram of the TypeScript codebase\" class=\"light:hidden\" loading=\"lazy\" decoding=\"async\" src=\"https://blog.andrewbran.ch/img/zFMy1A9c7Y-2047.png\" width=\"2047\" height=\"1667\" /></picture></p>\n<h3 id=\"know-your-nodes!\" tabindex=\"-1\"><a class=\"no-underline\" href=\"https://blog.andrewbran.ch/debugging-the-typescript-codebase/#know-your-nodes!\">Know Your Nodes!</a></h3>\n<p>If most of that review was new to you, don’t worry! You don’t need to know much more than that to start debugging, and it will make more sense once you dive in. A lot can be picked up on the fly. But, if there’s one thing you might not want to skimp on, it’s learning the proper terminology for syntax. By way of example, you might see the construct <code>x ? y : z</code> and think of the term “ternary operator,” but this sequence is properly called a <em>ConditionalExpression</em>. TypeScript uses the names from the <a href=\"https://www.ecma-international.org/ecma-262/9.0/index.html\">ECMAScript language specification</a> for grammar productions that are valid in JavaScript, but it can be a little tricky to read, and there’s no corresponding document for TypeScript-specific grammar. I often use <a href=\"https://astexplorer.net/\">astexplorer.net</a> (language set to JavaScript, parser set to TypeScript) to jog my memory of what a certain syntax is called and how it’s structured. If you need the reverse, and you’re really stuck—you have a SyntaxKind you’re not familiar with and want to know what code produces it—you can always <em>read the parser</em>! By way of example, if you’re not sure what a <code>TypeQueryNode</code> is, can you get an idea from this?</p>\n<pre class=\"shiki dark-modern\" style=\"background-color:#1F1F1F;color:#D4D4D4\" tabindex=\"0\"><code class=\"language-ts\"><span class=\"line\"><span style=\"color:#569CD6\">function</span><span style=\"color:#DCDCAA\"> parseTypeQuery</span><span style=\"color:#D4D4D4\">(): </span><span style=\"color:#4EC9B0\">TypeQueryNode</span><span style=\"color:#D4D4D4\"> {</span></span>\n<span class=\"line\"><span style=\"color:#569CD6\">\tconst</span><span style=\"color:#4FC1FF\"> node</span><span style=\"color:#D4D4D4\"> = </span><span style=\"color:#DCDCAA\">createNode</span><span style=\"color:#D4D4D4\">(</span><span style=\"color:#9CDCFE\">SyntaxKind</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#9CDCFE\">TypeQuery</span><span style=\"color:#D4D4D4\">) </span><span style=\"color:#C586C0\">as</span><span style=\"color:#4EC9B0\"> TypeQueryNode</span><span style=\"color:#D4D4D4\">;</span></span>\n<span class=\"line\"><span style=\"color:#DCDCAA\">\tparseExpected</span><span style=\"color:#D4D4D4\">(</span><span style=\"color:#9CDCFE\">SyntaxKind</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#9CDCFE\">TypeOfKeyword</span><span style=\"color:#D4D4D4\">);</span></span>\n<span class=\"line\"><span style=\"color:#9CDCFE\">\tnode</span><span style=\"color:#D4D4D4\">.</span><span style=\"color:#9CDCFE\">exprName</span><span style=\"color:#D4D4D4\"> = </span><span style=\"color:#DCDCAA\">parseEntityName</span><span style=\"color:#D4D4D4\">(</span><span style=\"color:#6A9955\">/*allowReservedWords*/</span><span style=\"color:#569CD6\"> true</span><span style=\"color:#D4D4D4\">);</span></span>\n<span class=\"line\"><span style=\"color:#C586C0\">\treturn</span><span style=\"color:#DCDCAA\"> finishNode</span><span style=\"color:#D4D4D4\">(</span><span style=\"color:#9CDCFE\">node</span><span style=\"color:#D4D4D4\">);</span></span>\n<span class=\"line\"><span style=\"color:#D4D4D4\">}</span></span>\n<span class=\"line\"></span></code></pre>\n<p><code>SyntaxKind.TypeOfKeyword</code> followed by an <code>exprName</code> of something called an “entity name”? Yep, it’s the bit after the colon token in <code>const x: typeof y</code>!</p>\n<h3 id=\"setting-breakpoints-by-syntax\" tabindex=\"-1\"><a class=\"no-underline\" href=\"https://blog.andrewbran.ch/debugging-the-typescript-codebase/#setting-breakpoints-by-syntax\">Setting Breakpoints by Syntax</a></h3>\n<p>The reason you want to be familiar with proper names for syntax is that the parser, binder, checker, transformer, and emitter are in large part a collection of functions with names in the form <em><span>[verb]</span>[SyntaxKind]</em>. Some examples:</p>\n<ul>\n<li><a href=\"https://github.com/microsoft/TypeScript/blob/8cf13249eab1562de81bc4c426aa8aa8a979b6fb/src/compiler/parser.ts#L5749\"><code>parseVariableDeclaration</code></a></li>\n<li><a href=\"https://github.com/microsoft/TypeScript/blob/8cf13249eab1562de81bc4c426aa8aa8a979b6fb/src/compiler/binder.ts#L3002\"><code>bindCallExpression</code></a></li>\n<li><code>checkComputedPropertyName</code> (GitHub can’t render checker.ts, so I can’t link to the line)</li>\n<li><a href=\"https://github.com/microsoft/TypeScript/blob/8cf13249eab1562de81bc4c426aa8aa8a979b6fb/src/compiler/transformers/ts.ts#L2396\"><code>transformEnumMember</code></a></li>\n<li><a href=\"https://github.com/microsoft/TypeScript/blob/8cf13249eab1562de81bc4c426aa8aa8a979b6fb/src/compiler/emitter.ts#L2427\"><code>emitConditionalExpression</code></a></li>\n</ul>\n<p>This is very frequently the strategy I use to start debugging a problem. We should issue an error on <code>x.badProperty</code> but we don’t? Look for a function in checker.ts called <code>checkPropertyAccessExpression</code>. An expando property assignment fails to create a declaration on its container? Assignment is a form of binary expression, and there are only eight references to <code>SyntaxKind.BinaryExpression</code> in the binder, so one of them should be near the culprit.</p>\n<h3 id=\"setting-breakpoints-by-diagnostic-message\" tabindex=\"-1\"><a class=\"no-underline\" href=\"https://blog.andrewbran.ch/debugging-the-typescript-codebase/#setting-breakpoints-by-diagnostic-message\">Setting Breakpoints by Diagnostic Message</a></h3>\n<p>If you have a test case that emits a diagnostic message (read: red squiggly error) you don’t understand, finding the place to set a breakpoint is <em>really</em> easy. Simply run a find-all inside the <code>src</code> directory for a few words of the error message, with spaces replaced by underscores. For example, if you want to find out why you got the message “JSX element 'a' has no corresponding closing tag,” try searching for <code>has_no_corresponding_closing</code> and you’ll <a href=\"https://github.com/microsoft/TypeScript/blob/8cf13249eab1562de81bc4c426aa8aa8a979b6fb/src/compiler/parser.ts#L4412\">find it</a>. Set a breakpoint and work backwards by inspecting up the call stack if necessary.</p>\n<p>Be aware that substitutions like <code>'a'</code> in that error are represented as numerals in the diagnostic property (<code>Diagnostics.JSX_element_0_has_no_corresponding_closing_tag</code>), so you might want to avoid areas of the message that look dynamic or highly specific in your search terms.</p>\n<h3 id=\"inspecting-stuff-while-paused\" tabindex=\"-1\"><a class=\"no-underline\" href=\"https://blog.andrewbran.ch/debugging-the-typescript-codebase/#inspecting-stuff-while-paused\">Inspecting Stuff while Paused</a></h3>\n<p>Finally, now that you know where to pause the debugger, you’ll want to be able to inspect the current state of the compiler. Sure, you’re paused on <code>checkPropertyAccessExpression</code>, but is this the property access expression you’re interested in? Here are a few of the most useful tips for determining just what you’re looking at:</p>\n<ul>\n<li>\n<p>Every Node object has a <code>__debugKind</code> property (since <code>kind</code> is just a number) and a <code>__debugGetText()</code> method.</p>\n</li>\n<li>\n<p>You can get a node’s parent node through its <code>parent</code> property. This is really useful in combination with <code>__debugGetText()</code>, as it gives you a printout of the node you’re interested in, surrounded by some context.</p>\n</li>\n<li>\n<p>You can skip all the way up to a node’s source file by its <code>getSourceFile()</code> method. A source file has a <code>fileName</code> property, which is really handy for setting conditional breakpoints.</p>\n</li>\n<li>\n<p>Many types of object have some sort of bit flags property, set to an inscrutable number at runtime. These <em>usually</em> have an accompanying property like <code>__debugFlags</code> containing a string representation of the flags, but sometimes you wind up in a function with <em>just</em> a variable with the flags value. In those circumstances, you can often find helper functions to format that number into a string under <code>ts.Debug</code>:</p>\n<p><picture><source type=\"image/webp\" srcset=\"https://blog.andrewbran.ch/img/T65jmIvtjy-628.webp 628w\" /><img alt=\"A screenshot of a VS Code debug session, paused inspecting a variable `symbolFlags`, which is a long, inscrutable number. The debug console is open showing the user typed the command, `ts.Debug.formatSymbolFlags(symbolFlags)`. The evaluation shows the string value `Property|Module|Assignment`.\" class=\"\" loading=\"lazy\" decoding=\"async\" src=\"https://blog.andrewbran.ch/img/T65jmIvtjy-628.png\" width=\"628\" height=\"273\" /></picture></p>\n</li>\n<li>\n<p>You can access a function exported from the <code>ts</code> namespace in another file by writing its fully qualified name in the debug console. I feel like this is worth mentioning because at most places in the source code, you can drop the <code>ts</code> prefix and write functions like <code>isIdentifier(node)</code>, but in the debug console, you have to write <code>ts.isIdentifier(node)</code>.</p>\n</li>\n</ul>\n<h2 id=\"you%E2%80%99re-an-expert!-now-what%3F\" tabindex=\"-1\"><a class=\"no-underline\" href=\"https://blog.andrewbran.ch/debugging-the-typescript-codebase/#you%E2%80%99re-an-expert!-now-what%3F\">You’re an expert! Now what?</a></h2>\n<p>If this has made contributing to TypeScript feel less daunting, peruse through the issues labeled “<a href=\"https://github.com/microsoft/TypeScript/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22\">good first issue</a>” or “<a href=\"https://github.com/microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+\">help wanted</a>.” Maybe you’ll find something that inspires you!<sup class=\"footnote-ref\"><a href=\"https://blog.andrewbran.ch/debugging-the-typescript-codebase/#fn3\" id=\"fnref3\">[3]</a></sup></p>\n<hr class=\"footnotes-sep\" />\n<section class=\"footnotes\">\n<ol class=\"footnotes-list\">\n<li id=\"fn1\" class=\"footnote-item\"><p>VS Code’s <a href=\"https://code.visualstudio.com/docs/editor/debugging\">docs on debugging</a> are good if you’re not familiar with it. <a href=\"https://blog.andrewbran.ch/debugging-the-typescript-codebase/#fnref1\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n<li id=\"fn2\" class=\"footnote-item\"><p>I’m not sure why the debugger starts in the built tsc.js file instead of the source tsc.ts file, but once you step into a different file, the debugger will bring up the TypeScript source instead of the built JavaScript. <a href=\"https://blog.andrewbran.ch/debugging-the-typescript-codebase/#fnref2\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n<li id=\"fn3\" class=\"footnote-item\"><p>Orta’s <a href=\"https://github.com/orta/typescript-notes\">typescript-notes</a> also serve well as a first-time contributer’s guide. <a href=\"https://blog.andrewbran.ch/debugging-the-typescript-codebase/#fnref3\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n</ol>\n</section>\n",
      "date_published": "2019-10-23T00:00:00Z"
    }
    ,
    {
      "id": "https://blog.andrewbran.ch/one-does-not-simply-walk-into-mordor/",
      "url": "https://blog.andrewbran.ch/one-does-not-simply-walk-into-mordor/",
      "title": "One Does Not Simply Walk Into Mordor",
      "content_html": "<p><a class=\"photo-link\" target=\"_blank\" href=\"https://blog.andrewbran.ch/img/-uIxlNWik3-5400.jpeg\"><picture><source type=\"image/webp\" srcset=\"https://blog.andrewbran.ch/img/-uIxlNWik3-1536.webp 1536w\" sizes=\"(max-width: 768px) 100vw, 768px\" /><img alt=\"Loaded touring bike leaning against a rock overlooking a panoramic view of the New Zealand countryside with a road winding down the mountain in the distance.\" class=\"\" loading=\"lazy\" decoding=\"async\" src=\"https://blog.andrewbran.ch/img/-uIxlNWik3-1536.jpeg\" width=\"1536\" height=\"1018\" /></picture></a></p>\n<p><span class=\"small-caps\"><em>“Oh! Agh!”</em> are the profound words</span> with which I take the first pedals of a thousand mile bicycle tour across New Zealand. Two years ago, Jeb and I completed a trek of comparable length in Scandinavia, my first bike tour. I remember the first few pedal strokes of that trip as a highlight of the adventure: months of planning and the tedious logistics of schlepping a bicycle and two panniers of camping gear from San Francisco to Norway had come to fruition, and finally my world was simple. All I had to do was move my feet in a circle for two weeks. Now, though, I’m rolling out of Auckland International Airport with a rather different feeling. An unlikely mixture of panic and bewildered amusement shoots adrenaline through my veins. It’s midnight, it’s raining, and I have just turned into the oncoming traffic lane. Cars drive on the left here.</p>\n<p>Of course, I knew that, on some intellectual level. But as it turns out, learning a fact about traffic patterns six weeks ago does not exactly prepare you to fight your entire life’s experience of driving and riding on the right side of the road. Especially not in the dark. And the rain.</p>\n<p>We ride our first three miles (five kilometers, I remind myself) to the motel Jeb booked, and I consider other ways in which I might be underprepared for this trip. I haven’t been on a real bike ride in months. I commute to work by bike in San Francisco most days, but only a few miles. And it’s been cold and wet lately, so I’ve been taking public transit more often.</p>\n<p>To my own surprise and deep gratitude, this kind of physical unpreparedness hasn’t been much of a problem in the past. In the months leading up to my Scandinavia tour, I was recovering from an injury and didn’t ride much, but didn’t have a problem adjusting to the daily rigor of touring. Lately though, it has occurred to me that this unearned and undeserved ability will not last forever. Someday, I’ll get on my bicycle after three months of sitting on the couch, with the intention of pedaling 75 miles, only to find that my muscles have finally and permanently transmuted into pizza and left me to suffer the consequences of my sloth. That day could be tomorrow, it occurs to me. Guess we’ll find out soon.</p>\n<p class=\"asterism\">⁂</p>\n<p><a class=\"photo-link\" target=\"_blank\" href=\"https://blog.andrewbran.ch/img/0OZYJi_4KW-4473.jpeg\"><picture><source type=\"image/webp\" srcset=\"https://blog.andrewbran.ch/img/0OZYJi_4KW-1536.webp 1536w\" sizes=\"(max-width: 768px) 100vw, 768px\" /><img alt=\"Jeb rides by on a cycle path nestled between rows of tall grass next to a green pasture in rural North Island, New Zealand.\" class=\"\" loading=\"lazy\" decoding=\"async\" src=\"https://blog.andrewbran.ch/img/0OZYJi_4KW-1536.jpeg\" width=\"1536\" height=\"1018\" /></picture></a></p>\n<p>The first days are hard, and I know they’re probably the easiest we’ll have. I take some solace in the fact that Jeb is struggling too, because he’s probably in better shape than I am. This is a normal adjustment period. Nonetheless, it’s a little unsettling when we decide to cut our first day 20 kilometers short, partly because we’re running out of daylight.</p>\n<p>Still, it feels good to be on the bike. It feels good to be on vacation. I vocalize this sentiment several times during our first day.</p>\n<p>The morning of day two, the good feelings are temporarily supplanted by a different one: burning pain. Sunburn. Yesterday was overcast, even with misty rain at times, so I completely forgot sunscreen. My arms and legs are bright red. Idiot.</p>\n<p>A few hours later, we’re on a short section of gravel cycle path bypassing a busy highway junction. I think about when we took a “gravel” trail in Norway that we learned later was totally impassable by bike. We pushed our bikes for almost the entire day, but I got a few miles of practice over gravel, rocks, and sand, which is considerably trickier on a loaded touring bike than on a mountain bike. My balance and maneuvering got to be pretty good, and it makes shorter, more manageable trails like this a fun diversion from the typical monotony of asphalt.</p>\n<p>Thus hubris brings me down. As soon as I think that I’m doing well to be going so fast down this trail, I hit a patch of sand. The bike excuses itself out from under me to the right while I continue, according to the laws of physics, straight ahead and down. Gravel and sand grind into my freshly sunburned forearm, tear through the shoulder of my shirt, and generally leave the right half of my body looking like something out of <em>The Walking Dead</em>.</p>\n<p>As I dig rocks out of my sunburned arm and Jeb helps wrap it with gauze, I’m so annoyed with myself. “Wow, you’re out of practice,” a friend had said on Instagram that morning in reply to a picture of my sunburn. She’s right. It’s day two, and I’m making stupid mistakes that I’m going to pay for throughout the rest of the trip. I grimly concede that this fall was actually really lucky. For every way it could have gone better, there are ten ways it could have ended my trip. So I try to regard my pain with some gratitude, put those darker hypotheticals out of my head, and pedal on.</p>\n<p class=\"asterism\">⁂</p>\n<p>Every day, without fail, someone asks me, “Where’d you start this morning?” and each time I’m caught completely off guard. I feel like one of any dozen amnesic movie characters who awakes in an unfamiliar world and must struggle through the most basic human encounters as he tries to reassemble the pieces of his forgotten life. “Um. I’m not sure. What year is it?” Put on the spot, I have no memory of this morning. My body is trying desperately to keep my vital organs functioning in spite of the abuse I’m administering it, and there just aren’t enough resources to go around. All non-essential personnel have been escorted from the premises. The short-term memory department is vacant. My insides are squirming as I find myself utterly incapable of answering what was surely not intended to be a trick question.</p>\n<p>Even if I could readily recall what my campsite looked like or what I ate for breakfast, I still wouldn’t be able to summon the name of the town. New Zealand’s largest cities have almost caricatural British names: Queenstown, Christchurch, Wellington; but nearly everything else is named in Māori, a language that shares its name with New Zealand’s earliest inhabitants, Polynesian settlers who arrived by canoe in the 13th century. The syllables jumble hopelessly in my linguistically Western-accustomed brain, and I have no idea how to pronounce anything. Was it Tokoroa, or Rotokawa? No wait, Rotorua? Turangi or Tauranga? How exactly does one pronounce Waiouru? Hatepe? Rerewhakaaitu? It turns out that if you can’t convincingly pronounce a place to yourself as you read it on a sign, you’re not going to remember it when someone asks you ten hours later, “Where’d you start this morning?”</p>\n<p>“Um. North. 70 miles north. Um. 110 kilometers.” I can’t remember a single detail that would help me identify my extremely recent location on earth, but I can still do fast mental math. My survival mechanisms have weird priorities.</p>\n<p class=\"asterism\">⁂</p>\n<p>Taupo feels like a ski resort village grew up and became a town. Granola-chic shops and restaurants line the lakefront, along with a green belt and shared pathway featuring a human-scale, made-for-Instagram block letter sculpture reading “<span class=\"small-caps\">#lovetaupo.</span>” It’s a breathtakingly beautiful hub either for the modern outdoor adventurer or for someone who intends to become one through enough shopping of outdoorsy brands. Either way, it feels good to be here, as we’re finally seeing our distance from Auckland grow appreciably on the map. The next day, we tackle the longest and tallest climb of the North Island with less strain than we had expected. The weather has been kind so far. Finally, we’re getting in a groove.</p>\n<figure>\n<a class=\"photo-link\" target=\"_blank\" href=\"https://blog.andrewbran.ch/img/7teeEFPmrS-5204.jpeg\"><picture><source type=\"image/webp\" srcset=\"https://blog.andrewbran.ch/img/7teeEFPmrS-1536.webp 1536w\" sizes=\"(max-width: 768px) 100vw, 768px\" /><img alt=\"My bare feet enter the bottom of the frame of a photo looking out on blue Lake Taupo from the rocky shore a couple hours before sunset.\" class=\"\" loading=\"lazy\" decoding=\"async\" src=\"https://blog.andrewbran.ch/img/7teeEFPmrS-1536.jpeg\" width=\"1536\" height=\"1018\" /></picture></a>\n<figcaption>After a swim at our campsite on the shore of Lake Taupo.</figcaption>\n</figure>\n<p class=\"asterism\">⁂</p>\n<p>We awake in Foxton Beach just after the sun, our gear soaked through with dew. Australian magpies haunt the campground with their other-worldly songs. Barring catastrophe, it will be our last day on the North Island. The last few have been strong, and we can taste the sweetness of such a significant checkpoint already.</p>\n<figure>\n<a class=\"photo-link\" target=\"_blank\" href=\"https://blog.andrewbran.ch/img/z2ZeLXDb_Z-3902.jpeg\"><picture><source type=\"image/webp\" srcset=\"https://blog.andrewbran.ch/img/z2ZeLXDb_Z-1536.webp 1536w\" sizes=\"(max-width: 768px) 100vw, 768px\" /><img alt=\"A silhouette of Jeb on the shore of Foxton Beach after sunset, the horizon glowing pinkish-orange.\" class=\"\" loading=\"lazy\" decoding=\"async\" src=\"https://blog.andrewbran.ch/img/z2ZeLXDb_Z-1536.jpeg\" width=\"1536\" height=\"1152\" /></picture></a>\n<figcaption>Foxton Beach after sunset.</figcaption>\n</figure>\n<p>We ride a frigid few kilometers to the local cafe for breakfast. These cafe stops are one of my favorite parts of these trips. I’m well aware of the privileged white tourist stereotype this makes me resemble, but I think it’s defensible here: New Zealand takes breakfast <em>seriously</em>. We enter the cafe minutes after it opens, rubbing our hands together for warmth. <em>Piano Man</em> is blaring over the sound system, which feels oddly perfect. In every other way, it’s an archetypal beach cafe for a sleepy beach town, with distressed wood tables and pastel walls. Everything about it is bright and warm and adorable, including the owner. She greets us and brings us each a flat white. Feeling returns to my fingers as I cradle the mug in my hands. I think Jeb is in love.</p>\n<p>Breakfast—Eggs Benedict with prosciutto and sautéed spinach—looks like something you might find in a Michelin star restaurant. In the States, a place with this casual of a vibe can be expected to serve bran muffins. Oatmeal at best. Jeb notes his observation about Kiwis’ devotion to breakfast foods to the owner, who tells us her passion for breakfast is so great that when she opened the cafe, she intended to serve it all day. But alas, her husband, the full-time professionally trained chef, refuses to poach an egg after 12:00 PM. As I shovel the deliciously and artfully prepared egg and prosciutto into my mouth, there’s no denying her husband’s culinary prowess. But I can’t help thinking that, given the chance, Jeb would probably poach her an egg at any time of day or night.</p>\n<figure>\n<a class=\"photo-link\" target=\"_blank\" href=\"https://blog.andrewbran.ch/img/Lm8ZPcoLQN-4032.jpeg\"><picture><source type=\"image/webp\" srcset=\"https://blog.andrewbran.ch/img/Lm8ZPcoLQN-1536.webp 1536w\" sizes=\"(max-width: 768px) 100vw, 768px\" /><img alt=\"A plate of Eggs Benedict in the cafe at Foxton Beach sitting on the distressed wood table. The presentation is elegant, with the prosciutto neatly wrapped into a cylinder and the hollandaise garnished with micro greens.\" class=\"\" loading=\"lazy\" decoding=\"async\" src=\"https://blog.andrewbran.ch/img/Lm8ZPcoLQN-1536.jpeg\" width=\"1536\" height=\"1152\" /></picture></a>\n<figcaption>Eggs Benedict in the cafe at Foxton Beach.</figcaption>\n</figure>\n<p class=\"asterism\">⁂</p>\n<p>At the beginning of these kinds of trips, I feel a little self-conscious when we intersect too sharply with civilization. We don’t often smell great, we look ridiculous, at any given moment it’s nearly inevitable that <em>somehow</em> I’ve smudged chain grease onto my face, and whenever we enter an establishment, we surreptitiously scan the room for power outlets to charge the small army of dying electronics we’re cradling in our arms. We look like two homeless men in skin-tight clothing. It can be a little uncomfortable.</p>\n<p>After about three days, though, something magic happens: all self-consciousness dissipates to make room for some brand of delusional confidence, probably born of the necessity to avoid freaking out about the hundreds of grueling miles that lie ahead. By the time we reach downtown Wellington, where we’ll catch a ferry to the South Island the next morning, this confidence is running high and inhibitions are running low. Which is a good thing, because all the accommodations close to the ferry terminal are, well, not quite <em>ritzy</em>, but they’re places with porters and concierges and very modern looking lobbies attached to moodily lit bars. I’ve reserved us a room in one of these hotels, so when we pull up, Jeb stays outside with the bikes and I walk in. A week ago, this would have felt like a nightmare: walking into <em>here</em> looking like <em>this</em>. But now, having just completed 430 miles (around 700 kilometers, I remind myself, in case the girl checking us in is impressed and wants to know), clear from one end of the island to the other, I’m strutting into the lobby in my size-too-small bike shorts and fingerless gloves feeling <em>kind of awesome</em> instead of mildly bashful.</p>\n<p>No one asks how far we’ve come, but I’m sure they’re impressed anyway.</p>\n<p class=\"asterism\">⁂</p>\n<p>Someone once asked me the totally reasonable question of what Jeb and I <em>do</em> while riding, how we pass the long hours in the saddle. Do we ride side by side? Do we talk?</p>\n<p>Traffic usually constrains us to riding single-file, which makes conversation difficult. It’s impossible to hear each other when a car is passing (much less a semi full of sheep), so depending on the conditions, it can take a while to get through a single sentence. Most sentences just aren’t worth the hassle.</p>\n<p>Jeb likes to watch our progress on the elevation profile to prepare himself mentally for climbs. I listen to podcasts sometimes, with a single AirPod in the ear that faces away from traffic. But the best way to pass the time is to let your mind fall deep into some train of thought you can get totally lost in. These are the times when double-digit miles can disappear without notice. For me, this often takes the form of replaying events from the last few days over and over in my head, trying to uncover thoughts and stories to write about later. Psychologists call this mental repetition <em>elaborative rehearsal</em>, and it plays a significant role in forming long-term memories. I like to think that the long, mindless hours help me remember the highlights of these trips forever.</p>\n<figure>\n<a class=\"photo-link\" target=\"_blank\" href=\"https://blog.andrewbran.ch/img/WN2lFr4kNO-5400.jpeg\"><picture><source type=\"image/webp\" srcset=\"https://blog.andrewbran.ch/img/WN2lFr4kNO-1536.webp 1536w\" sizes=\"(max-width: 768px) 100vw, 768px\" /><img alt=\"Looking across a road into a scrub desert with two mountains side-by-side in the distance, the left craggy and snow-covered, the right volcanic gray with a cinder cone.\" class=\"\" loading=\"lazy\" decoding=\"async\" src=\"https://blog.andrewbran.ch/img/WN2lFr4kNO-1536.jpeg\" width=\"1536\" height=\"1018\" /></picture></a>\n<figcaption>Mt. Ruapehu and Mt. Ngauruhoe.</figcaption>\n</figure>\n<p>Perhaps the most compelling reason to ride single-file, though, is that it’s easier. Drafting off another cyclist, minimizing the force of air resistance on yourself, makes the work significantly easier—up to 50% easier at high speeds or in the face of a strong headwind. Plus, trying to keep my front wheel within inches of Jeb’s rear wheel without causing an accident is a surprisingly effective distraction for passing the time. I used this advantage extensively on the North Island, which Jeb had planned and therefore navigated, riding in front most of the time. But now, on the South Island, it was my turn to ride in front. The increased wind resistance is a rude awakening, and most of the time, Jeb inexplicably rides several meters behind, seemingly throwing away the priceless gift being extended to him by the physics of fluid dynamics. It’s tempting to point out that if he’s going to work that hard, he may as well continue to ride in front so <em>I</em> can draft. But, that’s not how this works. My job now is to navigate to Queenstown, and Jeb can ride wherever he likes. He would even probably switch with me from time to time if I asked, but I want to uphold my end of this tacit agreement with the same grace that he did.</p>\n<p>Ugh. Grace and maturity are so hard when you’re tired.</p>\n<p class=\"asterism\">⁂</p>\n<p>A few days into the South Island, I’m beginning to worry that the window between hitting my stride and deteriorating into crippling fatigue might be closing rapidly. We’ve been riding for ten days without a day off, and while the South Island offers a reprieve from the heavier traffic of the North Island, it brings bigger climbs, longer stretches without food or water, sand flies, and <em>bees</em>. Everyone told us how beautiful the South Island is; nobody told us it’s infested with <em>bees</em>. Sometimes it’s impossible to stop and rest without getting swarmed. Jeb and I both have stings, bites, and healing sunburns that make it hard to sleep some nights, compounding the mounting exhaustion the next day.</p>\n<figure>\n<a class=\"photo-link\" target=\"_blank\" href=\"https://blog.andrewbran.ch/img/1EAVKXfgBY-5400.jpeg\"><picture><source type=\"image/webp\" srcset=\"https://blog.andrewbran.ch/img/1EAVKXfgBY-1536.webp 1536w\" sizes=\"(max-width: 768px) 100vw, 768px\" /><img alt=\"A girl sits, back turned to the camera, on the shore of a glacial lake, looking out into the mountains that frame the lake in the horizon. A dock extends a few meters into the lake on the left.\" class=\"\" loading=\"lazy\" decoding=\"async\" src=\"https://blog.andrewbran.ch/img/1EAVKXfgBY-1536.jpeg\" width=\"1536\" height=\"1018\" /></picture></a>\n<figcaption>Lake Rotoiti at St. Arnaud.</figcaption>\n</figure>\n<p>As we leave St. Arnaud and head toward the West Coast, I’m expecting an easy day. Yesterday we spent the whole day climbing from sea level to several thousand feet, and today we’ll do the opposite, reaping the reward of the hard work we put in. Right?</p>\n<p>No such luck. The descent is gradual and rolling, and a strong headwind pushes against us the entire day. I have to pedal hard to make forward progress <em>down</em> the hills—I’m convinced that if I coasted, I’d be blown back up in reverse. It feels like a personal affront, like I’ve been robbed of something I earned—no, something I <em>made</em> for myself through a tireless labor of love. The day is, in fact, probably easier than several we’ve put behind us, but the dissonance between expectation and reality is devastating.</p>\n<p>We pull into our camping spot at 8:00, our latest arrival yet. It’s dusk, and the air is dark with sand flies that commence biting as soon as we stop moving. Why do I do this?</p>\n<p class=\"asterism\">⁂</p>\n<p><span class=\"small-caps\">I stand in front of my tent</span>, facing south, and switch off my headlamp. Giving my eyes a moment to adjust, I hear the faint rush of the Buller River behind me. The air is cool and damp, and the sand flies have retreated back to wherever bugs go at night. Then, I look up and gaze out into the universe into a direction I’ve never looked before.</p>\n<p>The International Space Station floats by, leading my eye east across constellations and galaxies I’ve never seen: the Large Magellanic Cloud, faintly visible. The Southern Cross. Centaurus, Corvus, Crater.</p>\n<p>Since I can remember, I’ve felt a child-like wonder at the vastness of the cosmos while staring at the stars in the northern sky. Now, pinpricks of light from countless billions more meet my eyes for the first time, as if to say, <em>You have no idea what vastness is.</em></p>\n<p>Oh, right. <em>This</em> is why I do this.</p>\n<p class=\"asterism\">⁂</p>\n<figure>\n<a class=\"photo-link\" target=\"_blank\" href=\"https://blog.andrewbran.ch/img/iiAELqqe2K-3581.jpeg\"><picture><source type=\"image/webp\" srcset=\"https://blog.andrewbran.ch/img/iiAELqqe2K-1536.webp 1536w\" sizes=\"(max-width: 768px) 100vw, 768px\" /><img alt=\"Distinctively layered rock formations jut out of the ocean and form a small canyon. The sky is hazy and overcast; the water fades white into the horizon.\" class=\"\" loading=\"lazy\" decoding=\"async\" src=\"https://blog.andrewbran.ch/img/iiAELqqe2K-1536.jpeg\" width=\"1536\" height=\"2316\" /></picture></a>\n<figcaption>“Pancake Rocks”</figcaption>\n</figure>\n<p>The weather turns wet along the West Coast, alternating between rain and the threat of rain. I’ve planned for us to take our first and only day off at Fox Glacier, which my body desperately needs. Every morning, my legs threaten to go on strike, and a more insidious pain creeps into my lower back after five to ten miles of continuous riding, and becomes unbearable after five more. A ten-minute stretch break seems to fend off the pain for another ten miles or so, but I’m worried that these breaks will have to become longer and more frequent to have the same effect, especially once we head back into the mountains. Jeb and I are fairly well-matched in pace, and stamina, and stubbornness, so the idea of giving up or radically altering our plans has really never come up before, which makes this creeping pain all the more terrifying. I don’t want an injury to force that conversation.</p>\n<p>Whenever I find myself in a situation like this, I tend to convince myself that I have no pride to lose over the matter, that I have nothing I need to prove to myself. In 2014, I completed an Appalachian Trail thru-hike, five months walking through woods and over mountains from Georgia to Maine. Only a few hundred people a year successfully complete a thru-hike, and I had no idea if I had what it takes when I started. Ever since, I’ve been able to use that success to reject the idea that I have to overcome every next extreme physical challenge in order to maintain self worth. Sometimes it’s a healthy tool that helps me avoid the risk of injury, and sometimes it’s just a barrier for my ego.</p>\n<figure>\n<a class=\"photo-link\" target=\"_blank\" href=\"https://blog.andrewbran.ch/img/TAwE465LL0-3581.jpeg\"><picture><source type=\"image/webp\" srcset=\"https://blog.andrewbran.ch/img/TAwE465LL0-1536.webp 1536w\" sizes=\"(max-width: 768px) 100vw, 768px\" /><img alt=\"In the early morning light, a large mountain topped with a glacier peeks out from clouds. Mist hangs above a choppy, glacial-blue stream that recedes into the horizon.\" class=\"\" loading=\"lazy\" decoding=\"async\" src=\"https://blog.andrewbran.ch/img/TAwE465LL0-1536.jpeg\" width=\"1536\" height=\"2316\" /></picture></a>\n<figcaption>Fox Glacier peeks out at sunrise.</figcaption>\n</figure>\n<p>When we arrive in Fox Glacier, I make the call that we can’t take a day off. Even though I’m terrified that my body might just shut down at the foot of the next hill we encounter, there’s a more dangerous forcing function to consider. We have three days of riding left, the last two containing mountain passes—Haast Pass and Crown Range Pass—and storms are forecast four days from now. If we take a day off, we could very well end up on steep and winding climbs and descents in the rain, with wet roads, low visibility, and heavier traffic as we approach Queenstown. We have to keep moving.</p>\n<p>I can’t exactly explain how or why, but during the following day, something shifts in my mind. Even though I’m exhausted, I’m feeling confident, even excited, about the challenging terrain ahead. Realistically, Fox Glacier was my last chance to give up or change course, and it feels good that it was fully <em>my</em> decision to keep going, even to turn up the heat by eliminating our rest day. Two more days sounds doable. I can endure anything for two days.</p>\n<figure>\n<a class=\"photo-link\" target=\"_blank\" href=\"https://blog.andrewbran.ch/img/AQC60Fwajb-5400.jpeg\"><picture><source type=\"image/webp\" srcset=\"https://blog.andrewbran.ch/img/AQC60Fwajb-1536.webp 1536w\" sizes=\"(max-width: 768px) 100vw, 768px\" /><img alt=\"A rocky ocean shore on a sunny day. The sky is blue but a bright white haze hangs close to the coast as a few days’ worth of rain evaporates in the sun.\" class=\"\" loading=\"lazy\" decoding=\"async\" src=\"https://blog.andrewbran.ch/img/AQC60Fwajb-1536.jpeg\" width=\"1536\" height=\"1018\" /></picture></a>\n<figcaption>The sun comes out on the West Coast before we go back into the mountains.</figcaption>\n</figure>\n<p>The last two days and their monstrous ascents are definitely the toughest of the trip, but the closer I come to the finish line, the more unflappably optimistic I become. Haast Pass nearly does Jeb in, so I don enough positivity for both of us. We each do the same physical work day after day, carrying our own bodies, bikes, and gear for 75 miles over thousands of feet of climbing, but we bear the psychological burden in shifts. A few days ago, I might have given up if not for Jeb. At this point, he would still succeed without me, but it feels good to be the one pushing us forward to the end nonetheless.</p>\n<p>And so, as Queenstown comes into view and we cross the thousand mile threshold, I realize I was wrong. As soon as I doubted my ability to make it, I <em>did</em> have something to prove to myself: not that I could continue to spin my feet for another 200 miles, but that I could overcome the obstacle of fear and doubt and make those miles a highlight of the trip, that I could be awestruck by the spectacular beauty of the surrounding glacial landscapes instead of being consumed by my own discomfort, and that I could keep our spirits high when Jeb needed encouragement instead of dragging both of us down.</p>\n<figure>\n<a class=\"photo-link\" target=\"_blank\" href=\"https://blog.andrewbran.ch/img/ghBio55WFQ-5400.jpeg\"><picture><source type=\"image/webp\" srcset=\"https://blog.andrewbran.ch/img/ghBio55WFQ-1536.webp 1536w\" sizes=\"(max-width: 768px) 100vw, 768px\" /><img alt=\"Jeb looks through the viewfinder of a DSLR camera, mountains in the distance.\" class=\"\" loading=\"lazy\" decoding=\"async\" src=\"https://blog.andrewbran.ch/img/ghBio55WFQ-1536.jpeg\" width=\"1536\" height=\"1018\" /></picture></a>\n<figcaption>Jeb photographs Queenstown from Crown Range Pass, the top of our last big climb.</figcaption>\n</figure>\n<p>Someday, I <em>will</em> lose the ability to bike a thousand miles in 16 days without training, but I hope I don’t lose the ability to take charge of my own happiness and find the beauty in challenging circumstances. It takes practice. It’s a muscle that has to be trained and exercised sometimes.</p>\n<p>Next time, I’ll try to prepare with some <em>actual</em> training too, and get on the bike some before setting out. Probably.</p>\n<p><a class=\"photo-link\" target=\"_blank\" href=\"https://blog.andrewbran.ch/img/L6I5KeluqZ-5400.jpeg\"><picture><source type=\"image/webp\" srcset=\"https://blog.andrewbran.ch/img/L6I5KeluqZ-1536.webp 1536w\" sizes=\"(max-width: 768px) 100vw, 768px\" /><img alt=\"View of Lake Wakatipu surrounded by houses and evergreens with mountains in the distance.\" class=\"\" loading=\"lazy\" decoding=\"async\" src=\"https://blog.andrewbran.ch/img/L6I5KeluqZ-1536.jpeg\" width=\"1536\" height=\"1018\" /></picture></a></p>\n",
      "date_published": "2019-03-19T00:00:00Z"
    }
    
  ]
}