<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Adrian</title>
    <description>The latest articles on DEV Community by Adrian (@afl_ext).</description>
    <link>https://dev.to/afl_ext</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1007562%2F59d657b4-91fe-43be-9627-9c4fba871ef1.png</url>
      <title>DEV Community: Adrian</title>
      <link>https://dev.to/afl_ext</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/afl_ext"/>
    <language>en</language>
    <item>
      <title>Do you really need Dependency Injection? OOP roundtrip journey.</title>
      <dc:creator>Adrian</dc:creator>
      <pubDate>Sat, 17 Feb 2024 15:10:05 +0000</pubDate>
      <link>https://dev.to/afl_ext/do-you-really-need-dependency-injection-oop-roudtrip-journey-202g</link>
      <guid>https://dev.to/afl_ext/do-you-really-need-dependency-injection-oop-roudtrip-journey-202g</guid>
      <description>&lt;h2&gt;
  
  
  What is the idea?
&lt;/h2&gt;

&lt;p&gt;Through my development career I've seen many codebases written in various ways. I remember struggling with code architecture and slowly learning what belongs where and why. Once I reached the full object oriented programming way of doing web development I thought that this is it, this is the way forward now. &lt;/p&gt;

&lt;p&gt;But after some time I started having ideas for improvements, noticing things that are very rarely done but are enginnered to make possible. Turns out a lot of the OOP weight can be cut down, and it can go as far as removing all OOP altogether.&lt;/p&gt;

&lt;p&gt;Let's see what we can trim down, but first, some background.&lt;/p&gt;

&lt;h2&gt;
  
  
  TLDR
&lt;/h2&gt;

&lt;p&gt;Here's the simplified architecture without DI:&lt;br&gt;
&lt;a href="https://github.com/adrian-afl/oop-to-fn-experiment-blog-result"&gt;https://github.com/adrian-afl/oop-to-fn-experiment-blog-result&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  How it was before Nest.js (or Spring, Symfony) OOP web dev
&lt;/h2&gt;

&lt;p&gt;Before OOP, my approach to architecture was very messy. I think code like that can be found everywhere, basically things like executing domain logic or persistence actions at the controller level, or scattering the code in various utility files with no general idea what should go where.&lt;/p&gt;

&lt;p&gt;Code in this stage is very hard to adjust. If one day a requirement appear to add CLI interface to the app, or freeze the api for some external app, can make the team very frustrated about the architecture.&lt;/p&gt;

&lt;p&gt;Main issues here are tightly coupled code layers, inflexibility and no idea about the code layout structure, where files are and what should be contained inside.&lt;/p&gt;
&lt;h2&gt;
  
  
  How it was like with the full OOP
&lt;/h2&gt;

&lt;p&gt;After some time I discovered frameworks that at least try to guide their users about how to lay down the files and what are the general thing to do in most scenarios. &lt;/p&gt;

&lt;p&gt;There is an essential tools in most of those framework, and that also makes them pretty similar, and it is dependency injection.&lt;/p&gt;

&lt;p&gt;Let's analyze how dependency injection, for short, DI, is so awesome and why it looks like such a good idea.&lt;/p&gt;

&lt;p&gt;The idea is that all code that do things is saved in form of classes, then classes are automatically instantiated and used. DI framework will take care of instantiating all classes in the correct order.&lt;/p&gt;

&lt;p&gt;But the crucial thing is that if you want to use code from another class, for example, using user data access in some user logic class, you put it in the constructor as a parameter. You never call this constructor yourself, this is the DI job to read the content of the constructor and then provide correct dependencies based on this, all automatic.&lt;/p&gt;

&lt;p&gt;Another crucial thing is that, in most cases, injecting via an interface is possible as well. This works well for alternate implementations, for example, various email sending services. If there's only one class implementing the interface in the app, it's usually automatically injected, if there are more you need to specify which will be used.&lt;/p&gt;

&lt;p&gt;Looks awesome, but we will see later how it is in real life.&lt;/p&gt;

