PageSourceSearch

https://johnnyreilly.com/assets/js/d9adbd36.6765d94c.js

js johnnyreilly.com collected 2026-09-24 19:31:48 UTC 24,595 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunkjohnnyreilly_com=self.webpackChunkjohnnyreilly_com||[]).push([["2301"],{63465(e,n,t){t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>r,default:()=>c,frontMatter:()=>a,metadata:()=>s,toc:()=>d});var s=t(66664),i=t(74848),o=t(28453);let a={slug:"migrating-from-ts-node-to-bun",title:"Migrating from ts-node to Bun",authors:"johnnyreilly",date:new Date("2023-03-18T00:00:00.000Z"),tags:["node.js","typescript","open graph"],image:"./title-image.png",description:"Migrating from ts-node to Bun is surprisingly easy - this post ports a console app from ts-node to Bun and compares performance.",hide_table_of_contents:!1,atUri:"at://did:plc:yy3apqjlms24kso7ahn7lbmb/site.standard.document/3mr5bsh6uhu2b"},r,l={image:t(33821).A,authorsImageUrls:[void 0]},d=[{value:"The ts-node app",id:"the-ts-node-app",level:2},{value:"Installing Bun",id:"installing-bun",level:2},{value:"Porting the install from yarn to bun",id:"porting-the-install-from-yarn-to-bun",level:2},{value:"From <code>@types/node</code> to <code>bun/types</code>",id:"from-typesnode-to-buntypes",level:2},{value:"<code>moduleResolution</code> with Bun",id:"moduleresolution-with-bun",level:2},{value:"File APIs with Bun",id:"file-apis-with-bun",level:2},{value:"Clarification on <code>fs.promises</code>",id:"clarification-on-fspromises",level:3},{value:"Running the app",id:"running-the-app",level:2},{value:"Top level <code>await</code> and Bun",id:"top-level-await-and-bun",level:2},{value:"GitHub Actions and Bun",id:"github-actions-and-bun",level:2},{value:"Performance comparison; Bun vs ts-node",id:"performance-comparison-bun-vs-ts-node",level:2},{value:"ts-node",id:"ts-node",level:3},{value:"Bun",id:"bun",level:3},{value:"Conclusion",id:"conclusion",level:2}];function h(e){let n={a:"a",blockquote:"blockquote",code:"code",h2:"h2",h3:"h3",img:"img",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,o.R)(),...e.components},{Head:s}=n;return s||function(e,n){throw Error("Expected "+(n?"component":"object")+" `"+e+"` to be defined: you likely forgot to import, pass, or provide it.")}("Head",!0),(0,i.jsxs)(i.Fragment,{children:[(0,i.jsxs)(n.p,{children:["I've wanted to take a look at some of the alternative JavaScript runtimes for a while. The thing that has held me back is npm compatibility. I want to be able to run my code in a runtime that isn't Node.js and still be able to use npm packages. I've been using ",(0,i.jsx)(n.a,{href:"https://typestrong.org/ts-node/",children:"ts-node"})," for a long time now; it's what I reach for when I'm building any kind of console app. In this post I want to port a console app from ts-node to ",(0,i.jsx)(n.a,{href:"https://bun.sh/",children:"Bun"})," and see how easy it is."]}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.img,{alt:"title image reading &quot;From ts-node to Bun&quot;",src:`https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com${t(34972).A}`,width:"800",height:"450",loading:"eager",fetchpriority:"high"})}),"\n",(0,i.jsx)(n.h2,{id:"the-ts-node-app",children:"The ts-node app"}),"\n",(0,i.jsxs)(n.p,{children:["I have a ",(0,i.jsx)(n.a,{href:"https://johnnyreilly.com/",children:"technical blog"})," which is built on Docusaurus. When the Docusaurus build completes, a post processing script runs to do things like:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["update the ",(0,i.jsx)(n.code,{children:"sitemap.xml"})," to include the ",(0,i.jsx)(n.code,{children:"lastmod"})," date based on ",(0,i.jsx)(n.a,{href:"https://johnnyreilly.com/docusaurus-createfeeditems-api-git-commit-date",children:"git commit date"}),", and truncate the number of entries in the file"]}),"\n",(0,i.jsx)(n.li,{children:"patch the html files to use Cloudinary as an image CDN for open graph images"}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["These scripts are implemented as a simple ts-node console app. For historical reasons it's called ",(0,i.jsx)(n.code,{children:"trim-xml"})," (it originally just truncated the ",(0,i.jsx)(n.code,{children:"sitemap.xml"})," file). It's not a particularly good name but I'm not going to change it now. As the blog is open source, you can see the [code of ",(0,i.jsx)(n.code,{children:"trim-xml"})," here](",(0,i.jsx)(n.a,{href:"https://github.com/johnnyreilly/blog.johnnyreilly.com/tree/main/tr
1im-xml",children:"https://github.com/johnnyreilly/blog.johnnyreilly.com/tree/main/trim-xml"}),"]."]}),"\n",(0,i.jsx)(n.p,{children:"What we're interested in, is porting this app from ts-node to Bun. The app has a few dependencies; so npm compatibility is important to us. Let's see how it goes."}),"\n",(0,i.jsx)(n.h2,{id:"installing-bun",children:"Installing Bun"}),"\n",(0,i.jsx)(n.p,{children:"I installed Bun on my Ubuntu machine using the following command:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"curl -fsSL https://bun.sh/install | bash\n"})}),"\n",(0,i.jsx)(n.p,{children:"Which resulted in the following output:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:'bun was installed successfully to ~/.bun/bin/bun\n\nAdded "~/.bun/bin" to $PATH in "~/.zshrc"\n\nTo get started, run:\n\n exec /usr/bin/zsh\n  bun --help\n'})}),"\n",(0,i.jsxs)(n.p,{children:["I was a little weirded out by the inconsistent indentation in the output but I'm sure that's just a formatting issue. (I submitted a ",(0,i.jsx)(n.a,{href:"https://github.com/oven-sh/bun/pull/2175",children:"PR to fix this"}),".) When I ran the suggested commands it looked like bun was happy and healthy."]}),"\n",(0,i.jsx)(n.h2,{id:"porting-the-install-from-yarn-to-bun",children:"Porting the install from yarn to bun"}),"\n",(0,i.jsxs)(n.p,{children:["With bun in place I was ready to port the app. I opened up the (as I say, badly named) ",(0,i.jsx)(n.code,{children:"trim-xml"})," directory and triggered installation of the dependencies using ",(0,i.jsx)(n.code,{children:"bun install"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"cd trim-xml\nbun install\n"})}),"\n",(0,i.jsx)(n.p,{children:"Output looked like this:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"bun install v0.5.7 (5929daee)\n + @types/[email protected]\n + [email protected]\n + [email protected]\n + [email protected]\n\n 5 packages installed [2.34s]\n"})}),"\n",(0,i.jsxs)(n.p,{children:["As well, a new ",(0,i.jsx)(n.code,{children:"bun.lockb"})," file had appeared in the directory alongside the ",(0,i.jsx)(n.code,{children:"package.json"}),". Although I can't find any documentation on it, I'm guessing that this is the Bun equivalent of ",(0,i.jsx)(n.code,{children:"package-lock.json"})," or ",(0,i.jsx)(n.code,{children:"yarn.lock"}),". It's a binary file, so you can't read it. I did find this ",(0,i.jsx)(n.a,{href:"https://github.com/JacksonKearl/bun-lockb",children:"project which allows you read bun.lockb files"})," which looks like a useful way to solve that problem."]}),"\n",(0,i.jsxs)(n.p,{children:["To avoid confusion, I also deleted the ",(0,i.jsx)(n.code,{children:"yarn.lock"})," file. Yay - I've installed things! And pretty fast! What next?"]}),"\n",(0,i.jsxs)(n.h2,{id:"from-typesnode-to-buntypes",children:["From ",(0,i.jsx)(n.code,{children:"@types/node"})," to ",(0,i.jsx)(n.code,{children:"bun/types"})]}),"\n",(0,i.jsxs)(n.p,{children:["As I looked at the output for the install I realised that the ",(0,i.jsx)(n.code,{children:"@types/node"})," package had been installed. The ",(0,i.jsx)(n.code,{children:"@types/node"})," package is a package that contains TypeScript definitions for the Node.js runtime. Given we're moving to using Bun, it seemed likely that I didn't need these. But I likely did need something that represented the Bun runtime types. (Which incidentally, I would imagine to be pretty similar to the Node.js runtime types.)"]}),"\n",(0,i.jsxs)(n.p,{children:["I had a quick look at the Bun documentation and found the ",(0,i.jsx)(n.a,{href:"https://oven-sh.github.io/bun-types/",children:(0,i.jsx)(n.code,{children:"bun/types"})})," package. I added it to my project, whilst removing ",(0,i.jsx)(n.code,{children:"@types/node"})," and ",(0,i.jsx)(n.code,{children:"ts-node"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"bun remove @types/node\nbun remove ts-node\nbun add bun-types\n"})}),"\n",(0,i.jsx)(n.p,{children:"Output looked like this:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"bun remove v0.5.7 (5929daee)\n - @types/node\n\n 1 packages removed [3.00ms]\nbun remove v0.5.7 (5929daee)\n - ts-node\n\n 1 packages removed [843.00ms]\nbun add v0.5.7 (5929daee)\n\n installed [email protected]\n\n\n 1 packages installed [1.97s]\n"})}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.a,{href:"https://oven-sh.github.io/bun-types/#usage",children:"docs also say"}),":"]}),"\n",(0,i.jsxs)(n.blockquote,{children:["\n",(0,i.jsxs)(n.p,{children:["Add this to your ",(0,i.jsx)(n.code,{children:"tsconfig.json"})," or ",(0,i.jsx)(n.code,{children:"jsconfig.json"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-json",children:'{\n  "compilerOptions": {\n    "lib": ["ESNext"],\n    "module": "esnext",\n    "target": "esnext",\n    // "bun-types" is the important part\n    "types": ["bun-types"]\n  }\n}\n'})}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["I aligned my existing ",(0,i.jsx)(n.code,{children:"tsconfig.json"})," with the above. For my console app this meant the following changes:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-diff",children:'  {\n    "compilerOptions": {\n-      "target": "ES2022",\n+      "target": "esnext",\n-      // "lib": [],\n+      "lib": ["ESNext"],\n-      "module": "NodeNext",\n+      "module": "esnext",\n-      // "types": [],\n+      "types": ["bun-types"],\n    },\n  }\n'})}),"\n",(0,i.jsxs)(n.h2,{id:"moduleresolution-with-bun",children:[(0,i.jsx)(n.code,{children:"moduleResolution"})," with Bun"]}),"\n",(0,i.jsx)(n.p,{children:"I'd imagined that at this point I'd be able to run the app, but when I navigated around in VS Code I saw that I had a bunch of errors. I was getting errors like this:"}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.img,{alt:"screenshot of VS Code saying &quot;Cannot find module &#39;fast-xml-parser&#39;. Did you mean to set the &#39;moduleResolution&#39; option to &#39;node&#39;, or to add aliases to the &#39;paths&#39; option?ts(2792)&quot;",src:`https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com${t(53839).A}`,width:"1270",height:"230",loading:"lazy"})}),"\n",(0,i.jsxs)(n.p,{children:["The error message was suggesting I needed to explicitly state that I wanted to use the Node.js module resolution algorithm. Whilst we're using Bun, we're porting a Node app - so this made sense. So I made one more change to the ",(0,i.jsx)(n.code,{children:"tsconfig.json"})," to satisy this:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-diff",children:'  {\n    "compilerOptions": {\n-      // "moduleResolution": "node",\n+      "moduleResolution": "Bundler",\n    },\n  }\n'})}),"\n",(0,i.jsx)(n.p,{children:"With that in place, the module resolution errors were... resolved. (Sorry.)"}),"\n",(0,i.jsx)(n.h2,{id:"file-apis-with-bun",children:"File APIs with Bun"}),"\n",(0,i.jsxs)(n.p,{children:["However, I was still getting errors. This time they were about the ",(0,i.jsxs)(n.a,{href:"https://nodejs.org/api/fs.html#promises-api",children:[(0,i.jsx)(n.code,{children:"fs.promises"})," API"]}),". I was getting errors like this:"]}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.img,{alt:"screenshot of errors in VS Code reporting the absence of the fs.promises API",src:`https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com${t(96107).A}`,width:"761",height:"165",loading:"lazy"})}),"\n",(0,i.jsxs)(n.p,{children:["It looked like the version of bun I was using didn't support that API. As I dug through my code I realised that I was using the ",(0,i.jsx)(n.code,{children:"fs.promises"})," API in a few places. I was using it in the following ways:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.code,{children:"await fs.promises.readdir"})}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.code,{children:"await fs.promises.readFile"})}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.code,{children:"await fs.promises.writeFile"})}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["For ",(0,i.jsx)(n.code,{children:"fs.promises.readFile"})," and ",(0,i.jsx)(n.code,{children:"fs.promises.writeFile"})," I was able to replace them with the Bun equivalents ",(0,i.jsx)(n.a,{href:"https://bun.sh/docs/api/file-io#reading-files",children:(0,i.jsx)(n.code,{children:"Bun.file(path).text()"})})," and ",(0,i.jsx)(n.a,{href:"https://bun.sh/docs/api/file-io#writing-files",children:(0,i.jsx)(n.code,{children:"Bun.write(path, content)"})})," respectively:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-diff",children:"- `await fs.promises.readFile`\n+ `await Bun.file(path).text()`\n- `await fs.promises.writeFile(path, content)`\n+ `await Bun.write(path, content)`\n"})}),"\n",(0,i.jsxs)(n.p,{children:["There appeared to be no Bun equivalent for ",(0,i.jsx)(n.code,{children:"fs.promises.readdir"}),", so I used the ",(0,i.jsx)(n.a,{href:"https://nodejs.org/api/fs.html#fsreaddirsyncpath-options",children:"sync Node.js API"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-diff",children:"- `await fs.promises.readdir`\n+ `fs.readdirSync(path)`\n"})}),"\n",(0,i.jsx)(n.p,{children:"We now had code without any errors. (At least in VS Code as far as TypeScript was concerned. I had yet to run the app to see if it worked.)"}),"\n",(0,i.jsxs)(n.h3,{id:"clarification-on-fspromises",children:["Clarification on ",(0,i.jsx)(n.code,{children:"fs.promises"})]}),"\n",(0,i.jsxs)(n.p,{children:["I was tweeting about my findings as I wrote this, and ",(0,i.jsx)(n.a,{href:"https://twitter.com/jarredsumner/status/1629818921904902145",children:"Jarred Sumner (who works on Bun) was kind enough to share"})," that the ",(0,i.jsx)(n.code,{children:"fs.promises"})," API is implemented but the types aren't as yet."]}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.img,{alt:"Screenshot of exchange on Twitter with Jarred responding &quot;it sort of exists, but looks like the types are out of date. I say sort of because, actually everything async is sync for node and it just wraps in a Promise. If you use fs createReadStream / fs.createWriteStream or Bun.file(path).stream() it\u2019ll be concurrent / async&quot;",src:`https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com${t(42334).A}`,width:"558",height:"833",loading:"lazy"})}),"\n",(0,i.jsx)(n.h2,{id:"running-the-app",children:"Running the app"}),"\n",(0,i.jsx)(n.p,{children:"I now needed to do one more thing:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-diff",children:'-    "start": "ts-node index.ts"\n+    "start": "bun index.ts"\n'})}),"\n",(0,i.jsxs)(n.p,{children:["That's right; update the ",(0,i.jsx)(n.code,{children:"start"})," script in ",(0,i.jsx)(n.code,{children:"package.json"})," to use ",(0,i.jsx)(n.code,{children:"bun"})," instead of ",(0,i.jsx)(n.code,{children:"ts-node"}),". And now I was able to run the app with ",(0,i.jsx)(n.code,{children:"bun start"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"Loading /home/john/code/github/blog.johnnyreilly.com/blog-website/build/sitemap.xml\nReducing 526 urls to 512 urls\n"})}),"\n",(0,i.jsx)(n.p,{children:"The first positive thing about what I saw, was that we appeared to have running code. Yay! The program also appeared to be executing instantaneously, which seemed surprising. I was expecting Bun to be faster, but this seemed too fast."}),"\n",(0,i.jsx)(n.p,{children:"Also, we seemed to be lacking many of the log messages I'd expect. I was expecting to see about 1000 log messages. Something wasn't right."}),"\n",(0,i.jsxs)(n.h2,{id:"top-level-await-and-bun",children:["Top level ",(0,i.jsx)(n.code,{children:"await"})," and Bun"]}),"\n",(0,i.jsxs)(n.p,{children:["The issue was that my ",(0,i.jsx)(n.code,{children:"main"})," function was asynchronous. However, because support for top level ",(0,i.jsx)(n.code,{children:"await"})," wasn't available in Node.js when I originally wrote the code, I'd called the ",(0,i.jsx)(n.code,{children:"main"})," function synchronously. Fortunately Node didn't complain about that, and the program behaved in the way required."]}),"\n",(0,i.jsxs)(n.p,{children:["However Bun looked like it was respecting the fact that ",(0,i.jsx)(n.code,{children:"main"})," was asynchronous. That's why it was apparently executing so quickly; it wasn't waiting for the ",(0,i.jsx)(n.code,{children:"main"})," method to complete before terminating."]}),"\n",(0,i.jsxs)(n.p,{children:["To be honest, Bun's behaviour here is just right; the code as is didn't suggest that it was interested in waiting for the ",(0,i.jsx)(n.code,{children:"main"})," function to complete. But it turns out that waiting is exactly the desired behaviour. To bring things right, we could use top level ",(0,i.jsx)(n.code,{children:"await"}),". So I made the following change to my ",(0,i.jsx)(n.code,{children:"index.ts"})," file:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-diff",children:"- main();\n+ await main();\n"})}),"\n",(0,i.jsx)(n.p,{children:"And now I was getting the expected log messages; and the program appeared to be working as expected."}),"\n",(0,i.jsx)(n.h2,{id:"github-actions-and-bun",children:"GitHub Actions and Bun"}),"\n",(0,i.jsxs)(n.p,{children:["I was now able to run the app locally. But I wanted to run it in GitHub Actions. I just needed to add the ",(0,i.jsx)(n.code,{children:"setup-bun"})," action to my workflow, so bun was available in the GitHub Actions environment:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-yaml",children:"- name: Setup bun \u{1F527}\n  uses: oven-sh/setup-bun@v1\n  with:\n    bun-version: latest\n"})}),"\n",(0,i.jsx)(n.h2,{id:"performance-comparison-bun-vs-ts-node",children:"Performance comparison; Bun vs ts-node"}),"\n",(0,i.jsx)(n.p,{children:"I was expecting Bun to be faster than ts-node. Let's take a run of our app in GitHub Actions with ts-node and compare it to a run of our app with Bun:"}),"\n",(0,i.jsx)(n.h3,{id:"ts-node",children:"ts-node"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"Post processing finished in 17.09 seconds\nDone in 19.52s.\n"})}),"\n",(0,i.jsx)(n.h3,{id:"bun",children:"Bun"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"Post processing finished in 12.367 seconds\nDone in 12.72s.\n"})}),"\n",(0,i.jsx)(n.p,{children:"I haven't done any formal benchmarking, but it looks like Bun is about 50% faster than ts-node for this usecase. That's pretty good. It's also worth expanding on how this breaks down."}),"\n",(0,i.jsx)(n.p,{children:"You'll notice in the logs above there's two log entries:"}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{children:['The "Post processing" reflects the time taken to run the ',(0,i.jsx)(n.code,{children:"main"})," function."]}),"\n",(0,i.jsxs)(n.li,{children:['The "Done" reflects the time taken to run the ',(0,i.jsx)(n.code,{children:"bun"})," command end to end."]}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["What can we learn from this? First of all, running code in ts-node takes 17 seconds, compared to 12 seconds with Bun. ",(0,i.jsx)(n.strong,{children:"So Bun is performing about 40% faster at running code."})]}),"\n",(0,i.jsxs)(n.p,{children:["The end to end is 19 seconds with ts-node, compared to 14 seconds with Bun. ",(0,i.jsx)(n.strong,{children:"So Bun is performing about 50% faster end to end."})," There's two parts to this;
1 the time taken to compile the code and the time taken to start up. We're doing type checking with ts-node; which if deactivated would make a difference."]}),"\n",(0,i.jsx)(n.p,{children:"However, when you look at the difference between the end to end runtime and code runtime with Bun, it's a mere 0.353 seconds. ts-node clocks in at 2.43 seconds for the same. So ts-node is about 6.5 times slower at starting up. That's a pretty big difference; it's unlikely that all of this is TypeScript compilation; Node.js is fundamentally slower at getting going than Bun is."}),"\n",(0,i.jsx)(n.h2,{id:"conclusion",children:"Conclusion"}),"\n",(0,i.jsx)(n.p,{children:"Moving from ts-node to Bun was a pretty easy process. I was able to do it in a few hours. I was able to run the app locally and in GitHub Actions. And I was able to run the app in less time."}),"\n",(0,i.jsx)(n.p,{children:"This all makes me feel very positive about Bun. I'm looking forward to using it more in the future."}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.a,{href:"https://blog.logrocket.com/migrating-typescript-app-node-js-bun/",children:"This post was originally published on LogRocket."})}),"\n",(0,i.jsx)(s,{children:(0,i.jsx)("link",{rel:"canonical",href:"https://blog.logrocket.com/migrating-typescript-app-node-js-bun/"})})]})}function c(e={}){let{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(h,{...e})}):h(e)}},33821(e,n,t){t.d(n,{A:()=>s});let s=t.p+"assets/images/title-image-59128fb2fcf34321698642211daaad26.png"},53839(e,n,t){t.d(n,{A:()=>s});let s=t.p+"assets/images/screenshot-cannot-find-module-2c4a1e2ec791d0084998c18f3930a9bc.png"},96107(e,n,t){t.d(n,{A:()=>s});let s=t.p+"assets/images/screenshot-file-apis-bff7e0019757fd8bacbd8d36107b013a.png"},42334(e,n,t){t.d(n,{A:()=>s});let s=t.p+"assets/images/screenshot-tweet-fs-promises-exists-84f95682cf54e900231b5c959d974a95.png"},34972(e,n,t){t.d(n,{A:()=>s});let s=t.p+"assets/images/title-image-59128fb2fcf34321698642211daaad26.png"},28453(e,n,t){t.d(n,{R:()=>a,x:()=>r});var s=t(96540);let i={},o=s.createContext(i);function a(e){let n=s.useContext(o);return s.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function r(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),s.createElement(o.Provider,{value:n},e.children)}},66664(e){e.exports=JSON.parse('{"permalink":"/migrating-from-ts-node-to-bun","editUrl":"https://github.com/johnnyreilly/blog.johnnyreilly.com/edit/main/blog-website/blog/2023-03-18-migrating-from-ts-node-to-bun/index.md","source":"@site/blog/2023-03-18-migrating-from-ts-node-to-bun/index.md","title":"Migrating from ts-node to Bun","description":"Migrating from ts-node to Bun is surprisingly easy - this post ports a console app from ts-node to Bun and compares performance.","date":"2023-03-18T00:00:00.000Z","tags":[{"inline":false,"label":"Node.js","permalink":"/tags/node-js","description":"The Node.js runtime."},{"inline":false,"label":"TypeScript","permalink":"/tags/typescript","description":"The TypeScript programming language."},{"inline":false,"label":"Open Graph","permalink":"/tags/open-graph","description":"The Open Graph protocol for social media sharing."}],"readingTime":9.72,"hasTruncateMarker":true,"authors":[{"name":"John Reilly","title":"OSS Engineer - TypeScript, Azure, React, Node.js, .NET","url":"https://johnnyreilly.com/about","imageURL":"https://johnnyreilly.com/img/profile-2025.jpg","key":"johnnyreilly","page":null}],"frontMatter":{"slug":"migrating-from-ts-node-to-bun","title":"Migrating from ts-node to Bun","authors":"johnnyreilly","date":"2023-03-18T00:00:00.000Z","tags":["node.js","typescript","open graph"],"image":"./title-image.png","description":"Migrating from ts-node to Bun is surprisingly easy - this post ports a console app from ts-node to Bun and compares performance.","hide_table_of_contents":false,"atUri":"at://did:plc:yy3apqjlms24kso7ahn7lbmb/site.standard.document/3mr5bsh6uhu2b"},"unlisted":false,"prevItem":{"title":"Playwright, GitHub Actions and Azure Static Web Apps staging environments","permalink":"/playwright-github-actions-and-azure-static-web-apps-staging-environments"},"nextItem":{"title":"Node.js 18, Axios and unsafe legacy renegotiation disabled","permalink":"/node-18-axios-and-unsafe-legacy-renegotiation-disabled"}}')}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.