<?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: Florian Rappl</title>
    <description>The latest articles on DEV Community by Florian Rappl (@florianrappl).</description>
    <link>https://dev.to/florianrappl</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F268296%2Feb6a1ad0-17d3-4302-9c44-a263a666b072.jpg</url>
      <title>DEV Community: Florian Rappl</title>
      <link>https://dev.to/florianrappl</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/florianrappl"/>
    <language>en</language>
    <item>
      <title>My OSS Projects: Mages</title>
      <dc:creator>Florian Rappl</dc:creator>
      <pubDate>Wed, 16 Sep 2026 12:30:00 +0000</pubDate>
      <link>https://dev.to/florianrappl/my-oss-projects-mages-31cl</link>
      <guid>https://dev.to/florianrappl/my-oss-projects-mages-31cl</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This is the second post in my &lt;strong&gt;"My Open-Source Projects"&lt;/strong&gt; series, where I go through some of the OSS projects I started or maintain and tell you the story behind them. &lt;a href="https://dev.to/florianrappl/my-oss-projects-anglesharp-3b8j"&gt;Last time it was AngleSharp&lt;/a&gt; - the HTML parser that started life as a plane-ride fever dream. This time: &lt;strong&gt;MAGES&lt;/strong&gt;, a much smaller project with a surprisingly cool client list.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What Is MAGES, Actually?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/FlorianRappl/Mages" rel="noopener noreferrer"&gt;&lt;strong&gt;MAGES&lt;/strong&gt;&lt;/a&gt; - officially standing for "Mages: Another Generalized Expression Simplifier," because every good project needs a slightly forced backronym - is a small, fast expression parser and interpreter for .NET. You hand it a string like &lt;code&gt;"sin(2) * cos(pi / 4)"&lt;/code&gt;, it hands you back a number. Except it also does variables, functions, closures, objects, lists, complex numbers, and - as of a couple of years ago - a bit of JSX for fun.&lt;/p&gt;

&lt;p&gt;Think of it as "give your .NET app a tiny, embeddable scripting language" without dragging in a full-blown runtime, a NuGet dependency tree the size of a phone book, or a licensing conversation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Mages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Interpret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sin(2) * cos(pi / 4)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 0.642970376623918&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the whole "hello world." No config, no ceremony.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz7g8h9c0p86jv1keoffg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz7g8h9c0p86jv1keoffg.gif" alt="It just works" width="640" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The YAMP Prehistory
&lt;/h2&gt;

&lt;p&gt;MAGES didn't start as MAGES. It started as &lt;a href="https://github.com/FlorianRappl/YAMP" rel="noopener noreferrer"&gt;&lt;strong&gt;YAMP&lt;/strong&gt;&lt;/a&gt; - "Yet Another Math Parser" - an experiment to see whether I could build a full parser and evaluator with MATLAB-like syntax, powered almost entirely by reflection. Reflection made it wonderfully easy to extend: want a new function? Slap an attribute on a method and YAMP would pick it up. It was never meant to be fast. It was meant to be &lt;em&gt;flexible&lt;/em&gt;, and as far as experiments go, it worked.&lt;/p&gt;

&lt;p&gt;YAMP ended up seeing more real usage than an "experiment" really has any right to. The best example: it powered the math engine behind &lt;strong&gt;SineRider&lt;/strong&gt;, a Unity web game from 2013 where you graph functions to sled a character through checkpoints - think Line Rider, but the slope comes from an equation you type in, not a mouse drag. It's a delightful bit of edutainment, and knowing that YAMP's reflection-driven, definitely-not-optimized-for-speed parser was quietly evaluating someone's sine wave in a browser game is exactly the kind of thing that makes open source fun.&lt;/p&gt;

&lt;p&gt;If you want to see what that original version actually looked like, &lt;a href="https://www.youtube.com/watch?v=9FU103w2EWg" rel="noopener noreferrer"&gt;there's footage of it right here&lt;/a&gt; - very much a product of its era. The good news is you don't have to rely on old footage: after the original Unity Web Player build was killed off by browsers dropping NPAPI support, a team of teenagers at &lt;a href="https://hackclub.com/" rel="noopener noreferrer"&gt;Hack Club&lt;/a&gt; rebuilt SineRider from scratch in vanilla JavaScript, and it's &lt;a href="https://sinerider.com/" rel="noopener noreferrer"&gt;still there, still free, still very playable today&lt;/a&gt;. Current sources live at &lt;a href="https://github.com/hackclub/sinerider" rel="noopener noreferrer"&gt;hackclub/sinerider&lt;/a&gt; if you're curious how it's built these days (spoiler: no MAGES or YAMP in there anymore - it's its own thing now).&lt;/p&gt;

&lt;h2&gt;
  
  
  From Experiment to Grant-Funded Successor
&lt;/h2&gt;

&lt;p&gt;Here's where the story gets a step more interesting than "I wrote a parser on a plane" (that's a different article; see first part of the series). The person behind SineRider wanted to go bigger: not just a game about graphing one function, but a whole world built on the idea that everything - terrain, objects, physics - could be governed by changeable math functions. Picture Minecraft, except the blocks are mutable equations. That's a genuinely ambitious pitch, and it was ambitious enough to catch a &lt;strong&gt;Peter Thiel grant&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Part of that grant went toward funding the next generation of the expression engine underneath it. YAMP's syntax could carry over, but its reflection-based guts were never going to be fast enough for a real-time, function-driven game world. So MAGES was built from scratch with one primary constraint: &lt;strong&gt;speed&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7yfsv1p6nqyi5o50f8rn.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7yfsv1p6nqyi5o50f8rn.gif" alt="We have to speed this up" width="640" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How MAGES Actually Runs Your Expression
&lt;/h2&gt;

&lt;p&gt;The core architectural decision that makes MAGES fast is that it doesn't walk an AST and evaluate it node-by-node at runtime (which is what YAMP effectively did, dressed up with reflection). Instead, every expression gets &lt;strong&gt;compiled down into a small set of VM instructions&lt;/strong&gt;, which are then executed sequentially by a lightweight stack-based virtual machine. Parse once, compile once, run the compact bytecode as many times as you want.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff68fv6a7hjrts8yeveys.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff68fv6a7hjrts8yeveys.png" alt="MAGES pipeline: expression string parsed into an AST, compiled into VM instructions, executed on a stack-based VM to produce a result, with an alternate JS transpilation backend shown as a side path" width="800" height="245"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Because the VM is just consuming a flat instruction stream, the interesting side effect is that the &lt;em&gt;front end&lt;/em&gt; (parser, AST) and &lt;em&gt;back end&lt;/em&gt; (what actually executes the instructions) are decoupled. Theoretically you could target something other than the built-in VM. I actually did this for fun once: I wrote a backend that "compiles" MAGES code into equivalent JavaScript and runs it on V8. It works, and it was a satisfying afternoon of yak-shaving. The more natural target would honestly be &lt;strong&gt;WebAssembly&lt;/strong&gt; - same idea, different, more useful destination - but that one's still sitting in the "would be cool" pile.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Compile once, reuse the compiled delegate as many times as you like&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;expOne&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"exp(1)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;expOne&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// 2.71828182845905&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Install it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Mages
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Interpret a one-off expression:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Mages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Interpret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sin(2) * cos(pi / 4)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 0.642970376623918&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pull a function back out and call it directly from C#:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Interpret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"(x, y) =&amp;gt; x * y + 3 * sqrt(x)"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;Mages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 18.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since v2.0, complex numbers are first-class citizens - this was a hard requirement to reach parity with what YAMP already offered, since a fair chunk of MAGES's real-world users do actual signal-processing-flavored math and were not going to be pleased with an engine that couldn't take a square root of &lt;code&gt;-1&lt;/code&gt; without complaining:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Interpret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sqrt(-1)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// i&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And since v3.0 (2024), things got a little playful: MAGES understands &lt;strong&gt;JSX&lt;/strong&gt;. Not "JSX-inspired" or "JSX-ish" - the actual JSX grammar, the same tags-with-embedded-expression-blocks syntax React (or Vue, or pretty much any modern web toolchain) uses. The tag structure, attributes, and children all follow the standard JSX rules you already know. The only twist is what happens &lt;em&gt;inside&lt;/em&gt; the curly-brace expression blocks: instead of that being JavaScript, it's MAGES doing the evaluating. Same shape, different engine running the logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Interpret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"&amp;lt;div class={\"hello\" + \",\" + \"there\"}&amp;gt;&amp;lt;h1&amp;gt;Hi&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;World.&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt; | html"&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// &amp;lt;div class="hello,there"&amp;gt;&amp;lt;h1&amp;gt;Hi&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;World.&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So &lt;code&gt;{"hello" + "," + "there"}&lt;/code&gt; there isn't JavaScript string concatenation - it's a MAGES expression, evaluated by the same stack-based VM as everything else, just sitting inside a JSX attribute the way you'd expect from any React component. Pipe the whole thing through &lt;code&gt;html&lt;/code&gt; and you get a serialized string back. It's a genuinely fun corner of the language: you get a templating syntax that looks completely familiar to anyone who's touched a modern frontend, backed by an evaluator that has nothing to do with a JS engine.&lt;/p&gt;

&lt;p&gt;Reflect on objects and functions at runtime via &lt;code&gt;type&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Interpret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"((x, y, z) =&amp;gt; x + y + z) | type | json"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// { "name": "Function", "create": "[Function]",&lt;/span&gt;
&lt;span class="c1"&gt;//   "parameters": { "0": "x", "1": "y", "2": "z" } }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And use placeholders (&lt;code&gt;_&lt;/code&gt;) to curry arguments through the pipe operator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Interpret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;@"
    var f = (x, y, z) =&amp;gt; x + 2 * y + 3 * z;
    5 | f(1, _, 2)
"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 17, computed as 1 + 2*5 + 3*2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That pipe-and-placeholder combo is one of my favorite bits of syntax in the whole language - it reads almost like a small shell pipeline, but it's fully typed C# objects flowing through.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where It Shines
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Small and dependency-free.&lt;/strong&gt; MAGES itself has zero runtime dependencies. It's a &lt;code&gt;netstandard2.1&lt;/code&gt; library, which means it happily runs on things like Unity or Mono, not just modern .NET.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast by construction.&lt;/strong&gt; Compiling to a flat instruction stream for a stack-based VM means repeated evaluation is cheap - exactly what you want if you're evaluating the same handful of user-authored formulas thousands of times a second (looking at you, real-time math games and signal processing tools).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A genuinely pleasant expression syntax.&lt;/strong&gt; Closures, objects, lists, complex numbers, string interpolation, a pipe operator, and now JSX-flavored templating - it covers far more ground than "just arithmetic."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy to embed.&lt;/strong&gt; &lt;code&gt;new Engine()&lt;/code&gt;, call &lt;code&gt;.Interpret()&lt;/code&gt; or &lt;code&gt;.Compile()&lt;/code&gt;, done. No hosting ceremony.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Great for domain-specific math.&lt;/strong&gt; If your app's users need to type in formulas - a measurement tool, a spreadsheet-like calculator, a scripting console - MAGES gives you that without writing your own parser (please don't write your own parser).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where It Struggles
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It's a niche tool by nature.&lt;/strong&gt; It's not going to replace a general-purpose scripting language embedded in your app if you need full standard-library breadth, multi-file modules, or a package ecosystem. It's an expression/formula engine that grew some scripting-language features, not the other way around.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The (optional) type system doesn't exist yet.&lt;/strong&gt; More on this below, but right now everything is dynamically typed, and if you pass the wrong shape of data into a compiled expression, you find out at runtime like it's 2005.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tooling is thin.&lt;/strong&gt; There's no language server, no syntax highlighting extension, no step-through debugger. If you're building something where non-developers will be writing MAGES expressions, you're currently on your own for the authoring experience.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smaller community, smaller surface area for stuff to just already exist.&lt;/strong&gt; Compared to AngleSharp's few-hundred-million downloads, MAGES sits at a comparatively modest ~200K total NuGet downloads. That's not a criticism of the project so much as an honest expectation-setter: this is a focused tool for a specific job, not an ecosystem.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqh3dvbwgbnsgw91vdrry.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqh3dvbwgbnsgw91vdrry.gif" alt="Small, but mighty" width="586" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Small Crew, Big Reach
&lt;/h2&gt;

&lt;p&gt;MAGES has never had a huge contributor list - it's a much more focused, much smaller codebase than something like AngleSharp. But it has picked up real sponsors along the way, credited right in the README: &lt;strong&gt;polytroper&lt;/strong&gt; (the studio behind that "SineRider, but the whole world is math" follow-up game I mentioned earlier), &lt;strong&gt;smapiot&lt;/strong&gt; (a company I previously worked for), and &lt;strong&gt;OMICRON Lab&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That last one deserves its own section.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where MAGES Shows Up Today
&lt;/h2&gt;

&lt;p&gt;For a project with a comparatively small download count, MAGES keeps turning up in places that make me genuinely proud:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F47kx6td3gwpxnbqe5ddv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F47kx6td3gwpxnbqe5ddv.png" alt="MAGES sitting at the center, with Microsoft PowerToys, Flow Launcher, and OMICRON Lab measurement instruments as consumers above it, and LisaCore, embeddable scripting, and the SineRider game lineage below it" width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/microsoft/PowerToys" rel="noopener noreferrer"&gt;Microsoft PowerToys&lt;/a&gt;&lt;/strong&gt; uses MAGES - it's listed directly in the project's own README as one of its notable consumers. Every time someone runs a calculation through a PowerToys utility, there's a decent chance MAGES is quietly doing the arithmetic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/Flow-Launcher/Flow.Launcher" rel="noopener noreferrer"&gt;Flow Launcher&lt;/a&gt;&lt;/strong&gt;, the 15K+ star Windows app launcher, depends on MAGES too - a natural fit, since "type an expression into a launcher and get an instant answer" is exactly the kind of thing MAGES was built for.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OMICRON Lab&lt;/strong&gt;, an Austrian company building programmable test-and-measurement gear, uses MAGES as the expression engine behind their instruments. This is the one I'm most proud of: their hardware runs user-programmable math over live measurement data, which means MAGES had to be fast &lt;em&gt;and&lt;/em&gt; correct with complex numbers - no hand-waving allowed when someone's oscilloscope trace depends on getting the math right. This is also, not coincidentally, exactly why complex number support in v2.0 was a "must ship," not a "nice to have."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.nuget.org/packages/LisaCore/" rel="noopener noreferrer"&gt;LisaCore&lt;/a&gt;&lt;/strong&gt; builds dynamic runtime code execution on top of it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fke37uwbq5nn2gf52b8wb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fke37uwbq5nn2gf52b8wb.gif" alt="It's in more places than you'd expect" width="600" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;None of this was the plan when MAGES was a grant-funded rewrite of a reflection-based math parser. But "a small, fast, embeddable expression engine" turns out to be one of those quietly universal building blocks - much like AngleSharp's "a proper HTML parser for .NET" from the last post in this series.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next for MAGES
&lt;/h2&gt;

&lt;p&gt;A few directions I keep circling back to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Real language tooling.&lt;/strong&gt; The dream is an actual &lt;strong&gt;Language Server Protocol (LSP)&lt;/strong&gt; implementation with a companion VS Code extension - proper syntax highlighting, inline diagnostics, maybe even autocomplete for the built-in function library. Right now, writing MAGES expressions means writing them blind.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration into a new .NET web framework.&lt;/strong&gt; There are some interesting possibilities for using MAGES as a lightweight expression layer inside web tooling - think templated logic without pulling in a full scripting runtime. Nothing concrete to announce, but it's on the table.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optional types.&lt;/strong&gt; This is the big one, and it's a lot harder than it looks. Adding &lt;em&gt;syntax&lt;/em&gt; for type annotations is the easy 10%. The actual hard part is making type checking meaningful - catching real errors before runtime, without turning simple one-line formulas into a TypeScript-style compiler project, and producing error messages that are actually useful rather than a wall of "type mismatch at position 47." I don't have a timeline for this. I have opinions about it, which is a different thing entirely.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fumsekiasavpz1w35q8lj.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fumsekiasavpz1w35q8lj.gif" alt="Everything's fine" width="400" height="288"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;That's the MAGES story - from a reflection-powered math experiment riding along in a Unity sledding game, to a Thiel-grant-funded rewrite, to quietly doing arithmetic inside Microsoft PowerToys and Austrian test equipment. If you want to poke around, the code lives on &lt;a href="https://github.com/FlorianRappl/Mages" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, and the package is on &lt;a href="https://www.nuget.org/packages/Mages/" rel="noopener noreferrer"&gt;NuGet&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next up in this series: another project, another origin story. Stay tuned.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>opensource</category>
      <category>csharp</category>
      <category>compiling</category>
    </item>
    <item>
      <title>My OSS Projects: AngleSharp</title>
      <dc:creator>Florian Rappl</dc:creator>
      <pubDate>Wed, 09 Sep 2026 13:30:00 +0000</pubDate>
      <link>https://dev.to/florianrappl/my-oss-projects-anglesharp-3b8j</link>
      <guid>https://dev.to/florianrappl/my-oss-projects-anglesharp-3b8j</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This is the first post in a new series, &lt;strong&gt;"My Open-Source Projects"&lt;/strong&gt;, where I go through some of the OSS projects I started or maintain and tell you the story behind them - the good, the bad, and the "why did I think this was a good idea at 30,000 feet" parts. First up: &lt;strong&gt;AngleSharp&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What Is AngleSharp, Actually?
&lt;/h2&gt;

&lt;p&gt;Before the story, the pitch: &lt;a href="https://github.com/AngleSharp/AngleSharp" rel="noopener noreferrer"&gt;&lt;strong&gt;AngleSharp&lt;/strong&gt;&lt;/a&gt; is a .NET library that parses HTML, SVG, MathML, CSS, and (to a good extent) XML, and gives you back a fully-featured, spec-conformant DOM. Not "DOM-ish." Not "good enough for scraping." The actual W3C DOM API, the same &lt;code&gt;querySelector&lt;/code&gt;, &lt;code&gt;querySelectorAll&lt;/code&gt;, and element interfaces you already know from the browser console - except it's C#, it runs headless, and there's no browser in sight.&lt;/p&gt;

&lt;p&gt;If you've ever reached for a regex to parse HTML: please don't. You know why. AngleSharp exists so that nobody has to write &lt;code&gt;&amp;lt;div[^&amp;gt;]*&amp;gt;&lt;/code&gt; ever again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fstl9ykpvhne4kbr3f3ku.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fstl9ykpvhne4kbr3f3ku.gif" alt="What could go wrong?" width="320" height="180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flqzgrrk0f5sel3xqewq6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flqzgrrk0f5sel3xqewq6.png" alt="AngleSharp parsing pipeline: raw markup to tokenizer to tree construction to DOM, then out to scraping, testing, or rendering" width="799" height="212"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, onto how it got here.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Original (Slightly Unhinged) Idea
&lt;/h2&gt;

&lt;p&gt;Once upon a time, I had what I thought was a brilliant idea: build a cross-platform GUI toolkit using HTML, CSS, and C#. I was certainly not the first person to think of this - plenty of people had similar thoughts before me - but back then it was still a fairly fresh take.&lt;/p&gt;

&lt;p&gt;The more I thought it through, though, the more obvious it became: you can't just take HTML and CSS and strip them down to "the easy 80%". If you want this to actually work, you need the real deal - a proper HTML parser, a proper CSS engine, the whole circus. No shortcuts.&lt;/p&gt;

&lt;p&gt;So naturally, the reasonable thing to do was to just... write an HTML5 parser. From scratch. In C#. Sure, why not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Coding at 30,000 Feet
&lt;/h2&gt;

&lt;p&gt;In 2013 I was flying to the Microsoft MVP Summit, and for reasons that made complete sense to me at the time, I brought a printed copy of the HTML5 spec with me. Not a novel. Not a magazine. The spec.&lt;/p&gt;

&lt;p&gt;I started coding on the plane. By the time we landed, the parser was already "working" - in the sense that the happy paths worked. Feed it well-formed markup and it would dutifully produce a DOM. Great success, right?&lt;/p&gt;

&lt;p&gt;Well, that's roughly the point where I discovered the dirty little secret of the HTML5 spec: it's massive not because HTML is a complicated language, but because of everything around it - error handling, edge cases, and the glorious pile of "what should happen when the markup is garbage" scenarios that browsers have been quietly agreeing on for decades. Turns out the "happy path" is maybe 20% of the actual spec. The other 80% is browsers being incredibly forgiving about human mistakes.&lt;/p&gt;

&lt;p&gt;I kept grinding away at it, and eventually most of the edge cases and tests turned green. And then, like clockwork, I hit the point every side project hits: frustration, a pause, and a "why am I doing this to myself" moment. Every maintainer knows this phase. It has no official name, but "why did I do this to myself" captures it well enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ship It Before It Goes Nowhere
&lt;/h2&gt;

&lt;p&gt;Eventually I picked it back up with a very simple thought: before this thing quietly dies in a local folder, let's write a CodeProject article about it and push the code to GitHub.&lt;/p&gt;

&lt;p&gt;I already had some open-source experience at that point - mostly as a contributor. The one project I actually maintained was &lt;a href="https://github.com/FlorianRappl/YAMP" rel="noopener noreferrer"&gt;YAMP&lt;/a&gt;, and thanks to some very patient people with a lot more GitHub experience than me, I'd picked up a few things about "doing OSS properly."&lt;/p&gt;

&lt;p&gt;So I published the article, pushed the repo, and went to bed.&lt;/p&gt;

&lt;p&gt;When I woke up, something had clearly happened. The repository already had 120+ stars, and the article was ranking surprisingly well. Nothing makes you suddenly very motivated about a side project like waking up to numbers you didn't expect.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkfln8mh9t2lyhhxow2tj.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkfln8mh9t2lyhhxow2tj.gif" alt="Quite surprising, right" width="470" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So I kept going. And going.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stabilizing, Then Breaking Everything (On Purpose)
&lt;/h2&gt;

&lt;p&gt;For a while, most of the effort went into the internals - making the parsing actually correct, not just "correct enough." Once that settled down, the API itself went through some pretty massive changes. Not always changes that users loved, if I'm honest. Breaking changes are never fun to be on the receiving end of, and I was on the giving end a fair bit during this period.&lt;/p&gt;

&lt;p&gt;The big turning point was &lt;strong&gt;AngleSharp 0.10&lt;/strong&gt;. This release quietly laid the groundwork for what would define AngleSharp going forward: modularity. Suddenly you could just add another library and your document could parse JavaScript. Add another one and you got CSS. This came from a lightweight-but-surprisingly-capable dependency injection system and a service-based configuration approach that let you chain extensions together like Lego bricks.&lt;/p&gt;

&lt;p&gt;That modular shape is still exactly how the ecosystem looks today:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F95khb9xduguddgr8n7kp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F95khb9xduguddgr8n7kp.png" alt="AngleSharp core with satellite libraries: Css, Js, Xml, Io, Diffing, and Wasm" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Long Road to 1.0
&lt;/h2&gt;

&lt;p&gt;For years, I genuinely wasn't sure I could call the API "stable" and commit to semver. Not because it was shaky - it just felt like a big commitment. Eventually, after years of the API being &lt;em&gt;de facto&lt;/em&gt; stable (even if I hadn't officially blessed it as such), I released &lt;strong&gt;1.0&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;From that point on, my main driver hasn't been "let's avoid a 2.0 or 3.0 forever." I have nothing against major version bumps. My actual rule is simpler: a breaking change needs to earn its place. If it's not genuinely useful enough to justify the pain it causes downstream, it doesn't happen.&lt;/p&gt;

&lt;h2&gt;
  
  
  It Was Never a Solo Project
&lt;/h2&gt;

&lt;p&gt;I've told this story mostly in first person so far, which is a bit unfair, because AngleSharp would have died somewhere around "some happy paths seemed to work" if it had stayed a one-person effort.&lt;/p&gt;

&lt;p&gt;The project's &lt;code&gt;CONTRIBUTORS.md&lt;/code&gt; reads like a highlight reel of people who showed up over the years to fix a parser edge case, argue with me about API naming (we've had &lt;em&gt;opinions&lt;/em&gt;), or add a feature I didn't know I needed. In order of first contribution: Andreas Augustin, Jerrie Pelser, Liwen Guo, Raphael Ducom, Georgii Dolzhykov, Joel Verhagen, Michael Ganss, Robin Sue, Henry Roeland, Adrian Phinney, Andreas Håkansson, Jeremy Meng, Yehudah Asher, Dennis Daume, Jakub Świętek, Dennis Gorelik, Georgios Diamantopoulos, Brian Ricketts, Laurynas Ruškys, Martin Wakley, Bastian Buchholz, Keith Hall, Nikita Ilinykh, Thomas Bolon, Alexander Ubillus, Max Katz - and plenty more across the ecosystem repos who deserve just as much credit.&lt;/p&gt;