&lt;p&gt;Usually, code in this stage is structured in a way, that there is a separate api layer, and separate logic layer. For example, a typical feature module would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;userModule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;controllers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;UserController class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="s"&gt;constructor(UserService)&lt;/span&gt;
      &lt;span class="s"&gt;getUser(...)&lt;/span&gt;
      &lt;span class="s"&gt;createUser(...)&lt;/span&gt;
      &lt;span class="s"&gt;updateUser(...)&lt;/span&gt;
      &lt;span class="s"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;dtos&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;UserDto interface&lt;/span&gt;
    &lt;span class="s"&gt;CreateUserDto interface&lt;/span&gt;
  &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;UserService class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="s"&gt;getUser(...)&lt;/span&gt;
      &lt;span class="s"&gt;createUser(...)&lt;/span&gt;
      &lt;span class="s"&gt;updateUser(...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this layout there is a slight problem that the user controller and user service actually does multiple things at once. This interestingly causes another problem, confusion what should be where when the user logic grows more. &lt;/p&gt;

&lt;p&gt;For example, you can end up with a method in user service that takes 150 lines to do the thing needed, and should you extract it? Based on what metric, where is the threshold when doing many things is too many already, and move to what? Another service? This is problematic because developer needs to think about it, rather than just know where to put it in the first place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Going very strict with SRP
&lt;/h2&gt;

&lt;p&gt;Multiple things done by services problem can be mitigated by strictly following the Single Responsibility Principle.&lt;/p&gt;

&lt;p&gt;This will change the code a bit, because now a class is allowed to do only one main thing and handle all other related stuff.&lt;/p&gt;

&lt;p&gt;So now we have separate classes for each endpoints, separate classes for each logical action, and dtos are gone because those are part of controllers now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;userModule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;controllers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;GetUserController class&lt;/span&gt;
    &lt;span class="s"&gt;CreateUserController class&lt;/span&gt;
    &lt;span class="s"&gt;UpdateUserController class&lt;/span&gt;
      &lt;span class="s"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;logic&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;GetUser class&lt;/span&gt;
    &lt;span class="s"&gt;CreateUser class&lt;/span&gt;
    &lt;span class="s"&gt;UpdateUser class&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This also means that most of the times, almost always, the classes will have only 1 public method, and you might want to call it the same always, for example &lt;code&gt;execute&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This code also puts the related data interfaces for each class inside the class file, so you get the input UpdateUser data interface inside the file.&lt;/p&gt;

&lt;p&gt;Now the developer knows where to put the code at first glance, everything is structured in a way that there is no doubt where something should go.&lt;/p&gt;

&lt;p&gt;This is mostly very nice, is such a pleasure to test and generally code like that looks good and is easy to follow.&lt;/p&gt;

&lt;p&gt;But I had some ideas...&lt;/p&gt;

&lt;h2&gt;
  
  
  Domain classes simplified
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Now this part will be a bit more related to languages that use duck typing and imports, like TypeScript, but otherwise no worries, it applies to other languages too.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To understand what will happen we need to talk about some observations with DI first:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;in DI, most of the times all of the instantiated classes are singletons, there exist only one instance always&lt;/li&gt;
&lt;li&gt;swapping implementations around with DI is very rarely done in real like, it's often implemented and handled correctly but then never used, so the effort is wasted&lt;/li&gt;
&lt;li&gt;adds a lot of verbosity in the multiple classes approach&lt;/li&gt;
&lt;li&gt;injecting by interfaces, apart from swapping implementations is almost never done too especially for domain code, and if done, would involve immense amount of verbosity by requiring interfaces for every single class&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, now we have a lot of classes that have dependencies and only 1 public method. &lt;/p&gt;

&lt;p&gt;And here is the main idea.&lt;/p&gt;

&lt;p&gt;What if the dependencies were imports and the public method was the only one exported method, and the class would be gone? &lt;/p&gt;

&lt;p&gt;This way a class turns into a method, and imports do the injecting. And suddenly if all classes are rewritten this way, no more need for DI for domain code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tearing down the api code
&lt;/h2&gt;

&lt;p&gt;After this transformation was done for logic code, I tackled the controllers as well, and this went a bit technical, but thanks for Fastify and Zod I managed to create a very simple api layer with automatic validation that doesn't require DI as well.&lt;/p&gt;

&lt;p&gt;And at this point, my backend code works like it is using a framework, but there is no framework involved, there is not even something like "our own framework", rather the code is just gone completely, not needed anymore.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final minimalistic result
&lt;/h2&gt;

&lt;p&gt;To test this hypothetical architecture approach, I decided to adjust my bootstrap-like user management project backend, this is a simple app that manages users, sends emails, verifies, etc.&lt;br&gt;
This is the result:&lt;br&gt;
&lt;a href="https://github.com/adrian-afl/oop-to-fn-experiment-blog-result"&gt;https://github.com/adrian-afl/oop-to-fn-experiment-blog-result&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's similar to the last one but the classes became files with functions&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;userModule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;controllers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;getUserController fn&lt;/span&gt;
    &lt;span class="s"&gt;createUserController fn&lt;/span&gt;
    &lt;span class="s"&gt;updateUserController fn&lt;/span&gt;
      &lt;span class="s"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;logic&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;getUser fn&lt;/span&gt;
    &lt;span class="s"&gt;createUser fn&lt;/span&gt;
    &lt;span class="s"&gt;updateUser fn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Pros of the final result
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Removal of several big framework dependencies &lt;/li&gt;
&lt;li&gt;Code greatly simplified&lt;/li&gt;
&lt;li&gt;Architecture is very clear to follow and use&lt;/li&gt;
&lt;li&gt;More control over what is happening&lt;/li&gt;
&lt;li&gt;Less abstraction layers on api level - no framework involved in request handling adding unnecessary complexity or abstractions&lt;/li&gt;
&lt;li&gt;Can apply layer boundaries with linter restricting imports&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Cons of the final result
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Sometimes import order will matter if the code inside executes immediately, for example, config files that read env variables immediately, but this is easily resolved anyway&lt;/li&gt;
&lt;li&gt;Swapping implementations is no longer that easy and requires more work, but as I mentioned, this rarely happens anyway, and ever if needed would be very simple to add&lt;/li&gt;
&lt;li&gt;There is some code needed to instantiate the api or other entrypoints that needs to be maintaned, but its mostly simple and gives much more control over handling anyway&lt;/li&gt;
&lt;li&gt;Mocking can be problematic depending on language, but inside Node.js and using Jest this is not a problem at all, just mock the function and done&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Remarks
&lt;/h2&gt;

&lt;p&gt;I think this is good way to structure the code and to remove unnecessary dependencies. Give it a try in your next prototype and tell me what you think!&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>programming</category>
      <category>oop</category>
      <category>functional</category>
    </item>
    <item>
      <title>Are pre-commit git hooks a good idea? I don't think so.</title>
      <dc:creator>Adrian</dc:creator>
      <pubDate>Sun, 17 Dec 2023 16:36:05 +0000</pubDate>
      <link>https://dev.to/afl_ext/are-pre-commit-git-hooks-a-good-idea-i-dont-think-so-38j6</link>
      <guid>https://dev.to/afl_ext/are-pre-commit-git-hooks-a-good-idea-i-dont-think-so-38j6</guid>
      <description>&lt;p&gt;Here's a little rant from a technical owner perspective.&lt;/p&gt;

&lt;p&gt;I've seen this at various places. A senior developer sets up a repository, everything is well and all right, the main branch is protected and there are CICD pipelines set up. Everything is fine. And then, with a little thought given to it, pre-commit git hooks are set up.&lt;/p&gt;

&lt;p&gt;Obviously, I mean the hooks that run on your very machine, before you can actually &lt;em&gt;commit&lt;/em&gt; stuff.&lt;/p&gt;

&lt;p&gt;Is it a good idea? Maybe it's a bad and micro-manager-like way to power flex? Well, let's see.&lt;/p&gt;

&lt;h1&gt;
  
  
  The good intentions
&lt;/h1&gt;

&lt;p&gt;I get it. This option looks attractive on the surface. Here are points that look all right at the first glance.&lt;/p&gt;

&lt;h5&gt;
  
  
  Git history looks cleaner
&lt;/h5&gt;

&lt;p&gt;This point is stupid because you will squash pull requests anyway so any weird commits not passing tests are removed.&lt;/p&gt;

&lt;h5&gt;
  
  
  Save time on CI
&lt;/h5&gt;

&lt;p&gt;Fair point, you will save some time, but are you &lt;em&gt;really&lt;/em&gt; that tight on budget that you can't spare some seconds?&lt;/p&gt;

&lt;p&gt;If this is a problem, maybe your pipelines can be configured better. Follow the fail-early method, run linter first, then units, then integration, and so on.&lt;/p&gt;

&lt;p&gt;Ultimately, the developer working time is much more expensive, and with pre-commit hooks you lose a bit of this time.&lt;/p&gt;

&lt;h5&gt;
  
  
  Force developers to test before pushing
&lt;/h5&gt;

&lt;p&gt;This point is distrust in your workers, just disguised. If you don't trust your developers, fire them, or fire yourself.&lt;/p&gt;

&lt;h1&gt;
  
  
  The bad reality
&lt;/h1&gt;

&lt;p&gt;Usually, pre-commit hooks are a terrible idea. To clarify why, here are some my thoughts on this topic&lt;/p&gt;

&lt;h5&gt;
  
  
  Most likely your tests suite doesn't run in seconds
&lt;/h5&gt;

&lt;p&gt;Tests can be speed up (see my other articles) but usually if you are working on non-trivial code base, the tests will take a considerable amount of time. Maybe it's 5 minutes, maybe 10, who knows, but you really do not want to do it before commit.&lt;/p&gt;

&lt;p&gt;You already have CICD that will run exactly the same suite, why bother running it locally? Use the resources you yourself set up.&lt;/p&gt;

&lt;h5&gt;
  
  
  Developers will be either afraid of commiting often or will just skip this stupidity with --no-verify
&lt;/h5&gt;

&lt;p&gt;This is a very important point, maybe even the most. You want your developers to commit often. This allows more experimentation, which can be reverted if they want, at any time. With long hook any commit is a long investment of time, nothing can be done in this time as well.&lt;/p&gt;

&lt;p&gt;It will hinder your developers performance because of that. They will make less commits, make big commits, and will not be able to tests various solutions to a problem with easy way to revert them.&lt;/p&gt;

&lt;h5&gt;
  
  
  Are you sure tests can be run locally, or you only pretend?
&lt;/h5&gt;

&lt;p&gt;Sure there can be projects that are set up &lt;em&gt;correctly&lt;/em&gt; that everything can be run locally. &lt;/p&gt;

&lt;p&gt;But even more often, nowadays, you will encounter absolutely rubbish setups that require, for example, AWS credentials to run tests.&lt;/p&gt;

&lt;p&gt;Not only this makes it impossible to work locally, for example in a plane or on a train without internet, but also means that developers that are joining the project will need to wait for accesses to remote systems before even trying to commit anything! Hilarious situation, and complete waste of time.&lt;/p&gt;

&lt;p&gt;Most likely one developer will not completely overhaul the whole system, in a way that every single test must be run locally when developing that refactor. Most of the time developer will work on one feature, quickly iterate with tests just for this feature, and once it's ready, will commit and push. Only then on CICD the whole suite will run, and your pipelines are already well configured for this task.&lt;/p&gt;

&lt;p&gt;I should probably write a checklist style article about how to set up a project correctly. Follow me, it will happen soon.&lt;/p&gt;

&lt;h5&gt;
  
  
  Scope of testing in very big projects
&lt;/h5&gt;

&lt;p&gt;Sometimes you encounter a monorepo with 10 subprojects inside it. Why the heck would a developer even want to run tests of completely unrelated projects when developing a small change in one of them? Again, waste of time. Anyway, CICD will validate stuff in the end.&lt;/p&gt;

&lt;p&gt;Not to mention such monorepos are a stupid idea too, why group unrelated projects inside one single repository? There is no good answer to that.&lt;/p&gt;

&lt;h5&gt;
  
  
  Trust issues
&lt;/h5&gt;

&lt;p&gt;Do you really want to show that you do not trust your developers to do the right thing? You should want them to experiment, push half working stuff to their branches, see the outcomes, who cares whats going on in their experimental branches. &lt;/p&gt;

&lt;p&gt;The end goal is the same, working code, with passed pipeline tests, merged into main/dev/whatever.&lt;/p&gt;

&lt;p&gt;By micromanaging your developers by enforcing pre-commit hooks you show that you don't trust them. What's the next step? Watching them code via screen sharing? Believe or not, I've seen that.&lt;/p&gt;

&lt;h1&gt;
  
  
  Final words
&lt;/h1&gt;

&lt;p&gt;Just don't. If you already made a mistake and set up this feature already, remove it. Everyone already does --no-verify anyway.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>git</category>
      <category>commit</category>
      <category>cicd</category>
    </item>
    <item>
      <title>Detect what calls your Node.js code</title>
      <dc:creator>Adrian</dc:creator>
      <pubDate>Mon, 11 Dec 2023 16:30:02 +0000</pubDate>
      <link>https://dev.to/afl_ext/detect-what-calls-your-nodejs-code-49l6</link>
      <guid>https://dev.to/afl_ext/detect-what-calls-your-nodejs-code-49l6</guid>
      <description>&lt;p&gt;While most of the time it's actively discouraged to detect and react to how your code is called, there are times when you would really want to know how the execution came around calling it.&lt;/p&gt;

&lt;p&gt;Examples when this would be very useful are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Logging libraries that autodetect and describe the caller&lt;/li&gt;
&lt;li&gt;Invisible caching for example if you were to reimplement useEffect on the backend&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm sure you will find more creative ways to utilize this madness. Let's jump in and see how to do it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How is this possible
&lt;/h2&gt;

&lt;p&gt;The trick is to utilize V8 stack capturing functionality. No errors are thrown here, just a simple V8 function is being called to get the stack, and then some parsing happens to make the result useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prepare
&lt;/h2&gt;

&lt;p&gt;Let's first set up a function with following signature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getExecutionPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;filterDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="nx"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;filterDir&lt;/code&gt; - will be used to filter out calls from outside your code, for example, from node_modules or linked libraries&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;limit&lt;/code&gt; - how far into the execution history we will reach&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Capture the stack
&lt;/h2&gt;

&lt;p&gt;Now get the most important part, a fake Error stack trace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inobj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;satisfies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;oldLimit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stackTraceLimit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stackTraceLimit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;captureStackTrace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inobj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stackTraceLimit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;oldLimit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code sets the stack limit from the function parameter, captures the stack trace at this very point and restores the original limit.&lt;/p&gt;

&lt;p&gt;The result is in pretty rough format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error
    at getExecutionPath (C:\Code\server-side-state\src\state\getExecutionPath.ts:23:9)
    at new SynchronizedStateful (C:\Code\server-side-state\src\state\state.ts:27:33)
    at state (C:\Code\server-side-state\src\state\state.ts:90:24)
    at Index (C:\Code\server-side-state\src\test\Index.tsx:7:20)
    at C:\Code\server-side-state\src\server.ts:18:25
    at Layer.handle [as handle_request] (C:\Code\server-side-state\node_modules\express\lib\router\layer.js:95:5)
    at next (C:\Code\server-side-state\node_modules\express\lib\router\route.js:144:13)
    at Route.dispatch (C:\Code\server-side-state\node_modules\express\lib\router\route.js:114:3)
    at Layer.handle [as handle_request] (C:\Code\server-side-state\node_modules\express\lib\router\layer.js:95:5)
    at C:\Code\server-side-state\node_modules\express\lib\router\index.js:284:15
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to process that to make it useful. &lt;/p&gt;

&lt;h2&gt;
  
  
  Parse the stack
&lt;/h2&gt;

&lt;p&gt;First run some regex on the lines to get only lines starting with &lt;code&gt;at&lt;/code&gt; and extract the identifier and its location. At the end remove the call of our detection method from the results and  reverse the order.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;detectorRegex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/at &lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;.+&lt;/span&gt;&lt;span class="se"&gt;?)&lt;/span&gt;&lt;span class="sr"&gt; &lt;/span&gt;&lt;span class="se"&gt;\((&lt;/span&gt;&lt;span class="sr"&gt;.+&lt;/span&gt;&lt;span class="se"&gt;?)\)&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;inobj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;at&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;detectorRegex&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;RegExpMatchArray&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, transform the array of matches by parsing those strings inside. Get the identifier, and parse the second parameter to get the path, line and column in mre useful form.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;detections&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;StackEntry&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;identifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pathlinecol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;column&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;pathlinecol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pathlinecol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;pathlinecol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pathlinecol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pathlinecol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;column&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After those steps, the &lt;code&gt;detections&lt;/code&gt; array looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Layer.handle [as handle_request]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;C:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;Code&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;server-side-state&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;node_modules&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;lib&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;router&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;layer.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;95&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Route.dispatch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;C:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;Code&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;server-side-state&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;node_modules&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;lib&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;router&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;route.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;114&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;C:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;Code&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;server-side-state&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;node_modules&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;lib&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;router&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;route.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;144&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Layer.handle [as handle_request]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;C:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;Code&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;server-side-state&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;node_modules&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;lib&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;router&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;layer.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;95&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Index&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;C:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;Code&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;server-side-state&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;src&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;Index.tsx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;once&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;C:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;Code&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;server-side-state&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;src&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;state&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;once.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OnceRunner.produce&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;C:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;Code&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;server-side-state&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;src&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;state&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;once.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Filter the stack
&lt;/h2&gt;

