{"componentChunkName":"component---src-templates-blog-post-js","path":"/blog/run-tsx-from-command-line/","result":{"data":{"markdownRemark":{"html":"<p>I know how to <a href=\"../run-jsx-from-command-line/\">run a <code>.jsx</code> file using <code>babel-node</code></a>, but now want to add TypeScript to the mix.</p>\n<p>Here’s a starter <code>index.tsx</code> file I’m going to use to generate HTML from the command-line.</p>\n<pre class=\"grvsc-container default-dark\" data-language=\"js\" data-index=\"0\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk3\">// Use import statements:</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk15\">import</span><span class=\"mtk1\"> </span><span class=\"mtk4\">*</span><span class=\"mtk1\"> </span><span class=\"mtk15\">as</span><span class=\"mtk1\"> </span><span class=\"mtk12\">React</span><span class=\"mtk1\"> </span><span class=\"mtk15\">from</span><span class=\"mtk1\"> </span><span class=\"mtk8\">&quot;react&quot;</span><span class=\"mtk1\">;</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk15\">import</span><span class=\"mtk1\"> { </span><span class=\"mtk12\">renderToStaticMarkup</span><span class=\"mtk1\"> } </span><span class=\"mtk15\">from</span><span class=\"mtk1\"> </span><span class=\"mtk8\">&quot;react-dom/server&quot;</span><span class=\"mtk1\">;</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk3\">// Use JSX (with a TypeScript annotation)</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk4\">const</span><span class=\"mtk1\"> </span><span class=\"mtk11\">App</span><span class=\"mtk1\">: </span><span class=\"mtk10\">React</span><span class=\"mtk1\">.</span><span class=\"mtk10\">FunctionComponent</span><span class=\"mtk1\"> = () </span><span class=\"mtk4\">=&gt;</span><span class=\"mtk1\"> </span><span class=\"mtk17\">&lt;</span><span class=\"mtk4\">div</span><span class=\"mtk17\">&gt;</span><span class=\"mtk1\">The app</span><span class=\"mtk17\">&lt;/</span><span class=\"mtk4\">div</span><span class=\"mtk17\">&gt;</span><span class=\"mtk1\">;</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk3\">// Use an API only available in node (and some more JSX):</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk12\">process</span><span class=\"mtk1\">.</span><span class=\"mtk12\">stdout</span><span class=\"mtk1\">.</span><span class=\"mtk11\">write</span><span class=\"mtk1\">(</span><span class=\"mtk11\">renderToStaticMarkup</span><span class=\"mtk1\">(</span><span class=\"mtk17\">&lt;</span><span class=\"mtk10\">App</span><span class=\"mtk1\"> </span><span class=\"mtk17\">/&gt;</span><span class=\"mtk1\">));</span></span></span></code></pre>\n<p>First I install the dependencies used in <code>import</code> statements (and note that <code>yarn</code> will generate a <code>package.json</code> as a side-effect if it doesn’t already exist):</p>\n<pre class=\"grvsc-container default-dark\" data-language=\"bash\" data-index=\"1\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">yarn add react react-dom</span></span></span></code></pre>\n<p>If I try using <code>node index.tsx</code> to run the script, I’ll get an error (because of the <code>import</code>):</p>\n<pre class=\"grvsc-container default-dark\" data-language=\"\" data-index=\"2\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"grvsc-source\">(node:73369) Warning: To load an ES module, set &quot;type&quot;: &quot;module&quot; in the package.json or use the .mjs extension.</span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\">(Use `node --trace-warnings ...` to show where the warning was created)</span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\">/.../index.tsx:1</span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\">import * as React from &#39;react&#39;;</span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\">^^^^^^</span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\">SyntaxError: Cannot use import statement outside a module</span></span></code></pre>\n<p>After renaming the file to <code>index.mjs</code> and running <code>node index.mjs</code> we can get a different error (because of the TypeScript annotation):</p>\n<pre class=\"grvsc-container default-dark\" data-language=\"\" data-index=\"3\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"grvsc-source\">file:///.../index.mjs:6</span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\">const App: React.FunctionComponent = () =&gt; &lt;div&gt;The app&lt;/div&gt;;</span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\">      ^^^</span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\">      SyntaxError: Missing initializer in const declaration</span></span></code></pre>\n<p>Reverting the file name to <code>index.tsx</code>, adding <code>\"type\": \"module\"</code> to <code>package.json</code> and re-running with <code>node index.tsx</code> gives a different error:</p>\n<pre class=\"grvsc-container default-dark\" data-language=\"\" data-index=\"4\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"grvsc-source\">internal/process/esm_loader.js:74</span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\">    internalBinding(&#39;errors&#39;).triggerUncaughtException(</span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\">                              ^</span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\">TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension &quot;.tsx&quot; for /.../index.tsx</span></span></code></pre>\n<p>This isn’t getting me anywhere, so it’s time to look for at alternative approaches:</p>\n<ol>\n<li>Use <a href=\"https://github.com/TypeStrong/ts-node\"><code>ts-node</code></a></li>\n<li>Use <code>babel-node</code></li>\n</ol>\n<h2>Setup ts-node</h2>\n<pre class=\"grvsc-container default-dark\" data-language=\"bash\" data-index=\"5\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk3\"># Install all libs needed to run ts-node on a file containing TypeScript + TSX:</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">yarn add -D ts-node typescript</span></span></span></code></pre>\n<p>Create a TypeScript config file (<code>tsconfig.json</code>) to tell TypeScript how to treat the <code>import</code> statements and JSX:</p>\n<pre class=\"grvsc-container default-dark\" data-language=\"json\" data-index=\"6\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">{</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">  </span><span class=\"mtk12\">&quot;compilerOptions&quot;</span><span class=\"mtk1\">: {</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">    </span><span class=\"mtk12\">&quot;module&quot;</span><span class=\"mtk1\">: </span><span class=\"mtk8\">&quot;commonjs&quot;</span><span class=\"mtk1\">,</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">    </span><span class=\"mtk12\">&quot;jsx&quot;</span><span class=\"mtk1\">: </span><span class=\"mtk8\">&quot;react&quot;</span><span class=\"mtk1\">,</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">    </span><span class=\"mtk12\">&quot;esModuleInterop&quot;</span><span class=\"mtk1\">: </span><span class=\"mtk4\">true</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">  }</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">}</span></span></span></code></pre>\n<p>Run with <code>npx ts-node index.tsx</code> and we get an error:</p>\n<pre class=\"grvsc-container default-dark\" data-language=\"\" data-index=\"7\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"grvsc-source\">⨯ Unable to compile TypeScript:</span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\">index.tsx:6:1 - error TS2580: Cannot find name &#39;process&#39;. Do you need to install type definitions for node? Try `npm i --save-dev @types/node`.</span></span></code></pre>\n<p>We’re using <code>yarn</code>, so do <code>yarn add -D @types/node</code> to install the type definitions required for TypeScript to validate how we use the <code>process</code> global. After installing, we re-run with <code>npx ts-node index.tsx</code> again and see:</p>\n<pre class=\"grvsc-container default-dark\" data-language=\"html\" data-index=\"8\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk17\">&lt;</span><span class=\"mtk4\">div</span><span class=\"mtk17\">&gt;</span><span class=\"mtk1\">The app</span><span class=\"mtk17\">&lt;/</span><span class=\"mtk4\">div</span><span class=\"mtk17\">&gt;</span></span></span></code></pre>\n<p>It’s working. Great.</p>\n<h2>Setup babel-node</h2>\n<p>After noticing babel has a TypeScript preset, I thought I’d give that a go too.</p>\n<p>Create <code>babel.config.json</code>:</p>\n<pre class=\"grvsc-container default-dark\" data-language=\"json\" data-index=\"9\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">{</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">  </span><span class=\"mtk12\">&quot;presets&quot;</span><span class=\"mtk1\">: [</span><span class=\"mtk8\">&quot;@babel/preset-env&quot;</span><span class=\"mtk1\">, </span><span class=\"mtk8\">&quot;@babel/preset-react&quot;</span><span class=\"mtk1\">, </span><span class=\"mtk8\">&quot;@babel/preset-typescript&quot;</span><span class=\"mtk1\">]</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">}</span></span></span></code></pre>\n<p>Install everything we need to run <code>babel-node</code> with that config:</p>\n<pre class=\"grvsc-container default-dark\" data-language=\"bash\" data-index=\"10\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">yarn add -D @babel/core @babel/node @babel/preset-env @babel/preset-react @babel/preset-typescript</span></span></span></code></pre>\n<p>Run our script with <code>npx babel-node index.jsx</code>, and … there’s an error:</p>\n<pre class=\"grvsc-container default-dark\" data-language=\"\" data-index=\"11\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"grvsc-source\">(node:75993) Warning: To load an ES module, set &quot;type&quot;: &quot;module&quot; in the package.json or use the .mjs extension.</span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\">(Use `node --trace-warnings ...` to show where the warning was created)</span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\">/.../index.tsx:2</span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\">import * as React from &quot;react&quot;;</span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\">^^^^^^</span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\">SyntaxError: Cannot use import statement outside a module</span></span></code></pre>\n<p>It might not be crystal clear from the error message, but the problem here is babel’s not transpiling my <code>.tsx</code> file at all. I need to rerun with additional command-line options: <code>npx babel-node -x '.tsx' index.tsx</code> - which results in:</p>\n<pre class=\"grvsc-container default-dark\" data-language=\"html\" data-index=\"12\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk17\">&lt;</span><span class=\"mtk4\">div</span><span class=\"mtk17\">&gt;</span><span class=\"mtk1\">The app</span><span class=\"mtk17\">&lt;/</span><span class=\"mtk4\">div</span><span class=\"mtk17\">&gt;</span></span></span></code></pre>\n<p>Great: that’s a second option: transipling and running a <code>.tsx</code> file using <code>babel-node</code>, without having to create <code>tsconfig.json</code>, rename our files as <code>.mjs</code> or update <code>package.json</code> to add <code>\"type\": \"module\"</code>.</p>\n<h2>esbuild</h2>\n<p>I’m happy to use <code>ts-node</code> and <code>babel-node</code> to run random <code>.tsx</code> files and avoid the overhead of configuring a bundler. However, <a href=\"https://esbuild.github.io/\"><code>esbuild</code></a> is a slightly different beast: it’s blazing fast, and I don’t think I’ll have to configure anything to make it run TypeScript files.</p>\n<p>Install with <code>yarn</code>:</p>\n<pre class=\"grvsc-container default-dark\" data-language=\"bash\" data-index=\"13\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">yarn add -D esbuild</span></span></span></code></pre>\n<p>Iterate our way towards bundling transpiled code appropriate to pipe into <code>node</code>:</p>\n<pre class=\"grvsc-container default-dark\" data-language=\"bash\" data-index=\"14\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">esbuild index.tsx | node -</span></span></span></code></pre>\n<p>This transpiles the JSX, but leaves the <code>import</code> statements in, presenting a problem when piped into <code>node</code>:</p>\n<pre class=\"grvsc-container default-dark\" data-language=\"\" data-index=\"15\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"grvsc-source\">[stdin]:1</span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\">import * as React from &quot;react&quot;;</span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\">^^^^^^</span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\">SyntaxError: Cannot use import statement outside a module</span></span></code></pre>\n<p>To flatten the imports, we’ll bundle the code</p>\n<pre class=\"grvsc-container default-dark\" data-language=\"bash\" data-index=\"16\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">esbuild index.tsx --bundle | node -</span></span></span></code></pre>\n<pre class=\"grvsc-container default-dark\" data-language=\"\" data-index=\"17\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"grvsc-source\"> &gt; node_modules/react/index.js: warning: Define &quot;process.env.NODE_ENV&quot; when bundling for the browser</span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\">     3 │ if (process.env.NODE_ENV === &#39;production&#39;) {</span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\">       ╵     ~~~~~~~~~~~~~~~~~~~~</span></span></code></pre>\n<p>That’s a good error message; we should bundle for node:</p>\n<pre class=\"grvsc-container default-dark\" data-language=\"bash\" data-index=\"18\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">npx esbuild index.tsx --bundle --platform=node | node -</span></span></span></code></pre>\n<p>Output:</p>\n<pre class=\"grvsc-container default-dark\" data-language=\"html\" data-index=\"19\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk17\">&lt;</span><span class=\"mtk4\">div</span><span class=\"mtk17\">&gt;</span><span class=\"mtk1\">The app</span><span class=\"mtk17\">&lt;/</span><span class=\"mtk4\">div</span><span class=\"mtk17\">&gt;</span></span></span></code></pre>\n<p>Excellent. That was an easy fix.</p>\n<p>I’m aware I may want to run the development React build (with warnings) during development, and production builds for speed or if I want to ignore warnings for some reason. For completeness I want to note two ways to do this:</p>\n<pre class=\"grvsc-container default-dark\" data-language=\"bash\" data-index=\"20\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk3\"># 1. Use `esbuild --define`</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">npx esbuild index.tsx --bundle --define:process.env.NODE_ENV=</span><span class=\"mtk6\">\\&quot;</span><span class=\"mtk1\">production</span><span class=\"mtk6\">\\&quot;</span><span class=\"mtk1\"> | node -</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk3\"># 2. Set NODE_ENV environment variable for node:</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">npx esbuild index.tsx --bundle --platform=node | NODE_ENV=production node -</span></span></span></code></pre>\n<h2>TypeScript is ignored</h2>\n<p>I had to make several attempts before stumbling on the right combination of options in <code>tsconfig.json</code> to make <code>ts-node</code> work, and wonder if there’s a reason it’s so much easier to setup <code>babel-node</code> and <code>esbuild</code>.</p>\n<p>It turns out there is: <code>@babel/preset-typescript</code> and <code>esbuild</code> don’t do any type-checking; they simply ignore the TypeScript annotations.</p>\n<p>Here’s a proof of the problem: <code>invalid-typings.ts</code>:</p>\n<pre class=\"grvsc-container default-dark\" data-language=\"js\" data-index=\"21\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk4\">const</span><span class=\"mtk1\"> </span><span class=\"mtk12\">myVariable</span><span class=\"mtk1\">: </span><span class=\"mtk10\">invalidType</span><span class=\"mtk1\"> = </span><span class=\"mtk7\">3</span><span class=\"mtk1\">;</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk10\">console</span><span class=\"mtk1\">.</span><span class=\"mtk11\">log</span><span class=\"mtk1\">({ </span><span class=\"mtk12\">myVariable</span><span class=\"mtk1\"> });</span></span></span></code></pre>\n<p>When run with <code>ts-node</code>, <code>babel-node</code> and <code>esbuild</code>, only <code>ts-node</code> shows the appropriate error:</p>\n<pre class=\"grvsc-container default-dark\" data-language=\"\" data-index=\"22\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"grvsc-source\">invalid-typings.ts:1:19 - error TS2304: Cannot find name &#39;invalidType&#39;.</span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\">1 const myVariable: invalidType = 3;</span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\">                    ~~~~~~~~~~~</span></span></code></pre>\n<p>This is a signifcant difference, and important not to overlook when deciding which method to adopt to run TypeScript.</p>\n<h2>Using swc transpiler with ts-node</h2>\n<p>Attempting to run without installing react packages first doesn’t work:</p>\n<pre class=\"grvsc-container default-dark\" data-language=\"bash\" data-index=\"23\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">npx --package=react --package=react-dom --package=@swc/core --package=ts-node -c </span><span class=\"mtk8\">&#39;ts-node --swc --compilerOptions {\\&quot;jsx\\&quot;:\\&quot;react\\&quot;} index.tsx&#39;</span></span></span></code></pre>\n<p>But assuming we have <code>index.tsx</code> and install react packages as usual, we can run use ts-node without installing typescript to run the script using the swc transpiler:</p>\n<pre class=\"grvsc-container default-dark\" data-language=\"bash\" data-index=\"24\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">yarn init -y</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">yarn add react react-dom</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">npx --package=@swc/core --package=ts-node -c </span><span class=\"mtk8\">&#39;ts-node --swc --compilerOptions {\\&quot;jsx\\&quot;:\\&quot;react\\&quot;} index.tsx&#39;</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk3\"># Alternative syntax:</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">npx --package=@swc/core --package=ts-node ts-node --swc --compilerOptions </span><span class=\"mtk8\">&#39;{&quot;jsx&quot;:&quot;react&quot;}&#39;</span><span class=\"mtk1\"> index.tsx</span></span></span></code></pre>\n<pre class=\"grvsc-container default-dark\" data-language=\"bash\" data-index=\"25\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk11\">cd</span><span class=\"mtk1\"> </span><span class=\"mtk8\">$(mktemp -d)</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">yarn init -y</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">yarn add react react-dom ts-node @swc/core</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk3\"># Theoretically we shouldn&#39;t have to install typescript, but we still do:</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">yarn --silent ts-node --swc index.tsx</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk3\"># → Error: Cannot find module &#39;typescript&#39;</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">yarn add typescript</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">yarn --silent ts-node --swc index.tsx</span></span></span></code></pre>\n<h2>Running with bun</h2>\n<pre class=\"grvsc-container default-dark\" data-language=\"bash\" data-index=\"26\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">bun add react react-dom</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk1\">bun run index.tsx</span></span></span></code></pre>\n<p>At time of writing bun doesn’t implement <code>process.stdout</code>, so the code needed modifying to write to stdout using a different method:</p>\n<pre class=\"grvsc-container default-dark\" data-language=\"js\" data-index=\"27\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk3\">// → TypeError: undefined is not an object (evaluating &#39;process.stdout.write&#39;)</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk12\">process</span><span class=\"mtk1\">.</span><span class=\"mtk12\">stdout</span><span class=\"mtk1\">.</span><span class=\"mtk11\">write</span><span class=\"mtk1\">(</span><span class=\"mtk11\">renderToStaticMarkup</span><span class=\"mtk1\">(</span><span class=\"mtk17\">&lt;</span><span class=\"mtk10\">App</span><span class=\"mtk1\"> </span><span class=\"mtk17\">/&gt;</span><span class=\"mtk1\">));</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk3\">// Bun-specific solution doesn&#39;t work due to an issue:</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk3\">// https://github.com/oven-sh/bun/issues/646</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk12\">Bun</span><span class=\"mtk1\">.</span><span class=\"mtk11\">write</span><span class=\"mtk1\">(</span><span class=\"mtk12\">Bun</span><span class=\"mtk1\">.</span><span class=\"mtk12\">stdout</span><span class=\"mtk1\">, </span><span class=\"mtk11\">renderToStaticMarkup</span><span class=\"mtk1\">(</span><span class=\"mtk17\">&lt;</span><span class=\"mtk10\">App</span><span class=\"mtk17\">/&gt;</span><span class=\"mtk1\">));</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk3\">// An alernative preserving the &#39;no newline&#39; behaviour:</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk11\">require</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&#39;fs&#39;</span><span class=\"mtk1\">).</span><span class=\"mtk11\">writeFileSync</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&quot;/dev/stdout&quot;</span><span class=\"mtk1\">, </span><span class=\"mtk11\">renderToStaticMarkup</span><span class=\"mtk1\">(</span><span class=\"mtk17\">&lt;</span><span class=\"mtk10\">App</span><span class=\"mtk1\"> </span><span class=\"mtk17\">/&gt;</span><span class=\"mtk1\">), </span><span class=\"mtk8\">&quot;utf-8&quot;</span><span class=\"mtk1\">)</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk3\">// USing console.log adds newline, so is a change in behaviour:</span></span></span>\n<span class=\"grvsc-line\"><span class=\"grvsc-source\"><span class=\"mtk10\">console</span><span class=\"mtk1\">.</span><span class=\"mtk11\">log</span><span class=\"mtk1\">(</span><span class=\"mtk11\">renderToStaticMarkup</span><span class=\"mtk1\">(</span><span class=\"mtk17\">&lt;</span><span class=\"mtk10\">App</span><span class=\"mtk1\"> </span><span class=\"mtk17\">/&gt;</span><span class=\"mtk1\">));</span></span></span></code></pre>\n<style class=\"grvsc-styles\">\n  .grvsc-container {\n    overflow: auto;\n    position: relative;\n    -webkit-overflow-scrolling: touch;\n    padding-top: 1rem;\n    padding-top: var(--grvsc-padding-top, var(--grvsc-padding-v, 1rem));\n    padding-bottom: 1rem;\n    padding-bottom: var(--grvsc-padding-bottom, var(--grvsc-padding-v, 1rem));\n    border-radius: 8px;\n    border-radius: var(--grvsc-border-radius, 8px);\n    font-feature-settings: normal;\n    line-height: 1.4;\n  }\n  \n  .grvsc-code {\n    display: table;\n  }\n  \n  .grvsc-line {\n    display: table-row;\n    box-sizing: border-box;\n    width: 100%;\n    position: relative;\n  }\n  \n  .grvsc-line > * {\n    position: relative;\n  }\n  \n  .grvsc-gutter-pad {\n    display: table-cell;\n    padding-left: 0.75rem;\n    padding-left: calc(var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem)) / 2);\n  }\n  \n  .grvsc-gutter {\n    display: table-cell;\n    -webkit-user-select: none;\n    -moz-user-select: none;\n    user-select: none;\n  }\n  \n  .grvsc-gutter::before {\n    content: attr(data-content);\n  }\n  \n  .grvsc-source {\n    display: table-cell;\n    padding-left: 1.5rem;\n    padding-left: var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem));\n    padding-right: 1.5rem;\n    padding-right: var(--grvsc-padding-right, var(--grvsc-padding-h, 1.5rem));\n  }\n  \n  .grvsc-source:empty::after {\n    content: ' ';\n    -webkit-user-select: none;\n    -moz-user-select: none;\n    user-select: none;\n  }\n  \n  .grvsc-gutter + .grvsc-source {\n    padding-left: 0.75rem;\n    padding-left: calc(var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem)) / 2);\n  }\n  \n  /* Line transformer styles */\n  \n  .grvsc-has-line-highlighting > .grvsc-code > .grvsc-line::before {\n    content: ' ';\n    position: absolute;\n    width: 100%;\n  }\n  \n  .grvsc-line-diff-add::before {\n    background-color: var(--grvsc-line-diff-add-background-color, rgba(0, 255, 60, 0.2));\n  }\n  \n  .grvsc-line-diff-del::before {\n    background-color: var(--grvsc-line-diff-del-background-color, rgba(255, 0, 20, 0.2));\n  }\n  \n  .grvsc-line-number {\n    padding: 0 2px;\n    text-align: right;\n    opacity: 0.7;\n  }\n  \n  .default-dark {\n    background-color: #1E1E1E;\n    color: #D4D4D4;\n  }\n  .default-dark .mtk3 { color: #6A9955; }\n  .default-dark .mtk15 { color: #C586C0; }\n  .default-dark .mtk1 { color: #D4D4D4; }\n  .default-dark .mtk4 { color: #569CD6; }\n  .default-dark .mtk12 { color: #9CDCFE; }\n  .default-dark .mtk8 { color: #CE9178; }\n  .default-dark .mtk11 { color: #DCDCAA; }\n  .default-dark .mtk10 { color: #4EC9B0; }\n  .default-dark .mtk17 { color: #808080; }\n  .default-dark .mtk6 { color: #D7BA7D; }\n  .default-dark .mtk7 { color: #B5CEA8; }\n  .default-dark .grvsc-line-highlighted::before {\n    background-color: var(--grvsc-line-highlighted-background-color, rgba(255, 255, 255, 0.1));\n    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(255, 255, 255, 0.5));\n  }\n</style>","timeToRead":6,"frontmatter":{"title":"Running .tsx from the command-line","blurb":"A few setup steps to help write and run .tsx from the command-line","date":"2020-11-30T15:24:33.495Z","modified":"2020-11-30T18:39:57.962Z"}}},"pageContext":{"slug":"/blog/run-tsx-from-command-line/","next":null,"previous":{"fields":{"slug":"/blog/run-jsx-from-command-line/"},"frontmatter":{"title":"Running .jsx from the command-line"}}}},"staticQueryHashes":["1192980692"]}