&lt;p&gt;One name on that list deserves a special callout: &lt;strong&gt;Michael Ganss&lt;/strong&gt;. He contributed to AngleSharp early on, then went off and built &lt;a href="https://github.com/mganss/HtmlSanitizer" rel="noopener noreferrer"&gt;&lt;strong&gt;HtmlSanitizer&lt;/strong&gt;&lt;/a&gt; - a library that cleans HTML of XSS payloads - directly on top of it. So somewhere out there, a chunk of the web's HTML sanitization is running through code that started as a side project on an airplane, maintained partly by someone who used to fix that airplane project's bugs. I don't think you can plan a story arc like that.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwvw1pk10v629qq4z1w1d.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwvw1pk10v629qq4z1w1d.gif" alt="Student becomes a master" width="480" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also worth saying clearly: AngleSharp is a &lt;a href="https://dotnetfoundation.org/" rel="noopener noreferrer"&gt;.NET Foundation&lt;/a&gt; project. That's not nothing - it means the project has a support structure behind it beyond just "one person's spare time and mild sleep deprivation."&lt;/p&gt;

&lt;h2&gt;
  
  
  Where AngleSharp Shows Up Today
&lt;/h2&gt;

&lt;p&gt;It's easy to undersell this part, so let me just put some numbers on the table. As of writing, the core &lt;a href="https://www.nuget.org/packages/AngleSharp/" rel="noopener noreferrer"&gt;AngleSharp NuGet package&lt;/a&gt; alone sits at &lt;strong&gt;over 310 million downloads&lt;/strong&gt;. Add the satellite packages and you're comfortably north of 500 million installs across the ecosystem.&lt;/p&gt;

&lt;p&gt;The curve is the fun part - this is basically a hockey stick with a decade-long windup:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkab7dwv84ijoc3y2nwfn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkab7dwv84ijoc3y2nwfn.png" alt="NuGet download growth for AngleSharp, AngleSharp.Css, and AngleSharp.Diffing from 2013 to 2026, showing near-flat early years followed by steep exponential growth after 2019" width="800" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Barely a blip until around 2019, and then it just... doesn't stop. That little dip in early 2025 on the core package is a NuGet stats quirk, not a mass exodus - don't worry, nobody suddenly stopped parsing HTML.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffcxutht4zfywnjrqmsxd.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffcxutht4zfywnjrqmsxd.gif" alt="Nice hockey stick" width="480" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A few of the places AngleSharp quietly does the heavy lifting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.nuget.org/packages/HtmlSanitizer/" rel="noopener noreferrer"&gt;HtmlSanitizer&lt;/a&gt;&lt;/strong&gt; (128M+ downloads on its own) uses AngleSharp to parse and re-render HTML while stripping out anything that could be used for cross-site scripting. If a .NET app on the internet safely accepts user-submitted HTML, there's a decent chance AngleSharp is standing guard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://bunit.dev/" rel="noopener noreferrer"&gt;bUnit&lt;/a&gt;&lt;/strong&gt;, the go-to unit testing library for Blazor components, uses AngleSharp under the hood (via &lt;code&gt;AngleSharp.Diffing&lt;/code&gt;) for its semantic HTML comparisons - the thing that lets you assert "this rendered markup is equivalent to that markup" without caring about attribute order or whitespace.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.nuget.org/packages/PreMailer.Net/" rel="noopener noreferrer"&gt;PreMailer.Net&lt;/a&gt;&lt;/strong&gt; inlines CSS into HTML emails (because email clients are still living in 2003) using AngleSharp to actually understand the markup and styles it's rewriting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.nuget.org/packages/WebDriverManager/" rel="noopener noreferrer"&gt;WebDriverManager&lt;/a&gt;&lt;/strong&gt; for .NET, which manages Selenium WebDriver binaries, also leans on it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And then there's the list of projects that depend on it directly - some of which you've almost certainly used: &lt;strong&gt;Bitwarden&lt;/strong&gt; (the password manager), &lt;strong&gt;Jackett&lt;/strong&gt; and &lt;strong&gt;Prowlarr&lt;/strong&gt; (torrent/indexer tooling), &lt;strong&gt;Playnite&lt;/strong&gt; (game library manager), &lt;strong&gt;ArchiSteamFarm&lt;/strong&gt;, &lt;strong&gt;OrchardCore&lt;/strong&gt; (a full CMS/app framework), &lt;strong&gt;OpenIddict&lt;/strong&gt; (OAuth2/OpenID Connect for .NET), &lt;strong&gt;YoutubeExplode&lt;/strong&gt;, and even Microsoft's own &lt;a href="https://github.com/dotnet/AspNetCore.Docs" rel="noopener noreferrer"&gt;&lt;strong&gt;ASP.NET Core documentation tooling&lt;/strong&gt;&lt;/a&gt; repository.&lt;/p&gt;

&lt;p&gt;None of this was the plan when I was scribbling parser states on a plane. But it turns out "a really solid, standards-compliant HTML parser for .NET" is one of those unglamorous building blocks that ends up everywhere once it exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Install the core package from NuGet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package AngleSharp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And parse your first document:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;AngleSharp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;AngleSharp.Html.Parser&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HtmlParser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ParseDocumentAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&amp;lt;h1&amp;gt;Hello!&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;AngleSharp says hi.&amp;lt;/p&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;heading&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;QuerySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"h1"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TextContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Hello!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the "hello world" version. Most real usage, though, goes through a &lt;code&gt;BrowsingContext&lt;/code&gt;, which acts like a browser tab - it can load documents from the network, follow links, submit forms, and generally behave like something a bit more alive than a static parser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;AngleSharp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Default&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithDefaultLoader&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BrowsingContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OpenAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://example.com"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;link&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;QuerySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"href"&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;Want CSS support too? Add the plugin and register it in the configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package AngleSharp.Css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Default&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithDefaultLoader&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithCss&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BrowsingContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OpenAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://example.com"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;QuerySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;style&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ComputeCurrentStyle&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetPropertyValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"background-color"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same idea for scripting (&lt;code&gt;AngleSharp.Js&lt;/code&gt;), XML validation (&lt;code&gt;AngleSharp.Xml&lt;/code&gt;), or semantic diffing of HTML fragments (&lt;code&gt;AngleSharp.Diffing&lt;/code&gt;) - one &lt;code&gt;With...()&lt;/code&gt; call, and a new capability shows up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where It Shines
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Standards over vibes.&lt;/strong&gt; The parser follows the actual HTML5 parsing algorithm, including its error-recovery rules. Feed it broken markup, and it recovers the way a browser would - not the way a regex-and-hope solution would.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A real DOM, not a wrapper.&lt;/strong&gt; &lt;code&gt;querySelector&lt;/code&gt;, &lt;code&gt;querySelectorAll&lt;/code&gt;, LINQ-to-DOM, form submission, navigation via &lt;code&gt;BrowsingContext&lt;/code&gt; - it feels like scripting a browser tab from C#, minus the browser.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modular by design.&lt;/strong&gt; Only pay (in dependencies and startup cost) for what you actually use. Need CSS cascade computation? Add one package. Need to run inline &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags? Add another.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance.&lt;/strong&gt; It's not trying to be a browser engine, and that's exactly why it's fast - large documents parse in milliseconds, with internal reuse of elements to keep allocations down.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It composes well with automated testing.&lt;/strong&gt; &lt;code&gt;AngleSharp.Diffing&lt;/code&gt; in particular is great for asserting "this HTML is semantically the same as that HTML" without caring about attribute order or whitespace.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where It Struggles
&lt;/h2&gt;

&lt;p&gt;No project is without trade-offs, and I'd rather tell you about them than let you find out the hard way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It's not a browser.&lt;/strong&gt; There's no layout engine, no painting, no actual visual rendering (yet - more on that below). If you need pixel-accurate rendering, you still want something like Playwright or a headless Chromium.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JavaScript support is limited.&lt;/strong&gt; &lt;code&gt;AngleSharp.Js&lt;/code&gt; runs on &lt;a href="https://github.com/sebastienros/jint" rel="noopener noreferrer"&gt;Jint&lt;/a&gt;, which is a real ECMAScript engine, but it's not V8. Heavy, modern JS-driven pages (SPAs relying on a full browser runtime) are not its comfort zone, but definitely possible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The ecosystem is spread across several repositories.&lt;/strong&gt; Core, Css, Js, Xml, Io, Diffing, Wasm - it's modular by design, which is a strength, but it also means version compatibility between packages occasionally needs a bit of attention (dependency ranges across separately-versioned repos are always a &lt;em&gt;fun&lt;/em&gt; problem to own - but generally the only dependency they all have is the core lib).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Breaking changes did happen historically.&lt;/strong&gt; Pre-1.0 AngleSharp went through real API churn. It's stable and semver-respecting now, but if you find decade-old blog posts referencing the API, expect it to look different today.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Wider Ecosystem
&lt;/h2&gt;

&lt;p&gt;Depending on what you need, the packages you'll likely run into are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/AngleSharp/AngleSharp" rel="noopener noreferrer"&gt;&lt;strong&gt;AngleSharp&lt;/strong&gt;&lt;/a&gt; (&lt;a href="https://www.nuget.org/packages/AngleSharp/" rel="noopener noreferrer"&gt;NuGet&lt;/a&gt;) - the core: HTML5/XML parsing, DOM, configuration.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/AngleSharp/AngleSharp.Css" rel="noopener noreferrer"&gt;&lt;strong&gt;AngleSharp.Css&lt;/strong&gt;&lt;/a&gt; - CSSOM, selectors, cascades, computed styles.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/AngleSharp/AngleSharp.Js" rel="noopener noreferrer"&gt;&lt;strong&gt;AngleSharp.Js&lt;/strong&gt;&lt;/a&gt; - JavaScript execution via Jint.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/AngleSharp/AngleSharp.Xml" rel="noopener noreferrer"&gt;&lt;strong&gt;AngleSharp.Xml&lt;/strong&gt;&lt;/a&gt; - XML parsing plus XSD/DTD validation.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/AngleSharp/AngleSharp.Io" rel="noopener noreferrer"&gt;&lt;strong&gt;AngleSharp.Io&lt;/strong&gt;&lt;/a&gt; - requesters, cookies, and the networking stack.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/AngleSharp/AngleSharp.Diffing" rel="noopener noreferrer"&gt;&lt;strong&gt;AngleSharp.Diffing&lt;/strong&gt;&lt;/a&gt; - semantic DOM diffing, great for tests.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/AngleSharp/AngleSharp.Wasm" rel="noopener noreferrer"&gt;&lt;strong&gt;AngleSharp.Wasm&lt;/strong&gt;&lt;/a&gt; - running AngleSharp inside the browser via WebAssembly/Blazor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to talk to actual humans about it best case is to open an issue in one of the repos. Historically, there's also a &lt;a href="https://gitter.im/AngleSharp/AngleSharp" rel="noopener noreferrer"&gt;Gitter chat&lt;/a&gt; and a tag that was / is used on &lt;a href="https://stackoverflow.com/tags/anglesharp" rel="noopener noreferrer"&gt;Stack Overflow&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next: Coming Full Circle
&lt;/h2&gt;

&lt;p&gt;Remember that original goal - a cross-platform GUI built on HTML and CSS? That idea got shelved pretty early on in favor of "let's just get the fundamentals right first." But it never really left.&lt;/p&gt;

&lt;p&gt;The next big frontier for AngleSharp is &lt;strong&gt;rendering&lt;/strong&gt;. The plan is a general-purpose headless renderer that can be used interactively - moving a pointer, sending keystrokes, the works - or just to grab a screenshot. On top of that, the goal is a cross-platform control (most likely built on &lt;strong&gt;Avalonia&lt;/strong&gt;) that can be embedded into apps.&lt;/p&gt;

&lt;p&gt;Am I building a browser? Let's not get ahead of ourselves. But it's fair to say the project is closer to its original vision now than it's been at any point in the last decade.&lt;/p&gt;




&lt;p&gt;That's the AngleSharp story - from a stack of printed spec pages on an airplane to a mature parsing ecosystem with XSD validation and WASM support. If you want to poke around, the code lives on &lt;a href="https://github.com/AngleSharp/AngleSharp" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, and the package is on &lt;a href="https://www.nuget.org/packages/AngleSharp/" rel="noopener noreferrer"&gt;NuGet&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next up in this series: another one of my projects, and probably another origin story that started with me underestimating how much work something would be. Stay tuned!&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>opensource</category>
      <category>csharp</category>
      <category>webdev</category>
    </item>
    <item>
      <title>netpack</title>
      <dc:creator>Florian Rappl</dc:creator>
      <pubDate>Thu, 09 Jan 2025 23:51:37 +0000</pubDate>
      <link>https://dev.to/florianrappl/netpack-5ej4</link>
      <guid>https://dev.to/florianrappl/netpack-5ej4</guid>
      <description>&lt;p&gt;This will be a short post... but hopefully with a longer one following up.&lt;/p&gt;

&lt;p&gt;During my winter vacation I got some time to do one experiment:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can web tooling created using C#/.NET yield comparable performance to Rust / Go / Zig ...?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So I did some coding... (that you can &lt;a href="https://github.com/FlorianRappl/netpack" rel="noopener noreferrer"&gt;find on GitHub&lt;/a&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  The Process
&lt;/h2&gt;

&lt;p&gt;I started with a crude bundler logic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open files&lt;/li&gt;
&lt;li&gt;Read their content&lt;/li&gt;
&lt;li&gt;Use a regular expression to detect, e.g., import statements in JS files&lt;/li&gt;
&lt;li&gt;Resolve linked modules&lt;/li&gt;
&lt;li&gt;Open resolved / existing package.json files to identify module paths&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result was easy: Using AoT (ahead-of-time compilation) .NET can certainly be used for highly performing web projects.&lt;/p&gt;

&lt;p&gt;So I continued a bit with the experiment; replacing regular expressions with actual code understanding.&lt;/p&gt;

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

&lt;p&gt;The answer is: yes! 🤯&lt;/p&gt;

&lt;p&gt;The bundler is at the moment feature-incomplete, but the first results are quite strong. The benchmarks shown in the &lt;a href="https://github.com/FlorianRappl/netpack" rel="noopener noreferrer"&gt;README&lt;/a&gt; indicate that the performance is definitely in the same ballpark as other tools. So fast enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  More Details
&lt;/h2&gt;

&lt;p&gt;Personally, I think that C#/.NET is much less complicated than Rust and more powerful than Go. It comes with some drawbacks as well - not gonna lie.&lt;/p&gt;

&lt;p&gt;The main reason why C#/.NET can be viable in that space is AoT. Without AoT the startup performance (as well as runtime requirements) is killing the whole idea.&lt;/p&gt;

&lt;p&gt;AoT, on the other hand, comes with some challenges. Some libraries cannot be used or require some work to be integrated. Hence, some of the flexibility of .NET cannot be used.&lt;/p&gt;

&lt;p&gt;For the largest test project also used by tools such as rspack we get these results:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fwc9g850i2pseizq9klpi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fwc9g850i2pseizq9klpi.png" alt="Bundler performance" width="700" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that even the bundler is feature incomplete, it is crafted enough to produce a valid result on the project. So even though all results are at the moment preliminary there is at least some validity to it.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test&lt;/th&gt;
&lt;th&gt;esbuild&lt;/th&gt;
&lt;th&gt;rspack&lt;/th&gt;
&lt;th&gt;Vite&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;netpack&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Small lib&lt;/td&gt;
&lt;td&gt;326ms&lt;/td&gt;
&lt;td&gt;611ms&lt;/td&gt;
&lt;td&gt;601ms&lt;/td&gt;
&lt;td&gt;359ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Small project&lt;/td&gt;
&lt;td&gt;670ms&lt;/td&gt;
&lt;td&gt;912ms&lt;/td&gt;
&lt;td&gt;1658ms&lt;/td&gt;
&lt;td&gt;418ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Medium project&lt;/td&gt;
&lt;td&gt;1931ms&lt;/td&gt;
&lt;td&gt;2877ms&lt;/td&gt;
&lt;td&gt;10601ms&lt;/td&gt;
&lt;td&gt;974ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Large project&lt;/td&gt;
&lt;td&gt;2189ms&lt;/td&gt;
&lt;td&gt;2422ms&lt;/td&gt;
&lt;td&gt;13710ms&lt;/td&gt;
&lt;td&gt;1357ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So yes, netpack already beats the competition and even has potential for even better performance. While it can be optimized further, it will also loose some performance when things such as sourcemaps or tree shaking are introduced. Right now I am positive that in total it should be around the same as of now due to the potential optimizations (such as streaming in the JS AST generation).&lt;/p&gt;

&lt;p&gt;The biggest hurdle at the moment is that it only supports JS(X) - no TypeScript yet (it tries to parse these files, but once types are used it will fail). It would be "fairly" easy to support, but I would need to fork &lt;a href="https://github.com/adams85/acornima/" rel="noopener noreferrer"&gt;Acornima&lt;/a&gt; for that and that's something I'd only do if there is enough buzz around the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Outlook
&lt;/h2&gt;

&lt;p&gt;There are many more things that would be superb to get into this. Some basics need to be cleared first though. Things such as sourcemaps, TypeScript support, or maybe a configuration system would be great.&lt;/p&gt;

&lt;p&gt;There are some things in this experiment that no other bundler does. For instance, if your HTML entry point has an importmap, then the entries in the importmap are &lt;em&gt;automatically&lt;/em&gt; taken as externals. Likewise, you can set certain dependencies as shared - in this case there are automatically importmap entries / an importmap created in the resulting HTML. Quite neat.&lt;/p&gt;

&lt;p&gt;In the future the bundler will have native (i.e., out-of-the-box) support for SASS, CSS modules, CSS-in-JS, as well as module federation and native federation.&lt;/p&gt;

&lt;p&gt;What are your thoughts? Do you think this is a viable idea or just trash? Is a fast .NET-native bundler with sensible defaults something we need?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>dotnet</category>
      <category>javascript</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Great edition - thanks to all the authors for their hard work!</title>
      <dc:creator>Florian Rappl</dc:creator>
      <pubDate>Wed, 04 Dec 2024 08:31:48 +0000</pubDate>
      <link>https://dev.to/florianrappl/great-edition-thanks-to-all-the-authors-for-their-hard-work-4mm5</link>
      <guid>https://dev.to/florianrappl/great-edition-thanks-to-all-the-authors-for-their-hard-work-4mm5</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/florianrappl" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F268296%2Feb6a1ad0-17d3-4302-9c44-a263a666b072.jpg" alt="florianrappl"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/florianrappl/whats-hot-in-web-dev-42ng" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;What's Hot in Web Dev?&lt;/h2&gt;
      &lt;h3&gt;Florian Rappl ・ Dec 4&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#frontend&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#node&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
    </item>
    <item>
      <title>What's Hot in Web Dev?</title>
      <dc:creator>Florian Rappl</dc:creator>
      <pubDate>Wed, 04 Dec 2024 08:31:02 +0000</pubDate>
      <link>https://dev.to/florianrappl/whats-hot-in-web-dev-42ng</link>
      <guid>https://dev.to/florianrappl/whats-hot-in-web-dev-42ng</guid>
      <description>&lt;p&gt;This year I started a new series on LinkedIn - "Advanced Links for Frontend". Each issue has 10 links to outstanding posts / articles. This bundle contains the links from the last 5 issues (issue 66 to issue 70).&lt;/p&gt;