&lt;p&gt;We see we got some calls that are outside our code, let's filter them out and make the paths relative to our base directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;detections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filterDir&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node_modules&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For breadcrumbs, we still need to filter out anonymous functions and some other stuff, but better to put it in a separate variable &lt;code&gt;named&lt;/code&gt;, because the things inside &lt;code&gt;local&lt;/code&gt; can be very useful too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;named&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;anonymous&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Object.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Module.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final results
&lt;/h2&gt;

&lt;p&gt;And now finally make the breadcrumbs string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;breadcrumbs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;named&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it! In this particular example I used, the breadcrumbs will look like this:&lt;br&gt;
&lt;code&gt;Index/once/OnceRunner.produce&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and the &lt;code&gt;local&lt;/code&gt; and &lt;code&gt;named&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Index&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;C:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;Code&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;server-side-state&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;src&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;Index.tsx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;once&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;C:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;Code&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;server-side-state&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;src&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;state&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;once.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OnceRunner.produce&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;C:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;Code&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;server-side-state&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;src&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;state&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;once.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;33&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="nx"&gt;named&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Index&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;C:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;Code&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;server-side-state&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;src&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;Index.tsx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;once&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;C:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;Code&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;server-side-state&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;src&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;state&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;once.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OnceRunner.produce&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;C:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;Code&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;server-side-state&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;src&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;state&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;once.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;33&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cheers and happy coding as always!&lt;/p&gt;

</description>
      <category>node</category>
      <category>stack</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>How to render JSX to whatever you want with a custom JSX Renderer</title>
      <dc:creator>Adrian</dc:creator>
      <pubDate>Mon, 04 Dec 2023 18:21:02 +0000</pubDate>
      <link>https://dev.to/afl_ext/how-to-render-jsx-to-whatever-you-want-with-a-custom-jsx-renderer-cjk</link>
      <guid>https://dev.to/afl_ext/how-to-render-jsx-to-whatever-you-want-with-a-custom-jsx-renderer-cjk</guid>
      <description>&lt;p&gt;Most of the time, you don't need to write your own JSX renderer. But what if you needed to? Well, this guide will show you how to do it.&lt;/p&gt;

&lt;p&gt;It's actually surprisingly simple! So let's dive in.&lt;/p&gt;

&lt;p&gt;TLDR: If you want to see the code straight away, here is the repository: &lt;a href="https://github.com/adrian-afl/render-jsx-reupload"&gt;https://github.com/adrian-afl/render-jsx-reupload&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  What will be needed
&lt;/h4&gt;

&lt;p&gt;You will need TypeScript to follow this guide, and that's it, no external libraries, just your code.&lt;/p&gt;

&lt;h4&gt;
  
  
  Preparation
&lt;/h4&gt;

&lt;p&gt;Create a project - I mean, with package.json - and install TypeScript.&lt;/p&gt;

&lt;p&gt;Then create a tsconfig file, configure it however you want, but in &lt;code&gt;compilerOptions&lt;/code&gt; include those two values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"jsx"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-jsx"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"jsxImportSource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./src/app"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;jsx&lt;/code&gt; set to &lt;code&gt;react-jsx&lt;/code&gt; tells the compiler that we want JSX, but we will be using a different implementation than React, in fact, we will write our own. &lt;code&gt;jsxImportSource&lt;/code&gt; set to &lt;code&gt;./src/app&lt;/code&gt; tells the compiler that this is the place where to find the necessary exported stuff that makes handling JSX possible. Beware, this is a path, and TypeScript will append to it a postfix &lt;code&gt;/jsx-runtime&lt;/code&gt; and will import that file.&lt;/p&gt;

&lt;p&gt;Once this is done, let's go to the implementation. &lt;/p&gt;

&lt;h4&gt;
  
  
  The JSX namespace
&lt;/h4&gt;

&lt;p&gt;Create a file &lt;code&gt;./src/app/jsx-runtime.ts&lt;/code&gt;. This is the file that TypeScript will actually load to do the JSX stuff.&lt;/p&gt;

&lt;p&gt;TypeScript requires you to export very specific things from this file. Those are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;JSX&lt;/code&gt; namespace

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;IntrinsicElements&lt;/code&gt; type - an object type, with keys being the allowed tags, and values being the type of attributes for tags&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Element&lt;/code&gt; type - can be absolutely anything, defines the type that is the result of the JSX node rendering&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;jsx&lt;/code&gt; function - a function which needs a specific arguments and return value type, this is the function that is called by the runtime when rendering JSX nodes.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;jsxs&lt;/code&gt; and &lt;code&gt;jsxDEV&lt;/code&gt; which for now can be aliased to point to the same function as &lt;code&gt;jsx&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is also the place where you can limit what tags are allowed and what attributes are allowed. To limit that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To limit what tags are allowed, narrow the type of &lt;code&gt;IntrinsicElements&lt;/code&gt; key type.&lt;/li&gt;
&lt;li&gt;To limit what attributes are allowed, narrow the type of &lt;code&gt;IntrinsicElements&lt;/code&gt; value type&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This limiting is usually done by making &lt;code&gt;IntrinsicElements&lt;/code&gt; an interface looking like that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IntrinsicElements&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AnhorAttributes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;div&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DivAttributes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;span&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SpanAttibutes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;so&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the attribute types are also interfaces, for example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;AnhorAttributes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;href&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;target&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I just made it in a way that all tags are allowed - by setting the &lt;code&gt;IntrinsicElements&lt;/code&gt; key type to a &lt;code&gt;string&lt;/code&gt;, and all attributes are allowed, by setting the &lt;code&gt;IntrinsicElements&lt;/code&gt; value type to &lt;code&gt;Record&amp;lt;string, JSXNode | undefined&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Example in this article renders the JSX nodes to objects with only one field, a string containing the rendered HTML.&lt;/p&gt;

&lt;p&gt;Here's how it looks like in this file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Set the attributes to allow any keys and very permissive values&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;HTMLAttributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSXNode&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;
        &lt;span class="nx"&gt;JSXChildren&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nx"&gt;JSX&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Allow any HTML tag&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;IntrinsicElements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;HTMLAttributes&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Declare the shape of JSX rendering result&lt;/span&gt;
  &lt;span class="c1"&gt;// This is required so the return types of components can be inferred&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;RenderedNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Export the main namespace&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;JSX&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Export factories&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jsx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;renderJSX&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jsxs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;renderJSX&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jsxDEV&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;renderJSX&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see there are names that are not yet defined, &lt;code&gt;RenderedNode&lt;/code&gt;, &lt;code&gt;JSXNode&lt;/code&gt; and &lt;code&gt;JSXChildren&lt;/code&gt;. Let's explain what they are.&lt;/p&gt;

&lt;h4&gt;
  
  
  Types
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;RenderedNode&lt;/code&gt; is the result shape of the rendering, &lt;code&gt;jsx&lt;/code&gt; function returns values that are of this type. I decided to make it an object, actually a class, because if I went with just a string, I wouldn't be able to do proper escaping of the HTML. This is because I would not be able to differentiate between rendered HTML and a string literal. Anyway, here's how it looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RenderedNode&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, onto JSXChildren. It defines how a &lt;code&gt;children&lt;/code&gt; property looks like. This is the thing that defines what is inside the JSX tags, for example in this JSX:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    Hello
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the string &lt;code&gt;Hello&lt;/code&gt; is the children prop.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;JSXChildren&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;JSXNode&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;JSXNode&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now, the big one. &lt;code&gt;JSXNode&lt;/code&gt; defines the values inside JSX attributes and JSX children. I made it pretty permissive about what can be rendered. You can define separate types for values available for attributes and separate for children, I used the same type for both, which allowed me to use the same serializer function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;JSXNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;RenderedNode&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;RawContentNode&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;JSXNode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;bigint&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While we are on types, let's define 2 additional types that will come in handy soon:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;RawContentNode&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;htmlContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;FunctionComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;RenderedNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;RawContentNode&lt;/code&gt; is a special object that if passed as a value will be rendered to a string but without any escaping. Handy for CSS and style tags, for example. Obviously we need to handle it, and this is not something standard, I just thought it's necessary, for example to handle &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tags, and decided this is a cool way to specify it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;FunctionComponent&lt;/code&gt; is a type for a function component, will be used during the rendering.&lt;/p&gt;

&lt;h4&gt;
  
  
  Rendering
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;jsx&lt;/code&gt; function and its friends are mapped to the &lt;code&gt;renderJSX&lt;/code&gt; function. Here's how it looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;renderJSX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;FunctionComponent&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HTMLAttributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;_key&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;JSX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Element&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// handling Function Components&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// handling &amp;lt;&amp;gt;&amp;lt;/&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RenderedNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;renderChildren&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// handling plain HTML codes&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RenderedNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nf"&gt;renderTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;renderAttributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;renderChildren&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not that complicated, in &lt;code&gt;renderJSX&lt;/code&gt; 3 execution paths are possible:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If &lt;code&gt;tag&lt;/code&gt; is a function, we got a function component to handle. It's already a function, and we got the &lt;code&gt;props&lt;/code&gt; that should be passed to it, so just call it with the &lt;code&gt;props&lt;/code&gt; and return whatever it returns.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;tag&lt;/code&gt; is undefined, we have a &lt;code&gt;&amp;lt;&amp;gt;&amp;lt;/&amp;gt;&lt;/code&gt; fragment. It cannot take props, and can only have one or more children. Rendering it boils down to rendering every child inside, we will see soon how it's done.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;tag&lt;/code&gt; is a string, it's a plain HTML element. Render it using the &lt;code&gt;renderTag&lt;/code&gt; method that I will describe below.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We see that we need 3 helper functions so far, those will again have helper functions, but bear with me.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;renderTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tagWithAttributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// render open and close tags&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tagWithAttributes&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// render only one self-closing tag&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tagWithAttributes&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;renderTag&lt;/code&gt; function is straightforward, renders a plain HTML element to a string, using the parameters passed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;renderChildren&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HTMLAttributes&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;childrenArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;childrenArray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;escapeHTML&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;renderChildren&lt;/code&gt; functions gets a special attribute named &lt;code&gt;children&lt;/code&gt; from the props object that &lt;code&gt;jsx&lt;/code&gt; got. This attribute, if present, holds whatever is between the JSX tags, like in the example before, &lt;code&gt;&amp;lt;div&amp;gt;Hello&amp;lt;div&amp;gt;&lt;/code&gt; would contain &lt;code&gt;Hello&lt;/code&gt; as children. This might be just one element, then it's not an array, or an array, then it comes in as an array. To handle it easier, transform the value to always be an array, then serialize all elements to strings and join them together.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;renderAttributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HTMLAttributes&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;children&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;escapeProp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s2"&gt;="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;renderAttributes&lt;/code&gt; is responsible for rendering the HTML attributes string that will ultimately make its way into an HTML tag. First, filter out the special &lt;code&gt;children&lt;/code&gt; property, it was handled with &lt;code&gt;renderChildren&lt;/code&gt;. Then with the remaining elements, serialize the value to a string and render the attribute string. Finally, join the resulting strings, but this time with a space, because we need a space between the attributes in final HTML string.&lt;/p&gt;