&lt;p&gt;I hope you enjoy this collection. Let me know in the comments which of these articles is your favorite (and why).&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue 66
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;How Bun supports V8 APIs without using V8 (part 2)&lt;/strong&gt; (&lt;a href="https://bun.sh/blog/how-bun-supports-v8-apis-without-using-v8-part-2" rel="noopener noreferrer"&gt;https://bun.sh/blog/how-bun-supports-v8-apis-without-using-v8-part-2&lt;/a&gt;) by Ben Grant&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the best React Native list component?&lt;/strong&gt; (&lt;a href="https://expo.dev/blog/what-is-the-best-react-native-list-component" rel="noopener noreferrer"&gt;https://expo.dev/blog/what-is-the-best-react-native-list-component&lt;/a&gt;) by Simon Grimm&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Centering things: a solved problem?&lt;/strong&gt; (&lt;a href="https://fullystacked.net/centering-things/" rel="noopener noreferrer"&gt;https://fullystacked.net/centering-things/&lt;/a&gt;) by Ollie Williams&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WebVM 2.0: A complete Linux Desktop Environment in the browser via WebAssembly&lt;/strong&gt; (&lt;a href="https://labs.leaningtech.com/blog/webvm-20" rel="noopener noreferrer"&gt;https://labs.leaningtech.com/blog/webvm-20&lt;/a&gt;) by Alessandro Pignotti&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I Followed the Official AWS Amplify Guide and was Charged $1,100&lt;/strong&gt; (&lt;a href="https://elliott-king.github.io/2024/10/amplify-overcharge/" rel="noopener noreferrer"&gt;https://elliott-king.github.io/2024/10/amplify-overcharge/&lt;/a&gt;) by Elliott King&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Different (and Modern) Ways to Toggle Content&lt;/strong&gt; (&lt;a href="https://css-tricks.com/the-different-and-modern-ways-to-toggle-content/" rel="noopener noreferrer"&gt;https://css-tricks.com/the-different-and-modern-ways-to-toggle-content/&lt;/a&gt;) by Daniel Schwarz&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using Typescript in node.js scripts without actually writing Typescript&lt;/strong&gt; (&lt;a href="https://www.jonathancreamer.com/using-typescript-in-node-js-scripts-without-actually-writing-typescript/" rel="noopener noreferrer"&gt;https://www.jonathancreamer.com/using-typescript-in-node-js-scripts-without-actually-writing-typescript/&lt;/a&gt;) by Jonathan Creamer&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Overflow Clip&lt;/strong&gt; (&lt;a href="https://ishadeed.com/article/overflow-clip/" rel="noopener noreferrer"&gt;https://ishadeed.com/article/overflow-clip/&lt;/a&gt;) by Ahmad Shadeed&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conditional Props in React Using Type Discrimination&lt;/strong&gt; (&lt;a href="https://elanmed.dev/blog/conditional-props-using-type-discrimination" rel="noopener noreferrer"&gt;https://elanmed.dev/blog/conditional-props-using-type-discrimination&lt;/a&gt;) by Elan Medoff&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Micro Components - a different approach to a simpler component-based web&lt;/strong&gt; (&lt;a href="https://www.wearedevelopers.com/magazine/micro-components---an-alternative-to-web-components-and-frameworks/" rel="noopener noreferrer"&gt;https://www.wearedevelopers.com/magazine/micro-components---an-alternative-to-web-components-and-frameworks/&lt;/a&gt;) by Chris Heilmann&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue 67
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;JavaScript's ??= Operator: Default Values Made Simple&lt;/strong&gt; (&lt;a href="https://www.trevorlasn.com/blog/javascript-nullish-coalescing-assignment-operator" rel="noopener noreferrer"&gt;https://www.trevorlasn.com/blog/javascript-nullish-coalescing-assignment-operator&lt;/a&gt;) by Trevor Lasn&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fit-to-Width Text: A New Technique&lt;/strong&gt; (&lt;a href="https://kizu.dev/fit-to-width/" rel="noopener noreferrer"&gt;https://kizu.dev/fit-to-width/&lt;/a&gt;) by Roman Komarov&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Self-contained Executable Programs with Deno Compile&lt;/strong&gt; (&lt;a href="https://deno.com/blog/deno-compile-executable-programs" rel="noopener noreferrer"&gt;https://deno.com/blog/deno-compile-executable-programs&lt;/a&gt;) by Ryan Dahl and Andy Jiang&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;New in React 19 - Pre-warming&lt;/strong&gt; (&lt;a href="https://github.com/facebook/react/issues/29898" rel="noopener noreferrer"&gt;https://github.com/facebook/react/issues/29898&lt;/a&gt;) by Ricky Hanlon&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Will we care about frameworks in the future?&lt;/strong&gt; (&lt;a href="https://paul.kinlan.me/will-we-care-about-frameworks-in-the-future/" rel="noopener noreferrer"&gt;https://paul.kinlan.me/will-we-care-about-frameworks-in-the-future/&lt;/a&gt;) by Paul Kinlan&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Introducing the vlt Package Manager &amp;amp; Serverless Registry&lt;/strong&gt; (&lt;a href="https://blog.vlt.sh/blog/introducing-vlt-and-vsr" rel="noopener noreferrer"&gt;https://blog.vlt.sh/blog/introducing-vlt-and-vsr&lt;/a&gt;) by Darcy Clark et al.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Saga Pattern in Elixir&lt;/strong&gt; (&lt;a href="https://peterullrich.com/saga-pattern-in-elixir" rel="noopener noreferrer"&gt;https://peterullrich.com/saga-pattern-in-elixir&lt;/a&gt;) by Peter Ullrich&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Generating Random Mazes with JavaScript&lt;/strong&gt; (&lt;a href="https://cloudfour.com/thinks/generating-random-mazes-with-javascript/" rel="noopener noreferrer"&gt;https://cloudfour.com/thinks/generating-random-mazes-with-javascript/&lt;/a&gt;) by Paul Hebert&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Beautiful focus outlines&lt;/strong&gt; (&lt;a href="https://medienbaecker.com/articles/focus-outlines" rel="noopener noreferrer"&gt;https://medienbaecker.com/articles/focus-outlines&lt;/a&gt;) by Thomas Günther&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Tragedy of Running an Old Node Project&lt;/strong&gt; (&lt;a href="https://abdisalan.com/posts/tragedy-running-old-node-project" rel="noopener noreferrer"&gt;https://abdisalan.com/posts/tragedy-running-old-node-project&lt;/a&gt;) by Abdisalan Mohamud&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue 68
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Long Frames and INP: Understanding Post-Load Performance&lt;/strong&gt; (&lt;a href="https://dev.to/viniciusdallacqua/long-frames-and-inp-understanding-post-load-performance-2maa"&gt;https://dev.to/viniciusdallacqua/long-frames-and-inp-understanding-post-load-performance-2maa&lt;/a&gt;) by &lt;a class="mentioned-user" href="https://dev.to/viniciusdallacqua"&gt;@viniciusdallacqua&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reporting Core Web Vitals With The Performance API&lt;/strong&gt; (&lt;a href="https://www.smashingmagazine.com/2024/02/reporting-core-web-vitals-performance-api/" rel="noopener noreferrer"&gt;https://www.smashingmagazine.com/2024/02/reporting-core-web-vitals-performance-api/&lt;/a&gt;) by Geoff Graham&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;React Router v7&lt;/strong&gt; (&lt;a href="https://remix.run/blog/react-router-v7" rel="noopener noreferrer"&gt;https://remix.run/blog/react-router-v7&lt;/a&gt;) by Michael Jackson&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Shadow DOM is in the front&lt;/strong&gt; (&lt;a href="https://abstract.properties/the-shadow-dom-is-in-the-front.html" rel="noopener noreferrer"&gt;https://abstract.properties/the-shadow-dom-is-in-the-front.html&lt;/a&gt;) by Sufian Rhazi&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tailwind CSS v4.0 Beta 1&lt;/strong&gt; (&lt;a href="https://tailwindcss.com/blog/tailwindcss-v4-beta" rel="noopener noreferrer"&gt;https://tailwindcss.com/blog/tailwindcss-v4-beta&lt;/a&gt;) by Adam Wathan&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Announcing TypeScript 5.7&lt;/strong&gt; (&lt;a href="https://devblogs.microsoft.com/typescript/announcing-typescript-5-7/" rel="noopener noreferrer"&gt;https://devblogs.microsoft.com/typescript/announcing-typescript-5-7/&lt;/a&gt;) by Daniel Rosenwasser&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Handling Cookies is a Minefield&lt;/strong&gt; (&lt;a href="https://grayduck.mn/2024/11/21/handling-cookies-is-a-minefield/" rel="noopener noreferrer"&gt;https://grayduck.mn/2024/11/21/handling-cookies-is-a-minefield/&lt;/a&gt;) by April King&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This Website is Hosted on Bluesky&lt;/strong&gt; (&lt;a href="https://danielmangum.com/posts/this-website-is-hosted-on-bluesky/" rel="noopener noreferrer"&gt;https://danielmangum.com/posts/this-website-is-hosted-on-bluesky/&lt;/a&gt;) by Daniel Mangum&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero Disk Architecture&lt;/strong&gt; (&lt;a href="https://avi.im/blag/2024/zero-disk-architecture/" rel="noopener noreferrer"&gt;https://avi.im/blag/2024/zero-disk-architecture/&lt;/a&gt;) by Avinash Sajjanshetty&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Google’s Chrome Worth Up to $20 Billion If Judge Orders Sale&lt;/strong&gt; (&lt;a href="https://www.bloomberg.com/news/articles/2024-11-18/doj-will-push-google-to-sell-off-chrome-to-break-search-monopoly" rel="noopener noreferrer"&gt;https://www.bloomberg.com/news/articles/2024-11-18/doj-will-push-google-to-sell-off-chrome-to-break-search-monopoly&lt;/a&gt;) by Leah Nylen and Josh Sisco&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue 69
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Webflow Design Language: Bridging The Gap Between Design &amp;amp; Code&lt;/strong&gt; (&lt;a href="https://webflow.com/blog/webflow-design-language" rel="noopener noreferrer"&gt;https://webflow.com/blog/webflow-design-language&lt;/a&gt;) by Merrick Christensen&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Building Reddit’s Frontend with Vite&lt;/strong&gt; (&lt;a href="https://www.reddit.com/r/RedditEng/comments/1dhztk8/building_reddits_frontend_with_vite/" rel="noopener noreferrer"&gt;https://www.reddit.com/r/RedditEng/comments/1dhztk8/building_reddits_frontend_with_vite/&lt;/a&gt;) by Jim Simon&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interop and Hard Problems&lt;/strong&gt; (&lt;a href="https://bkardell.com/blog/debt.html" rel="noopener noreferrer"&gt;https://bkardell.com/blog/debt.html&lt;/a&gt;) by Brian Kardell&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vite 6.0 is out!&lt;/strong&gt; (&lt;a href="https://vite.dev/blog/announcing-vite6.html" rel="noopener noreferrer"&gt;https://vite.dev/blog/announcing-vite6.html&lt;/a&gt;) by Evan You&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Complete Guide To Live Validation UX&lt;/strong&gt; (&lt;a href="https://www.smashingmagazine.com/2022/09/inline-validation-web-forms-ux/" rel="noopener noreferrer"&gt;https://www.smashingmagazine.com/2022/09/inline-validation-web-forms-ux/&lt;/a&gt;) by Vitaly Friedman&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Placeholders in Form Fields Are Harmful&lt;/strong&gt; (&lt;a href="https://www.nngroup.com/articles/form-design-placeholders/" rel="noopener noreferrer"&gt;https://www.nngroup.com/articles/form-design-placeholders/&lt;/a&gt;) by Katie Sherwin&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Browser APIs: Must-Know Secrets for Every Frontend Developer!&lt;/strong&gt; (&lt;a href="https://dev.to/mukhilpadmanabhan/browser-apis-must-know-secrets-for-every-frontend-developer-55lk"&gt;https://dev.to/mukhilpadmanabhan/browser-apis-must-know-secrets-for-every-frontend-developer-55lk&lt;/a&gt;) by &lt;a class="mentioned-user" href="https://dev.to/mukhilpadmanabhan"&gt;@mukhilpadmanabhan&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I learned React Native as a web developer, and I got everything wrong&lt;/strong&gt; (&lt;a href="https://fernandorojo.co/mistakes" rel="noopener noreferrer"&gt;https://fernandorojo.co/mistakes&lt;/a&gt;) by Fernando Rojo&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why TanStack Start is Ditching Adapters&lt;/strong&gt; (&lt;a href="https://tanstack.com/blog/why-tanstack-start-is-ditching-adapters" rel="noopener noreferrer"&gt;https://tanstack.com/blog/why-tanstack-start-is-ditching-adapters&lt;/a&gt;) by Tanner Linsley&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vitest vs. Jest&lt;/strong&gt; (&lt;a href="https://www.speakeasy.com/post/vitest-vs-jest" rel="noopener noreferrer"&gt;https://www.speakeasy.com/post/vitest-vs-jest&lt;/a&gt;) by Nolan Sullivan&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue 70
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Why am I writing a JavaScript toolchain in Zig?&lt;/strong&gt; (&lt;a href="https://injuly.in/blog/announcing-jam/index.html" rel="noopener noreferrer"&gt;https://injuly.in/blog/announcing-jam/index.html&lt;/a&gt;) by Srijan Paul&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;November 20, 2024 Release&lt;/strong&gt; (&lt;a href="https://react-spectrum.adobe.com/releases/2024-11-20.html" rel="noopener noreferrer"&gt;https://react-spectrum.adobe.com/releases/2024-11-20.html&lt;/a&gt;) by React Aria&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Effortless UI Spring Animations: A Two-Parameter Approach&lt;/strong&gt; (&lt;a href="https://www.kvin.me/posts/effortless-ui-spring-animations" rel="noopener noreferrer"&gt;https://www.kvin.me/posts/effortless-ui-spring-animations&lt;/a&gt;) by Kevin Grajeda&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If Not React, Then What?&lt;/strong&gt; (&lt;a href="https://infrequently.org/2024/11/if-not-react-then-what/" rel="noopener noreferrer"&gt;https://infrequently.org/2024/11/if-not-react-then-what/&lt;/a&gt;) by Alex Russell&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Comparing AWS S3 with Cloudflare R2: Price, Performance and User Experience&lt;/strong&gt; (&lt;a href="https://kerkour.com/aws-s3-vs-cloudflare-r2-price-performance-user-experience" rel="noopener noreferrer"&gt;https://kerkour.com/aws-s3-vs-cloudflare-r2-price-performance-user-experience&lt;/a&gt;) by Sylvain Kerkour&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Must-Have Obsidian plugins for 2025&lt;/strong&gt; (&lt;a href="https://www.dsebastien.net/2022-10-19-the-must-have-obsidian-plugins/" rel="noopener noreferrer"&gt;https://www.dsebastien.net/2022-10-19-the-must-have-obsidian-plugins/&lt;/a&gt;) by Sebastien Dubois&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Layered Approach to Speculation Rules&lt;/strong&gt; (&lt;a href="https://csswizardry.com/2024/12/a-layered-approach-to-speculation-rules/" rel="noopener noreferrer"&gt;https://csswizardry.com/2024/12/a-layered-approach-to-speculation-rules/&lt;/a&gt;) by Harry Roberts&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nothing Is Something&lt;/strong&gt; (&lt;a href="https://blog.jim-nielsen.com/2024/nothing-is-something/" rel="noopener noreferrer"&gt;https://blog.jim-nielsen.com/2024/nothing-is-something/&lt;/a&gt;) by Jim Nielsen&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Increasing Vite's potential with the Environment API&lt;/strong&gt; (&lt;a href="https://green.sapphi.red/blog/increasing-vites-potential-with-the-environment-api" rel="noopener noreferrer"&gt;https://green.sapphi.red/blog/increasing-vites-potential-with-the-environment-api&lt;/a&gt;) by sapphi-red&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Existential React questions and a perfect Modal Dialog&lt;/strong&gt; (&lt;a href="https://www.developerway.com/posts/hard-react-questions-and-modal-dialog" rel="noopener noreferrer"&gt;https://www.developerway.com/posts/hard-react-questions-and-modal-dialog&lt;/a&gt;) by Nadia Makarevich&lt;/p&gt;

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

&lt;p&gt;These are all outstanding articles by masterful authors. I enjoyed reading them all - I hope you did find something in there, too.&lt;/p&gt;

&lt;p&gt;👉 Follow me on &lt;a href="https://www.linkedin.com/in/florian-rappl/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, &lt;a href="https://twitter.com/FlorianRappl" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, &lt;a href="https://bsky.app/profile/florianrappl.bsky.social" rel="noopener noreferrer"&gt;BlueSky&lt;/a&gt;, or here for more to come.&lt;/p&gt;

&lt;p&gt;🙏 Thanks to all the authors and contributors for their hard work!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>frontend</category>
      <category>node</category>
    </item>
    <item>
      <title>🤯 50 Articles to Become a Web Dev Guru</title>
      <dc:creator>Florian Rappl</dc:creator>
      <pubDate>Fri, 15 Nov 2024 19:15:31 +0000</pubDate>
      <link>https://dev.to/florianrappl/50-articles-to-become-a-web-dev-guru-71e</link>
      <guid>https://dev.to/florianrappl/50-articles-to-become-a-web-dev-guru-71e</guid>
      <description>&lt;p&gt;This year I started a new series on LinkedIn - "Advanced Links for Frontend". Each issue has 10 links to outstanding posts / articles. This bundle contains the links from the last 5 issues (issue 61 to issue 65).&lt;/p&gt;

&lt;p&gt;I hope you enjoy this collection. Let me know in the comments which of these articles is your favorite (and why).&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue 61
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Liskov’s Gun: The parallel evolution of React and Web Components&lt;/strong&gt; (&lt;a href="https://www.baldurbjarnason.com/2024/liskovs-gun/" rel="noopener noreferrer"&gt;https://www.baldurbjarnason.com/2024/liskovs-gun/&lt;/a&gt;) by Baldur Bjarnason&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mozilla rebrand takes from early internet aesthetics, to move its cause to reclaim the web&lt;/strong&gt; (&lt;a href="https://www.itsnicethat.com/articles/jkr-mozilla-rebrand-graphic-design-project-240924" rel="noopener noreferrer"&gt;https://www.itsnicethat.com/articles/jkr-mozilla-rebrand-graphic-design-project-240924&lt;/a&gt;) by Ellis Tree&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's New in Ruby on Rails 8&lt;/strong&gt; (&lt;a href="https://blog.appsignal.com/2024/10/07/whats-new-in-ruby-on-rails-8.html" rel="noopener noreferrer"&gt;https://blog.appsignal.com/2024/10/07/whats-new-in-ruby-on-rails-8.html&lt;/a&gt;) by Damilola Olantuji&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A modest critique of Htmx&lt;/strong&gt; (&lt;a href="https://chrisdone.com/posts/htmx-critique/" rel="noopener noreferrer"&gt;https://chrisdone.com/posts/htmx-critique/&lt;/a&gt;) by Chris Done&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HTML is for people&lt;/strong&gt; (&lt;a href="https://htmlforpeople.com/" rel="noopener noreferrer"&gt;https://htmlforpeople.com/&lt;/a&gt;) by Blake Watson&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;17 Must-know React Projects for Developers 👩‍💻 🔥&lt;/strong&gt; (&lt;a href="https://dev.to/tolgee_i18n/17-must-know-react-projects-for-developers-28a2"&gt;https://dev.to/tolgee_i18n/17-must-know-react-projects-for-developers-28a2&lt;/a&gt;) by &lt;a class="mentioned-user" href="https://dev.to/anmolbaranwal"&gt;@anmolbaranwal&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The story of web framework Hono, from the creator of Hono&lt;/strong&gt; (&lt;a href="https://blog.cloudflare.com/the-story-of-web-framework-hono-from-the-creator-of-hono/" rel="noopener noreferrer"&gt;https://blog.cloudflare.com/the-story-of-web-framework-hono-from-the-creator-of-hono/&lt;/a&gt;) by Yusuke Wada&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sites Now Become Interactive 50% Faster&lt;/strong&gt; (&lt;a href="https://www.framer.com/blog/sites-interactive-faster/" rel="noopener noreferrer"&gt;https://www.framer.com/blog/sites-interactive-faster/&lt;/a&gt;) by Jacob Groß&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Drag to Select&lt;/strong&gt; (&lt;a href="https://www.joshuawootonn.com/react-drag-to-select" rel="noopener noreferrer"&gt;https://www.joshuawootonn.com/react-drag-to-select&lt;/a&gt;) by Joshua Wootonn&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why I’m skeptical of rewriting JavaScript tools in “faster” languages&lt;/strong&gt; (&lt;a href="https://nolanlawson.com/2024/10/20/why-im-skeptical-of-rewriting-javascript-tools-in-faster-languages/" rel="noopener noreferrer"&gt;https://nolanlawson.com/2024/10/20/why-im-skeptical-of-rewriting-javascript-tools-in-faster-languages/&lt;/a&gt;) by Nolan Lawson&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue 62
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Our Journey with Caching&lt;/strong&gt; (&lt;a href="https://nextjs.org/blog/our-journey-with-caching" rel="noopener noreferrer"&gt;https://nextjs.org/blog/our-journey-with-caching&lt;/a&gt;) by Sebastian Markbåge&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Offline UX Patterns&lt;/strong&gt; (&lt;a href="https://github.com/Expensify/App/blob/main/contributingGuides/OFFLINE_UX.md" rel="noopener noreferrer"&gt;https://github.com/Expensify/App/blob/main/contributingGuides/OFFLINE_UX.md&lt;/a&gt;) by Expensify&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;World Wide Web Foundation closes so Tim Berners-Lee can spend more time with his protocol&lt;/strong&gt; (&lt;a href="https://www.theregister.com/2024/09/30/world_wide_web_foundation_closes/" rel="noopener noreferrer"&gt;https://www.theregister.com/2024/09/30/world_wide_web_foundation_closes/&lt;/a&gt;) by Thomas Claburn&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;why I'm turning off Firefox ad tracking: the PPA paradox&lt;/strong&gt; (&lt;a href="https://blog.zgp.org/why-turn-off-firefox-ad-tracking/" rel="noopener noreferrer"&gt;https://blog.zgp.org/why-turn-off-firefox-ad-tracking/&lt;/a&gt;) by Don Marti&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hyper-Scale Activated! Ship Your Own FaaS 🤖&lt;/strong&gt; (&lt;a href="https://dev.to/smapiot/hyper-scale-activated-ship-your-own-faas-40kg"&gt;https://dev.to/smapiot/hyper-scale-activated-ship-your-own-faas-40kg&lt;/a&gt;) by &lt;a class="mentioned-user" href="https://dev.to/florianrappl"&gt;@florianrappl&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next.js 15&lt;/strong&gt; (&lt;a href="https://nextjs.org/blog/next-15" rel="noopener noreferrer"&gt;https://nextjs.org/blog/next-15&lt;/a&gt;) by Delba de Oliveira et al&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Data Center Latencies - Visualized&lt;/strong&gt; (&lt;a href="https://benjdd.com/aws/" rel="noopener noreferrer"&gt;https://benjdd.com/aws/&lt;/a&gt;) by Benjamin Dicken&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where web components shine&lt;/strong&gt; (&lt;a href="https://daverupert.com/2024/10/super-web-components-sunshine/" rel="noopener noreferrer"&gt;https://daverupert.com/2024/10/super-web-components-sunshine/&lt;/a&gt;) by Dave Rupert&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Netlify joins OpenNext&lt;/strong&gt; (&lt;a href="https://www.netlify.com/blog/netlify-joins-opennext/" rel="noopener noreferrer"&gt;https://www.netlify.com/blog/netlify-joins-opennext/&lt;/a&gt;) by Mathias Biilmann&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;New Architecture is here&lt;/strong&gt; (&lt;a href="https://reactnative.dev/blog/2024/10/23/the-new-architecture-is-here" rel="noopener noreferrer"&gt;https://reactnative.dev/blog/2024/10/23/the-new-architecture-is-here&lt;/a&gt;) by The React Team&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue 63
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Expo SDK 52 beta is now available&lt;/strong&gt; (&lt;a href="https://expo.dev/changelog/2024/10-24-sdk-52-beta" rel="noopener noreferrer"&gt;https://expo.dev/changelog/2024/10-24-sdk-52-beta&lt;/a&gt;) by Brent Vatne&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That's Not an Abstraction, That's Just a Layer of Indirection&lt;/strong&gt; (&lt;a href="https://fhur.me/posts/2024/thats-not-an-abstraction" rel="noopener noreferrer"&gt;https://fhur.me/posts/2024/thats-not-an-abstraction&lt;/a&gt;) by Fernando Hurtado&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An Early Glimpse of TanStack Start&lt;/strong&gt; (&lt;a href="https://www.youtube.com/watch?v=AuHqwQsf64o" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=AuHqwQsf64o&lt;/a&gt;) by Tanner Lindsey&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How we shrunk our Javascript monorepo git size by 94%&lt;/strong&gt; (&lt;a href="https://www.jonathancreamer.com/how-we-shrunk-our-git-repo-size-by-94-percent/" rel="noopener noreferrer"&gt;https://www.jonathancreamer.com/how-we-shrunk-our-git-repo-size-by-94-percent/&lt;/a&gt;) by Jonathan Creamer&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Making content-aware components using CSS :has(), grid, and quantity queries&lt;/strong&gt; (&lt;a href="https://piccalil.li/blog/making-content-aware-components-using-css-has-grid-and-quantity-queries/" rel="noopener noreferrer"&gt;https://piccalil.li/blog/making-content-aware-components-using-css-has-grid-and-quantity-queries/&lt;/a&gt;) by Eric Bailey&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remix Todo App: Part 1 - Building the App Layout and Structure&lt;/strong&gt; (&lt;a href="https://www.udohjeremiah.com/blog/remix-todo-app-part-1-building-the-app-layout-and-structure" rel="noopener noreferrer"&gt;https://www.udohjeremiah.com/blog/remix-todo-app-part-1-building-the-app-layout-and-structure&lt;/a&gt;) by Udoh Jeremiah&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using Rust in Non-Rust Servers to Improve Performance&lt;/strong&gt; (&lt;a href="https://github.com/pretzelhammer/rust-blog/blob/master/posts/rust-in-non-rust-servers.md" rel="noopener noreferrer"&gt;https://github.com/pretzelhammer/rust-blog/blob/master/posts/rust-in-non-rust-servers.md&lt;/a&gt;) by Pretzelhammer&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interactive Select component using Phoenix LiveView and vanilla JS Hook&lt;/strong&gt; (&lt;a href="https://aurmartin.fr/posts/phoenix-liveview-select/" rel="noopener noreferrer"&gt;https://aurmartin.fr/posts/phoenix-liveview-select/&lt;/a&gt;) by Aurélien Martin&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unleash the Power of Scroll-Driven Animations&lt;/strong&gt; (&lt;a href="https://css-tricks.com/unleash-the-power-of-scroll-driven-animations/" rel="noopener noreferrer"&gt;https://css-tricks.com/unleash-the-power-of-scroll-driven-animations/&lt;/a&gt;) by Geoff Graham&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Incremental adoption of micro-frontends with Cloudflare Workers&lt;/strong&gt; (&lt;a href="https://blog.cloudflare.com/fragment-piercing/" rel="noopener noreferrer"&gt;https://blog.cloudflare.com/fragment-piercing/&lt;/a&gt;) by Peter Bacon Darwin, Dario Piotrowicz, James Culveyhouse, Igor Minar&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue 64
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Headless, boneless, skinless &amp;amp; lifeless UI&lt;/strong&gt; (&lt;a href="https://nerdy.dev/headless-boneless-and-skinless-ui" rel="noopener noreferrer"&gt;https://nerdy.dev/headless-boneless-and-skinless-ui&lt;/a&gt;) by Adam Argyle&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On Crafting Painterly Shaders&lt;/strong&gt; (&lt;a href="https://blog.maximeheckel.com/posts/on-crafting-painterly-shaders/" rel="noopener noreferrer"&gt;https://blog.maximeheckel.com/posts/on-crafting-painterly-shaders/&lt;/a&gt;) by Maxime Heckel&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Smarter than 'Ctrl+F': Linking Directly to Web Page Content&lt;/strong&gt; (&lt;a href="https://alfy.blog/2024/10/19/linking-directly-to-web-page-content.html" rel="noopener noreferrer"&gt;https://alfy.blog/2024/10/19/linking-directly-to-web-page-content.html&lt;/a&gt;) by Ahmad Alfy&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Simplfying Islands Architecture&lt;/strong&gt; (&lt;a href="https://preactjs.com/blog/simplifying-islands-arch/" rel="noopener noreferrer"&gt;https://preactjs.com/blog/simplifying-islands-arch/&lt;/a&gt;) by Siddharth Gelera&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Going through the “State of CSS” results…&lt;/strong&gt; (&lt;a href="https://www.joshwcomeau.com/email/2024-10-22-css-survey/" rel="noopener noreferrer"&gt;https://www.joshwcomeau.com/email/2024-10-22-css-survey/&lt;/a&gt;) by Josh W. Comeau&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rendering Outlines with a Post-processing Shader&lt;/strong&gt; (&lt;a href="https://www.atomwolf.org/posts/rendering-outlines-with-a-post-processing-shader/" rel="noopener noreferrer"&gt;https://www.atomwolf.org/posts/rendering-outlines-with-a-post-processing-shader/&lt;/a&gt;) by Adam Wolf&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should JavaScript be split into two languages? New Google-driven proposal divides opinion&lt;/strong&gt; (&lt;a href="https://devclass.com/2024/10/22/should-javascript-be-split-into-two-languages-new-google-driven-proposal-divides-opinion/" rel="noopener noreferrer"&gt;https://devclass.com/2024/10/22/should-javascript-be-split-into-two-languages-new-google-driven-proposal-divides-opinion/&lt;/a&gt;) by Tim Anderson&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Introducing Wasmer 5.0&lt;/strong&gt; (&lt;a href="https://wasmer.io/posts/introducing-wasmer-v5" rel="noopener noreferrer"&gt;https://wasmer.io/posts/introducing-wasmer-v5&lt;/a&gt;) by Syrus Akbary&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should masonry be part of CSS grid?&lt;/strong&gt; (&lt;a href="https://ishadeed.com/article/css-grid-masonry/" rel="noopener noreferrer"&gt;https://ishadeed.com/article/css-grid-masonry/&lt;/a&gt;) by Ahmad Shadeed&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recap: Next.js Conf 2024&lt;/strong&gt; (&lt;a href="https://vercel.com/blog/recap-next-js-conf-2024" rel="noopener noreferrer"&gt;https://vercel.com/blog/recap-next-js-conf-2024&lt;/a&gt;) by Lee Robinson, Delba de Oliveira&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue 65
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;How headless components became the future for building UI libraries&lt;/strong&gt; (&lt;a href="https://www.subframe.com/blog/how-headless-components-became-the-future-for-building-ui-libraries" rel="noopener noreferrer"&gt;https://www.subframe.com/blog/how-headless-components-became-the-future-for-building-ui-libraries&lt;/a&gt;) by Irvin Zhan&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hyperkit&lt;/strong&gt; (&lt;a href="https://www.chrsgrrtt.com/hyperkit" rel="noopener noreferrer"&gt;https://www.chrsgrrtt.com/hyperkit&lt;/a&gt;) by Chris Garrett&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Converting any website to a React component&lt;/strong&gt; (&lt;a href="https://www.magicpatterns.com/blog/any-website-to-react-component" rel="noopener noreferrer"&gt;https://www.magicpatterns.com/blog/any-website-to-react-component&lt;/a&gt;) by Teddy Ni&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HTML Form Validation is heavily underused&lt;/strong&gt; (&lt;a href="https://expressionstatement.com/html-form-validation-is-heavily-underused" rel="noopener noreferrer"&gt;https://expressionstatement.com/html-form-validation-is-heavily-underused&lt;/a&gt;) by everdimension&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using Shadcn UI without a Tailwind Config File&lt;/strong&gt; (&lt;a href="https://www.luisball.com/blog/shadcn-ui-with-tailwind-v4" rel="noopener noreferrer"&gt;https://www.luisball.com/blog/shadcn-ui-with-tailwind-v4&lt;/a&gt;) by Luis Ball&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Debugging Microservices &amp;amp; Distributed Systems&lt;/strong&gt; (&lt;a href="https://sentry.io/resources/debugging-microservices-and-distributed-systems/" rel="noopener noreferrer"&gt;https://sentry.io/resources/debugging-microservices-and-distributed-systems/&lt;/a&gt;) by Sarah Guthals&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RFC: An updated style guide for the year 2024&lt;/strong&gt; (&lt;a href="https://github.com/angular/angular/discussions/58412" rel="noopener noreferrer"&gt;https://github.com/angular/angular/discussions/58412&lt;/a&gt;) by Jeremy Elbourn&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My solar-powered and self-hosted website&lt;/strong&gt; (&lt;a href="https://dri.es/my-solar-powered-and-self-hosted-website" rel="noopener noreferrer"&gt;https://dri.es/my-solar-powered-and-self-hosted-website&lt;/a&gt;) by Dries Buytaert&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HTTP Tunnel and Proxy in JavaScript&lt;/strong&gt; (&lt;a href="https://dbushell.com/2024/10/22/http-tunnel-proxy/" rel="noopener noreferrer"&gt;https://dbushell.com/2024/10/22/http-tunnel-proxy/&lt;/a&gt;) by David Bushell&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's a Single-Page App?&lt;/strong&gt; (&lt;a href="https://jakelazaroff.com/words/whats-a-single-page-app/" rel="noopener noreferrer"&gt;https://jakelazaroff.com/words/whats-a-single-page-app/&lt;/a&gt;) by Jake Lazaroff&lt;/p&gt;

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

&lt;p&gt;These are all outstanding articles by masterful authors. I enjoyed reading them all - I hope you did find something in there, too.&lt;/p&gt;

&lt;p&gt;👉 Follow me on &lt;a href="https://www.linkedin.com/in/florian-rappl/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, &lt;a href="https://twitter.com/FlorianRappl" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, or here for more to come.&lt;/p&gt;

&lt;p&gt;🙏 Thanks to all the authors and contributors for their hard work!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>frontend</category>
      <category>node</category>
    </item>
    <item>
      <title>Hyper-Scale Activated! Ship Your Own FaaS 🤖</title>
      <dc:creator>Florian Rappl</dc:creator>
      <pubDate>Fri, 18 Oct 2024 12:34:36 +0000</pubDate>
      <link>https://dev.to/smapiot/hyper-scale-activated-ship-your-own-faas-40kg</link>
      <guid>https://dev.to/smapiot/hyper-scale-activated-ship-your-own-faas-40kg</guid>
      <description>&lt;p&gt;In the past decade I've done a lot of work on distributed web applications. In that area you definitely require processes that work implicitly - fully decoupled, with autonomy for individual teams and modules.&lt;/p&gt;

&lt;p&gt;The key question quite often is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How awesome would it be to just extend your application at runtime without any downtime whatsoever?!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this article I want to look at how we can extend a Node server using Express.js to act like a FaaS provider (such as AWS Lambda or Azure Functions).&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To follow this article you'll need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js with npm installed (I recommend version 22 or higher - we'll use &lt;code&gt;fetch&lt;/code&gt; in Node, which is available since version 18)&lt;/li&gt;
&lt;li&gt;A code editor&lt;/li&gt;
&lt;li&gt;If you want to &lt;a href="https://github.com/piral-samples/piral-cloud-express-plugins-demo" rel="noopener noreferrer"&gt;clone the sample project&lt;/a&gt; you'll need to have &lt;code&gt;git&lt;/code&gt; installed (but you can download it as a ZIP from GitHub)&lt;/li&gt;
&lt;li&gt;Access to the &lt;a href="https://feed.piral.cloud" rel="noopener noreferrer"&gt;Piral Cloud Feed Service&lt;/a&gt; - you can use it for free using a Microsoft or GitHub account&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;Before we start to write some code let's look at the anticipated architecture:&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%2F0owb6x5e1y9myj26v93m.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%2F0owb6x5e1y9myj26v93m.png" alt="Architecture diagram" width="491" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using the &lt;a href="https://feed.piral.cloud" rel="noopener noreferrer"&gt;Piral Cloud Feed Service&lt;/a&gt; we get access to a dynamic service / module registry, which we can leverage to load a list of modules, evaluate them directly from their URLs, and then use them to populate a list of (sub-)routers.&lt;/p&gt;

&lt;p&gt;The routers are necessary to extend the default router middleware. Let's see the whole thing in action. We start by writing the server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing the Server
&lt;/h2&gt;

&lt;p&gt;As server we use a pretty standard and lightweight Express.js server. It can be created by running the following commands in a new directory.&lt;/p&gt;

&lt;p&gt;We first initialize a new npm project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we install the required dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i express
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We add a new file (&lt;em&gt;index.js&lt;/em&gt;) with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&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;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3000&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&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;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello world!&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="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&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="p"&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="s2"&gt;`Running at http://localhost:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we finally run the application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go to localhost:3000 in the browser of your choice and see that everything is running as it should:&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%2F0e0q1t8rgqiqqm27lzdm.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%2F0e0q1t8rgqiqqm27lzdm.png" alt="Server running" width="800" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great! But there is a bit more to it for making it truly dynamic...&lt;/p&gt;

&lt;h2&gt;
  
  
  Making the Server Dynamic
&lt;/h2&gt;

&lt;p&gt;So what we are after is to run modules that we can just publish whenever we feel the need for it. The first thing to look for is to avoid having routing issues.&lt;/p&gt;

&lt;p&gt;To prevent different modules for occupying the same route we will actually namespace the routes by their module names. In Express.js this also allows us to use dynamic &lt;code&gt;Router&lt;/code&gt; objects, which are bound to their module names.&lt;/p&gt;

&lt;p&gt;In code this can be done like that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;routers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/:namespace&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;namespace&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&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;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;routers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;namespace&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;router&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;router&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&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;next&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;We introduce a middleware that is sensitive to a &lt;code&gt;namespace&lt;/code&gt; parameter. In a productive setup we might want to constraint this a bit further, e.g., &lt;code&gt;/api/:namespace&lt;/code&gt; or &lt;code&gt;/apps/:namespace&lt;/code&gt;, to avoid clashes between in-built functionality and the modules.&lt;/p&gt;

&lt;p&gt;The job of the middleware is to use the retrieved &lt;code&gt;namespace&lt;/code&gt; value by looking at all available sub-routers. If a sub-router is matched we will use the sub-router as follow-up middleware. Otherwise, we continue with the next middleware. Usually, the next middleware would be the "not-found-route" from Express.js.&lt;/p&gt;

&lt;p&gt;The question to answer now is how to populate the &lt;code&gt;routers&lt;/code&gt; object. How can we obtain these sub-routers?&lt;/p&gt;

&lt;p&gt;What we can do is to introduce a function to obtain the information from a discovery service. This service gets us all the relevant pieces of information - we then only need to load / run the referenced modules.&lt;/p&gt;

&lt;p&gt;The necessary code to built something like that is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;feed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://feed.piral.cloud/api/v1/pilet/express-demo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;makeId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadPlugins&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Loading plugins ...&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;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;feed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;for &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;item&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;items&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;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;makeId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&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="s2"&gt;`Integrating plugin "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&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="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;installPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&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="s2"&gt;`Integrated plugin "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&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="nf"&gt;watchPlugins&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;loadPlugins&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What are we doing here?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We want to start loading the plugins / sub-routers as fast as possible. So we immediately run the &lt;code&gt;loadPlugins&lt;/code&gt; function. Ideally we use the top-level &lt;code&gt;await&lt;/code&gt; here; if it's not available (or if we want to save a bit of startup time) we just call it as shown.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;loadPlugins&lt;/code&gt; fetches the information from the discovery service.&lt;/li&gt;
&lt;li&gt;Once the information is fetched we go over the contained &lt;code&gt;items&lt;/code&gt; array. Each item is treated like the meta information of a plugin - so we just call &lt;code&gt;installPlugin&lt;/code&gt; to use the found meta information.&lt;/li&gt;
&lt;li&gt;Finally we also want to react when something changes - so the &lt;code&gt;loadPlugins&lt;/code&gt; part concludes by watching for changes using the &lt;code&gt;watchPlugins&lt;/code&gt; function.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;/p&gt;
  Important Note
  &lt;br&gt;
If you want to follow this on your own then replace &lt;code&gt;https://feed.piral.cloud/api/v1/pilet/express-demo&lt;/code&gt; with your own discovery feed URL. Using the &lt;a href=""&gt;Piral Cloud Feed Service&lt;/a&gt; you can see the URL on the feed overview page.

&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%2F6s2r9k06ml4lkuibnndt.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%2F6s2r9k06ml4lkuibnndt.png" alt="Finding the Feed Service URL in the feed overview" width="800" height="397"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;br&gt;
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Let's look at the &lt;code&gt;installPlugin&lt;/code&gt; first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;installPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;link&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;item&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;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;setup&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;loadModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;link&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;setup&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="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;routers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;makeId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;name&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;Doesn't look too complicated, does it? What's happening here?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We decompose the item - but we are only interested in the &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;link&lt;/code&gt; (module reference / URL) of the module.&lt;/li&gt;
&lt;li&gt;We create a new Express.js &lt;code&gt;Router&lt;/code&gt; instance (the "sub-router").&lt;/li&gt;
&lt;li&gt;We load the respective module - it should get us a function called &lt;code&gt;setup&lt;/code&gt; (now this is a design choice; you could either name it differently, make it a &lt;code&gt;default&lt;/code&gt; export, or export a &lt;code&gt;Router&lt;/code&gt; instance directly...&lt;/li&gt;
&lt;li&gt;If we really obtained a &lt;code&gt;setup&lt;/code&gt; function we call it with the &lt;code&gt;Router&lt;/code&gt; instance. This can now be modified properly.&lt;/li&gt;
&lt;li&gt;Finally, the created router is assigned to the &lt;code&gt;routers&lt;/code&gt; object and the &lt;code&gt;current&lt;/code&gt; array is extended with the proper entry.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As you might have guessed the &lt;code&gt;current&lt;/code&gt; array is for book-keeping purposes. It allows us to easily patch / change the routers in case of changes. For this, we'll need to look at the &lt;code&gt;watchPlugins&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;WebSocket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ws&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;changeEventTypes&lt;/span&gt; &lt;span class="o"&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;add-pilet&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;update-pilet&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;remove-pilet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;watchPlugins&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Watching plugins ...&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;ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;feed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http&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;ws&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;changeEventTypes&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="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&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;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;feed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;removeItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;current&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;id&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;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;n&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;makeId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;id&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;addItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;items&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;item&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;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;id&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;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nf"&gt;makeId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="k"&gt;for &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;item&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;removeItems&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;uninstallPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="k"&gt;for &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;item&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;addItems&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;installPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this code to run we rely on the &lt;code&gt;ws&lt;/code&gt; library. So we'll need to install it first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i ws
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;ws&lt;/code&gt; active we can have a look at what the code does:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We define some events we want to listen to. The provided events are send via the WebSocket channel in case the contained modules change.&lt;/li&gt;
&lt;li&gt;Once a message is received we transform it (from raw bytes to a JSON object) and check if the type matches one of the events defined in (1).&lt;/li&gt;
&lt;li&gt;We could now use the information from the event, but some events (e.g., &lt;code&gt;update-pilet&lt;/code&gt;) don't tell us what changed. In such cases we could be in a problematic zone. So we follow the safe route and just re-load all the modules.&lt;/li&gt;
&lt;li&gt;Finally, we need to compare the &lt;em&gt;current&lt;/em&gt; information from the discovery service vs the stored / previous information. For the changed (removed, updated) modules we remove them using the &lt;code&gt;uninstallPlugin&lt;/code&gt;. All the ones that are not yet instantiated we add them using the &lt;code&gt;installPlugin&lt;/code&gt; function.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;uninstallPlugin&lt;/code&gt; looks as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;uninstallPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;routers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&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;That was easy, right? But so far we avoided the most complicated topic. How are we evaluating those modules?&lt;/p&gt;

&lt;h2&gt;
  
  
  Evaluating Modules from URLs
&lt;/h2&gt;

&lt;p&gt;In the browser you can always evaluate ESMs - they are coming from an URL by default. The same is true in runtimes such as Deno. But in Node.js this is not possible. So should we give up?&lt;/p&gt;

&lt;p&gt;As it turns out evaluating URLs is actually not so difficult. We need 2 things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Code to use the &lt;code&gt;vm&lt;/code&gt; module. We need to rely on the &lt;code&gt;SourceTextModule&lt;/code&gt; to create modules on the fly.&lt;/li&gt;
&lt;li&gt;The experimental flag &lt;code&gt;--experimental-vm-modules&lt;/code&gt; for running Node. This way, the &lt;code&gt;SourceTextModule&lt;/code&gt; is available at runtime.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Using those two ways we can add the following code to our server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;linkModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&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;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&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;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&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;mod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SourceTextModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;link&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;specifier&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;newUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;specifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&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;linkModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evaluate&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;mod&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&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;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;try&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;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;linkModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&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;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Failed to evaluate "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;url&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;ex&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="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;Finally, we have everything together and can just run the server with the flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--experimental-vm-modules&lt;/span&gt; index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We should see output like this in the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Loading plugins ...
Running at http://localhost:3000
Integrating plugin "app1@2.0.0" ...
Integrated plugin "app1@2.0.0"!
Integrating plugin "app2@1.0.4" ...
(node:23052) ExperimentalWarning: VM Modules is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
Integrated plugin "app2@1.0.4"!
Integrating plugin "app3@1.0.0" ...
Integrated plugin "app3@1.0.0"!
Watching plugins ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we are already including a couple of modules (referred to as plugins). Let's see what we can do here - how we can write, deploy, and update them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example Functions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  One Module
&lt;/h3&gt;

&lt;p&gt;The first example is a really simple one; just a single function to return a constant string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&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;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/foo&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello from app1 v2: /foo&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Importantly, we initialize a new project and define the &lt;em&gt;package.json&lt;/em&gt; like this:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"app1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"index.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deploy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"publish-microfrontend --url https://feed.piral.cloud/api/v1/pilet/express-demo --interactive"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"devDependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"publish-microfrontend"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.6.2"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;publish-microfrontend&lt;/code&gt; script is used to deploy the module. This little script just packages the current project and invokes a POST against the provided discovery service URL. The &lt;code&gt;--interactive&lt;/code&gt; flag then allows us to dynamically log into the discovery service to deploy our function.&lt;/p&gt;

&lt;p&gt;We'll see how this process looks in a second. Let's look at another example.&lt;/p&gt;

&lt;h3&gt;
  
  
  Two Modules
&lt;/h3&gt;

&lt;p&gt;While publishing the package with a single module works as expected the question is: Does it also work with more modules? The answer is yes.&lt;/p&gt;

&lt;p&gt;Pretty much the same definition in the &lt;em&gt;package.json&lt;/em&gt;:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"app2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lib/index.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deploy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"publish-microfrontend --url https://feed.dev.piral.cloud/api/v1/pilet/express-demo --interactive"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"devDependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"publish-microfrontend"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.6.2"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But now &lt;em&gt;lib/index.js&lt;/em&gt; is different:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;compute&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./other.js&lt;/span&gt;&lt;span class="dl"&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;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/compute&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&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;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;b&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="nf"&gt;isNaN&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="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&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;c&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Only numbers allowed.`&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 only is this a bit more logic - it also uses a function from another module located at &lt;em&gt;lib/other.js&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&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;compute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&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;a&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="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;b&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;b&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="kc"&gt;NaN&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;As you might have guessed our ESM URL loader just works as it should. No questions asked. Great!&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%2Fhhs1gkr4elzp5ovt855h.gif" 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%2Fhhs1gkr4elzp5ovt855h.gif" alt="Impressive" width="500" height="281"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We won't answer the burning question: Can it do three modules? I guess we know the answer, but if you want to find out get active now!&lt;/p&gt;

&lt;p&gt;In the meantime we go already to the complex case of having the modules written in TypeScript - and including some dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  TypeScript Modules with Dependencies
&lt;/h3&gt;

&lt;p&gt;Consider the code at &lt;em&gt;src/index.ts&lt;/em&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;cors&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Router&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express-serve-static-core&lt;/span&gt;&lt;span class="dl"&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;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&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;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`NOT FOUND.`&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;Here we are using types and dependencies. This won't work - we don't do any dependency resolution and we don't transpile TypeScript. So should we be worried? Not at all!&lt;/p&gt;

&lt;p&gt;Let's bundle it using esbuild:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;esbuild &lt;span class="nt"&gt;--outdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dist src/index.ts &lt;span class="nt"&gt;--bundle&lt;/span&gt; &lt;span class="nt"&gt;--platform&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;node &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;esm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even better - we can just extend the &lt;em&gt;package.json&lt;/em&gt; to take care of this in the &lt;code&gt;deploy&lt;/code&gt; task:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"app3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist/index.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"esbuild --outdir=dist src/index.ts --bundle --platform=node --format=esm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deploy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run build &amp;amp;&amp;amp; publish-microfrontend --url https://feed.dev.piral.cloud/api/v1/pilet/express-demo --interactive"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"devDependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@types/cors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^2.8.17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@types/express"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^5.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"esbuild"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.24.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"publish-microfrontend"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.6.2"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^2.8.5"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, we can just write anything - bundle it using &lt;code&gt;esbuild&lt;/code&gt; and ship it with &lt;code&gt;publish-microfrontend&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now let's step back a bit and see how we actually deploy the modules:&lt;/p&gt;

&lt;h3&gt;
  
  
  Deployment
&lt;/h3&gt;

&lt;p&gt;Maybe let's see first how it looks in the discovery service when we deployed all of our modules:&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%2F1c541s2pz9etr7v8qwt8.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%2F1c541s2pz9etr7v8qwt8.png" alt="Overview of modules" width="800" height="209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As already mentioned for deploying such a module we can use the available &lt;code&gt;publish-microfrontend&lt;/code&gt; npm package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx publish-microfrontend &lt;span class="nt"&gt;--url&lt;/span&gt; &amp;lt;your-discovery-service-url&amp;gt; &lt;span class="nt"&gt;--api-key&lt;/span&gt; &amp;lt;your-key&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In cases such as the ones above we might swap the &lt;code&gt;--api-key&lt;/code&gt; with an &lt;code&gt;--interactive&lt;/code&gt; flow, where we need to use the browser for obtaining a user token instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx publish-microfrontend &lt;span class="nt"&gt;--url&lt;/span&gt; &amp;lt;your-discovery-service-url&amp;gt; &lt;span class="nt"&gt;--interactive&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's see how this process looks in real life:&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%2F8p39e4ejphb5ysrznaco.gif" 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%2F8p39e4ejphb5ysrznaco.gif" alt="The publish process in motion" width="800" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the end having such a powerful service registry between our server and the respective modules is not only clean, but also useful for flexibility reasons.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Dynamics
&lt;/h2&gt;

&lt;p&gt;We already saw that the dynamics with respect to partial rollbacks are in place from the beginning. In general, the service registry's portal gives us the ability to control every bit of the delivery process:&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%2Fxxobl0sbqeqblhbjdww5.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%2Fxxobl0sbqeqblhbjdww5.png" alt="Version selector" width="800" height="207"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The version selector in particular is quite useful. Besides the possibility of making rollbacks, we can also do feature flags, A/B testing, blue-green deployments, or canary releases.&lt;/p&gt;

&lt;p&gt;Quite powerful stuff...&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%2F5y45mlet4a9ut9phpmgq.gif" 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%2F5y45mlet4a9ut9phpmgq.gif" alt="Homelander" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This allows us, e.g., to have multiple instances of our Express.js server, where each instance might get a different set of modules. Sounds crazy (how can this be useful?!) but if you don't consider these instances equal (i.e., meant for scaling), but rather as different sites (e.g., one for US, one for EU etc.) then backend behavior might be different - at least in some areas.&lt;/p&gt;

&lt;p&gt;Beforehand, that was a lot of overhead. You had to produce countless variations of the same server - now you can just have one and have the different behavior qualified through the loaded modules.&lt;/p&gt;

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

&lt;p&gt;In this article you've seen how you can easily extend your Express.js server into a FaaS platform allowing you to add, update, and remove endpoints on the fly.&lt;/p&gt;

&lt;p&gt;Of course, the provided sample is rather simplistic and does not solve some of the issues including advanced isolation, memory footprint (unload and restrictions of modules), local module development using an emulator, and many others. But it's a very convenient start - showing what's possible with just a few lines of code.&lt;/p&gt;

&lt;p&gt;Check out the sample code at &lt;a href="https://github.com/piral-samples/piral-cloud-express-plugins-demo" rel="noopener noreferrer"&gt;github.com/piral-samples/piral-cloud-express-plugins-demo&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>node</category>
      <category>express</category>
    </item>
    <item>
      <title>🎃 50 Articles to Avoid Web Dev Nightmares</title>
      <dc:creator>Florian Rappl</dc:creator>
      <pubDate>Tue, 15 Oct 2024 13:28:34 +0000</pubDate>
      <link>https://dev.to/florianrappl/50-articles-to-web-dev-nightmares-15ah</link>
      <guid>https://dev.to/florianrappl/50-articles-to-web-dev-nightmares-15ah</guid>
      <description>&lt;p&gt;This year I started a new series on &lt;a href="https://www.linkedin.com/in/florian-rappl/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; - "Advanced Links for Frontend". Each issue has 10 links to outstanding posts / articles. This bundle contains the links from the last 5 issues (issue 56 to issue 60).&lt;/p&gt;

&lt;p&gt;I hope you enjoy this collection. Let me know in the comments which of these articles is your favorite (and why).&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue 56
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The CSS radial composer&lt;/strong&gt; (&lt;a href="https://zumerlab.github.io/orbit-docs/" rel="noopener noreferrer"&gt;https://zumerlab.github.io/orbit-docs/&lt;/a&gt;) by Orbit&lt;br&gt;
Gravity is everywhere! For some reason I feel attracted to this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remix's concurrent submissions are fundamentally flawed&lt;/strong&gt; (without causal ordering) (&lt;a href="https://dashbit.co/blog/remix-concurrent-submissions-flawed" rel="noopener noreferrer"&gt;https://dashbit.co/blog/remix-concurrent-submissions-flawed&lt;/a&gt;) by José Valim&lt;br&gt;
Honestly, I am not surprised.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ts-blank-space&lt;/strong&gt; (&lt;a href="https://bloomberg.github.io/ts-blank-space/" rel="noopener noreferrer"&gt;https://bloomberg.github.io/ts-blank-space/&lt;/a&gt;) by Ashley Claymore&lt;br&gt;
This can be really useful, but I wonder why not just esbuild is used. It's fast and can also be used for more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;date-fns v4.0 is out with first-class time zones support!&lt;/strong&gt; (&lt;a href="https://blog.date-fns.org/v40-with-time-zone-support/" rel="noopener noreferrer"&gt;https://blog.date-fns.org/v40-with-time-zone-support/&lt;/a&gt;) by Sasha Koss&lt;br&gt;
Well, having time zones support is one of the things I'd consider necessary for any good time library.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fastify v5 is Now Officially Released!&lt;/strong&gt; (&lt;a href="https://openjsf.org/blog/fastifys-growth-and-success" rel="noopener noreferrer"&gt;https://openjsf.org/blog/fastifys-growth-and-success&lt;/a&gt;) by Fastify Team&lt;br&gt;
Fastify is great and presumably under-appreciated in the Node.js community.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WebKit Features in Safari 18.0&lt;/strong&gt; (&lt;a href="https://webkit.org/blog/15865/webkit-features-in-safari-18-0/" rel="noopener noreferrer"&gt;https://webkit.org/blog/15865/webkit-features-in-safari-18-0/&lt;/a&gt;) by Jen Simmons&lt;br&gt;
Keep an eye on this - view transitions and WebXR are huge! Fantastic release!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Biome v1.9 Anniversary Release&lt;/strong&gt; (&lt;a href="https://biomejs.dev/blog/biome-v1-9/" rel="noopener noreferrer"&gt;https://biomejs.dev/blog/biome-v1-9/&lt;/a&gt;) by Victorien Elvinger&lt;br&gt;
Alright I can happily say that finally Biome is also becoming interesting to me. Goodbye Prettier + eslint!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A milestone for TypeScript Performance in TanStack Router&lt;/strong&gt; (&lt;a href="https://tanstack.com/blog/tanstack-router-typescript-performance" rel="noopener noreferrer"&gt;https://tanstack.com/blog/tanstack-router-typescript-performance&lt;/a&gt;) by Christopher Horobin&lt;br&gt;
Great write-up with some insights on how the type checker / TypeScript performance can be evaluated and improved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;New Values and Functions in CSS&lt;/strong&gt; (&lt;a href="https://dev.to/alvaromontoro/new-values-and-functions-in-css-1b9o"&gt;https://dev.to/alvaromontoro/new-values-and-functions-in-css-1b9o&lt;/a&gt;) by &lt;a class="mentioned-user" href="https://dev.to/alvaromontoro"&gt;@alvaromontoro&lt;/a&gt; &lt;br&gt;
Well, some of these functions are not really new, but it's always good to be reminded about them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deno 2.0 Release Candidate&lt;/strong&gt; (&lt;a href="https://deno.com/blog/v2.0-release-candidate" rel="noopener noreferrer"&gt;https://deno.com/blog/v2.0-release-candidate&lt;/a&gt;) by Andy Jiang and Bartek Iwańczuk&lt;br&gt;
The v2 promises to be a really well-rounded release. Time for everyone to take another look at Deno!&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue 57
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;How Discord Reduced Websocket Traffic by 40%&lt;/strong&gt; (&lt;a href="https://discord.com/blog/how-discord-reduced-websocket-traffic-by-40-percent" rel="noopener noreferrer"&gt;https://discord.com/blog/how-discord-reduced-websocket-traffic-by-40-percent&lt;/a&gt;) by Austin Whyte&lt;br&gt;
A lot of micro optimizations and lazy loading (or "sending") for the win!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You Can't Build Interactive Web Apps Except as Single Page Applications... And Other Myths&lt;/strong&gt; (&lt;a href="https://htmx.org/essays/you-cant/" rel="noopener noreferrer"&gt;https://htmx.org/essays/you-cant/&lt;/a&gt;) by Tony Alaribe&lt;br&gt;
Never heard that myth - but it's fine for me to claim that fire has been rediscovered.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Component Composition is great btw&lt;/strong&gt; (&lt;a href="https://tkdodo.eu/blog/component-composition-is-great-btw" rel="noopener noreferrer"&gt;https://tkdodo.eu/blog/component-composition-is-great-btw&lt;/a&gt;) by Dominik Dorfmeister&lt;br&gt;
Truly. What's also great is to have the right company boundaries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compile and run C in JavaScript&lt;/strong&gt; (&lt;a href="https://bun.sh/blog/compile-and-run-c-in-js" rel="noopener noreferrer"&gt;https://bun.sh/blog/compile-and-run-c-in-js&lt;/a&gt;) by Jarred Sumner&lt;br&gt;
More like: Compile and run C in Bun. As usual, I see the devil being in the detail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Nine Node Pillars&lt;/strong&gt; (&lt;a href="https://www.platformatichq.com/node-principles" rel="noopener noreferrer"&gt;https://www.platformatichq.com/node-principles&lt;/a&gt;) by Matteo Collina et al.&lt;br&gt;
It's great to recap those pillars, even though I did not find anything really Node.js specific (most of the points could be assigned on to any web server).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Module Federation Vite the new v1 is here 🎉&lt;/strong&gt; (&lt;a href="https://www.learnbydo.ing/diary/2024-09-23-module-federation-vite-v1" rel="noopener noreferrer"&gt;https://www.learnbydo.ing/diary/2024-09-23-module-federation-vite-v1&lt;/a&gt;) by Giorgio Boa&lt;br&gt;
Happy to see that progress is made! Now we only miss all the other bundlers ...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Golden Ratio in CSS&lt;/strong&gt; (&lt;a href="https://dev.to/madsstoumann/the-golden-ratio-in-css-53d0"&gt;https://dev.to/madsstoumann/the-golden-ratio-in-css-53d0&lt;/a&gt;) by &lt;a class="mentioned-user" href="https://dev.to/madsstoumann"&gt;@madsstoumann&lt;/a&gt; &lt;br&gt;
Sweet - starts with some boxes and ends with a lot of rings. I love it!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sticky Transitioning Veggie Hamburger 🍔&lt;/strong&gt; (&lt;a href="https://dev.to/florianrappl/sticky-transitioning-veggie-hamburger-7dd"&gt;https://dev.to/florianrappl/sticky-transitioning-veggie-hamburger-7dd&lt;/a&gt;) by &lt;a class="mentioned-user" href="https://dev.to/florianrappl"&gt;@florianrappl&lt;/a&gt; &lt;br&gt;
Put your web app on a diet and reduce the amount of JS - especially when not needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Typed Linting Needs TypeScript Today&lt;/strong&gt; (&lt;a href="https://www.joshuakgoldberg.com/blog/why-typed-linting-needs-typescript-today/" rel="noopener noreferrer"&gt;https://www.joshuakgoldberg.com/blog/why-typed-linting-needs-typescript-today/&lt;/a&gt;) by Josh Goldberg&lt;br&gt;
Every once in a while somebody tries to reimplement TypeScript - so far everyone failed. Yes, TypeScript is required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Improving rendering performance with CSS content-visibility&lt;/strong&gt; (&lt;a href="https://nolanlawson.com/2024/09/18/improving-rendering-performance-with-css-content-visibility/" rel="noopener noreferrer"&gt;https://nolanlawson.com/2024/09/18/improving-rendering-performance-with-css-content-visibility/&lt;/a&gt;) by Nolan Lawson&lt;br&gt;
This is indeed magical. I see some difficulties here, but certainly no less than doing actual lazy loading / virtualization in JavaScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue 58
&lt;/h2&gt;

&lt;p&gt;Ever faced the problem of dead space through gaps using flexbox or grid? Then read about&lt;br&gt;
&lt;strong&gt;Sanding UI&lt;/strong&gt; (&lt;a href="https://blog.jim-nielsen.com/2024/sanding-ui/" rel="noopener noreferrer"&gt;https://blog.jim-nielsen.com/2024/sanding-ui/&lt;/a&gt;) by Jim Nielsen&lt;/p&gt;

&lt;p&gt;As you are all aware Ahmad has become my favorite author for CSS related articles. Recently he gave his website some polish. Read about the&lt;br&gt;
&lt;strong&gt;Redesign Case Study&lt;/strong&gt; (&lt;a href="https://ishadeed.com/article/redesign-2024/" rel="noopener noreferrer"&gt;https://ishadeed.com/article/redesign-2024/&lt;/a&gt;) by Ahmad Shadeed&lt;/p&gt;

&lt;p&gt;Now that we conquered client islands it's time for server islands to become standard. One way is discussed in&lt;br&gt;
&lt;strong&gt;Partial Prerendering for Everyone with Cloudflare Workers&lt;/strong&gt; (&lt;a href="https://sunilpai.dev/posts/ppr-for-everyone/" rel="noopener noreferrer"&gt;https://sunilpai.dev/posts/ppr-for-everyone/&lt;/a&gt;) by Sunil Pai&lt;/p&gt;

&lt;p&gt;It was quite a week for web components. We started with&lt;br&gt;
&lt;strong&gt;Web Components Are Not the Future&lt;/strong&gt; (&lt;a href="https://dev.to/ryansolid/web-components-are-not-the-future-48bh"&gt;https://dev.to/ryansolid/web-components-are-not-the-future-48bh&lt;/a&gt;) by &lt;a class="mentioned-user" href="https://dev.to/ryansolid"&gt;@ryansolid&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;But as usual on this topic there had to be different opinions. Entering&lt;br&gt;
&lt;strong&gt;Web Components Are Not the Future — They’re the Present&lt;/strong&gt; (&lt;a href="https://www.abeautifulsite.net/posts/web-components-are-not-the-future-they-re-the-present/" rel="noopener noreferrer"&gt;https://www.abeautifulsite.net/posts/web-components-are-not-the-future-they-re-the-present/&lt;/a&gt;) by Cory LaViska&lt;/p&gt;

&lt;p&gt;In the end both have their points and perspectives, but like the old XKCD comic it was the job of a third person to summarize in&lt;br&gt;
&lt;strong&gt;Web components are okay&lt;/strong&gt; (&lt;a href="https://nolanlawson.com/2024/09/28/web-components-are-okay/" rel="noopener noreferrer"&gt;https://nolanlawson.com/2024/09/28/web-components-are-okay/&lt;/a&gt;) by Nolan Lawson&lt;/p&gt;

&lt;p&gt;Years ago we tried to introduce scoping in CSS - but without much success. Now the working group is back with power... Let's try&lt;br&gt;
&lt;strong&gt;Understanding the Concept of Scoping in CSS&lt;/strong&gt; (&lt;a href="https://dev.to/bilkeesu96/understanding-the-concept-of-scoping-in-css-1m9g"&gt;https://dev.to/bilkeesu96/understanding-the-concept-of-scoping-in-css-1m9g&lt;/a&gt;) by &lt;a class="mentioned-user" href="https://dev.to/bilkeesu96"&gt;@bilkeesu96&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;A major downside of classic cron jobs is the failure cascade. This can be countered by some worker frameworks - for instance offering something dubbed durable functions.&lt;br&gt;
&lt;strong&gt;What are Durable Functions? A visual JavaScript primer&lt;/strong&gt; (&lt;a href="https://www.inngest.com/blog/durable-functions-a-visual-javascript-primer" rel="noopener noreferrer"&gt;https://www.inngest.com/blog/durable-functions-a-visual-javascript-primer&lt;/a&gt;) by Lydia Hallie&lt;/p&gt;

&lt;p&gt;Having tests is both - a pain and necessity. Especially in the frontend things tend to become difficult - relying on the browser and user behavior for many features. Let's recap&lt;br&gt;
&lt;strong&gt;Testing Frontend — Lessons from over a million lines of TypeScript at Palantir&lt;/strong&gt; (&lt;a href="https://www.meticulous.ai/blog/lessons-from-a-decade" rel="noopener noreferrer"&gt;https://www.meticulous.ai/blog/lessons-from-a-decade&lt;/a&gt;) by Quentin Spencer-Harper&lt;/p&gt;

&lt;p&gt;Not only the web component department delivered... If you missed the excitement delivered by the WordPress community then read&lt;br&gt;
&lt;strong&gt;The messy WordPress drama, explained&lt;/strong&gt; (&lt;a href="https://www.theverge.com/2024/9/27/24256361/wordpress-wp-engine-drama-explained-matt-mullenweg" rel="noopener noreferrer"&gt;https://www.theverge.com/2024/9/27/24256361/wordpress-wp-engine-drama-explained-matt-mullenweg&lt;/a&gt;) by Emma Roth&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue 59
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Building a Single-Page App with htmx&lt;/strong&gt; (&lt;a href="https://jakelazaroff.com/words/building-a-single-page-app-with-htmx/" rel="noopener noreferrer"&gt;https://jakelazaroff.com/words/building-a-single-page-app-with-htmx/&lt;/a&gt;) by Jake Lazaroff&lt;br&gt;
Your scientists were so preoccupied with whether or not they could, they didn't stop to think if they should.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keeping an eye on changes to MDN’s Browser Compatibility Data&lt;/strong&gt; (&lt;a href="https://bcd-watch.igalia.com/" rel="noopener noreferrer"&gt;https://bcd-watch.igalia.com/&lt;/a&gt;) by igalia&lt;br&gt;
This is quite neat - not only knowing what changed, but also when it changed.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;OpenFreeMap *&lt;/em&gt;(&lt;a href="https://openfreemap.org/" rel="noopener noreferrer"&gt;https://openfreemap.org/&lt;/a&gt;) by Zsolt Ero&lt;br&gt;
Eagerly waiting to use this with Leaflet - but the Mapbox demos look great, too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bridging the hard and the soft&lt;/strong&gt; (&lt;a href="https://wattenberger.com/thoughts/hard-and-soft" rel="noopener noreferrer"&gt;https://wattenberger.com/thoughts/hard-and-soft&lt;/a&gt;) by Amelia Wattenberger&lt;br&gt;
Superb read - it's all about the interface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ESLint now officially supports linting of JSON and Markdown&lt;/strong&gt; (&lt;a href="https://eslint.org/blog/2024/10/eslint-json-markdown-support/" rel="noopener noreferrer"&gt;https://eslint.org/blog/2024/10/eslint-json-markdown-support/&lt;/a&gt;) by Nicholas Zakas&lt;br&gt;
I am not getting enough warnings already.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tauri 2.0 Stable Release&lt;/strong&gt; (&lt;a href="https://v2.tauri.app/blog/tauri-20/" rel="noopener noreferrer"&gt;https://v2.tauri.app/blog/tauri-20/&lt;/a&gt;) by Tillmann Weidinger&lt;br&gt;
It's now just a much better DX.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Running Clang in the browser using WebAssembly&lt;/strong&gt; (&lt;a href="https://wasmer.io/posts/clang-in-browser" rel="noopener noreferrer"&gt;https://wasmer.io/posts/clang-in-browser&lt;/a&gt;) by Syrus Akbary&lt;br&gt;
I need to repeat: Your scientists were so preoccupied with whether or not they could, they didn't stop to think if they should.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unleash JavaScript's Potential with Functional Programming&lt;/strong&gt; (&lt;a href="https://janhesters.com/blog/unleash-javascripts-potential-with-functional-programming" rel="noopener noreferrer"&gt;https://janhesters.com/blog/unleash-javascripts-potential-with-functional-programming&lt;/a&gt;) by Jan Hesters&lt;br&gt;
Personal opinion: Don't get too much into FP, but just take the best pieces and use them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Local-First Case Study&lt;/strong&gt; (&lt;a href="https://jakelazaroff.com/words/a-local-first-case-study/" rel="noopener noreferrer"&gt;https://jakelazaroff.com/words/a-local-first-case-study/&lt;/a&gt;) by Jake Lazaroff&lt;br&gt;
Local-first is underappreciated but should be a cornerstone for good DX.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Gumroad Didn't Choose htmx&lt;/strong&gt; (&lt;a href="https://htmx.org/essays/why-gumroad-didnt-choose-htmx/" rel="noopener noreferrer"&gt;https://htmx.org/essays/why-gumroad-didnt-choose-htmx/&lt;/a&gt;) by Sahil Lavingia&lt;br&gt;
It's fine. No one needs to choose everything. Everything has trade-offs and I believe in choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue 60
&lt;/h2&gt;

&lt;p&gt;If you look for icons to be used in your next project - then maybe start directly at&lt;br&gt;
&lt;strong&gt;15+ Best Icon Libraries of 2024&lt;/strong&gt; (&lt;a href="https://dev.to/vinishbhaskar/best-icon-libraries-28ce"&gt;https://dev.to/vinishbhaskar/best-icon-libraries-28ce&lt;/a&gt;) by &lt;a class="mentioned-user" href="https://dev.to/vinishbhaskar"&gt;@vinishbhaskar&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Ever wondered how to get or set up a great CRM for free? if so, check out&lt;br&gt;
&lt;strong&gt;Atomic CRM&lt;/strong&gt; (&lt;a href="https://marmelab.com/atomic-crm/" rel="noopener noreferrer"&gt;https://marmelab.com/atomic-crm/&lt;/a&gt;) by Marmelab&lt;/p&gt;

&lt;p&gt;In case you missed the preview you can now enjoy the full next gen Node.js competitor;&lt;br&gt;
&lt;strong&gt;Announcing Deno 2&lt;/strong&gt; (&lt;a href="https://deno.com/blog/v2.0" rel="noopener noreferrer"&gt;https://deno.com/blog/v2.0&lt;/a&gt;) by Ryan Dahl&lt;/p&gt;

&lt;p&gt;Are you sometimes hesitant to just install npm packages? Paranoid, maybe not - here are some&lt;br&gt;
&lt;strong&gt;Nightmares on npm: How Two Malicious Packages Facilitate Data Theft and Destruction&lt;/strong&gt; (&lt;a href="https://socket.dev/blog/nightmares-on-npm-how-two-malicious-packages-facilitate-data-theft-and-destruction" rel="noopener noreferrer"&gt;https://socket.dev/blog/nightmares-on-npm-how-two-malicious-packages-facilitate-data-theft-and-destruction&lt;/a&gt;) by Kush Pandya&lt;/p&gt;

&lt;p&gt;I think there is a great new framework in town (no week without it!) - server-first with JSX to web components for islands of interactivity. Entering&lt;br&gt;
&lt;strong&gt;The Web Platform Framework&lt;/strong&gt; (&lt;a href="https://brisa.build" rel="noopener noreferrer"&gt;https://brisa.build&lt;/a&gt;) by Aral Roca Gomez&lt;/p&gt;

&lt;p&gt;Ever thought that logs can provide much more insights, but are too clumsy to work with? Maybe strict sequential logs are not the answer. One approach can be found by reading an&lt;br&gt;
&lt;strong&gt;Introduction to Causal Logs&lt;/strong&gt; (&lt;a href="https://joelgustafson.com/posts/2024-09-30/introduction-to-causal-logs" rel="noopener noreferrer"&gt;https://joelgustafson.com/posts/2024-09-30/introduction-to-causal-logs&lt;/a&gt;) by Joel Gustafson&lt;/p&gt;

&lt;p&gt;Besides having a new framework each week there is also another attempt on explaining and visualizing how React renders (or reconciliates) your view - so let's dive into&lt;br&gt;
&lt;strong&gt;The Interactive Guide to Rendering in React&lt;/strong&gt; (&lt;a href="https://ui.dev/why-react-renders" rel="noopener noreferrer"&gt;https://ui.dev/why-react-renders&lt;/a&gt;) by UiDev&lt;/p&gt;

&lt;p&gt;Do you believe that JavaScript on the server is essentially PHP? I did not - but if you did then read&lt;br&gt;
&lt;strong&gt;React on the server is not PHP&lt;/strong&gt; (&lt;a href="https://www.artmann.co/articles/react-on-the-server-is-not-php" rel="noopener noreferrer"&gt;https://www.artmann.co/articles/react-on-the-server-is-not-php&lt;/a&gt;) by Christoffer Artmann&lt;/p&gt;

&lt;p&gt;Do you like mysteries? Then try solving the riddle of&lt;br&gt;
&lt;strong&gt;Node.js, pipes, and disappearing bytes&lt;/strong&gt; (&lt;a href="https://sxlijin.github.io/2024-10-09-node-stdout-disappearing-bytes" rel="noopener noreferrer"&gt;https://sxlijin.github.io/2024-10-09-node-stdout-disappearing-bytes&lt;/a&gt;) by Sam Lijin&lt;/p&gt;

&lt;p&gt;I have a feeling that the server-side story for React gets more fragmented and unnecessarily complex each day - even without talking about&lt;br&gt;
&lt;strong&gt;Serverless servers and the challenge of new React architecture&lt;/strong&gt; (&lt;a href="https://bobaekang.com/blog/serverless-servers-and-the-challenge-of-new-react-architecture/" rel="noopener noreferrer"&gt;https://bobaekang.com/blog/serverless-servers-and-the-challenge-of-new-react-architecture/&lt;/a&gt;) by Bobae Kang&lt;/p&gt;

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

&lt;p&gt;These are all outstanding articles by masterful authors. I enjoyed reading them all - I hope you did find something in there, too.&lt;/p&gt;

&lt;p&gt;👉 Follow me on &lt;a href="https://www.linkedin.com/in/florian-rappl/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, &lt;a href="https://twitter.com/FlorianRappl" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, or here for more to come.&lt;/p&gt;

&lt;p&gt;🙏 Thanks to all the authors and contributors for their hard work!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>node</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Sticky Transitioning Veggie Hamburger 🍔</title>
      <dc:creator>Florian Rappl</dc:creator>
      <pubDate>Wed, 25 Sep 2024 08:40:42 +0000</pubDate>
      <link>https://dev.to/florianrappl/sticky-transitioning-veggie-hamburger-7dd</link>
      <guid>https://dev.to/florianrappl/sticky-transitioning-veggie-hamburger-7dd</guid>
      <description>&lt;p&gt;In our efforts to make the page &lt;em&gt;faster&lt;/em&gt; and &lt;strong&gt;even faster&lt;/strong&gt; we also explored different ways of just omitting user interactivity altogether. One place we looked at intensively was the header (or general layout). The header contained a bit of interactivity in form of a React component.&lt;/p&gt;

&lt;p&gt;The job of the header component is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;on mobile show a persistent header bar with a hamburger instead of the normal entries&lt;/li&gt;
&lt;li&gt;on desktop show all the menu entries, but switch the appearance once scrolling occurs (full height to less height with a shadow below)&lt;/li&gt;
&lt;li&gt;the menu on mobile should not have any scrolling (i.e., remove scrollbar if any)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In practice this looks like the following on desktop:&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%2Fc2qhgrso198o5epq8vxk.gif" 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%2Fc2qhgrso198o5epq8vxk.gif" alt="Desktop header"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And for mobile the appearance changes to this:&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%2F5q2z9593csqbuin46tsk.gif" 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%2F5q2z9593csqbuin46tsk.gif" alt="Mobile header"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's see how we went from the original to a non-JS version that behaves the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  Original Meat Burger
&lt;/h2&gt;

&lt;p&gt;Alright, we start with the original version of our header (the "hamburger"). To stay rather generic for this article we prepared a small sample that evolves with every step.&lt;/p&gt;

&lt;p&gt;We start with the original:&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://stackblitz.com/edit/vitejs-vite-tpfcpd?embed=1&amp;amp;file=src%2FPageHeader.jsx&amp;amp;view=editor" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Most of the code is in the CSS-in-JS. You don't need to use the CSS-in-JS here (we actually use SASS), but it easily brings the two parts (CSS and React) that are needed for the component together.&lt;/p&gt;

&lt;p&gt;The critical part is that within the component we need to use some hooks (i.e., interactivity) to make it work:&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PageHeader&lt;/span&gt; &lt;span class="o"&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="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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setOpen&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&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;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useStickyHeader&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nf"&gt;useLockBodyScroll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&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 goal is thus to reduce it. Let's start with the easy thing first: How to get the hamburger menu working without JS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Less Meat - Same Taste
&lt;/h2&gt;

&lt;p&gt;How can we toggle two states using CSS and no JS? Well, first we need to understand that CSS inherently allows us to distinguish between states. For instance, CSS allows to select based on&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;empty&lt;/li&gt;
&lt;li&gt;hovered&lt;/li&gt;
&lt;li&gt;focused&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and more states directly. Among those are also special states of some form fields, e.g., if a checkbox is checked.&lt;/p&gt;

&lt;p&gt;So instead of toggling based on a &lt;code&gt;data-&lt;/code&gt; attribute, i.e., the following code in CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;header&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-open&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"true"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&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;we can toggle this based on another selector:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;header&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;#header-hamburger-open&lt;/span&gt;&lt;span class="nd"&gt;:checked&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&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 assumes that a new element with the ID &lt;code&gt;header-hamburger-open&lt;/code&gt; has been introduced. This is a checkbox that is hidden to the user. The effect of hiding the checkbox can be done quite easily using CSS, too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nf"&gt;#header-hamburger-open&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;collapse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&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;Personally, I like to use &lt;code&gt;visibility: collapse&lt;/code&gt; as it transports the meaning the best. Unfortunately, for most elements Webkit-based/derived browsers will behave for &lt;code&gt;collapse&lt;/code&gt; like for &lt;code&gt;hidden&lt;/code&gt; - i.e., reserving the original space but not rendering the element.&lt;/p&gt;

&lt;p&gt;Only Firefox behaves in a way that aligns with the original meaning (but contrary to the spec - which is actually weird in this scenario). That's why we need the additional &lt;code&gt;height: 0&lt;/code&gt; to also prevent allocating same space on Webkit-based browsers such as Chrome.&lt;/p&gt;

&lt;p&gt;With this in mind we replace the original &lt;code&gt;button&lt;/code&gt; inside the &lt;code&gt;navbar-toggler&lt;/code&gt; with the following code:&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;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"header-hamburger-open"&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;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"navbar-toggler"&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;label&lt;/span&gt;
    &lt;span class="na"&gt;htmlFor&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"header-hamburger-open"&lt;/span&gt;
    &lt;span class="na"&gt;data-toggle&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"collapse"&lt;/span&gt;
    &lt;span class="na"&gt;aria-controls&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"navbar-menu-content"&lt;/span&gt;
    &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Toggle navigation"&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;label&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;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;When clicking on the &lt;code&gt;label&lt;/code&gt; the &lt;code&gt;htmlFor&lt;/code&gt; attribute is used by the browser. This will lead to toggling the checkbox, which - as per our CSS structure - will toggle the menu.&lt;/p&gt;

&lt;p&gt;You can play around with this in the updated sample.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://stackblitz.com/edit/vitejs-vite-zdvtvx?embed=1&amp;amp;file=src%2FPageHeader.jsx&amp;amp;view=editor" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Now that the easy part is done - let's look at the final step for making the truly JS-free: Having the sticky positioning fulfilled.&lt;/p&gt;

&lt;h2&gt;
  
  
  Veggie Version - Original Taste
&lt;/h2&gt;

&lt;p&gt;The main complication when moving the sticky header from a JS transition to a CSS one is that CSS has no idea about scrolling.&lt;/p&gt;

&lt;p&gt;Let's see the original hook / code to trigger the transition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useStickyHeader&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;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&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;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useLayoutEffect&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&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;sticky&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offsetTop&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onscroll&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="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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageYOffset&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sticky&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sticky&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;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sticky&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="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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onscroll&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&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;header&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;So by applying the CSS class &lt;code&gt;sticky&lt;/code&gt; we transform the original header to the sticky one. Without JavaScript - this won't be possible.&lt;/p&gt;

&lt;p&gt;Maybe - so the idea - it is possible to have something set from the beginning. So the idea is to have the right layout and not rely on a scrolling-based transition.&lt;/p&gt;

&lt;p&gt;The starting point should be the CSS declaration &lt;code&gt;position: sticky&lt;/code&gt;. Using &lt;code&gt;position: sticky&lt;/code&gt; we can tell the browser to make the menu sticky (in relation to its scrolling container) without relying on a transition.&lt;/p&gt;

&lt;p&gt;The problem with &lt;code&gt;position: sticky&lt;/code&gt; is that we can only tell the browser what "position" we want to settle in. For instance, we can tell the browser &lt;code&gt;top: -10px&lt;/code&gt; to keep moving the header bar until it's 10 pixels inside the containers hidden area, i.e., shifted to the top by 10px. There is no way to add another shadow or other things we might be interested in...&lt;/p&gt;

&lt;p&gt;This is the point where we could call it a day, marking the task as "impossible" and move on.&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%2Fu063eb829fa3zkyaeuzz.jpg" 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%2Fu063eb829fa3zkyaeuzz.jpg" alt="CSS one does not simply meme"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Luckily, there are still some tricks we can apply in this case. Let's look at what we want:&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%2Fiysgn273ll6ey99u3qpp.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%2Fiysgn273ll6ey99u3qpp.png" alt="Header transition"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In other words, we want to "reveal" part of the shadow when we move up. The moving up part we get covered by using &lt;code&gt;position: sticky&lt;/code&gt; as outlined - now the shadow part could be tricked in by really introducing such layers.&lt;/p&gt;

&lt;p&gt;What we mean specifically is that we could introduce two more "virtual" elements (using &lt;code&gt;::before&lt;/code&gt; and &lt;code&gt;::after&lt;/code&gt; pseudo selectors):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one element moving together with the sticky header (i.e., absolutely positioned) - initially "hiding" the shadow&lt;/li&gt;
&lt;li&gt;one element as a shadow that is being fixed; thus not moving with the sticky header (initially being hidden behind the absolutely positioned element)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that both need to be in the foreground. This means we have a layering such as:&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%2Fb9d502ownlcsj13xhmv3.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%2Fb9d502ownlcsj13xhmv3.png" alt="Layering of elements"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The real header (blue) is now shorter, but extended with the overlay element (green), which is initially hiding the shadow (purple). Once the page (red) scrolls, the header scrolls up to a certain point. Together with it the overlay is supposed to scroll, thus revealing the shadow:&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%2Fclask9uidhs2colo8kx0.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%2Fclask9uidhs2colo8kx0.png" alt="Scrolling of elements"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In theory that should work - but in reality it will all be determined by the specific values we pick for the positioning of these elements.&lt;/p&gt;

&lt;p&gt;In CSS the following is needed to introduce the two virtual elements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="p"&gt;*::&lt;/span&gt;&lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;fixed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;%;&lt;/span&gt;
  &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;45&lt;/span&gt;&lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;ccc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="n"&gt;px&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="n"&gt;px&lt;/span&gt; &lt;span class="nf"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;57&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;63&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;72&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;9999&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="p"&gt;*::&lt;/span&gt;&lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;%;&lt;/span&gt;
  &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;55&lt;/span&gt;&lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;ccc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;9999&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 header itself is positioned in a way that makes it be in front of these layers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;header&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sticky&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-5px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10000&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 creates the effect we are looking for - and without requiring any JavaScript whatsoever.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://stackblitz.com/edit/vitejs-vite-ukluve?embed=1&amp;amp;file=src%2FPageHeader.jsx&amp;amp;view=editor" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

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

&lt;p&gt;Going to a full non-JS version of our header made the lighthouse score even better - being a full 100 on most / all pages. Now, whenever JS is needed it would be just there to enhance a certain view or give more details. There is no more JavaScript to actually enable navigation or other crucial means.&lt;/p&gt;

&lt;p&gt;As far as the sticky header is concerned, the shown trick only works in scenarios like ours. If you need more transformations then finding a good workaround might be more difficult. In the future that may change, but for now &lt;code&gt;position: sticky&lt;/code&gt; is quite limited and requires such tricks to also have scenarios such as ours covered.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>html</category>
      <category>css</category>
    </item>
    <item>
      <title>🧠 50 Articles to Master Web Dev</title>
      <dc:creator>Florian Rappl</dc:creator>
      <pubDate>Fri, 20 Sep 2024 16:25:02 +0000</pubDate>
      <link>https://dev.to/florianrappl/50-articles-to-master-web-dev-4jhl</link>
      <guid>https://dev.to/florianrappl/50-articles-to-master-web-dev-4jhl</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;a href="https://www.freepik.com/free-vector/gradient-step-illustration_37443122.htm#fromView=search&amp;amp;page=1&amp;amp;position=9&amp;amp;uuid=034a1fce-8fa5-40fe-9832-045f25551c3a" rel="noopener noreferrer"&gt;Image by freepik&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This year I started a new series on &lt;a href="https://www.linkedin.com/in/florian-rappl/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; - "Advanced Links for Frontend". Each issue has 10 links to outstanding posts / articles. This bundle contains the links from the last 5 issues (issue 51 to issue 55).&lt;/p&gt;

&lt;p&gt;I hope you enjoy this collection. Let me know in the comments which of these articles is your favorite (and why).&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue 51
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Fixing a Bug in Google Chrome as a First-Time Contributor&lt;/strong&gt; (&lt;a href="https://cprimozic.net/blog/fixing-a-bug-in-google-chrome/" rel="noopener noreferrer"&gt;https://cprimozic.net/blog/fixing-a-bug-in-google-chrome/&lt;/a&gt;) by Casey Primozic&lt;br&gt;
The last time I had a look at the Chromium codebase was years ago - and it is massive. So much respect to the author for getting in there!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Only Widely Recognized JavaScript Feature Ever Deprecated&lt;/strong&gt; (&lt;a href="https://www.trevorlasn.com/blog/the-only-javascript-feature-that-was-deprecated/" rel="noopener noreferrer"&gt;https://www.trevorlasn.com/blog/the-only-javascript-feature-that-was-deprecated/&lt;/a&gt;) by Trevor Indrek Lasn&lt;br&gt;
I am happy to say that I don't miss "with". I actually only used it once; and even though it made sense for in that case, there would have been other options.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;React Email 3.0&lt;/strong&gt; (&lt;a href="https://resend.com/blog/react-email-3" rel="noopener noreferrer"&gt;https://resend.com/blog/react-email-3&lt;/a&gt;) by Gabriel Miranda&lt;br&gt;
Fantastic changes, superb release!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementing React From Scratch&lt;/strong&gt; (&lt;a href="https://www.rob.directory/blog/react-from-scratch" rel="noopener noreferrer"&gt;https://www.rob.directory/blog/react-from-scratch&lt;/a&gt;) by Robby Pruzan&lt;br&gt;
Been there, done that. As usual, coming up with something that works is not hard - coming up with something that works as good as the original is difficult.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Developer tools are different than tools for any other profession&lt;/strong&gt; (&lt;a href="https://mxstbr.com/notes/developer-tools" rel="noopener noreferrer"&gt;https://mxstbr.com/notes/developer-tools&lt;/a&gt;) by Max Stoiber&lt;br&gt;
If Max says so!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scroll Snap Events&lt;/strong&gt; (&lt;a href="https://developer.chrome.com/blog/scroll-snap-events" rel="noopener noreferrer"&gt;https://developer.chrome.com/blog/scroll-snap-events&lt;/a&gt;) by Adam Argyle&lt;br&gt;
Funny - just a couple of weeks after I'd needed that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;State of JavaScript: Insights from the latest JavaScript developer survey&lt;/strong&gt; (&lt;a href="https://www.infoworld.com/article/3486850/state-of-javascript-insights-from-the-latest-javascript-community-survey.html" rel="noopener noreferrer"&gt;https://www.infoworld.com/article/3486850/state-of-javascript-insights-from-the-latest-javascript-community-survey.html&lt;/a&gt;) by Matthew Tyson&lt;br&gt;
Surprised that the Bun bundler gets a positive rating. That it does not even print some elemental error messages is a red flag for me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;50 TypeScript F!ck Ups Book&lt;/strong&gt; (&lt;a href="https://github.com/azat-co/50-ts/" rel="noopener noreferrer"&gt;https://github.com/azat-co/50-ts/&lt;/a&gt;) by Azat Mardan&lt;br&gt;
I like the collection. Definitely worth supporting the author!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common misconceptions about how to optimize LCP&lt;/strong&gt; (&lt;a href="https://web.dev/blog/common-misconceptions-lcp" rel="noopener noreferrer"&gt;https://web.dev/blog/common-misconceptions-lcp&lt;/a&gt;) by Brendan Kenny&lt;br&gt;
The images part, unfortunately, is underrated. Having just optimized a web app last week the images optimization part took the project fom a mid 70s rating to a 99 rating in lighthouse. In general I agree though.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Elastic Overflow Scrolling&lt;/strong&gt; (&lt;a href="https://css-tricks.com/elastic-overflow-scrolling/" rel="noopener noreferrer"&gt;https://css-tricks.com/elastic-overflow-scrolling/&lt;/a&gt;) by Dave Seidman&lt;br&gt;
While I don't see it being essential for the Y axis, I think this technique is useful for carousels / on the X axis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue 52
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Compilation of JavaScript to Wasm, Part 3: Partial Evaluation&lt;/strong&gt; (&lt;a href="https://cfallin.org/blog/2024/08/28/weval/" rel="noopener noreferrer"&gt;https://cfallin.org/blog/2024/08/28/weval/&lt;/a&gt;) by Chris Fallin&lt;br&gt;
The topic sounds more exotic than it is - personally, I think this will be quite standard in a couple of years.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Material UI v6 is out now 🎉&lt;/strong&gt; (&lt;a href="https://mui.com/blog/material-ui-v6-is-out/" rel="noopener noreferrer"&gt;https://mui.com/blog/material-ui-v6-is-out/&lt;/a&gt;) by Material UI&lt;br&gt;
Great to see that MUI is still progressing!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Announcing Rspack 1.0&lt;/strong&gt; (&lt;a href="https://rspack.dev/blog/announcing-1-0" rel="noopener noreferrer"&gt;https://rspack.dev/blog/announcing-1-0&lt;/a&gt;) by rspack&lt;br&gt;
Wonderful milestone - great project! More than 80% of the top 50 plugins for Webpack are supported - so chances are high that you can migrate quite smoothly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frontend Resources V2! 🚀&lt;/strong&gt; (&lt;a href="https://dev.to/miguelrodriguezp99/frontend-resources-v2-57mj"&gt;https://dev.to/miguelrodriguezp99/frontend-resources-v2-57mj&lt;/a&gt;) by &lt;a class="mentioned-user" href="https://dev.to/miguelrodriguezp99"&gt;@miguelrodriguezp99&lt;/a&gt; &lt;br&gt;
Collection of useful tools and libraries to simplify your development life.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;New to the web platform in August&lt;/strong&gt; (&lt;a href="https://web.dev/blog/web-platform-08-2024" rel="noopener noreferrer"&gt;https://web.dev/blog/web-platform-08-2024&lt;/a&gt;) by Rachel Andrew&lt;br&gt;
In the west the ruby element is quite unknown, but even here it can be super useful.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When 3rd party JavaScript attacks&lt;/strong&gt; (&lt;a href="https://changelog.com/jsparty/336" rel="noopener noreferrer"&gt;https://changelog.com/jsparty/336&lt;/a&gt;) by Simon Wijckmans&lt;br&gt;
When has a third-party JavaScript never attacked?!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript Generators Explained, But On A Senior-Level&lt;/strong&gt; (&lt;a href="https://www.reactsquad.io/blog/understanding-generators-in-javascript" rel="noopener noreferrer"&gt;https://www.reactsquad.io/blog/understanding-generators-in-javascript&lt;/a&gt;) by Jan Hesters&lt;br&gt;
They are certainly useful - especially in the pull-stream scenario.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Component testing in Storybook&lt;/strong&gt; (&lt;a href="https://storybook.js.org/blog/component-testing/" rel="noopener noreferrer"&gt;https://storybook.js.org/blog/component-testing/&lt;/a&gt;) by Michael Shilman&lt;br&gt;
Yeah sure - I am not surprised to see this in the Storybook blog.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Faster Pages with React ✨&lt;/strong&gt; (&lt;a href="https://dev.to/smapiot/faster-pages-with-react-h8j"&gt;https://dev.to/smapiot/faster-pages-with-react-h8j&lt;/a&gt;) by &lt;a class="mentioned-user" href="https://dev.to/florianrappl"&gt;@florianrappl&lt;/a&gt; &lt;br&gt;
If you really need more performance - you can always get more performant.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The secret inside One Million Checkboxes&lt;/strong&gt; (&lt;a href="https://eieio.games/essays/the-secret-in-one-million-checkboxes/" rel="noopener noreferrer"&gt;https://eieio.games/essays/the-secret-in-one-million-checkboxes/&lt;/a&gt;) by Nolen Royalty&lt;br&gt;
That was actually a superb read. What a journey! Seems like building this little site has given Nolen stories for the next decade.&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue 53
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CSS-only infinite scrolling carousel animation&lt;/strong&gt; (&lt;a href="https://blog.logto.io/css-only-infinite-scroll" rel="noopener noreferrer"&gt;https://blog.logto.io/css-only-infinite-scroll&lt;/a&gt;) by Gao Sun&lt;br&gt;
Since I have played around with this myself two weeks ago I thought the article was great.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;12 Keys to Write Senior-Level Tests&lt;/strong&gt; (&lt;a href="https://www.reactsquad.io/blog/testing-desiderata-in-javascript" rel="noopener noreferrer"&gt;https://www.reactsquad.io/blog/testing-desiderata-in-javascript&lt;/a&gt;) by Jan Hesters&lt;br&gt;
I think this one is the list to follow after the 10 commandments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MDX with Translations 🐠&lt;/strong&gt; (&lt;a href="https://dev.to/smapiot/mdx-with-translations-157c"&gt;https://dev.to/smapiot/mdx-with-translations-157c&lt;/a&gt;) by &lt;a class="mentioned-user" href="https://dev.to/florianrappl"&gt;@florianrappl&lt;/a&gt; &lt;br&gt;
MDX might be the secret sauce that actually provides the bridge between "just content" and "just code".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inside ECMAScript: JavaScript Standard Gets an Extra Stage&lt;/strong&gt; (&lt;a href="https://thenewstack.io/inside-ecmascript-javascript-standard-gets-an-extra-stage/" rel="noopener noreferrer"&gt;https://thenewstack.io/inside-ecmascript-javascript-standard-gets-an-extra-stage/&lt;/a&gt;) by Mary Branscombe&lt;br&gt;
What else is there to do? Do we really want or need a Frankenstein's monster of a language?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What’s new in React 19&lt;/strong&gt; (&lt;a href="https://vercel.com/blog/whats-new-in-react-19" rel="noopener noreferrer"&gt;https://vercel.com/blog/whats-new-in-react-19&lt;/a&gt;) by Michael Novotny&lt;br&gt;
I'm wondering that myself. Last time I checked it's rather "what will be new in React 19".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Preloading files to reduce download chains in the browser&lt;/strong&gt; (&lt;a href="https://www.lkhrs.com/blog/2024/preloading/" rel="noopener noreferrer"&gt;https://www.lkhrs.com/blog/2024/preloading/&lt;/a&gt;) by Luke Harris&lt;br&gt;
Or just batch them in the backend / at compile-time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The future is standalone!&lt;/strong&gt; (&lt;a href="https://blog.angular.dev/the-future-is-standalone-475d7edbc706" rel="noopener noreferrer"&gt;https://blog.angular.dev/the-future-is-standalone-475d7edbc706&lt;/a&gt;) by Alex Rickabaugh&lt;br&gt;
I think the future is standalone since more than a decade, but I am happy that finally Angular recognizes this, too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Introducing &lt;a class="mentioned-user" href="https://dev.to/bramus"&gt;@bramus&lt;/a&gt;/style-observer, a MutationObserver for CSS&lt;/strong&gt; (&lt;a href="https://www.bram.us/2024/08/31/introducing-bramus-style-observer-a-mutationobserver-for-css/" rel="noopener noreferrer"&gt;https://www.bram.us/2024/08/31/introducing-bramus-style-observer-a-mutationobserver-for-css/&lt;/a&gt;) by Bramus Van Damme&lt;br&gt;
While I applaud the effort I will definitely avoid scenarios that force me into using it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;i-html&lt;/strong&gt; (&lt;a href="https://www.keithcirkel.co.uk/i-html/" rel="noopener noreferrer"&gt;https://www.keithcirkel.co.uk/i-html/&lt;/a&gt;) by Keith Cirkel&lt;br&gt;
This is great - but pi-component can already do that (and more). Still happy that the need for fragment inclusion is becoming more recognized.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build-time Components&lt;/strong&gt; (&lt;a href="https://codehike.org/blog/build-time-components" rel="noopener noreferrer"&gt;https://codehike.org/blog/build-time-components&lt;/a&gt;) by Rodrigo Pombo&lt;br&gt;
Alright - what did I say about MDX?! But great write-up and it also goes into the different composition modes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue 54
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Look out, kids: PHP is the new JavaScript&lt;/strong&gt; (&lt;a href="https://www.mux.com/blog/php-is-the-new-javascript" rel="noopener noreferrer"&gt;https://www.mux.com/blog/php-is-the-new-javascript&lt;/a&gt;) by Dave Kiss&lt;br&gt;
No, it's not. But generally it's nice to see PHP (and its ecosystem) evolving.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An SSR Performance Showdown&lt;/strong&gt; (&lt;a href="https://blog.platformatic.dev/ssr-performance-showdown" rel="noopener noreferrer"&gt;https://blog.platformatic.dev/ssr-performance-showdown&lt;/a&gt;) by Matteo Collina&lt;br&gt;
Long story short: If you need something simple, choose something that does less work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Managing Angular&lt;/strong&gt; (&lt;a href="https://blog.mgechev.com/2024/08/25/managing-angular/" rel="noopener noreferrer"&gt;https://blog.mgechev.com/2024/08/25/managing-angular/&lt;/a&gt;) by Minko Gechev&lt;br&gt;
Certainly a great read. The only thing I missed is the objective - what's the goal? Seems to me that Angular is still playing catchup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Oops, I accidentally made our website faster by switching to Remix&lt;/strong&gt; (&lt;a href="https://echobind.com/post/oops-i-accidentally-made-our-website-faster-by-switching-to-remix" rel="noopener noreferrer"&gt;https://echobind.com/post/oops-i-accidentally-made-our-website-faster-by-switching-to-remix&lt;/a&gt;) by Alex Anderson&lt;br&gt;
Maybe drop frameworks all together to make it even faster!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Building the Same App Using Various Web Frameworks&lt;/strong&gt; (&lt;a href="https://eugeneyan.com/writing/web-frameworks/" rel="noopener noreferrer"&gt;https://eugeneyan.com/writing/web-frameworks/&lt;/a&gt;) by Eugene Yan&lt;br&gt;
Ah another comparison. Seems like this is the main trend for the current week. Spoiler: Performance is not considered in this one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;New Environment API&lt;/strong&gt; (&lt;a href="https://main.vitejs.dev/guide/api-environment" rel="noopener noreferrer"&gt;https://main.vitejs.dev/guide/api-environment&lt;/a&gt;) by Vite&lt;br&gt;
With the next iteration of Vite we'll get some exciting new APIs!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;11ty is joining Font Awesome&lt;/strong&gt; (&lt;a href="https://www.11ty.dev/blog/eleventy-font-awesome/" rel="noopener noreferrer"&gt;https://www.11ty.dev/blog/eleventy-font-awesome/&lt;/a&gt;) by Zach Leatherman&lt;br&gt;
With this partnership the foundation of 11ty is solidified even more. Great work!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Svelte 5 signals fix its glitchy and inconsistent reactivity&lt;/strong&gt; (&lt;a href="https://www.webdevladder.net/blog/svelte-5-signals-fix-its-glitchy-and-inconsistent-reactivity" rel="noopener noreferrer"&gt;https://www.webdevladder.net/blog/svelte-5-signals-fix-its-glitchy-and-inconsistent-reactivity&lt;/a&gt;) by Ryan Atkinson&lt;br&gt;
Another reason to switch to Svelte 5 as soon as possible - runes are really wonderful!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rethinking CSS in JS&lt;/strong&gt; (&lt;a href="https://dev.to/black7375/rethinking-css-in-js-5dip"&gt;https://dev.to/black7375/rethinking-css-in-js-5dip&lt;/a&gt;) by &lt;a class="mentioned-user" href="https://dev.to/black7375"&gt;@black7375&lt;/a&gt; &lt;br&gt;
One of the longer articles at dev.to, but most certainly worth the read.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tailwind CSS vs Pico CSS&lt;/strong&gt; (&lt;a href="https://edofic.com/posts/2022-01-18-tailwind-vs-pico/" rel="noopener noreferrer"&gt;https://edofic.com/posts/2022-01-18-tailwind-vs-pico/&lt;/a&gt;) by Andraž Bajt&lt;br&gt;
I am not sure if this is a fair comparison at all, but we are back to "if you need less features, use something with less features".&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue 55
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Understanding Concurrency, Parallelism and JS&lt;/strong&gt; (&lt;a href="https://www.rugu.dev/en/blog/concurrency-and-parallelism/" rel="noopener noreferrer"&gt;https://www.rugu.dev/en/blog/concurrency-and-parallelism/&lt;/a&gt;) by Uğur Erdem Seyfi&lt;br&gt;
In the last 15 years (and surely beyond that) this has been discussed every year - but I feel the topic has never been more relevant than today.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MDX in Next.js&lt;/strong&gt; (&lt;a href="https://spacejelly.dev/posts/mdx-in-nextjs" rel="noopener noreferrer"&gt;https://spacejelly.dev/posts/mdx-in-nextjs&lt;/a&gt;) by Colby Fayock&lt;br&gt;
MDX is indeed a great format for content - it's very close to the sweet spot between just static markup and rich interactivity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cleaner JavaScript promises with safe-await&lt;/strong&gt; (&lt;a href="https://alexjpate.com/posts/cleaner-promises-with-safe-await" rel="noopener noreferrer"&gt;https://alexjpate.com/posts/cleaner-promises-with-safe-await&lt;/a&gt;) by Alex Pate&lt;br&gt;
I am not sure it that's indeed cleaner (for everyone's taste), but I like it. Note that something similar is debated in TC39.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Announcing TypeScript 5.6&lt;/strong&gt; (&lt;a href="https://devblogs.microsoft.com/typescript/announcing-typescript-5-6/" rel="noopener noreferrer"&gt;https://devblogs.microsoft.com/typescript/announcing-typescript-5-6/&lt;/a&gt;) by Daniel Rosenwasser&lt;br&gt;
Whoop whoop. Let's all upgrade. Seriously - good new version, but nothing life changing. Back to work!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The web's clipboard, and how it stores data of different types&lt;/strong&gt; (&lt;a href="https://alexharri.com/blog/clipboard" rel="noopener noreferrer"&gt;https://alexharri.com/blog/clipboard&lt;/a&gt;) by Alex Harri&lt;br&gt;
Years ago this was a great struggle, but the async clipboard API is indeed quite nice and useful. Great writeup!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CSS display contents&lt;/strong&gt; (&lt;a href="https://ishadeed.com/article/display-contents/" rel="noopener noreferrer"&gt;https://ishadeed.com/article/display-contents/&lt;/a&gt;) by Ahmad Shadeed&lt;br&gt;
My favorite CSS author is back - and now this picks up one of my favorite CSS features.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Learning to Reason with LLMs&lt;/strong&gt; (&lt;a href="https://openai.com/index/learning-to-reason-with-llms/" rel="noopener noreferrer"&gt;https://openai.com/index/learning-to-reason-with-llms/&lt;/a&gt;) by OpenAI&lt;br&gt;
Keep in mind that it's unreasonable to call yourself open but do everything closed. There is an open source variation of this, too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Undeniable Utility Of CSS :has&lt;/strong&gt; (&lt;a href="https://www.joshwcomeau.com/css/has/" rel="noopener noreferrer"&gt;https://www.joshwcomeau.com/css/has/&lt;/a&gt;) by Josh Comeau&lt;br&gt;
Honestly, :has is exactly the selected we needed. The number of (previously) "impossible" selectors that I created with it speaks for itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Passwords have problems, but passkeys have more&lt;/strong&gt; (&lt;a href="https://world.hey.com/dhh/passwords-have-problems-but-passkeys-have-more-95285df9" rel="noopener noreferrer"&gt;https://world.hey.com/dhh/passwords-have-problems-but-passkeys-have-more-95285df9&lt;/a&gt;) by David Heinemeier Hansson&lt;br&gt;
I was also quite excited about passkeys first. But I never use them for the exact reasons that are outlined in the post.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A (more) Modern CSS Reset&lt;/strong&gt; (&lt;a href="https://piccalil.li/blog/a-more-modern-css-reset/" rel="noopener noreferrer"&gt;https://piccalil.li/blog/a-more-modern-css-reset/&lt;/a&gt;) by Andy Bell&lt;br&gt;
I have not used a CSS reset in ages. Might be useful - but just being explicit with some declarations (esp. if inconsistencies between browsers are seen) is usually enough.&lt;/p&gt;

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

&lt;p&gt;These are all outstanding articles by masterful authors. I enjoyed reading them all - I hope you did find something in there, too.&lt;/p&gt;

&lt;p&gt;👉 Follow me on &lt;a href="https://www.linkedin.com/in/florian-rappl/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, &lt;a href="https://twitter.com/FlorianRappl" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, or here for more to come.&lt;/p&gt;

&lt;p&gt;🙏 Thanks to all the authors and contributors for their hard work!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>frontend</category>
      <category>node</category>
    </item>
    <item>
      <title>MDX with Translations 🐠</title>
      <dc:creator>Florian Rappl</dc:creator>
      <pubDate>Tue, 03 Sep 2024 15:13:11 +0000</pubDate>
      <link>https://dev.to/smapiot/mdx-with-translations-157c</link>
      <guid>https://dev.to/smapiot/mdx-with-translations-157c</guid>
      <description>&lt;p&gt;In recent years the need for some easy yet flexible format for simple static content has grown. Originally, HTML was meant for that - but let's be honest: it is neither simple, nor flexible. There have been plenty replacement proposals - as pretty much every CMS comes with its own dialect.&lt;/p&gt;

&lt;p&gt;One good contender for static content that can be read and rendered nicely is Markdown. However, it has quite a bit of shortcomings. Most notably, Markdown is not fully specified - leading to multiple dialects that exist. Furthermore, many necessary things are only available in form of non-standard extensions. Finally, using custom components within Markdown is not possible.&lt;/p&gt;

&lt;p&gt;This is where &lt;a href="https://mdxjs.com/" rel="noopener noreferrer"&gt;MDX&lt;/a&gt; comes into play. It's essentially a hybrid of JSX / React and a well-specified dialect of Markdown. Essentially, this allows writing texts that are as simple as plain Markdown, as well as creating complex components as you might have done in full JSX.&lt;/p&gt;

&lt;h2&gt;
  
  
  Website Structure
&lt;/h2&gt;

&lt;p&gt;We build upon the structure set up in our recently published article &lt;a href="https://dev.to/smapiot/faster-pages-with-react-h8j"&gt;faster pages with React&lt;/a&gt; and &lt;a href="https://smapiot.com/blog/2019-10-19-how-we-build-smapiot-com/" rel="noopener noreferrer"&gt;our original blog post&lt;/a&gt; from 2019. In this structure we have all the pages placed in their path-relative order using React for each page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw9kcsj523wza8yk93vi1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw9kcsj523wza8yk93vi1.png" alt="Structure of the pages" width="217" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now there are two things that we want to take care of:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Improve the content writing (and reading) for each employee at smapiot&lt;/li&gt;
&lt;li&gt;Simplify translations; right now the structure requires duplicating pages (placing them with different / translated content in the "de" and "en" folders).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For the former we want to leverage MDX - for the latter we need to come up with a solution that allows us to have a flat structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrating MDX
&lt;/h2&gt;

&lt;p&gt;Bringing MDX into the solution is actually quite straight forward. The &lt;code&gt;@mdx-js/mdx&lt;/code&gt; package contains everything to compile and work with MDX files. However, as we are already using a bundler (Vite), we can just rely on the specific plugin that exists for that bundler.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @mdx-js/rollup &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why are we installing a package called &lt;code&gt;rollup&lt;/code&gt; if our bundler is Vite? It turns out, that Vite is actually just a build-tool - for the actual bundling two other things are used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;esbuild to bundle/process anything in the third-party packages (as well as use it, e.g., on the config file)&lt;/li&gt;
&lt;li&gt;rollup to bundle/process the user code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hence the rollup plugin - which is all we need.&lt;/p&gt;

&lt;p&gt;The integration into our &lt;em&gt;vite.config.mjs&lt;/em&gt; file looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;codegen&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vite-plugin-codegen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;mdx&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@mdx-js/rollup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;assetsInlineLimit&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="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;:&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="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src&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="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;codegen&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nf"&gt;mdx&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;Note that we are not providing any options right now. It just works!&lt;/p&gt;

&lt;p&gt;So what can we do here? Consider one of our previous pages, the legal disclaimer (who doesn't love legal pages?!):&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="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@smapiot/components/lib/basic/Page&lt;/span&gt;&lt;span class="dl"&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;const&lt;/span&gt; &lt;span class="nx"&gt;meta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Legal Disclaimer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;legal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Legal Disclaimer&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;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;smapiot - Legal Disclaimer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default &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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Page&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&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;section&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"container"&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;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Legal Disclaimer&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&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;h3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Liability for Contents&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h3&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;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        The contents of our pages have been compiled with the greatest care. However, we cannot guarantee for accuracy,
        completeness or topicality of the contents. As service providers, we are liable for own contents of these
        websites according to sec. 7, paragraph 1 German Telemedia Act (TMG). However, according to sec. 8 to 10 German
        Telemedia Act (TMG), service providers are not obligated to permanently monitor submitted or stored information
        or to search for evidences that indicate illegal activities. Legal obligations to removing information or to
        blocking the use of information remain unchallenged. In this case, liability is only possible at the time of
        knowledge about a specific violation of law. Illegal contents will be removed immediately at the time we get
        knowledge of them.
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&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;h3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Liability for Links&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h3&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;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Our web pages include links to external third party websites. We have no influence on the contents of those
        external websites, therefore we cannot guarantee for those contents. Providers or administrators of linked
        websites are always responsible for their own contents. The linked websites had been checked for possible
        violations of law at the time of the establishment of the link. Illegal contents were not detected at the time
        of the linking. A permanent monitoring of the contents of linked websites cannot be imposed without reasonable
        indications that there has been a violation of law. Illegal links will be removed immediately at the time we get
        knowledge of them.
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&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;h3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Copyright&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h3&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;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Contents and compilations published on these websites by the providers are subject to German copyright laws.
        Reproduction, editing, distribution as well as the use of any kind outside the scope of the copyright law
        require a written permission of the author or originator. Downloads and copies of these websites are permitted
        for private use only. The commercial use of our contents without permission of the originator is prohibited.
        Copyright laws of third parties are respected as long as the contents on these websites do not originate from
        the provider. Contributions of third parties on this site are indicated as such. However, if you notice any
        violations of copyright law, please inform us. Such contents will be removed immediately.
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&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;section&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;Page&lt;/span&gt;&lt;span class="p"&gt;&amp;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 isn't too bad! What we can now do is to rename it from &lt;em&gt;Disclaimer.tsx&lt;/em&gt; to &lt;em&gt;Disclaimer.mdx&lt;/em&gt; and change its content to become:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const meta = {
  title: 'Legal Disclaimer',
  legal: 'Legal Disclaimer',
};

&amp;lt;section className="container"&amp;gt;
  # Legal Disclaimer

  ### Liability for Contents

  The contents of our pages have been compiled with the greatest care. However, we cannot guarantee for accuracy, completeness or topicality of the contents. As service providers, we are liable for own contents of these websites according to sec. 7, paragraph 1 German Telemedia Act (TMG). However, according to sec. 8 to 10 German Telemedia Act (TMG), service providers are not obligated to permanently monitor submitted or stored information or to search for evidences that indicate illegal activities. Legal obligations to removing information or to blocking the use of information remain unchallenged. In this case, liability is only possible at the time of knowledge about a specific violation of law. Illegal contents will be removed immediately at the time we get knowledge of them.

  ### Liability for Links

  Our web pages include links to external third party websites. We have no influence on the contents of those external websites, therefore we cannot guarantee for those contents. Providers or administrators of linked websites are always responsible for their own contents. The linked websites had been checked for possible violations of law at the time of the establishment of the link. Illegal contents were not detected at the time of the linking. A permanent monitoring of the contents of linked websites cannot be imposed without reasonable indications that there has been a violation of law. Illegal links will be removed immediately at the time we get knowledge of them.

  ### Copyright

  Contents and compilations published on these websites by the providers are subject to German copyright laws. Reproduction, editing, distribution as well as the use of any kind outside the scope of the copyright law require a written permission of the author or originator. Downloads and copies of these websites are permitted for private use only. The commercial use of our contents without permission of the originator is prohibited. Copyright laws of third parties are respected as long as the contents on these websites do not originate from the provider. Contributions of third parties on this site are indicated as such. However, if you notice any violations of copyright law, please inform us. Such contents will be removed immediately.
&amp;lt;/section&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Much easier to read! Note that unnecessary imports such as &lt;code&gt;react&lt;/code&gt; could be removed, while the boilerplate wrapper component &lt;code&gt;Page&lt;/code&gt; will now be automatically inserted.&lt;/p&gt;

&lt;p&gt;But there was a second thing we needed, right? What about improvements for the localization / page structure?&lt;/p&gt;

&lt;p&gt;Let's see what we can do here!&lt;/p&gt;

&lt;h2&gt;
  
  
  Providing Translations
&lt;/h2&gt;

&lt;p&gt;What if we would just put the localization strings all in a little sidecar? For instance, for a file like &lt;em&gt;Disclaimer.mdx&lt;/em&gt; we'd also have a file &lt;em&gt;Disclaimer.yml&lt;/em&gt;. In this file we could place translations according to the languages.&lt;/p&gt;

&lt;p&gt;The whole structure could then look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fepvq7566frpfpph1vq7t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fepvq7566frpfpph1vq7t.png" alt="Renewed structure of the pages" width="237" height="543"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Where an individual file, e.g., for &lt;em&gt;Disclaimer.yml&lt;/em&gt; could be written as:&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;de&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;content$&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;# Haftungsausschluss&lt;/span&gt;

    &lt;span class="s"&gt;### Haftung für Inhalte&lt;/span&gt;

    &lt;span class="s"&gt;Die Inhalte unserer Seiten wurden mit größter Sorgfalt erstellt. Für die Richtigkeit, Vollständigkeit und Aktualität der Inhalte können wir jedoch keine Gewähr übernehmen. Als Diensteanbieter sind wir gemäß § 7 Abs.1 TMG für eigene Inhalte auf diesen Seiten nach den allgemeinen Gesetzen verantwortlich. Nach §§ 8 bis 10 TMG sind wir als Diensteanbieter jedoch nicht verpflichtet, übermittelte oder gespeicherte fremde Informationen zu überwachen oder nach Umständen zu forschen, die auf eine rechtswidrige Tätigkeit hinweisen. Verpflichtungen zur Entfernung oder Sperrung der Nutzung von Informationen nach den allgemeinen Gesetzen bleiben hiervon unberührt. Eine diesbezügliche Haftung ist jedoch erst ab dem Zeitpunkt der Kenntnis einer konkreten Rechtsverletzung möglich. Bei Bekanntwerden von entsprechenden Rechtsverletzungen werden wir diese Inhalte umgehend entfernen.&lt;/span&gt;

    &lt;span class="s"&gt;### Haftung für Links&lt;/span&gt;

    &lt;span class="s"&gt;Unser Angebot enthält Links zu externen Webseiten Dritter, auf deren Inhalte wir keinen Einfluss haben. Deshalb können wir für diese fremden Inhalte auch keine Gewähr übernehmen. Für die Inhalte der verlinkten Seiten ist stets der jeweilige Anbieter oder Betreiber der Seiten verantwortlich. Die verlinkten Seiten wurden zum Zeitpunkt der Verlinkung auf mögliche Rechtsverstöße überprüft. Rechtswidrige Inhalte waren zum Zeitpunkt der Verlinkung nicht erkennbar. Eine permanente inhaltliche Kontrolle der verlinkten Seiten ist jedoch ohne konkrete Anhaltspunkte einer Rechtsverletzung nicht zumutbar. Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Links umgehend entfernen.&lt;/span&gt;

    &lt;span class="s"&gt;### Urheberrecht&lt;/span&gt;

    &lt;span class="s"&gt;Die durch die Seitenbetreiber erstellten Inhalte und Werke auf diesen Seiten unterliegen dem deutschen Urheberrecht. Die Vervielfältigung, Bearbeitung, Verbreitung und jede Art der Verwertung außerhalb der Grenzen des Urheberrechtes bedürfen der schriftlichen Zustimmung des jeweiligen Autors bzw. Erstellers. Downloads und Kopien dieser Seite sind nur für den privaten, nicht kommerziellen Gebrauch gestattet. Soweit die Inhalte auf dieser Seite nicht vom Betreiber erstellt wurden, werden die Urheberrechte Dritter beachtet. Insbesondere werden Inhalte Dritter als solche gekennzeichnet. Sollten Sie trotzdem auf eine Urheberrechtsverletzung aufmerksam werden, bitten wir um einen entsprechenden Hinweis. Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Inhalte umgehend entfernen.&lt;/span&gt;
&lt;span class="na"&gt;en&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;content$&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;# Legal Disclaimer&lt;/span&gt;

    &lt;span class="s"&gt;### Liability for Contents&lt;/span&gt;

    &lt;span class="s"&gt;The contents of our pages have been compiled with the greatest care. However, we cannot guarantee for accuracy, completeness or topicality of the contents. As service providers, we are liable for own contents of these websites according to sec. 7, paragraph 1 German Telemedia Act (TMG). However, according to sec. 8 to 10 German Telemedia Act (TMG), service providers are not obligated to permanently monitor submitted or stored information or to search for evidences that indicate illegal activities. Legal obligations to removing information or to blocking the use of information remain unchallenged. In this case, liability is only possible at the time of knowledge about a specific violation of law. Illegal contents will be removed immediately at the time we get knowledge of them.&lt;/span&gt;

    &lt;span class="s"&gt;### Liability for Links&lt;/span&gt;

    &lt;span class="s"&gt;Our web pages include links to external third party websites. We have no influence on the contents of those external websites, therefore we cannot guarantee for those contents. Providers or administrators of linked websites are always responsible for their own contents. The linked websites had been checked for possible violations of law at the time of the establishment of the link. Illegal contents were not detected at the time of the linking. A permanent monitoring of the contents of linked websites cannot be imposed without reasonable indications that there has been a violation of law. Illegal links will be removed immediately at the time we get knowledge of them.&lt;/span&gt;

    &lt;span class="s"&gt;### Copyright&lt;/span&gt;

    &lt;span class="s"&gt;Contents and compilations published on these websites by the providers are subject to German copyright laws. Reproduction, editing, distribution as well as the use of any kind outside the scope of the copyright law require a written permission of the author or originator. Downloads and copies of these websites are permitted for private use only. The commercial use of our contents without permission of the originator is prohibited. Copyright laws of third parties are respected as long as the contents on these websites do not originate from the provider. Contributions of third parties on this site are indicated as such. However, if you notice any violations of copyright law, please inform us. Such contents will be removed immediately.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this file a set of conventions exist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The top-level keys must be valid languages (right now only &lt;code&gt;de&lt;/code&gt; and &lt;code&gt;en&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The keys can be nested, i.e., objects / arrays are allowed&lt;/li&gt;
&lt;li&gt;When a key ends with a &lt;code&gt;$&lt;/code&gt; sign it will be interpreted as Markdown (specifically, MDX)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How does the MDX file look with such a sidecar file?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const meta = {
  title: 'Legal Disclaimer',
  legal: 'Legal Disclaimer',
};

&amp;lt;section className="container"&amp;gt;
  {locale.content$}
&amp;lt;/section&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now this looks appealing!&lt;/p&gt;

&lt;p&gt;For integrating the localization we still need to do something. See the use of &lt;code&gt;locale&lt;/code&gt; in the code above. But how does &lt;code&gt;locale&lt;/code&gt; enter the landscape in the file? After all, we don't see it!&lt;/p&gt;

&lt;p&gt;Turns out we can manipulate the generated code by MDX. Let's configure the tool a bit by adding a custom plugin to the &lt;code&gt;recmaPlugins&lt;/code&gt; option. This allows us to have full control over the generated abstract syntax tree (AST), which is then used to generate the actual code representing the MDX file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;codegen&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vite-plugin-codegen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;mdx&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@mdx-js/rollup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;localize&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/tools/localize.mjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;assetsInlineLimit&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="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;:&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="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src&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="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;codegen&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nf"&gt;mdx&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;recmaPlugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;localize&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;The imported &lt;code&gt;localize&lt;/code&gt; function is the actual plugin - which just yields another function - the so-called transformer. The transformer is responsible for manipulating the AST.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;transform&lt;/code&gt; function in the next snippet receives the AST and the virtual file (i.e., it's name, content, ...) of the MDX file that is currently being handled.&lt;/p&gt;

&lt;p&gt;Let's see the code before we go over what it does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;compileSync&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@mdx-js/mdx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;loadLocale&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./localization.mjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fromMarkdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
  &lt;span class="nf"&gt;compileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;development&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;recmaPlugins&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ast&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="p"&gt;[,&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CallExpression&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;callee&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;fn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;arguments&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;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ObjectExpression&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;properties&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;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;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getExpression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&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="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;switch &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;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&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="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;content&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="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ArrayExpression&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;content&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;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&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;getExpression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&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;i&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ObjectExpression&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&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;content&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;name&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Property&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Identifier&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getExpression&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;name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;init&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="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;boolean&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="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Literal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endsWith&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="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// uses potentially markdown&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fromMarkdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&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="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Literal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;default&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="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Literal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&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;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;vfile&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;language&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="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WEBSITE_LOCALE&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&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;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vfile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&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;locale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;loadLocale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;language&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;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findLastIndex&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;m&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;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ImportDeclaration&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;imprt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;node&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;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ImportDeclaration&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="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;source&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react/jsx-runtime&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;requiredImports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_jsx&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="s1"&gt;jsx&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_jsxs&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="s1"&gt;jsxs&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_Fragment&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="s1"&gt;Fragment&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;for &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;requiredImport&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;requiredImports&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;original&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;requiredImport&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;imprt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;specifiers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;node&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;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ImportSpecifier&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="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;imported&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;original&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;imprt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;specifiers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ImportSpecifier&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;imported&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Identifier&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;original&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;local&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Identifier&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;alias&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;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&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="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;VariableDeclaration&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;const&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;declarations&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;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;VariableDeclarator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Identifier&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;locale&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="na"&gt;init&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getExpression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;language&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;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;plugin&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;transform&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks more complicated than it needs to be! In the end, it all boils down to changing the AST to&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;obtain the &lt;code&gt;locale&lt;/code&gt; for the document&lt;/li&gt;
&lt;li&gt;insert potentially missing named imports (to fully support the generated Markdown / content, if any) into the &lt;code&gt;react/jsx-runtime&lt;/code&gt; import&lt;/li&gt;
&lt;li&gt;insert the &lt;code&gt;locale&lt;/code&gt; declaration - first thing &lt;em&gt;after&lt;/em&gt; the imports (this way you can use &lt;code&gt;locale&lt;/code&gt; pretty much everywhere in the document&lt;/li&gt;
&lt;li&gt;initialize the &lt;code&gt;locale&lt;/code&gt; variable to be the object we know; with exception of Markdown strings (suffixed with &lt;code&gt;$&lt;/code&gt;): these are transformed into an IIFE keeping the original MDX code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A good way to see what we are doing can be observed by using &lt;a href="https://mdxjs.com/playground/" rel="noopener noreferrer"&gt;the MDX playground&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;As you can see the compiled code is a bit strange, but can be mapped nicely to our original MDX file.&lt;/p&gt;

&lt;p&gt;In contrast, the view that we are actually most interested about is the stage before the code is in that compiled state. It's the "esast" view in the playground (i.e., the AST of the ESTree stage).&lt;/p&gt;

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

&lt;p&gt;What we want to achieve with our plugin is that we manipulate the start. Unfortunately, directly in MDX code we'd need to use an &lt;code&gt;export&lt;/code&gt; for that, but by manipulating the AST directly we actually don't need it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Febj3ranmzxh6izea13tf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Febj3ranmzxh6izea13tf.png" alt="Concept of what we want to do" width="800" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To obtain the &lt;code&gt;locale&lt;/code&gt; variable / object we the &lt;code&gt;loadLocale&lt;/code&gt; function from a dedicated module.&lt;/p&gt;

&lt;p&gt;This module does:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read and parse the local / file specific translations&lt;/li&gt;
&lt;li&gt;Obtain the language-specific translations of the local file&lt;/li&gt;
&lt;li&gt;Read and parse the global translations&lt;/li&gt;
&lt;li&gt;Obtain the language-specific translations of the global file&lt;/li&gt;
&lt;li&gt;Merge with local translations being regarded higher&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For obtaining the language-specific translations we choose a base translation (in our case &lt;code&gt;en&lt;/code&gt;) and "walk" through the object. The current language (let's say &lt;code&gt;de&lt;/code&gt;) then either overrides the base translation or just uses the snippet obtained from the base translation.&lt;/p&gt;

&lt;p&gt;You can think of this process as a deep merge with the base translation being the original.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;readFile&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs/promises&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;parse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;yaml&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getLocalization&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="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&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;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;readFile&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&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="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error reading YAML file:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;mergeLocale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;baseLocale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newLocale&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;newLocale&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&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;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;baseLocale&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="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;baseLocale&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;name&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="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;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newLocale&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&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="s1"&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;mergeLocale&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&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="nx"&gt;c&lt;/span&gt; &lt;span class="o"&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="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;c&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;c&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="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&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="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;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getLocale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;locales&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lang&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;baseLocale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;locales&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;en&lt;/span&gt; &lt;span class="o"&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="nx"&gt;lang&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&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="nx"&gt;lang&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;locales&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;newLocale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;locales&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&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;mergeLocale&lt;/span&gt;&lt;span class="p"&gt;({},&lt;/span&gt; &lt;span class="nx"&gt;baseLocale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newLocale&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;baseLocale&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadLocale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;language&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="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.mdx&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;globalFn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dirname&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="s1"&gt;global.yml&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;targetFn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.mdx&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="s1"&gt;.yml&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;globals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getLocalization&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;globalFn&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;locales&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getLocalization&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;targetFn&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="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;getLocale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;globals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;getLocale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;locales&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nx"&gt;language&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;language&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 global translations are placed in the folder above the &lt;code&gt;pages&lt;/code&gt;. The name of the file is &lt;em&gt;global.yml&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;For the &lt;em&gt;global.yml&lt;/em&gt; we only have some very common translation terms stored. An example:&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;de&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;germany&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deutschland&lt;/span&gt;
&lt;span class="na"&gt;en&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;germany&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Germany&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Being able to just change a single file when new translations come (or just changing the structure in one place independent of language changes) is a big deal. For us this was quite important and the new structure reflects that.&lt;/p&gt;

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

&lt;p&gt;With the new approach the pages are much easier to write than beforehand. Also, since we now have a static translation system in place we do not need to duplicate the pages or come up with some complicated system for intermediate components. It's all already done at compile-time in the given structure.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>react</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Faster Pages with React ✨</title>
      <dc:creator>Florian Rappl</dc:creator>
      <pubDate>Fri, 30 Aug 2024 08:17:31 +0000</pubDate>
      <link>https://dev.to/smapiot/faster-pages-with-react-h8j</link>
      <guid>https://dev.to/smapiot/faster-pages-with-react-h8j</guid>
      <description>&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://unsplash.com/@cadop?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Mathew Schwartz&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/time-lapse-photography-of-tunnel-sb7RUrRMaC4?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is the story of our &lt;a href="https://smapiot.com" rel="noopener noreferrer"&gt;homepage&lt;/a&gt; make over to reach a solid lighthouse score. If you want to read how we made the homepage in the first place then &lt;a href="https://smapiot.com/blog/2019-10-19-how-we-build-smapiot-com" rel="noopener noreferrer"&gt;have a look at our blog post from 2019&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Many of the things I write about here can be seen in the &lt;a href="https://dev.to/florianrappl/fast-pages-with-react-27og"&gt;Fast Pages with React&lt;/a&gt; article.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Origins
&lt;/h2&gt;

&lt;p&gt;Our homepage started off as a React SPA. Why? It was 2019 and the UI designer created the components all in React. Plus, it was a super smooth experience that was actually quite acceptable.&lt;/p&gt;

&lt;p&gt;However, while some may argue that a SPA is not good for SEO this was not the reason why we wanted to have a pre-rendered page. SEO seemed actually fine, but some more simple crawlers did not find all the information as it was hidden behind some JavaScript.&lt;/p&gt;

&lt;p&gt;So what can we do here? Well, we can just render everything at build-time using an static-site generation (SSG) approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Going into SSG
&lt;/h2&gt;

&lt;p&gt;We did not want to change the overall content or engine behind the page. After all, the DX was great and we thought that reusability of the components is more important than migrating to the latest SSG framework.&lt;/p&gt;

&lt;p&gt;After the build (back then using the &lt;a href="https://v1.parceljs.org/" rel="noopener noreferrer"&gt;Parcel v1&lt;/a&gt; bundler) was done, we had a post-build process that took the generated &lt;em&gt;index.html&lt;/em&gt; and went through all the detected pages. As our routing is declarative (we already generated the routes from the file system paths) finding the pages was easy.&lt;/p&gt;

&lt;p&gt;For each page we ran simple script that does the following things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Teach Node.js how ESM works (&lt;code&gt;esm&lt;/code&gt; module)&lt;/li&gt;
&lt;li&gt;Allow using TypeScript via &lt;code&gt;ts-node&lt;/code&gt; (our source is using TypeScript)&lt;/li&gt;
&lt;li&gt;Add some globals like &lt;code&gt;document&lt;/code&gt; or &lt;code&gt;localStorage&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Register some additional extensions, e.g., for resolving images as modules in Node.js (these images should resolve to the already generated images from the bundler)&lt;/li&gt;
&lt;li&gt;Evaluate the page - using &lt;code&gt;renderToString&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Replace the app's content container with the pre-rendered page&lt;/li&gt;
&lt;li&gt;Save the HTML of the modified app in a new file that matches the path of the page&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In code this worked as follows:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mkdirSync&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;require&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;esm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ts-node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;compilerOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;commonjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;es6&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;jsx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;importHelpers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;moduleResolution&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node&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="na"&gt;transpileOnly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;XMLHttpRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="err"&gt;{};
&lt;/span&gt;&lt;span class="nc"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;XDomainRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="err"&gt;{};
&lt;/span&gt;&lt;span class="nc"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localStorage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;getItem&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="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="nf"&gt;setItem&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="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sample&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nf"&gt;querySelector&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="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;getAttribute&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="p"&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;React&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MemoryRouter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-router&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;renderToString&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-dom/server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lazy&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="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Loading ...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Suspense&lt;/span&gt; &lt;span class="o"&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Fragment&lt;/span&gt;&lt;span class="p"&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;children&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useLayoutEffect&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="p"&gt;{};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setupExtensions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;files&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.png&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="s1"&gt;.svg&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="s1"&gt;.jpg&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="s1"&gt;.jpeg&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="s1"&gt;.mp4&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="s1"&gt;.mp3&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="s1"&gt;.woff&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="s1"&gt;.tiff&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="s1"&gt;.tif&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="s1"&gt;.xml&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;extension&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;file&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;parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&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="s1"&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;ext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parts&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;front&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parts&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="s1"&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;ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;files&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;m&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;m&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;front&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="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ext&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="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&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="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;ref&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;require&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.codegen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;file&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;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;file&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;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;file&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;renderApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dist&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;sourceModule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&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;route&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sourceModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;substring&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;Page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sourceModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&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;Layout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../scripts/layout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;default&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;element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;MemoryRouter&lt;/span&gt;&lt;span class="p"&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;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Layout&lt;/span&gt;&lt;span class="p"&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;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Page&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="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;renderToString&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="na"&gt;outPath&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;dist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;route&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.html&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;makePage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;outPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&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;outDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;outPath&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;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;lt;div id="app"&amp;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;&amp;lt;&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;div&amp;gt;/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;div id="app"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/div&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;mkdirSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;outDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;recursive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;outPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&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="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;msg&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dist&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;setupExtensions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;setTimeout&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;outPath&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;renderApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dist&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;makePage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;outPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&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;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;content&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="mi"&gt;100&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 whole &lt;code&gt;process&lt;/code&gt; is used as the given module is called from a forked process. So each page is generated in an isolated process.&lt;/p&gt;

&lt;p&gt;The following diagram illustrates this process.&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%2Fzmyi9iwmglv40w0bpmke.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%2Fzmyi9iwmglv40w0bpmke.png" alt="Pipeline of building the website"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What did we win so far? We have a pre-rendered page that works already and transforms to a SPA. Nice. But not enough for us.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modernizing the Stack
&lt;/h2&gt;

&lt;p&gt;I already described the process as "was". Parcel v1 has not been updated in quite a while and is - from today's point of view - quite slow. It also does not support some modern concepts and should be retired for good.&lt;/p&gt;

&lt;p&gt;As a replacement we've chosen &lt;a href="https://vitejs.dev/" rel="noopener noreferrer"&gt;Vite&lt;/a&gt;. This is a suitable replacement as it comes with a super fast development server and a very well optimized release build.&lt;/p&gt;

&lt;p&gt;Also, since we are using &lt;a href="https://dev.to/florianrappl/getting-bundling-superpowers-using-codegen-2no8"&gt;codegen&lt;/a&gt; for the route retrieval we can still keep using it for Vite.&lt;/p&gt;

&lt;p&gt;After all, the whole configuration for Vite to allow the transition is shown below:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;codegen&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vite-plugin-codegen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;assetsInlineLimit&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="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;:&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="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src&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="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;codegen&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;To avoid Vite's original behavior of inlining smaller assets (which would lead to problems in our SSG behavior) we set the &lt;code&gt;assetsInlineLimit&lt;/code&gt; to 0.&lt;/p&gt;

&lt;p&gt;One thing we also improved when making the transition from Parcel v1 to Vite is that we introduced path aliases. With the configuration above we can write &lt;code&gt;import '@/foo/bar'&lt;/code&gt; instead of going explicitly via the relative path to &lt;code&gt;src/foo/bar&lt;/code&gt;. This makes modules more flexible and easier to maintain.&lt;/p&gt;

&lt;p&gt;Previously, we used a bit of tools (&lt;code&gt;esm&lt;/code&gt;, &lt;code&gt;ts-node&lt;/code&gt;, ...) to actually make the SSG happen. With the new setup it was time to reduce the amount of tools and replace them all with &lt;code&gt;esbuild&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%2F1smz7nws76xc7wh6bzz6.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%2F1smz7nws76xc7wh6bzz6.png" alt="New SSG pipeline with esbuild"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The updated code of the SSG kernel module therefore changed a bit, too:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;writeFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mkdir&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs/promises&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;relative&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;runInContext&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vm&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;compile&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./compile&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;React&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&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;ReactRouter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-router&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;ReactRouterDom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-router-dom&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;renderToString&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-dom/server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lazy&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="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Loading ...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Suspense&lt;/span&gt; &lt;span class="o"&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Fragment&lt;/span&gt;&lt;span class="p"&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;children&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useLayoutEffect&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="p"&gt;{};&lt;/span&gt;
&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useEffect&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="p"&gt;{};&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;renderApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dist&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;dist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
        import { MemoryRouter } from "react-router";
        import Page, { meta } from "./&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;";
        import Layout from "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&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;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../layouts/default&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))}&lt;/span&gt;&lt;span class="s2"&gt;";

        const page = (
          &amp;lt;MemoryRouter&amp;gt;
            &amp;lt;Layout language="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;
              &amp;lt;Page /&amp;gt;
            &amp;lt;/Layout&amp;gt;
          &amp;lt;/MemoryRouter&amp;gt;
        );

        export { page, meta };
      `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;sourcefile&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="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;temp-page.jsx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;resolveDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jsx&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;exports&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&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="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-router&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="nx"&gt;ReactRouter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-router-dom&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="nx"&gt;ReactRouterDom&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cannot require&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;return&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="p"&gt;},&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;XMLHttpRequest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="err"&gt;{},
    &lt;/span&gt;&lt;span class="nc"&gt;XDomainRequest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="err"&gt;{},
    &lt;/span&gt;&lt;span class="nc"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;getItem&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="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="nf"&gt;setItem&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;document&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sample&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nf"&gt;querySelector&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="p"&gt;{&lt;/span&gt;
          &lt;span class="nf"&gt;getAttribute&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="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;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outputFiles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;m&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;m&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;endsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;runInContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;meta&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&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="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;renderToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;meta&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;makeFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;outPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&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;outDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;outPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;outDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;recursive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;writeFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;outPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&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;function&lt;/span&gt; &lt;span class="nf"&gt;makePage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;outPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&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;makeFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;outPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;lt;div id="app"&amp;gt;.*&lt;/span&gt;&lt;span class="se"&gt;?&lt;/span&gt;&lt;span class="sr"&gt;&amp;lt;&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;div&amp;gt;/&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;div id="app"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/div&amp;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;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&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="nx"&gt;msg&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dist&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;replacements&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;meta&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;renderApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;html&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;route&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;substring&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;outPath&lt;/span&gt; &lt;span class="o"&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;dist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;route&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.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;makePage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;outPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&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;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;done&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;outPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;replacements&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="mi"&gt;100&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;While the overall flow has remained the same, we now convert the page with some imports (e.g., &lt;code&gt;MemoryRouter&lt;/code&gt;) to a plain JavaScript script using the CommonJS module system. Therefore, the evaluation with the Node.js &lt;code&gt;vm&lt;/code&gt; module does not require &lt;code&gt;esm&lt;/code&gt; or &lt;code&gt;ts-node&lt;/code&gt; any more. Everything has been taken care of by esbuild.&lt;/p&gt;

&lt;p&gt;In the code above the actual esbuild usage is hidden in the &lt;code&gt;compile&lt;/code&gt; function, so let's see how we set it up:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;build&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;esbuild&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;codegenPlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;esbuild-codegen-plugin&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;basename&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;dist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;entryPoints&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;opts&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;isBrowser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;platform&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;browser&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="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;entryPoints&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;outdir&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;dist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substring&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="na"&gt;write&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;bundle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;splitting&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isBrowser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;minify&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isBrowser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isBrowser&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cjs&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="s1"&gt;esm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.jpg&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="s1"&gt;file&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="s1"&gt;.png&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="s1"&gt;file&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="s1"&gt;.svg&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="s1"&gt;file&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="s1"&gt;.avif&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="s1"&gt;file&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="s1"&gt;.webp&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="s1"&gt;file&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="na"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;:&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="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&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="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;external&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&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="s1"&gt;react-router&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="s1"&gt;react-router-dom&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="s1"&gt;react-dom&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="s1"&gt;react-dom/client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nf"&gt;codegenPlugin&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dynamic-assets&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onResolve&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&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;args&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;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lastIndexOf&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;front&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substring&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="nx"&gt;idx&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;ext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;idx&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;prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;front&lt;/span&gt; &lt;span class="o"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;m&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;m&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;prefix&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="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ext&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;file&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="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;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dynamic-asset&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="k"&gt;return&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="nx"&gt;build&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onLoad&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dynamic-asset&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&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;args&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;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`/assets/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;args&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="s2"&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="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`export default &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&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="s2"&gt;;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;js&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="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;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;compile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Quite straight forward. The few things to note are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Again, the usage of &lt;code&gt;codegen&lt;/code&gt; is superb is there is literally a plugin for each bundler. So we can just use the codegen plugin for esbuild and we are covered here.&lt;/li&gt;
&lt;li&gt;The dynamic asset plugin that has been defined above looks within the files if the name and extension fits; if it does it will use the already generated one from Vite.&lt;/li&gt;
&lt;li&gt;We can reuse the &lt;code&gt;compile&lt;/code&gt; function not only for Node.js (the SSG part), but also for compiling some JS functions that run in the browser.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Especially the last point can be crucial. Right now this setup really moves the dynamic part (a SPA) to a fully static one. But we still have some interactive parts in there... So fully hydrating this thing - as we did beforehand - may not be good enough.&lt;/p&gt;

&lt;p&gt;But even with this more modern setup one thing was still from 2019: the overall performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lighthouse Issues
&lt;/h2&gt;

&lt;p&gt;In 2019 we still got a great Lighthouse score, but now it's mediocre at best. Lighthouse got a lot more pushy - especially regarding hydration scenarios as we have here.&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%2Fa3uj5f70t3y4v57p7cje.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%2Fa3uj5f70t3y4v57p7cje.png" alt="Initial lighthouse score"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The score summary is:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Desktop&lt;/th&gt;
&lt;th&gt;Mobile&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Score&lt;/td&gt;
&lt;td&gt;70&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best Practices&lt;/td&gt;
&lt;td&gt;81&lt;/td&gt;
&lt;td&gt;79&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;First Contentful Paint&lt;/td&gt;
&lt;td&gt;0.4&amp;nbsp;s&lt;/td&gt;
&lt;td&gt;1.7s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Largest Contentful Paint&lt;/td&gt;
&lt;td&gt;1.6&amp;nbsp;s&lt;/td&gt;
&lt;td&gt;8.4s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total Blocking Time&lt;/td&gt;
&lt;td&gt;0&amp;nbsp;ms&lt;/td&gt;
&lt;td&gt;20ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cumulative Layout Shift&lt;/td&gt;
&lt;td&gt;1.828&lt;/td&gt;
&lt;td&gt;1.899&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Speed Index&lt;/td&gt;
&lt;td&gt;0.7s&lt;/td&gt;
&lt;td&gt;2.1s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There are some findings that seem rather optional, but nevertheless could be implemented rather quickly (or not at all):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easy: No CSP header (as this is just served as static content we could embed it easily in the HTML as &lt;code&gt;meta&lt;/code&gt; tag)&lt;/li&gt;
&lt;li&gt;Tedious: On mobile some images are served with a lower resolution (presumably going into the &lt;code&gt;picture&lt;/code&gt; tag with different &lt;code&gt;source&lt;/code&gt; elements to return the "right" resolution would fit)&lt;/li&gt;
&lt;li&gt;Impossible: Deprecated API used (&lt;code&gt;unload&lt;/code&gt; handler; use &lt;code&gt;pagehide&lt;/code&gt; event instead - actually we don't use this: the problem is coming/injected from a Chrome extension)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Naturally, the question is why the score is so low - and what we can do about it. Notably, we have a CLS (Cumulative Layout Shift) and a quite large LCP (Largest Contentful Paint).&lt;/p&gt;

&lt;h2&gt;
  
  
  Improvements
&lt;/h2&gt;

&lt;p&gt;The first thing to do is to lower the amount of work that the JS engine has to do. For this we needed to rethink our hydration strategy. The standard one from a React SPA is to hydrate everything. But this comes with a lot of overhead and a quite slow start.&lt;/p&gt;

&lt;p&gt;A much better approach would be to include an island architecture style here. Instead of hydrating everything in the beginning, we only hydrate parts - and only hydrate these parts when we need them (e.g., when they become visible).&lt;/p&gt;

&lt;p&gt;How can we do that? &lt;strong&gt;Time for magic&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let's consider the following code:&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;Testimonials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&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="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;container&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Testimonials&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;quote-carousel&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TestimonialsSlides&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;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 is the testimonials part of the homepage. Everything should be static - except the &lt;code&gt;TestimonialsSlides&lt;/code&gt;. This is a Carousel with some content. Everything is defined in React.&lt;/p&gt;

&lt;p&gt;What if we could just tell the system to hydrate it? Let's imagine we rewrite the code to look like this:&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;Testimonials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&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="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;container&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Testimonials&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;quote-carousel&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;hydrate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;TestimonialsSlides&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;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TestimonialsSlides&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;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 only thing we changed is that we added a &lt;code&gt;data-hydrate&lt;/code&gt; attribute. Granted, this should be a string - but (surprise) we won't use the attribute anyway. We will actually change the attribute value at build-time.&lt;/p&gt;

&lt;p&gt;The architecture that we have in mind works as follows:&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%2Fvi3a5ywycsuzhinwj02o.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%2Fvi3a5ywycsuzhinwj02o.png" alt="Architecture for the hydration replacements"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The build-time change in the SSG kernel module is an addition for the replacements:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createHash&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;crypto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getUniqueName&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;basename&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substring&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="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lastIndexOf&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;readFile&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="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;createHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sha1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&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;hash&lt;/span&gt; &lt;span class="o"&gt;=&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;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;substring&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="mi"&gt;6&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;name&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;hash&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;matcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/"data-&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;hydrate|render|load&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;":&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;(\w&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;\s]&lt;/span&gt;&lt;span class="sr"&gt;/g&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;replacements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&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;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;code&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;match&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;break&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;lookup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;match&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;kind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;match&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;componentName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;match&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`var &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;componentName&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lastIndexOf&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="s1"&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;pos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;4&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;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&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="nx"&gt;idx&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;entry&lt;/span&gt; &lt;span class="o"&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;__dirname&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="nx"&gt;src&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;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getUniqueName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;replacements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`"data-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;": "/assets/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.js",`&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="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;lookup&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="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;replacements&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lookup&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;Nice! With this change we have a replacement of the attributes during the build process. We also identify the replacements to use them later on in one joint build process (such that they produce common chunks):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;writeFile&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs/promises&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;compile&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./compile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&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;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&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="nx"&gt;msg&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;replacements&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;dist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;browser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;entryPoints&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;replacements&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;m&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="na"&gt;in&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;})),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;for &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;file&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outputFiles&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;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;writeFile&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;dist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substring&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;name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&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="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;done&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="mi"&gt;100&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 only thing missing is a replacement for the SPA script. The replacement should be able to work with the &lt;code&gt;data-hydrate&lt;/code&gt; attribute, which tells the website to hydrate a container with a specific file:&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;integrate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getProps&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="nx"&gt;Element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&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;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&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="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-props&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;catch&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="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;react&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&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;reactDom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-dom/client&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;mod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&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;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;react&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reactDom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*[data-hydrate]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;forEach&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="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;fn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-hydrate&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;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IntersectionObserver&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;entries&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="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;entry&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isIntersecting&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
          &lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(([{&lt;/span&gt; &lt;span class="nx"&gt;createElement&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;hydrateRoot&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;m&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="nf"&gt;requestAnimationFrame&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;hydrateRoot&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="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;getProps&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="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;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&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="p"&gt;});&lt;/span&gt;

  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*[data-load]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;forEach&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="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;fn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-load&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(([{&lt;/span&gt; &lt;span class="nx"&gt;createElement&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;hydrateRoot&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;m&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="nf"&gt;requestIdleCallback&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;hydrateRoot&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="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;getProps&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="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*[data-render]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;forEach&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="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;fn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-render&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(([{&lt;/span&gt; &lt;span class="nx"&gt;createElement&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createRoot&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;m&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="nf"&gt;createRoot&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="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;getProps&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="p"&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readyState&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;loading&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DOMContentLoaded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;integrate&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="nf"&gt;integrate&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;That is pretty much it! The great thing is that this script is so small that it can be inlined. Since we also know when / if we need replacements we'd only inline the script when we would need it (i.e., when we might hydrate).&lt;/p&gt;

&lt;p&gt;Finally, we externalized &lt;code&gt;react&lt;/code&gt; (and others), but we did not specify a replacement. As esbuild produces esm files those externals are placed as standard imports such as:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&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;We would run into problems if such a thing is evaluated in the browser. The solution is to introduce an importmap in our page:&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;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"importmap"&lt;/span&gt;&lt;span class="nt"&gt;&amp;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;imports&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&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;https://esm.sh/react@18.3.1&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;react-dom&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;https://esm.sh/react-dom@18.3.1&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;react-dom/client&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;https://esm.sh/react-dom@18.3.1/client&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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Note that the importmap is lazyly loaded anyway. So it will perform optimally out of the box.&lt;/p&gt;

&lt;p&gt;With everything in place we can look again at the lighthouse score.&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%2Fnpdw0p2xm31m5uhi67y1.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%2Fnpdw0p2xm31m5uhi67y1.png" alt="Lighthouse score after improvements"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The score summary is:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Desktop&lt;/th&gt;
&lt;th&gt;Mobile&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Score&lt;/td&gt;
&lt;td&gt;78&lt;/td&gt;
&lt;td&gt;65&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best Practices&lt;/td&gt;
&lt;td&gt;81&lt;/td&gt;
&lt;td&gt;79&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;First Contentful Paint&lt;/td&gt;
&lt;td&gt;0.6&amp;nbsp;s&lt;/td&gt;
&lt;td&gt;4.3s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Largest Contentful Paint&lt;/td&gt;
&lt;td&gt;1.3&amp;nbsp;s&lt;/td&gt;
&lt;td&gt;9.4s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total Blocking Time&lt;/td&gt;
&lt;td&gt;0&amp;nbsp;ms&lt;/td&gt;
&lt;td&gt;10ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cumulative Layout Shift&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Speed Index&lt;/td&gt;
&lt;td&gt;0.6s&lt;/td&gt;
&lt;td&gt;4.3s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;While the score is already much better the LCP and overall speed index actually got worse. The reason the score is better is most notably that the CLS is now 0, which is great.&lt;/p&gt;

&lt;p&gt;So where did we go wrong?&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Touches
&lt;/h2&gt;

&lt;p&gt;While the SPA transition hurt our score, it did not hurt as much as badly hydrated components. One of these components is the main carousel, which is shown immediately.&lt;/p&gt;

&lt;p&gt;It turns out the carousel component was only made for SPAs. It dynamically computes the width and position of its slides based on the given content. The following diagram shows its working:&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%2Flxcevsn3a8kb9og8udpq.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%2Flxcevsn3a8kb9og8udpq.png" alt="Carousel component"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In short we have a container that gets the dimensions of the component. Inside the container we use a content element that has the width of the container multiplied by the number of slides + 2. We add two as we also introduce duplicates for the first and last slide. This way, we can have the experience of a real carousel. From the last slide you can keep on swiping right to get to the first slide and vice versa. Essentially, this allows you to keep scrolling in one direction.&lt;/p&gt;

&lt;p&gt;What can we do to improve the code here?&lt;/p&gt;

&lt;p&gt;Instead of relying on the runtime to set &lt;code&gt;style&lt;/code&gt; properties we return a pre-computed &lt;code&gt;style&lt;/code&gt; object. This way, we only change dynamic properties when a scrol operation starts.&lt;/p&gt;

&lt;p&gt;Originally, the code had a function like:&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;updateOffset&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;parentElement&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;c&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;o&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;translateX(0)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;transition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;desired&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;active&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;dist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;active&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;desired&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;pref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;o&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dist&lt;/span&gt; &lt;span class="o"&gt;&amp;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;2&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="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;desired&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;active&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;shift&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pref&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&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;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;transition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;smooth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`translateX(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;shift&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="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="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;o&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="nx"&gt;o&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="nx"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`translateX(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px)`&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="nx"&gt;transition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;elastic&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;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;transform&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;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;transition&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;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;active&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now we move to:&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;updateOffset&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&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;c&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;o&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;translateX(0)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;transition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;desired&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;active&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;shift&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getShift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;desired&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;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;transition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;smooth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`translateX(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;shift&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="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="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;o&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="nx"&gt;o&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="nx"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`translateX(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px)`&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="nx"&gt;transition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;elastic&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;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;transform&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;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;transition&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;The new &lt;code&gt;getShift&lt;/code&gt; function is also much improved:&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;getShift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;active&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;desired&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;o&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;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;3&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;desired&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;active&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;o&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&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;desired&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;end&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;active&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;o&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;o&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;pref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;o&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="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;pref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;total&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;active&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;desired&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="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;total&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 results in a better UX while also improving the story for SSG / SSR.&lt;/p&gt;

&lt;p&gt;Besides the work on the carousel component we also optimized the images and spend some time on content cleanup. Nothing dramatic - just little wins here and there.&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%2Fdofubqc7suste1d298nq.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%2Fdofubqc7suste1d298nq.png" alt="Final lighthouse score"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The score summary is:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Desktop&lt;/th&gt;
&lt;th&gt;Mobile&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Score&lt;/td&gt;
&lt;td&gt;99&lt;/td&gt;
&lt;td&gt;87&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best Practices&lt;/td&gt;
&lt;td&gt;78&lt;/td&gt;
&lt;td&gt;75&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;First Contentful Paint&lt;/td&gt;
&lt;td&gt;0.6&amp;nbsp;s&lt;/td&gt;
&lt;td&gt;2.6s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Largest Contentful Paint&lt;/td&gt;
&lt;td&gt;0.8&amp;nbsp;s&lt;/td&gt;
&lt;td&gt;3.4s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total Blocking Time&lt;/td&gt;
&lt;td&gt;0&amp;nbsp;ms&lt;/td&gt;
&lt;td&gt;10ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cumulative Layout Shift&lt;/td&gt;
&lt;td&gt;0.009&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Speed Index&lt;/td&gt;
&lt;td&gt;0.6s&lt;/td&gt;
&lt;td&gt;2.6s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is it! Wonderful that such a "little" change in a single component can have such a drastic outcome. But in the end it's all about the details.&lt;/p&gt;

&lt;p&gt;While every metric now looks good the best practices got a bit worse. Why? Because we added a small video in form of a YouTube embed (&lt;code&gt;iframe&lt;/code&gt;) into the homepage. As YouTube brings quite some cookies and other things Lighthouse is not really happy with the third-party content. So we can actually ignore this.&lt;/p&gt;

&lt;p&gt;As wanted to be in the 90s rating this is certainly a success and as desired.&lt;/p&gt;

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

&lt;p&gt;Overall, we kept the codebase stable and just gave the whole source code a bit of a makeover. This was sufficient to reach the next level of DX and performance.&lt;/p&gt;

&lt;p&gt;As next step we'll modernize some of our content and re-introduce the SPA transition by hijacking internal links; loading an HTML fragment during a page transition. We'll potentially also replace the importmap with an integrated bundle, using Preact in compat mode as a replacement.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>performance</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