&lt;p&gt;As you can see, there are some other helper functions. First of all we need the &lt;code&gt;serialize&lt;/code&gt; function, its helper type and helper error, here it is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SerializationError&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;invalidValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;RawContentNodeTest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;htmlContent&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSXNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;escaper&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Null and undefined handling&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// String node handling&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;escaper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// Number node handling&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bigint&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// Boolean node handling&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;boolean&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;false&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// Function node handling&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;escaper&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// RenderedNode node handling&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;RenderedNode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// Dangerous string handling&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="k"&gt;typeof &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;RawContentNodeTest&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;htmlContent&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;htmlContent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SerializationError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The error is only for semantics, I like to make custom errors for various occasions. The &lt;code&gt;RawContentNodeTest&lt;/code&gt; is a helper type to test if the value coming in is the special one that should be rendered without escaping special characters.&lt;/p&gt;

&lt;p&gt;You can also notice that the function takes a second parameter that will do the escaping. It's a simple function signature that takes a string and returns a string. I decided to make the serializer be the same for HTML content and attributes, but both require slightly different escaping, that's why the second parameter is needed.&lt;/p&gt;

&lt;p&gt;So the following happen in this function:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If the value is &lt;code&gt;null&lt;/code&gt; or &lt;code&gt;undefined&lt;/code&gt;, return an empty string&lt;/li&gt;
&lt;li&gt;If the value is a &lt;code&gt;string&lt;/code&gt;, escape special characters and return it&lt;/li&gt;
&lt;li&gt;If value is a &lt;code&gt;number&lt;/code&gt;, &lt;code&gt;bigint&lt;/code&gt; or &lt;code&gt;boolean&lt;/code&gt;, convert it to string and return it&lt;/li&gt;
&lt;li&gt;If value is a &lt;code&gt;function&lt;/code&gt;, call it, and pass the result via &lt;code&gt;serialize&lt;/code&gt; again, then return it&lt;/li&gt;
&lt;li&gt;If value is already a &lt;code&gt;RenderedNode&lt;/code&gt;, extract the content of it and return it&lt;/li&gt;
&lt;li&gt;If value is of that special &lt;code&gt;RawContentNode&lt;/code&gt; type, which is checked by checking that value is an &lt;code&gt;object&lt;/code&gt; and then the existence and type of value under the &lt;code&gt;htmlContent&lt;/code&gt;, then return it as is, without escaping&lt;/li&gt;
&lt;li&gt;Otherwise, throw and error&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What is left to make this implementation complete are the functions that escape special characters inside HTML content and HTML attributes. Those are very simple, look like that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;escapeProp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;amp;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;quot;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;#10;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;escapeHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;amp;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;quot;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;#39;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;br/&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those just replace some stuff inside the strings and return them.&lt;/p&gt;

&lt;h4&gt;
  
  
  Usage
&lt;/h4&gt;

&lt;p&gt;And here we have it! It will work from now automatically without any special imports or libraries. Just this TypeScript code.&lt;/p&gt;

&lt;p&gt;To use it, for example, create a Test.tsx file and put inside:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ComponentA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;RenderedNode&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Hello&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ComponentB&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;RenderedNode&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ComponentA&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rendered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ComponentB&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rendered&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running this code you will see this printed out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&amp;lt;span&amp;gt;&lt;/span&gt;Hello&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Why would anyone want to do that?
&lt;/h4&gt;

&lt;p&gt;I know preact to string exists, but it is not a fit for some usages because it still assumes you are targeting real HTML. For example, only HTML tags are allowed, and the values and names of attributes are type-checked to be correct HTML. It's not desired sometimes. With this code you don't need to worry about it, you can render the JSX to whatever you want.&lt;/p&gt;

&lt;p&gt;I personally need this for an upcoming terminal based UI library that will use JSX to define its content, but that's a story&lt;br&gt;
for another time :)&lt;/p&gt;

&lt;h4&gt;
  
  
  Thanks for reading, and happy rendering!
&lt;/h4&gt;

&lt;p&gt;Here you can find the code from this article: &lt;a href="https://github.com/adrian-afl/render-jsx-reupload"&gt;https://github.com/adrian-afl/render-jsx-reupload&lt;/a&gt;&lt;/p&gt;

</description>
      <category>jsx</category>
      <category>render</category>
      <category>typescript</category>
      <category>tsx</category>
    </item>
    <item>
      <title>Prisma VS TypeORM - description and comparison</title>
      <dc:creator>Adrian</dc:creator>
      <pubDate>Mon, 15 May 2023 09:23:49 +0000</pubDate>
      <link>https://dev.to/afl_ext/prisma-vs-typeorm-description-and-comparison-4bob</link>
      <guid>https://dev.to/afl_ext/prisma-vs-typeorm-description-and-comparison-4bob</guid>
      <description>&lt;p&gt;You probably encounter such discussion in the wild often, mostly probably if you are a backend developer. Which ORM to use? There are options and you cannot decide?&lt;/p&gt;

&lt;h3&gt;
  
  
  Well, let me help! But first...
&lt;/h3&gt;

&lt;h2&gt;
  
  
  What is an ORM?
&lt;/h2&gt;

&lt;p&gt;To make this article accessible for most developers, I will explain very quickly why you would want to have the ORM:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mostly a simplification, but truth nonetheless: It writes SQL for you!&lt;/li&gt;
&lt;li&gt;Maps objects in the code to what and how is saved in your relational database (hence the name, object &amp;lt;-&amp;gt; relational mapping)&lt;/li&gt;
&lt;li&gt;Most of libraries also expose some useful tools to keep the database in sync with the code, or other way around.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why would you want to use one, you ask? Why not use raw connection with let's say PostgreSQL and manage data this way?&lt;br&gt;
Well, you certainly could, and most people did that decade or two decades ago, and shock-you-not - it worked! But there were some disadvantages of doing this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need to keep the database up to date with the changes manually or spin up some tool to do that&lt;/li&gt;
&lt;li&gt;If migrations mechanics are not employed, database state is not versioned or may be versioned in less than ideal way&lt;/li&gt;
&lt;li&gt;You need to write code that maps raw stuff from the database into your code objects and vice-versa, map your code objects to database raw data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So in this case, you write mapping by yourself, migrations by yourself, and then only SQL is left for you to do manually and you are quickly approaching the inevitable implict outcome - you will build your own ORM this way.&lt;/p&gt;

&lt;p&gt;There is a reason there are so many new ORM appearing for the node ecosystem recently, many probably came into life this way, someone was writing some db connectivity convenience layer and it turned into an ORM. Fun activity, but don't do that when you code for the money in your current project.&lt;/p&gt;

&lt;p&gt;Right, with this out of our way...&lt;/p&gt;

&lt;h2&gt;
  
  
  Tell me about TypeORM!
&lt;/h2&gt;

&lt;p&gt;I'm glad you asked. I used this library for years now professionally. Which is the main reason I started looking into Prisma, but let's not get ahead of ourselves..&lt;/p&gt;

&lt;p&gt;TypeORM key concept is that usually uses the &lt;strong&gt;code-first&lt;/strong&gt; paradigm.&lt;br&gt;&lt;br&gt;
What does that mean? It means you write your code first, entity classes to be precise, then add decorators to those classes and fields inside that serve as a description of this class layout to the ORM, and ORM then uses that data, provided by the decorators, to map your classes into database data.&lt;/p&gt;

&lt;p&gt;This is similar to what Doctrine (PHP and Symfony world) and Hibernate (Java and Spring world) usually do as well, and TypeORM also can do the mapping in separate files if needed, but in general using decorators is encouraged by its docs.&lt;/p&gt;

&lt;p&gt;So summarizing, your write code, decorate it, and that's how you declare your mapping.&lt;/p&gt;

&lt;h2&gt;
  
  
  And now, Prisma
&lt;/h2&gt;

&lt;p&gt;I'm experimenting with Prisma for some time now, but not yet switched in professional jobs, but this is on my horizon. But when it comes to my personal hobby projects - I use Prisma all the way.&lt;/p&gt;

&lt;p&gt;Prisma has a very different approach to defining your mapping, and you do that in just one file, the schema file. This file is called usually schema.prisma and can be anywhere in your repo.&lt;/p&gt;

&lt;p&gt;Now comes the brilliant part: Once you have your mapping written in that schema.prisma file, Prisma will generate all entity and repository classes and functions with all possible typing variations for you.&lt;/p&gt;

&lt;p&gt;This means Prisma is &lt;strong&gt;schema-first&lt;/strong&gt;, you write your schema, and code is generated for you. Along with the types!&lt;/p&gt;

&lt;h2&gt;
  
  
  TypeORM Setup
&lt;/h2&gt;

&lt;p&gt;After installing the library, on npm called &lt;code&gt;typeorm&lt;/code&gt;, let's create a file with the connection. This is fairly standard and is needed for typeorm cli to work, but we will also use it later in the code. Call the file data-source.ts:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqlj34cs8ngswqq81x6d5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqlj34cs8ngswqq81x6d5.png" alt="TypeORM Setup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, all of the database credentials are loaded from the environment. There could also be some bit of validation for those values, but let's keep things simple now (looking at you zod, later).&lt;/p&gt;

&lt;p&gt;Now let's create 2 very simple entities&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn2w22gt3vya60w5wt8ln.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn2w22gt3vya60w5wt8ln.png" alt="TypeORM Entity 1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frz67clxti6jkurzztnwu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frz67clxti6jkurzztnwu.png" alt="TypeORM Entity 2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So this is it. This is how you do the mapping in the TypeORM. You can see there is a top level decorator making the class as an entity, and decorators for fields telling how to manage them. We can also see how relations are mapped, and the autogenerated uuid id field.&lt;/p&gt;

&lt;p&gt;Now that we have our entities, we need to create a migration that will apply our changes to the database. There is a CLI tool typeorm exposes to do that, so let's use it, this way:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn typeorm-ts-node-commonjs migration:generate -d ./data-source.ts "./src/migrations/CreateUserAndAgency"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And if it works: we will be presented with following code:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffwyn4ihtqmpy6gcjtbfv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffwyn4ihtqmpy6gcjtbfv.png" alt="TypeORM migration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see there are up and down methods, and that the code is in fact a class. You can write any code there, which is a blessing but also a curse. More on that later.&lt;/p&gt;

&lt;p&gt;And finally, apply the migration!&lt;br&gt;
&lt;code&gt;yarn typeorm-ts-node-commonjs migration:run -d ./data-source.ts&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If all went well, we can now use the ORM to do something:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnab7db6793ojqgtcewyw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnab7db6793ojqgtcewyw.png" alt="TypeORM usage 1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prisma setup
&lt;/h2&gt;

&lt;p&gt;When it comes to installing prisma, we need 2 libraries:&lt;br&gt;
&lt;code&gt;@prisma/client&lt;/code&gt; in production dependencies and &lt;code&gt;prisma&lt;/code&gt; in dev dependencies&lt;br&gt;
Once installed, let's go straight to writing our schema!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9g6ib3aiq7o911w9rvxx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9g6ib3aiq7o911w9rvxx.png" alt="Prisma Setup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that's it! This is how you define your mapping in Prisma. There are several pros of this we will talk on later, but notice now that there is just 1 type of the field, not "type from code" and "type from db" dualism.&lt;/p&gt;

&lt;p&gt;Once we have the schema ready, now it's time for the biggest trick, client code generation. Use this to do it:&lt;br&gt;
&lt;code&gt;prisma generate --schema=./schema.prisma&lt;/code&gt;&lt;br&gt;
And generated! By default, the client code is saved to &lt;code&gt;./node_modules/.prisma&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now, just as in TypeORM, we will create a migration, using the following command:&lt;br&gt;
&lt;code&gt;prisma migrate dev --schema=./schema.prisma --create-only --name UserAndAgency&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuegr2bi7llsfyigd20ae.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuegr2bi7llsfyigd20ae.png" alt="Prisma Migration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can now see a big different - Prisma migrations are just SQL files! This can be a curse or a blessing, more on that later.&lt;/p&gt;

&lt;p&gt;Now apply migrations, but now be careful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;on your dev environments where you don't care that much of data but care a lot about bug catching: &lt;code&gt;prisma migrate dev --schema=./schema.prisma&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;on your prod environment where you care a lot of your data: &lt;code&gt;prisma migrate deploy --schema=./schema.prisma&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is very important! Because Prisma in the dev migrate utilizes a powerful mechanism called Shadow Schema.&lt;br&gt;
Prisma will basically create a new database side by side, migrate it to the point where original db is migrated to, check for schema drift, and report on it. This will allow you to catch bugs. But you don't want this done on the production.&lt;/p&gt;

&lt;p&gt;And now, let's use the Prisma in the simpliest way possible:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpolby7urzqs770xx766q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpolby7urzqs770xx766q.png" alt="Prisma simple usage"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Side by Side comparison
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Fetching by relations:
&lt;/h3&gt;

&lt;p&gt;In this scenario we will see how to fetch something based on something related, particularly users belonging to an agency.&lt;/p&gt;

&lt;p&gt;In Prisma:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F79c3wm5znt5t39l6d78v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F79c3wm5znt5t39l6d78v.png" alt="Fetching By Relation Prisma"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In TypeORM:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9bij7p5546rtyopoq72z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9bij7p5546rtyopoq72z.png" alt="Fetching By Relation TypeORM"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Very similar, the repositories is a different approach between those ORMs. In TypeORM every entity has its own repository class configured to take care of that entity. In Prisma similar thing exists, but as subfields of the client object, like &lt;code&gt;prismClient.user&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fetching with relations:
&lt;/h3&gt;

&lt;p&gt;In this scenario, we want to get and user but also its agency along with it.&lt;/p&gt;

&lt;p&gt;Prisma:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fua96wid0jwdu2352d0bx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fua96wid0jwdu2352d0bx.png" alt="Fetching Relations Prisma"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;TypeORM:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj2862q03xo0a9a6esz02.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj2862q03xo0a9a6esz02.png" alt="Fetching Relations TypeORM"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This looks very similar, but there are some differences under the hood:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TypeORM will execute 1 query with a join clause to get the results&lt;/li&gt;
&lt;li&gt;Prisma will execute 1 query to get the user, and then 1 query to get the agency. If multiple users were fetched, there would still be 2 queries, 1 for users, and 2 for all agencies of fetched users. This is not n+1, rather 1+1 :)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;TypeORM typing in this situation shows its biggest weakness. If we did not include agency in relations field, we would get an User field, which type suggest there is a non nullable Agency field in it. And compilator would not know any better that TypeORM will happily ignore that and return you an User with agency set to undefined, despite the types saying its not undefined. You can't rely on types to be sure what your entity really contains, you need to know its path from being fetched to being used to understand it's format. This is really painful.&lt;/p&gt;

&lt;p&gt;Prisma, on the other hand, has types prepared for every situation, so in the example above it will return the result of type of a user with agency field, but if we removed the agency from include clause, the type returned would reflect that, and agency field wouldn't be there! This is perfect.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inserting new data
&lt;/h3&gt;

&lt;p&gt;In Prisma: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fko082rnn40g2hy58xloi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fko082rnn40g2hy58xloi.png" alt="Inserting Prisna"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In TypeORM:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb5shhesjhy70ph9j6x1s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb5shhesjhy70ph9j6x1s.png" alt="Inserting TypeORM"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some interesting points: look at this select clause in prisma api call. We put only 'id' there, and so the result will be typed to only have this one field and nothing else. This is how you get the ID back if it's generated by the database, usually. &lt;br&gt;
In TypeORM example, pay attention that the &lt;code&gt;create&lt;/code&gt; method doesn't write anything to database, it just creates an object instance, of type User, with fields set to that. This is needed so the repository knows that this object is in fact of the user entity type. The objects is then saved using the &lt;code&gt;save&lt;/code&gt; method that works like an upsert - inserts if not exist, updates if exists. Confusing? Now tell me what if I put the id in the body? Will it update? Insert new? Override ID generation? How knows, and docs would not help.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update existing entity
&lt;/h3&gt;

&lt;p&gt;In Prisma: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3j1ylw8g8l35anlaapwz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3j1ylw8g8l35anlaapwz.png" alt="Updating Prisma"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In TypeORM:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fozk303d98zuug1g3orua.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fozk303d98zuug1g3orua.png" alt="Updating TypeORM"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Prisma and first TypeORM examples are simple and clear.&lt;br&gt;
But the second TypeORM example shows something different, if you already have the entity in your code, you can change it directly and save it, the result will be the same.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running code in transaction
&lt;/h3&gt;

&lt;p&gt;In Prisma:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhg5ihvmjrzt7pah7q7c5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhg5ihvmjrzt7pah7q7c5.png" alt="Transactions Prisma"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In TypeORM: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3sth4k9yxswu4f3ord8z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3sth4k9yxswu4f3ord8z.png" alt="Transactions TypeORM"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This type of transactions are called interactive because arbirtary code can be put inside. Beware there are timeouts, in prisma, the defualt timeout in this transaction block is 5000ms but can be changed easily.&lt;br&gt;
Be sure to use the parameter of the callback method you pass to the transaction method instead!&lt;/p&gt;

&lt;h3&gt;
  
  
  Dockerization
&lt;/h3&gt;

&lt;p&gt;When it comes to TypeORM, it's fairly easy because no code generation so I won't go into details here.&lt;/p&gt;

&lt;p&gt;But when it comes to Prisma, here is an example of a Dockerfile how to approach it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1mmfyzb4w32eviwk0038.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1mmfyzb4w32eviwk0038.png" alt="Prisma Dockerfile"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let me explain.&lt;br&gt;
In the first step, production node modules are downloaded&lt;br&gt;
In next step, application is build, and prisma client code is generated&lt;br&gt;
In last step, production node modules are copied back but also autogenerated prisma client code from build stage&lt;br&gt;
And that should be enough to get it up and running.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion!
&lt;/h2&gt;

&lt;h3&gt;
  
  
  TypeORM
&lt;/h3&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Similar to other well known tooks - Doctrine, Hibernate&lt;/li&gt;
&lt;li&gt;Can be more modular fitting in more complex project architectures - for example giving one module access to only some entities&lt;/li&gt;
&lt;li&gt;I think integration with NestJS is very good, but Prisma integrates just as well in my opinion&lt;/li&gt;
&lt;li&gt;More optimal queries at times&lt;/li&gt;
&lt;li&gt;Migrations are code - Yay! Custom logic!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Very weird bugs happening at times&lt;/li&gt;
&lt;li&gt;Most usually your bug solving will result in you reading tons of github issues of the typeorm repo, instead of docs&lt;/li&gt;
&lt;li&gt;Types are commonly invalid at runtime&lt;/li&gt;
&lt;li&gt;Data-source file is not documented anywhere well while being crucial part of cli&lt;/li&gt;
&lt;li&gt;No idea why, but it looks like schema drift happens very quick&lt;/li&gt;
&lt;li&gt;Migrations are code - Oh no, people will use real entities in there and break migrations - happens too often and best is to disallow imports from app into migrations - you don't want a junior import and Enum and iterate over it in the migration file...&lt;/li&gt;
&lt;li&gt;Migrations are less readable, especially keys names&lt;/li&gt;
&lt;li&gt;At times error handling is very not helpful&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prisma
&lt;/h3&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;So fresh approach&lt;/li&gt;
&lt;li&gt;Very good type safety&lt;/li&gt;
&lt;li&gt;Much better error reporting&lt;/li&gt;
&lt;li&gt;Nice documentation&lt;/li&gt;
&lt;li&gt;Looks like the community around is more helpful&lt;/li&gt;
&lt;li&gt;Handy schema language - I recommend a JetBrains plugin for that, makes working with the file so easy&lt;/li&gt;
&lt;li&gt;Code generation saves a lot of time&lt;/li&gt;
&lt;li&gt;Can create and update multiple and nested entities in one api call in a migration - see nested writes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;At times relations are not obvious but the JetBrains plugin helps&lt;/li&gt;
&lt;li&gt;No query builder at all, either high level management or low level queies&lt;/li&gt;
&lt;li&gt;At times type errors are very verbose but at least are clear&lt;/li&gt;
&lt;li&gt;Especially at the beginning - easy to forget to rebuild client - best to have it as part of the build and dev commands&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  So we got it.
&lt;/h2&gt;

&lt;p&gt;I hope this article will help to make some educated decision on your next project! I will personally switch to Prisma.&lt;/p&gt;

&lt;p&gt;Happy mapping!&lt;/p&gt;

</description>
      <category>prisma</category>
      <category>typeorm</category>
      <category>typescript</category>
      <category>orm</category>
    </item>
    <item>
      <title>Dependency Injection without decorators in TypeScript</title>
      <dc:creator>Adrian</dc:creator>
      <pubDate>Mon, 13 Feb 2023 19:34:52 +0000</pubDate>
      <link>https://dev.to/afl_ext/dependency-injection-without-decorators-in-typescript-5gd5</link>
      <guid>https://dev.to/afl_ext/dependency-injection-without-decorators-in-typescript-5gd5</guid>
      <description>&lt;p&gt;Decorators will get a big revamp in TypeScript 5, and while decorators are cool, what about getting rid of them? And what if we can make the DI &lt;strong&gt;much&lt;/strong&gt; better along the way?&lt;/p&gt;

&lt;p&gt;As I worked in other languages as well with more established DI ecosystems, I see that a good DI framework should:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Be transparent - your domain should &lt;strong&gt;not&lt;/strong&gt; import anything related to the DI, and it should not know about the DI at all&lt;/li&gt;
&lt;li&gt;Support interfaces without silly decorators with the interface name&lt;/li&gt;
&lt;li&gt;Automatically provide the implementation for interfaces that are implemented only once&lt;/li&gt;
&lt;li&gt;Should work just fine when compiled down to a production package&lt;/li&gt;
&lt;li&gt;Autowires - means it will automatically create everything it can with no or very minimal configuration&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Matching those points using TypeScript is not trivial. I will ignore decorators for now and their experimental reflection features, and focus on what we will have on hand with TypeScript 5.&lt;/p&gt;

&lt;p&gt;There are a few obstacles along the way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TypeScript provides no reflection support whatsoever&lt;/li&gt;
&lt;li&gt;Due to lack of reflection, getting classes constructor parameter's types is impossible&lt;/li&gt;
&lt;li&gt;Runtime will also not preload all your classes so you would need to import them &lt;em&gt;somewhere&lt;/em&gt; so that the class declaration runs and the class is defined&lt;/li&gt;
&lt;li&gt;Interfaces are erased during compilation and have zero meaning in the running code&lt;/li&gt;
&lt;li&gt;Getting a list of implemented interfaces by a given class is impossible&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rough. But it turns out that overcoming all of those problems and matching all of the key features of a good DI implementation &lt;strong&gt;is possible&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The key here is &lt;strong&gt;Reflection&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fine, I will do it myself
&lt;/h2&gt;

&lt;p&gt;How to even approach this? We need to get information about every class in the application and a lot of information from it, including implemented interfaces and constructor parameters, without decorators. Right...&lt;/p&gt;

&lt;p&gt;What if we could scan our codebase and read every file, find class declarations in them, parse those and extract necessary data from it? Surely there must be a way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter TypeScript compiler API.
&lt;/h2&gt;

&lt;p&gt;The Compiler API can be used to get a tokenized representation of the source code.&lt;br&gt;
So we can do the following to get what we want:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scan over the source directory for typescript files&lt;/li&gt;
&lt;li&gt;For each file:

&lt;ul&gt;
&lt;li&gt;Parse it to get the abstract syntax tree - a tree of tokens&lt;/li&gt;
&lt;li&gt;Recursively find all nodes that are class declarations, then for each found:&lt;/li&gt;
&lt;li&gt;Read the node to find the class name, extends, implements&lt;/li&gt;
&lt;li&gt;Find the constructor node and read parameters from it&lt;/li&gt;
&lt;li&gt;Push found data to some array&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After this is completed, we will be left with an array of class reflection data objects. Using that, we can generate a typescript source file with that metadata and store it in the source folder itself.&lt;/p&gt;

&lt;p&gt;After that, we have class metadata, ready to use.&lt;/p&gt;
&lt;h2&gt;
  
  
  What now?
&lt;/h2&gt;

&lt;p&gt;In my implementation, after generating the metadata, the saved result is an array of the following objects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;fqcn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Fully qualified class name - path and name&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Class name&lt;/span&gt;
  &lt;span class="nl"&gt;ctor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Constructor&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Constructor for that the class - null if not public&lt;/span&gt;
  &lt;span class="nl"&gt;implementsInterfaces&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt; &lt;span class="c1"&gt;// Interfaces implemented by the class&lt;/span&gt;
  &lt;span class="nl"&gt;extendsClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Parent of the class - null if not extending&lt;/span&gt;
  &lt;span class="nl"&gt;constructorParameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ParameterData&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt; &lt;span class="c1"&gt;// names and types of constructor parameters&lt;/span&gt;
  &lt;span class="nl"&gt;constructorVisibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;protected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;private&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Constructor visibility&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see that we have everything needed to create a working DI.&lt;/p&gt;

&lt;p&gt;Now, given each class in the metadata:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We how its name, so we can find it by name when needed &lt;/li&gt;
&lt;li&gt;We know the interfaces it implements, so we can find it by interface names&lt;/li&gt;
&lt;li&gt;We have its constructor function, so we can create instances&lt;/li&gt;
&lt;li&gt;We know what parameters that constructor takes, its names, and its type names&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Autowiring will be done recursively, we only need to know the name of the type:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Find metadata of the class by the provided name&lt;/li&gt;
&lt;li&gt;If not found, find metadata of the class implementing the provided name&lt;/li&gt;
&lt;li&gt;For each constructor parameter in the metadata:

&lt;ul&gt;
&lt;li&gt;Autowire the parameter by type name the same way (recursively)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;With ready parameters array, construct the class&lt;/li&gt;

&lt;li&gt;Return the constructed instance&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This method will work just as fine with classes and interfaces, so both can be used in parameter names and during resolving&lt;/p&gt;

&lt;p&gt;And that's it! We successfully implemented a working DI without decorators.&lt;/p&gt;

&lt;p&gt;Of course, there are other details that need to be taken into consideration to make the DI fully functional, but those are easy things now that the difficult part is done.&lt;/p&gt;

&lt;p&gt;Happy autowiring!&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>Use Mocha instead of Jest and boost your tests speed</title>
      <dc:creator>Adrian</dc:creator>
      <pubDate>Wed, 08 Feb 2023 18:15:39 +0000</pubDate>
      <link>https://dev.to/afl_ext/use-mocha-instead-of-jest-and-boost-your-tests-speed-4cpl</link>
      <guid>https://dev.to/afl_ext/use-mocha-instead-of-jest-and-boost-your-tests-speed-4cpl</guid>
      <description>&lt;h2&gt;
  
  
  Don't you want to save time?
&lt;/h2&gt;

&lt;p&gt;I think it is common knowledge that testing applications is a crucial part of software development. It is also very helpful when it comes to developing separate features.&lt;/p&gt;

&lt;p&gt;But what if your tests are very slow? Maybe a single test case takes 30 seconds to execute? Or your local test setup is so cumbersome you want to avoid it? You will end up not using them much, and this will harm your velocity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Here are some traits of good tests in a project:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You can easily run just one test&lt;/li&gt;
&lt;li&gt;Running just one test is very quick&lt;/li&gt;
&lt;li&gt;You don't need to do any changes to the environment to run tests locally&lt;/li&gt;
&lt;li&gt;You can run tests repeatedly without reinitializing the database&lt;/li&gt;
&lt;li&gt;You can run locally the same test suite that is run on your pipeline&lt;/li&gt;
&lt;li&gt;Even running the whole test suite is fast&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this post, I will focus on the points related to speed.&lt;/p&gt;

&lt;h2&gt;
  
  
  We really need speed
&lt;/h2&gt;

&lt;p&gt;Just one time reiterating on this so it's clear - the speed of tests runtime will directly affect your delivery speed. And delivery is crucial.&lt;/p&gt;

&lt;p&gt;But what we can do about our tests? Turns out there is one key change that can be applied to a lot of projects, is almost a drop-in replacement and usually results in an extreme speed boost.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Jest does it
&lt;/h2&gt;

&lt;p&gt;Most projects use Jest to run their tests. It's the default runtime for React apps and NestJS apps.&lt;/p&gt;

&lt;p&gt;Assume we have the following NestJS application: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is Typescript based&lt;/li&gt;
&lt;li&gt;Integration tests start up the NestJS application module&lt;/li&gt;
&lt;li&gt;Supertest is used to hit the API&lt;/li&gt;
&lt;li&gt;Nest build takes 5 seconds&lt;/li&gt;
&lt;li&gt;Nest app startup takes 1 second&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's see how Jest runs tests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start a Typescript compiler instance and compile and run any global setup defined&lt;/li&gt;
&lt;li&gt;Find all files containing tests, most likely files ending with .spec.ts&lt;/li&gt;
&lt;li&gt;For every of found files:

&lt;ul&gt;
&lt;li&gt;Start a Typescript compiler instance and compile the file, which in turns compiles the whole application because NestJS is imported to create the application to test&lt;/li&gt;
&lt;li&gt;Run the tests in the file&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;If we had 10 test files, it will result in at least 10 * 5, so 50 seconds wasted on recompiling the application over and over again 10 times, and then another 10 seconds to reinitialize the application 10 times. &lt;br&gt;
The more files there are, the slower it gets.&lt;/p&gt;

&lt;p&gt;Why does Jest do that? Probably due to sandboxing, maybe it makes sense. Parallelization works fine and improves on that but be prepared to bump your runners 1 or 2 tiers up to make them handle parallelized compilation over and over again.&lt;/p&gt;

&lt;h3&gt;
  
  
  The secret sauce - Mocha
&lt;/h3&gt;

&lt;p&gt;Mocha (&lt;a href="https://mochajs.org/" rel="noopener noreferrer"&gt;https://mochajs.org/&lt;/a&gt;) is a test runtime just like Jest. But it does things a bit differently.&lt;br&gt;
Assuming the same setup we had in Jest example, here is what Mocha would do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Find all files containing tests, most likely files ending with .spec.ts&lt;/li&gt;
&lt;li&gt;Start a Typescript compiler instance and compile all tests at once like all of them were included in the tsconfig project&lt;/li&gt;
&lt;li&gt;Run your global setup&lt;/li&gt;
&lt;li&gt;Find all &lt;code&gt;describe&lt;/code&gt; blocks in compiled tests&lt;/li&gt;
&lt;li&gt;For every &lt;code&gt;describe&lt;/code&gt;:

&lt;ul&gt;
&lt;li&gt;Run the describe block, of course effectively running the tests&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;The key here is that compilation is happening only once. If you move your application setup to global setup you can also initialize the application just one time and reuse it.&lt;br&gt;
This will bring the same test runtime down to 5 + 1 seconds, making it over 8x faster. And adding more files won't affect the performance much.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to migrate if I already use Jest?
&lt;/h3&gt;

&lt;p&gt;This boils down to several steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install Mocha, Chai and Sinon - Chai is for assertions and Sinon is for mocking, you can skip Chai if you want to keep using Jest expect, which is possible&lt;/li&gt;
&lt;li&gt;Create mocha config file, you can use a template I made: &lt;a href="https://pastebin.com/m2x3H2Mr" rel="noopener noreferrer"&gt;https://pastebin.com/m2x3H2Mr&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Convert all of your &lt;code&gt;beforeAll&lt;/code&gt; and &lt;code&gt;afterAll&lt;/code&gt; calls to &lt;code&gt;before&lt;/code&gt; and &lt;code&gt;after&lt;/code&gt; - this is just a name change you can easily regex replace in all your tests&lt;/li&gt;
&lt;li&gt;Convert all your Jest mocks to use Sinon, the usage is very similar&lt;/li&gt;
&lt;li&gt;If you want to use Jest's expect, you can import it in every test from Jest package or export it globally somewhere, otherwise rewrite your assertions to use one of Chai apis, I think &lt;code&gt;assert&lt;/code&gt; one is the most similar to what Jest offers&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2024 update
&lt;/h3&gt;

&lt;p&gt;Chai is no longer recommended, best to use Jest Expect module instead!&lt;/p&gt;

&lt;p&gt;The biggest issue would be to convert all your Jest mocks to sinon. Maybe there is a way to use Jest's mocks but rewriting all of them is pretty simple, and many cases can be regex replaced.&lt;/p&gt;

&lt;p&gt;And that's it! After that, you are ready to run &lt;code&gt;mocha&lt;/code&gt; and enjoy your tests running much faster than before.&lt;/p&gt;

&lt;p&gt;Happy testing!&lt;/p&gt;

</description>
      <category>nvidia</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
