<?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>Forem: Tea Reggi</title>
    <description>The latest articles on Forem by Tea Reggi (@reggi).</description>
    <link>https://forem.com/reggi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F274085%2F61a32ca6-b8aa-496e-b01f-47db01e26b95.jpg</url>
      <title>Forem: Tea Reggi</title>
      <link>https://forem.com/reggi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/reggi"/>
    <language>en</language>
    <item>
      <title>When to use an Astro component over a Web Component.</title>
      <dc:creator>Tea Reggi</dc:creator>
      <pubDate>Thu, 14 Dec 2023 21:11:05 +0000</pubDate>
      <link>https://forem.com/reggi/when-to-use-an-astro-component-over-a-web-component-2bn6</link>
      <guid>https://forem.com/reggi/when-to-use-an-astro-component-over-a-web-component-2bn6</guid>
      <description>&lt;p&gt;For my personal website (built with Astro) I wanted a way to abstract out something I call a &lt;code&gt;heading fragment&lt;/code&gt;, it combines the concept of a heading tag (&lt;code&gt;h1&lt;/code&gt;, &lt;code&gt;h2&lt;/code&gt;, &lt;code&gt;h3&lt;/code&gt;, ect) and a fragment link &lt;code&gt;#reference-to-id-on-the-page&lt;/code&gt;. This seems like a no-brainer for any blog or website to provide permalinks to specific sections of a blog post or article. &lt;/p&gt;

&lt;p&gt;In an ideal world this looks like this:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This would:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wrap it all in an anchor&lt;/li&gt;
&lt;li&gt;Kebab-case the text and add it as the id somewhere near this element.&lt;/li&gt;
&lt;li&gt;For extra measure I wanted hover behavior so that when you hover you get a link icon that shows up to the left of the text.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, the question is should this be an astro component? Or a web component? &lt;/p&gt;

&lt;h2&gt;
  
  
  What is an Astro component?
&lt;/h2&gt;

&lt;p&gt;Astro is a web framework with a main-focus on static-site generation (ssg). It allows you to have &lt;code&gt;.astro&lt;/code&gt; files that are a bit different than your average &lt;code&gt;.js&lt;/code&gt; / &lt;code&gt;.ts&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
const text = 'hello world'
---
&amp;lt;div&amp;gt;{text}&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Astro files are a hybrid of Javascript / Typescript, JSX-like syntax, and HTML. Asto has a bunch of tricks up its sleeve to optimize all of these things, especially &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags. &lt;/p&gt;

&lt;h2&gt;
  
  
  What does &lt;code&gt;HeadingFragment&lt;/code&gt; look like in Astro?
&lt;/h2&gt;

&lt;p&gt;This code below might be new to you if you've never seen an astro component, but it's really simple. It just uses html, js, and css, that's it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
const { as: As = 'h1' } = Astro.props;
const text = await Astro.slots.render('default')

function toKebabCase(str: string): string {
    return str
        .match(/[A-Z]?[a-z]+|[0-9]+/g)!
        .map(word =&amp;gt; word.toLowerCase())
        .join('-');
}

const id = toKebabCase(text)
---

&amp;lt;As class="heading items-center" id={id}&amp;gt;
  &amp;lt;a class="flex" href={`#${id}`}&amp;gt;
    &amp;lt;span class="icon w-0 overflow-hidden" style="transition: width 0.3s;"&amp;gt;🔗&amp;lt;/span&amp;gt;
    &amp;lt;span&amp;gt;{text}&amp;lt;/span&amp;gt;
  &amp;lt;/a&amp;gt;
&amp;lt;/As&amp;gt;

&amp;lt;script&amp;gt;
  document.querySelectorAll('.heading').forEach(heading =&amp;gt; {
    const icon = heading.querySelector('.icon')

    heading.addEventListener('mouseover', () =&amp;gt; {
      icon.classList.add('hovered');
    });
    heading.addEventListener('mouseout', () =&amp;gt; {
      icon.classList.remove('hovered');
    });
  });
&amp;lt;/script&amp;gt;

&amp;lt;style&amp;gt;
  .hovered {
    width: 32px;
  }
&amp;lt;/style&amp;gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  What does &lt;code&gt;heading-fragment&lt;/code&gt; look like as a web component?
&lt;/h2&gt;

&lt;p&gt;The web component is a bit more involved. Disclaimer: it does have one "extra feature" which is to find the font-size of the tag used and compensate for the "icon" width dynamically (ignore this).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HeadingFragment&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;kabob &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&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;A-Z&lt;/span&gt;&lt;span class="se"&gt;]?[&lt;/span&gt;&lt;span class="sr"&gt;a-z&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;0-9&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+/g&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="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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;elmWidth &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;text&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;elm&lt;/span&gt; &lt;span class="o"&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;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;elm&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;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inline&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="nx"&gt;elm&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;visibility&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hidden&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="nx"&gt;elm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;text&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;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elm&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;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;elm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offsetWidth&lt;/span&gt;
    &lt;span class="nx"&gt;elm&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;defaultEmoji&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="nx"&gt;emoji&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;emojiWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&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;as&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emoji&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&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;emoji&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultEmoji&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emojiWidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;HeadingFragment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;elmWidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emoji&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nl"&gt;as&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="nf"&gt;connectedCallback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;HeadingFragment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;kabob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;heading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;heading&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;anchor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;anchor&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;icon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;icon&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

    &lt;span class="nx"&gt;heading&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;mouseenter&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;icon&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;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emojiWidth&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;5&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="s1"&gt;px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="nx"&gt;heading&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;mouseleave&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;icon&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;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;heading &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;e&lt;/span&gt; &lt;span class="o"&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;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;as&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="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;anchor &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;e&lt;/span&gt; &lt;span class="o"&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;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;a&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="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;href&lt;/span&gt;&lt;span class="dl"&gt;'&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;this&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;return&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;icon &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;e&lt;/span&gt; &lt;span class="o"&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;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;span&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="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;overflow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hidden&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="nx"&gt;e&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;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="nx"&gt;e&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;whiteSpace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nowrap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="nx"&gt;e&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;width 0.3s&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emoji&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;


  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;main &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;e&lt;/span&gt; &lt;span class="o"&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;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;span&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="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;
    &lt;span class="k"&gt;return&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;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;heading-fragment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;HeadingFragment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Now that I showed both ways which is better? I think the Astro component is better, here's why:&lt;/p&gt;

&lt;p&gt;1) It's simpler&lt;br&gt;
2) It's easier to read / maintain&lt;br&gt;
3) It's semantically better&lt;/p&gt;

&lt;h2&gt;
  
  
  What does "Semantically Better" mean?
&lt;/h2&gt;

&lt;p&gt;When using the web component you're dynamically adding a heading tag to the page using Javascript, this means when the page loads there's no headings on the page. This isn't great. I know that search engines have come a long way when it comes to crawling html, but for something essential for screen-readers it still scares me a little to not be rendering &lt;code&gt;h2&lt;/code&gt; tags statically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your thoughts?
&lt;/h2&gt;

&lt;p&gt;What do you think? Which do you think is "better", the Astro component or the Web Component?&lt;/p&gt;

</description>
      <category>javascript</category>
    </item>
    <item>
      <title>Editing Videos Using ffmpeg + TypeScript and Deno.</title>
      <dc:creator>Tea Reggi</dc:creator>
      <pubDate>Thu, 07 Dec 2023 17:31:01 +0000</pubDate>
      <link>https://forem.com/reggi/editing-videos-using-ffmpeg-typescript-and-deno-58ln</link>
      <guid>https://forem.com/reggi/editing-videos-using-ffmpeg-typescript-and-deno-58ln</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt;: I wrote a module using typescript / deno that wraps ffmpeg and offers a better developer experience when editing a bunch of video clips together 👉 &lt;a href="https://deno.land/x/movie@1.0.0" rel="noopener noreferrer"&gt;deno/x/movie&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I've been recording small clips of video on my old "retro" 🤣 "vintage" 😭 Nikon point-and-shoot digital camera. I'm trying to think more visually and thinking about using video as a medium to explore concepts I'm experiencing in life, and evoke a feeling. &lt;/p&gt;

&lt;p&gt;I kinda hate modern video editing software, mainly using iMovie. I briefly looked into other open-source alternatives to iMovie, but nothing stood out. Then I thought about &lt;code&gt;ffmpeg&lt;/code&gt;, a command-line tool I've used once or twice in the past to convert video or audio to another format. &lt;/p&gt;

&lt;p&gt;I started a deep-dive in learning &lt;code&gt;ffmpeg&lt;/code&gt;. How to do things like remove sound entirely from a clip. How to manipulate saturation, gamma, and contrast. How to raise the volume of a clip. All of these are done within &lt;code&gt;ffmpeg&lt;/code&gt; as "filters". Given that it's a command-line tool every character matters and a lot of information is condensed into a short command. The syntax is a bit hard to understand. I found that breaking  the command down using new lines and a &lt;code&gt;\&lt;/code&gt; was the only way to really read it. &lt;/p&gt;

&lt;h2&gt;
  
  
  Brief Explainer
&lt;/h2&gt;

&lt;p&gt;Here's a simple script that merges two videos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ffmpeg &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-i&lt;/span&gt; ./example/originals/DSCN3700.AVI &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-i&lt;/span&gt; ./example/originals/DSCN3701.AVI &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-filter_complex&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
[0:v:0][0:a:0]&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
[1:v:0][1:a:0]&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
concat=n=2:v=1:a=1[outv][outa]"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-map&lt;/span&gt; &lt;span class="s2"&gt;"[outv]"&lt;/span&gt; &lt;span class="nt"&gt;-map&lt;/span&gt; &lt;span class="s2"&gt;"[outa]"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
./example/output.mp4 &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's a simple breakdown of this command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-i&lt;/code&gt; are inputs 0 and 1&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0:v:0&lt;/code&gt; refers to input 0 and video feed 0&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0:a:1&lt;/code&gt; refers to input 0 and audio track 0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is because files can have different video and audio tracks, think about the feature of supporting a video with multiple language tracks. Track 0 may be English, track 1 may be Japanese.&lt;/p&gt;

&lt;p&gt;This is a simple example, a more complex example would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ffmpeg &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-i&lt;/span&gt; ./example/originals/DSCN3700.AVI &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-i&lt;/span&gt; ./example/originals/DSCN3701.AVI &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-filter_complex&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
[0:a:0]volume=3,atrim=start=4:duration=4,asetpts=PTS-STARTPTS[audio0];&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
[0:v:0]fps=25,crop=480:480:0:0,trim=start=4:duration=4,setpts=PTS-STARTPTS[video0];&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
[1:a:0]volume=4,atrim=end=17:start=15,asetpts=PTS-STARTPTS[audio1];&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
[1:v:0]fps=25,crop=480:480:0:0,trim=end=17:start=15,setpts=PTS-STARTPTS[video1];&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
[video0][audio0]&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
[video1][audio1]&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
concat=n=2:v=1:a=1[outv][outa]"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-map&lt;/span&gt; &lt;span class="s2"&gt;"[outv]"&lt;/span&gt; &lt;span class="nt"&gt;-map&lt;/span&gt; &lt;span class="s2"&gt;"[outa]"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
./example/output.mp4 &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This does the same for imports&lt;/li&gt;
&lt;li&gt;This adds in the notion of a "filter" assigned from a track to a "variable"
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;0:a:0]volume&lt;span class="o"&gt;=&lt;/span&gt;3,atrim&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4:duration&lt;span class="o"&gt;=&lt;/span&gt;4,asetpts&lt;span class="o"&gt;=&lt;/span&gt;PTS-STARTPTS[audio0]&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
  ^ the track &lt;span class="nt"&gt;-----------&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt; the filter&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nt"&gt;--&lt;/span&gt; the new variable    ^
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each of these definitions are followed by a &lt;code&gt;;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then we have the concatenation definition after the last &lt;code&gt;;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;video0][audio0]&lt;span class="se"&gt;\&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This specifies this audio video pair from the definitions followed by the next and so on and so on.&lt;/p&gt;

&lt;p&gt;So I view the &lt;code&gt;-filter_complex&lt;/code&gt; value as this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;{&lt;/span&gt;all the filter transformations, declaring a new variable &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;the concatenations, paired audio + video&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  I built a wrapper
&lt;/h2&gt;

&lt;p&gt;Early when learning the filters I wanted a better way so I created some glue-code to wrap &lt;code&gt;ffmpeg&lt;/code&gt; in a way that was easier to move clips around and be more flexible. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://deno.land/x/movie@1.0.0" rel="noopener noreferrer"&gt;https://deno.land/x/movie@1.0.0&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here's the video I made with it 👇🎬🎥&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/hpkMXIJsdaU"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>deno</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Recreating Mastodons Audio Player as a Web Component</title>
      <dc:creator>Tea Reggi</dc:creator>
      <pubDate>Mon, 04 Dec 2023 18:19:20 +0000</pubDate>
      <link>https://forem.com/reggi/recreating-mastodons-audio-player-as-a-web-component-4l7h</link>
      <guid>https://forem.com/reggi/recreating-mastodons-audio-player-as-a-web-component-4l7h</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt;: I recreated mastodons audio player as a web component, available &lt;a href="https://github.com/reggi/mastodon-audio-player-web-component" rel="noopener noreferrer"&gt;reggi/mastodon-audio-player-web-component&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What was the goal?
&lt;/h2&gt;

&lt;p&gt;The goal was to have a &lt;code&gt;audio-player&lt;/code&gt; embed like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;audio-player&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/file.mp3"&lt;/span&gt; &lt;span class="na"&gt;poster=&lt;/span&gt;&lt;span class="s"&gt;"/image.jpg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/audio-player&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal was also to have it look exactly like the &lt;code&gt;audio-player&lt;/code&gt; that &lt;a href="https://github.com/mastodon/mastodon/blob/8710bdb183073381250085ffa20e21e22d9069b6/app/javascript/mastodon/features/audio/index.jsx" rel="noopener noreferrer"&gt;mastodon uses&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.amazonaws.com%2Fuploads%2Farticles%2Foyzxc5jp1c6291is8cmw.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%2Foyzxc5jp1c6291is8cmw.png" alt="an image of the audio player" width="785" height="559"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why
&lt;/h2&gt;

&lt;p&gt;I was creating a &lt;a href="https://astro.build/" rel="noopener noreferrer"&gt;Astro&lt;/a&gt; component &lt;code&gt;MastodonStatus&lt;/code&gt;, something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MastodonStatus&lt;/span&gt; &lt;span class="nx"&gt;href&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://indieweb.social/@thomasreggi/111518126442493327&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And I really wanted to display all the extra post-types, &lt;code&gt;images&lt;/code&gt;, &lt;code&gt;poll&lt;/code&gt;, &lt;code&gt;video&lt;/code&gt;, but when it came to &lt;code&gt;audio&lt;/code&gt; there was an issue, there was no drop-in player that resembled the mastodon player. Mastodon wrote the original player using React, so it wasn't exactly easy to implement as it was. I really just wanted a &lt;code&gt;web-component&lt;/code&gt; version to exist for me to drop in and pass the &lt;code&gt;mp3&lt;/code&gt; and an &lt;code&gt;image&lt;/code&gt; and be on my way.&lt;/p&gt;

&lt;p&gt;Once I started to dive into it and reverse engineer the existing player I started to have hope that it was possible. Everything from the custom volume-slider to figuring out the visualizer was pretty complex. This was also my first time working with the web &lt;code&gt;Audio&lt;/code&gt; api, which was surprisingly easy to work with.&lt;/p&gt;

&lt;p&gt;Making and working with a complex web-component like this wasn't simple but there are some things that helped along the way. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;No &lt;code&gt;template&lt;/code&gt; or &lt;code&gt;style&lt;/code&gt; tags, I went pure in-js solutions for all dom elements and styles. I think this saved me in the long run. I hate working with long template strings for html and css.&lt;/li&gt;
&lt;li&gt;Grouping initializable elements in a class method, I would break down every element into a function and attach it to it's parent at the very end, makes it easy to call on elements in other methods.&lt;/li&gt;
&lt;li&gt;Grouping all event handlers, this made it easy to track all the events within the component.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;I'm hoping that other people will find this useful. In an ideal world mastodon also would adopt this component and the community can build on it and make it better. There's some work needed to make sure that the component is accessable, but it was a lot of work to get where I am (I feel like I just lifted a bounder up a mountain). I'm totally open to any pull-requests that address any accessability issues with the component. Let's try and push web-components to be a better standard that are meant to be shared, worked on, and maintained collectivley.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Context
&lt;/h2&gt;

&lt;p&gt;This project was born as a dependency to another project &lt;a href="https://github.com/reggi/astro-cache-embed" rel="noopener noreferrer"&gt;&lt;code&gt;astro-cache-embed&lt;/code&gt;&lt;/a&gt;. The projects look something like this, with each being a dependency of the other: &lt;a href="https://github.com/reggi/astro.reggi.com" rel="noopener noreferrer"&gt;reggi.com&lt;/a&gt; -&amp;gt; &lt;a href="https://github.com/reggi/astro-cache-embed" rel="noopener noreferrer"&gt;astro-cache-embed&lt;/a&gt; -&amp;gt; &lt;a href="https://github.com/reggi/mastodon-audio-player-web-component" rel="noopener noreferrer"&gt;mastodon-audio-player-web-component&lt;/a&gt;. Here's a &lt;a href="http://reggi.com/demo/mastodon-embed" rel="noopener noreferrer"&gt;demo of the mastodon-embed&lt;/a&gt;. Here's a demo of the &lt;a href="https://reggi.github.io/mastodon-audio-player-web-component/" rel="noopener noreferrer"&gt;&lt;code&gt;mastodon-audio-player-web-component&lt;/code&gt;&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>webcomponents</category>
    </item>
    <item>
      <title>In Defense of Having All Code in a Single File.</title>
      <dc:creator>Tea Reggi</dc:creator>
      <pubDate>Thu, 02 Nov 2023 15:51:15 +0000</pubDate>
      <link>https://forem.com/reggi/in-defense-of-having-all-code-in-a-single-file-18lb</link>
      <guid>https://forem.com/reggi/in-defense-of-having-all-code-in-a-single-file-18lb</guid>
      <description>&lt;p&gt;I recently wanted to split of a series of scripts I made for a postgres project I was working on into its own cli tool. I started by outlining the tool, I wanted a &lt;code&gt;pgrun bundle&lt;/code&gt;, and a &lt;code&gt;pgrun test&lt;/code&gt; command. This is what I had after a little bit of development:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── README.md
├── args.ts
├── bundle.ts
├── cli.ts
├── context.ts
├── main.ts
├── options.ts
├── organizer.ts
├── organizer2.ts
├── parse.ts
├── run.sh
├── shake.ts
├── sort.ts
├── sort2.ts
├── sql.ts
└── walk.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I had a whole bunch of functions spit into their own files, and it started to be a headache to maintain. I felt every time I made some new thing, I'd isolate it in it's own file. I've &lt;a href="https://dev.to/reggi/vscode-deno-notebook-48kp"&gt;vented about files and folders in the past&lt;/a&gt;, as well as invented &lt;a href="https://dev.to/reggi/i-invented-a-cli-tool-to-organize-my-typescript-projects-1j82"&gt;whole new ways to organize typescript projects&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But nothing is as simple as this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── README.md
├── bin.ts
├── deno.lock
├── mod.ts
├── test.ts
└── types.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What's the shift? Simply having all the functions in &lt;a href="https://github.com/reggi/pgrun/blob/main/mod.ts" rel="noopener noreferrer"&gt;one mod.ts file&lt;/a&gt;, it's still under 500 lines. It became a shift slightly but utilizing search and replace within the same file became much easier than digging around the files for the function, and having to deal with import / exports.&lt;/p&gt;

&lt;p&gt;This is a gentle reminder to myself to not break things out early on, and keep it simple, keep it all contained in one file if you can, it makes it so much easier to manage. &lt;/p&gt;

</description>
    </item>
    <item>
      <title>Why Can't I Just Use This Function? The Struggles with Code Reusability in JS</title>
      <dc:creator>Tea Reggi</dc:creator>
      <pubDate>Wed, 25 Oct 2023 19:30:07 +0000</pubDate>
      <link>https://forem.com/reggi/why-cant-i-just-use-this-function-the-struggles-with-code-reusability-in-js-23j3</link>
      <guid>https://forem.com/reggi/why-cant-i-just-use-this-function-the-struggles-with-code-reusability-in-js-23j3</guid>
      <description>&lt;p&gt;We've all been there. You know an open-source project does something you need, deeply nested in the dark trenches of a specific file in the repo. You finally, after hunting for a couple minutes you find it. You start to untangle that core functionality and see if there's any feasible way to decouple it from the rest of the project. Is it nested within a rats-nest of classes? You don't need or can't realistically call the parent's parent's parent's class without all the data needed to instantiate those classes. It's all a headache. It shouldn't be this darn hard. &lt;/p&gt;

&lt;p&gt;It's not a project maintainers fault that they didn't release this small piece of there project for reuse. However that doesn't take away that it's frustrating the way that code is bundled and distributed.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;A whole project might be released as a server or framework. Frameworks like &lt;a href="https://fresh.deno.dev/" rel="noopener noreferrer"&gt;fresh&lt;/a&gt;, and &lt;a href="https://astro.build/" rel="noopener noreferrer"&gt;astro&lt;/a&gt;) both have had things deep within them that I've wanted to reuse, within fresh it's the esbuild configuration, and islands functionality, and within astro it's the &lt;a href="https://stackoverflow.com/questions/73382889/how-can-i-render-a-astro-component-to-a-html-string" rel="noopener noreferrer"&gt;rendering of astro files themselves&lt;/a&gt;. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A project might be released as a CLI tool, with a whole host of flags and options, .rc files.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A project might be released as a module to run within a specific runtime, NPM, Deno, Bun&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A project may or may not be written with typescript, js, mjs, js modules and may or maynot be built within the repo.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This post is mostly just to vent about the sad state of JavaScript. There are more complexities when talking about how people organize their projects, and dependencies, barrel files, front-end frameworks, etc. &lt;/p&gt;

&lt;p&gt;I don't know what the right answer is. I just wish that there was a standard format that we could all use that sort of built up functions within a project and allowed us to call them via cli, or server, routes, natively through code. Deno as a runtime is the closest thing to that for me because of how it handles URL imports. It makes it really easy to manage scripts. It also makes it easy to have any file within a repository be the entry point, and it's dependencies, without the need to download the entirety of a project as the dependency, even within this mindset authors need to not use barrel files because it breaks tree-shaking. Overall URL imports are something I don't hear enough people rave about.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>The "Postgres First" Trend</title>
      <dc:creator>Tea Reggi</dc:creator>
      <pubDate>Tue, 17 Oct 2023 17:23:08 +0000</pubDate>
      <link>https://forem.com/reggi/the-postgres-first-trend-1jhl</link>
      <guid>https://forem.com/reggi/the-postgres-first-trend-1jhl</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt;: Because of serverless, the need for database wrapper api's like &lt;a href="https://postgrest.org/en/stable/" rel="noopener noreferrer"&gt;PostgREST&lt;/a&gt; have become mainstay. &lt;a href="https://supabase.com/" rel="noopener noreferrer"&gt;Supabase&lt;/a&gt; allows RPC postgres function calls which can house more complex "database logic" eliminating the need for an in-app model. &lt;a href="https://github.com/omnigres/omnigres" rel="noopener noreferrer"&gt;Omnigres&lt;/a&gt; is a project aiming to take this concept one step further with including an HTTP server within postgres itself. I also was writing a bunch of &lt;a href="https://github.com/reggi/pg_json_functions" rel="noopener noreferrer"&gt;pg functions&lt;/a&gt; this week which is why I noticed this trend.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There's a new way of thinking about databases / apps that's slowly morphing the way people think and opening developers up to new possibilities and ways of building apps. There's something brewing because i've been having this idea and I'm also seeing others have it as well.&lt;/p&gt;

&lt;p&gt;The idea overall is to set postgres as the main "backend api". That we shouldn't really ever need to bundle multiple queries into a transaction in a different programming language or server at all.&lt;/p&gt;

&lt;p&gt;To be honest i've fallen in love with &lt;a href="https://supabase.com/" rel="noopener noreferrer"&gt;Supabase&lt;/a&gt;. This post is not sponsored by them in any way. I just really love the product, and for me, as a champion of self-hosting and open source that's saying a lot. I believe that everyone should have their own personal database / api and "own their own data" and Supabase aids in that mission. I really love that they also have a storage solution so all your data (like a blog post and images) can live truly live one place.&lt;/p&gt;

&lt;p&gt;There's two ways to use Supabase through the API they provide, or directly with any postgres client. If you wanted to run postgres queries directly you can, but if you wanted some of their out-of-the-box features like auth and signup flow, you can use that too. It's a real swiss-army knife. What I find really interesting is something they provide called &lt;a href="https://supabase.com/docs/reference/javascript/rpc" rel="noopener noreferrer"&gt;Supabase RPC&lt;/a&gt; which allow you to call a custom postgres function from the supabase client. Calling functions directly can minimise the mount of vendor-lockin you have with supabase because if you use their api client, you can't make queries directly and so you'd have to make a model system using their library which is proprietary. This whole mess is made complicated and necessary when it comes to serverless hosting, which requires the need for an API rather then a direct postgres connection. &lt;/p&gt;

&lt;p&gt;So while I was brewing about all these ideas I've been thinking of designing a slew of postgres functions that would act somewhat as "the api" i'd write the to my postgres database and call those functions rather than a traditional model, things like pagination, joins, merging, user data etc, any data massaging you'd normally do within your app. &lt;/p&gt;

&lt;p&gt;Then the other day while scrolling hacker news this came up:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/omnigres" rel="noopener noreferrer"&gt;
        omnigres
      &lt;/a&gt; / &lt;a href="https://github.com/omnigres/omnigres" rel="noopener noreferrer"&gt;
        omnigres
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      The All-in-One Database
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Omnigres&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fomnigres%2Fomnigres%2Farch.svg" 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%2Fgithub.com%2Fomnigres%2Fomnigres%2Farch.svg" alt="Omnigres Architecture"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;
&lt;a href="https://docs.omnigres.org" rel="nofollow noopener noreferrer"&gt;&lt;b&gt;Documentation&lt;/b&gt;&lt;/a&gt; |
&lt;a href="https://github.com/omnigres/omnigres/wiki/Bounties" rel="noopener noreferrer"&gt;&lt;b&gt;Bounties&lt;/b&gt;&lt;/a&gt;
&lt;/p&gt;




&lt;p&gt;&lt;a href="https://discord.omnigr.es" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/0c76f3de40e715b48668bc7e63291455ca957267ca67626aa6980941c3952b56/68747470733a2f2f696d672e736869656c64732e696f2f646973636f72642f313036303536383938313732353030333738393f6c6162656c3d446973636f7264" alt="Discord Chat"&gt;&lt;/a&gt;
&lt;a href="https://docs.omnigres.org" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/b98ce828f60b5999c2531fca85fc9f06a3983bc48dc198c5969bc6c2d3cc0743/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f646f63732d72656164792d677265656e" alt="Documentation"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/e4ae4477464c32cf88754af0d6c667544ffe390bfbea3078acbe8b36690df93d/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f6f6d6e69677265732f6f6d6e6967726573"&gt;&lt;img src="https://camo.githubusercontent.com/e4ae4477464c32cf88754af0d6c667544ffe390bfbea3078acbe8b36690df93d/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f6f6d6e69677265732f6f6d6e6967726573" alt="License"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Omnigres makes Postgres a developer-first application platform. You can deploy a single database instance and it can host your entire application, scaling as needed.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Running application logic &lt;strong&gt;inside&lt;/strong&gt; or &lt;strong&gt;next to&lt;/strong&gt; the database instance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment&lt;/strong&gt; provisioning (&lt;strong&gt;Git&lt;/strong&gt;, &lt;strong&gt;containers&lt;/strong&gt;, etc.)&lt;/li&gt;
&lt;li&gt;Database instance serves &lt;strong&gt;HTTP&lt;/strong&gt;, &lt;strong&gt;WebSocket&lt;/strong&gt; and other protocols&lt;/li&gt;
&lt;li&gt;In-memory and volatile on-disk &lt;strong&gt;caching&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Routine application building blocks (&lt;strong&gt;authentication&lt;/strong&gt;, &lt;strong&gt;authorization&lt;/strong&gt;, &lt;strong&gt;payments&lt;/strong&gt;, etc.)&lt;/li&gt;
&lt;li&gt;Database-modeled application logic via &lt;strong&gt;reactive&lt;/strong&gt; queries&lt;/li&gt;
&lt;li&gt;Automagic remote &lt;strong&gt;APIs&lt;/strong&gt; and &lt;strong&gt;form&lt;/strong&gt; handling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live&lt;/strong&gt; data updates&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Blogs and Publications&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.omnigres.com" rel="nofollow noopener noreferrer"&gt;Omnigres blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🏃 Quick start&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;The fastest way to try Omnigres out is by using its &lt;a href="https://github.com/omnigres/omnigres/pkgs/container/omnigres" rel="noopener noreferrer"&gt;container image&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;docker volume create omnigres
docker run --name omnigres --mount source=omnigres,target=/var/lib/postgresql/data \
           -p 127.0.0.1:5432:5432 -p 127.0.0.1:8080:8080 -p 127.0.0.1:8081:8081 --rm ghcr.io/omnigres/omnigres-17:latest
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Now you can connect to it:&lt;/span&gt;
psql -h localhost -p 5432 -U omnigres omnigres &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; password is `omnigres`&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-alert markdown-alert-tip"&gt;
&lt;p class="markdown-alert-title"&gt;Tip&lt;/p&gt;
&lt;p&gt;Replace &lt;code&gt;ghcr.io/omnigres/omnigres-17&lt;/code&gt;…&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/omnigres/omnigres" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;a href="https://github.com/omnigres/omnigres" rel="noopener noreferrer"&gt;omnigres&lt;/a&gt; is "Postgres as a platform" that takes all of this one step further.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Omnigres makes Postgres a developer-first application platform. You can deploy a single database instance and it can host your entire application, scaling as needed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What's interesting is I had started working on a whole bunch of json function for postgres the same week I found omnigres on HN. Take a look at my progress here:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/reggi" rel="noopener noreferrer"&gt;
        reggi
      &lt;/a&gt; / &lt;a href="https://github.com/reggi/pg_json_functions" rel="noopener noreferrer"&gt;
        pg_json_functions
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A collection of Postgres functions designed to work with supabase's RPC calls.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What do I hope to achive?&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;I've been tinkering around with different ideas when it comes to the idea of having structured data in the database without the need to utilize the typical relational-table based (normal) way you'd use postgres.&lt;/p&gt;
&lt;p&gt;I've been messing around with things like &lt;a href="https://yunohost.org/" rel="nofollow noopener noreferrer"&gt;yunohost.org&lt;/a&gt;, my own personal json API for blog posts, and other forms of structured content using &lt;a href="https://strapi.io/" rel="nofollow noopener noreferrer"&gt;strapi.io&lt;/a&gt; (a headless cms that utilizes postgres). The state of self-hosted software has deviated slightly from my vision of a simple easy to use utopia. On one side of the spectrum is &lt;code&gt;yunohost.org&lt;/code&gt;, which offers a way to have a single server "install" other third-party self-hosted apps and using complex nginx rules route request to running ports all on the same machine, which is resource intensive, for each "app" running on the same box more ram, cpu, and storage is needed if you want to…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/reggi/pg_json_functions" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;It's a bunch of functions that make it slightly easier to interact with jsonb columns, and it's mostly me tinkering around with functions and trying to learn the syntax with ChatGPT.&lt;/p&gt;

&lt;p&gt;Anyway, that's all I got for this post. I do think there's a shift in how people are thinking about what a database should be and should do. Mainly because at some point in the 'serverless' architecture there needs to be a single server (or cluster of servers) to manage database connections, and what we ask of that server might change over time.&lt;/p&gt;

</description>
      <category>database</category>
      <category>postgres</category>
    </item>
    <item>
      <title>Hosting Services List</title>
      <dc:creator>Tea Reggi</dc:creator>
      <pubDate>Tue, 17 Oct 2023 16:17:57 +0000</pubDate>
      <link>https://forem.com/reggi/hosting-services-list-54pj</link>
      <guid>https://forem.com/reggi/hosting-services-list-54pj</guid>
      <description>&lt;p&gt;This is a list of hosting services I find I want to keep all in one place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Static-Site hosting
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://surge.sh/" rel="noopener noreferrer"&gt;https://surge.sh/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pages.github.com/" rel="noopener noreferrer"&gt;https://pages.github.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bip.sh/" rel="noopener noreferrer"&gt;https://bip.sh/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  App hosting
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://render.com/" rel="noopener noreferrer"&gt;https://render.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dokku.com/" rel="noopener noreferrer"&gt;https://dokku.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://easypanel.io/" rel="noopener noreferrer"&gt;https://easypanel.io/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.flightcontrol.dev/" rel="noopener noreferrer"&gt;https://www.flightcontrol.dev/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://railway.app/" rel="noopener noreferrer"&gt;https://railway.app/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://coolify.io/" rel="noopener noreferrer"&gt;https://coolify.io/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/" rel="noopener noreferrer"&gt;https://www.digitalocean.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.netlify.com/" rel="noopener noreferrer"&gt;https://www.netlify.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hostineer.com/" rel="noopener noreferrer"&gt;https://hostineer.com/&lt;/a&gt; (worker owned)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fly.io/" rel="noopener noreferrer"&gt;https://fly.io/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tunneling Self-hosting / Install platform
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kubesail.com/homepage" rel="noopener noreferrer"&gt;https://kubesail.com/homepage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yunohost.org/" rel="noopener noreferrer"&gt;https://yunohost.org/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Framework Interoperable Component Libraries Using Lit Web Components.</title>
      <dc:creator>Tea Reggi</dc:creator>
      <pubDate>Sun, 08 Oct 2023 04:08:32 +0000</pubDate>
      <link>https://forem.com/reggi/framework-interoperable-component-libraries-using-lit-web-components-43ac</link>
      <guid>https://forem.com/reggi/framework-interoperable-component-libraries-using-lit-web-components-43ac</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TLDR:&lt;/strong&gt; This post is made to rave about repo I found called &lt;a href="https://github.com/json-schema-form-element/core" rel="noopener noreferrer"&gt;json-schema-form-element&lt;/a&gt; a lit-based component library for form generation that works with all major front-end / metaframeworks, but also to show how I'm betting on Lit for the future of the web._&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://lit.dev/" rel="noopener noreferrer"&gt;Lit&lt;/a&gt; is having a moment, one would say it's on fire 🔥🤣. It's being used everywhere from the &lt;a href="https://medium.com/@addyosmani/photoshop-is-now-on-the-web-38d70954365a" rel="noopener noreferrer"&gt;web version of Photoshop&lt;/a&gt; to &lt;a href="https://www.theverge.com/2023/10/6/23906082/microsoft-windows-app-store-web-version-launch" rel="noopener noreferrer"&gt;Microsoft's new windows app store&lt;/a&gt;. I think something clicked for me recently as to why lit is the major choice when it comes to the future of the web, and web components overall. &lt;/p&gt;

&lt;p&gt;Component libraries, love them or hate them it's how web developers quickly cobble together bits of code to form a semi-usable website. From &lt;a href="https://getbootstrap.com/2.0.2/" rel="noopener noreferrer"&gt;Bootstrap&lt;/a&gt; to things like &lt;a href="https://v4.mui.com/" rel="noopener noreferrer"&gt;Material UI&lt;/a&gt; and more recently &lt;a href="https://www.radix-ui.com/" rel="noopener noreferrer"&gt;Radix UI&lt;/a&gt; and &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind&lt;/a&gt;. With the way that front-end frameworks have been proliferating there are a lot of frameworks out there React, Angular, Solid, Vue, Astro and Lit. How are we able to write framework agnostic component libraries? That way you don't need to re-learn and find alternatives to the things you love to use if you want to build an app in a different framework.&lt;/p&gt;

&lt;p&gt;Enter &lt;a href="https://shoelace.style/" rel="noopener noreferrer"&gt;Shoelace&lt;/a&gt; an undoubtedly witty pin on "Bootstrap", that aims at bringing back direct usable components but by offering web-components by default, but it also offers web-components wrapped in a &lt;a href="https://github.com/shoelace-style/shoelace/blob/next/scripts/make-react.js#L59C9-L59C16" rel="noopener noreferrer"&gt;react component as well&lt;/a&gt;. This is at the core of building a framework interoperable component library, 1) use web-components, 2) wrap them in that specific language's syntax.&lt;/p&gt;

&lt;p&gt;While I haven't used &lt;code&gt;Shoelace&lt;/code&gt; directly yet, they tout that it's possible to fully customize the components styles, unlike bootstrap which was expected to be used as is. The overuse of bootstrap ultimately led to the homogenization of web-design, making websites feel "bootstrap". With things like CSS Variables and custom styles I'm hoping &lt;code&gt;Shoelace&lt;/code&gt; doesn't fall into this trap.&lt;/p&gt;

&lt;p&gt;I've been very passionate about a project called &lt;code&gt;react-jsonschema-form&lt;/code&gt; (&lt;a href="https://github.com/rjsf-team/react-jsonschema-form" rel="noopener noreferrer"&gt;github&lt;/a&gt;, &lt;a href="https://rjsf-team.github.io/react-jsonschema-form/" rel="noopener noreferrer"&gt;editor&lt;/a&gt;). I personally hate writing forms, and love the idea of serializable components, schema, validation all in one. I've always wanted an alternative to this project that offered an alternative to react, and possibly the ability to render a schema form to static HTML (like ssg). &lt;/p&gt;

&lt;p&gt;I've thought about this a lot while using other frameworks like &lt;a href="https://fresh.deno.dev/" rel="noopener noreferrer"&gt;Deno Fresh&lt;/a&gt; which uses &lt;a href="https://preactjs.com/" rel="noopener noreferrer"&gt;Preact&lt;/a&gt; under the hood, mainly for JSX templating, but also for islands functionality. Within that framework you can't really use React component libraries. You start to think more about generating static HTML like this example from the Deno blog [A Whole Website in a Single JavaScript File, cont'd](&lt;a href="https://deno.com/blog/a-whole-website-in-a-single-js-file-continued" rel="noopener noreferrer"&gt;https://deno.com/blog/a-whole-website-in-a-single-js-file-continued&lt;/a&gt;, which shows building a simple webpage with routes all in one typescript file, a site that serves no Javascript to the browser.&lt;/p&gt;

&lt;p&gt;The other day I stumbled upon my dream project the other day &lt;code&gt;json-schema-form-element&lt;/code&gt; (&lt;a href="https://github.com/json-schema-form-element/core" rel="noopener noreferrer"&gt;github&lt;/a&gt;, &lt;a href="https://jsfe.js.org/AllFeatures" rel="noopener noreferrer"&gt;editor&lt;/a&gt;) which is heavily inspired by the react counterpart. It's exactly what I wanted to make. This project, &lt;code&gt;json-schema-form-element&lt;/code&gt; is a masterclass in how to make a modern web-component-first / "authored" library which is also interoperable with all other frameworks.&lt;/p&gt;

&lt;p&gt;The example repo uses Astro which allows the author to demo all of the usages for the library, check it out here: &lt;a href="https://github.com/json-schema-form-element/examples/blob/main/src/pages/index.astro#L70-L85" rel="noopener noreferrer"&gt;index.astro&lt;/a&gt;. Using Astro for this is just another thing the author got right here.&lt;/p&gt;

&lt;p&gt;I'm really excited about all this, and it makes me have some faith in the web again. I think that Lit is a step in the right direction especially the ability to do SSR / SSG and hydrate a web page. Hopefully 🤞 Shoelace can get SSR running, &lt;a href="https://github.com/shoelace-style/shoelace/issues/778" rel="noopener noreferrer"&gt;which is currently one hurdle&lt;/a&gt;, but I think it is achievable.&lt;/p&gt;

&lt;p&gt;Anyway, what do you think of Lit? What do you think of the approach that &lt;code&gt;json-schema-form-element&lt;/code&gt; takes at making an interoperable component library? Comment below 👇 and let me know!&lt;/p&gt;

&lt;p&gt;Follow me on Mastodon for more thoughts on the future of web-development, web components, and typescript stuff &lt;a href="https://indieweb.social/@thomasreggi" rel="noopener noreferrer"&gt;@indieweb.social@thomasreggi&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://indieweb.social/@thomasreggi" rel="noopener noreferrer"&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%2Fyykf8is1q8viw7ss97w7.png" alt="Thomas Reggi's Mastodon Profile Image" width="578" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webcomponents</category>
      <category>lit</category>
      <category>shoelace</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Using Supabase as a database and image store with Strapi CMS.</title>
      <dc:creator>Tea Reggi</dc:creator>
      <pubDate>Sun, 01 Oct 2023 17:03:46 +0000</pubDate>
      <link>https://forem.com/reggi/using-supabase-as-a-database-and-image-store-with-strapi-cms-2kkk</link>
      <guid>https://forem.com/reggi/using-supabase-as-a-database-and-image-store-with-strapi-cms-2kkk</guid>
      <description>&lt;p&gt;I love the idea of a headless CMS to maintain my own personal API. The goal is to have a central source for all your data, blog posts, link lists, even job history data for a resume, small blurbs, summaries, and I have one for a list of github projects.&lt;/p&gt;

&lt;p&gt;I fell in love with Strapi a couple months back and was interested in revisiting the project I created locally. What's cool about it is you can create custom content types with their own fields and you get an API from it. &lt;/p&gt;

&lt;p&gt;I also love Supabase I think it's a great service with a ton of useful parts, it's a full postgres database, and an API layer too, so you don't even need to use Strapi's api if you wanted to. Strapi could just be used to write data to Supabase. Strapi also supports media uploads, and rather then using AWS or GCP I liked the idea of everything being under one service.&lt;/p&gt;

&lt;p&gt;Here's the setup on how to use Supabase as both your database and media storage. &lt;/p&gt;

&lt;p&gt;Once you have a strapi project setup you need to simply set your env vars to your supabase credentials that's the easy part then you're interacting with supabase directly.&lt;/p&gt;

&lt;p&gt;To get the image store to work I had a bunch of issues. I found a module on github &lt;a href="https://github.com/crubier/strapi-provider-upload-supabase" rel="noopener noreferrer"&gt;https://github.com/crubier/strapi-provider-upload-supabase&lt;/a&gt; I ended up going with a more up to date fork &lt;code&gt;https://github.com/arkivedao/strapi-provider-upload-supabase&lt;/code&gt; you can npm install this by using the github url. &lt;/p&gt;

&lt;p&gt;Create a new file &lt;code&gt;config/plugins.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="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="nx"&gt;env&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;upload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;strapi-provider-upload-supabase&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;providerOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;apiUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SUPABASE_API_URL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SUPABASE_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SUPABASE_BUCKET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SUPABASE_DIRECTORY&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once I set this up I was getting this error:&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;"statusCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"401"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Invalid JWT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"new row violates row-level security policy for table &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;objects&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&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;I had to turn of RLS for the object store in order to get it to work using the answer found here: &lt;a href="https://stackoverflow.com/questions/72861584/supabase-bucket-policy-to-insert-file-not-working" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/72861584/supabase-bucket-policy-to-insert-file-not-working&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then to get thumbnails to work locally I needed my &lt;code&gt;config/middlewares.ts&lt;/code&gt; file to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="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="nx"&gt;env&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;strapi::errors&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;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;strapi::security&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;contentSecurityPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;useDefaults&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;directives&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;connect-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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'self'&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;https:&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;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="s1"&gt;img-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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'self'&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;data:&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;blob:&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;res.cloudinary.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// cloudinary images&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lh3.googleusercontent.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// google avatars&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;platform-lookaside.fbsbx.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// facebook avatars&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dl.airtable.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// strapi marketplace,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;market-assets.strapi.io&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SUPABASE_API_URL&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;media-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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'self'&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;data:&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;blob:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SUPABASE_API_URL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
          &lt;span class="na"&gt;upgradeInsecureRequests&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="p"&gt;},&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;strapi::cors&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;strapi::poweredBy&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;strapi::logger&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;strapi::query&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;strapi::body&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;strapi::session&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;strapi::favicon&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;strapi::public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it worked. Now I have a Strapi server wired up to use Supabase! Hope this helps someone out there.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>database</category>
    </item>
    <item>
      <title>A custom "iterable" class</title>
      <dc:creator>Tea Reggi</dc:creator>
      <pubDate>Sat, 08 Jul 2023 06:31:28 +0000</pubDate>
      <link>https://forem.com/reggi/a-custom-iterable-class-ej8</link>
      <guid>https://forem.com/reggi/a-custom-iterable-class-ej8</guid>
      <description>&lt;p&gt;Let's say you want to create a custom class, this class has a constructor parameter. For this example let's say we want to manage a "person", more like a "Person".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;PlainPerson&lt;/span&gt; &lt;span class="o"&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;age&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's say from this plain object we want to add some cool getters or methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PlainPerson&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&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;return&lt;/span&gt; &lt;span class="k"&gt;this&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="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;age &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="k"&gt;this&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;age&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;isOver20 &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="k"&gt;this&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;age&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&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;Let's now say I want to iterate over many people. I can easily use an Array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;john&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;Person&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;john&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&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;people&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;john&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;filteredPeople&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;people&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;person&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isOver20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this works, this is perfectly acceptable. But what if you wanted more?&lt;/p&gt;

&lt;p&gt;What if you wanted a collection of &lt;code&gt;Person&lt;/code&gt; to do more then just what a normal array could provide?&lt;/p&gt;

&lt;p&gt;What if I wanted to add all ages up for all the "people"?&lt;/p&gt;

&lt;p&gt;Then I might consider a &lt;code&gt;People&lt;/code&gt; class as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;People&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;totalAge &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;person&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;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;acc&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But now the question is what should the constructor of &lt;code&gt;People&lt;/code&gt; recieve? Should it recieve &lt;code&gt;Person&lt;/code&gt;, or &lt;code&gt;PlainPerson&lt;/code&gt;? Should it recieve an array of these? Can they be spread? Can &lt;code&gt;People&lt;/code&gt; recieve many &lt;code&gt;People&lt;/code&gt; and flatten them down? &lt;/p&gt;

&lt;p&gt;I found myself asking these questions and decided to create something that would fix this issue and create a basic reproducable structure for any custom iterable. It kind of works like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;People&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FactoryIterable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns two standard classes that you could use similar to how I described above. A &lt;code&gt;Person&lt;/code&gt; and &lt;code&gt;People&lt;/code&gt; they don't have any custom methods yet. In order to add them we need to extend these base-classes. So it may be best to rename them. I usually use the prefix &lt;code&gt;Proto&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ProtoPerson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ProtoPeople&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;FactoryIterable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;PlainPerson&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;multipleConverter&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="nx"&gt;PlainPerson&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;PlainPerson&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;PlainPerson&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;value&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="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;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="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;value&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;ProtoPerson&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&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;return&lt;/span&gt; &lt;span class="k"&gt;this&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="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;age &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="k"&gt;this&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;age&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;isOver20 &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="k"&gt;this&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;age&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;People&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;ProtoPeople&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;Person&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;People&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;arrayMethods&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;People&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;totalAge &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;person&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;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;acc&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The coolest thing about how these work is the input resolving. A &lt;code&gt;Person&lt;/code&gt; can take &lt;code&gt;PlainPerson&lt;/code&gt; and&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;plainJohn&lt;/span&gt; &lt;span class="o"&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="s2"&gt;John&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&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;john&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;Person&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;plainJohn&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;john2&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;Person&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;john&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;john&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;plainJohn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;john2&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;plainJohn&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;a&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;People&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;plainJohn&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;b&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;People&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;john&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;People&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;d&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;People&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;e&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;People&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;plainJohn&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;f&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;People&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;john&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;g&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;People&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="nf"&gt;assertEquals&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;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;plainJohn&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nf"&gt;assertEquals&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="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="nx"&gt;plainJohn&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nf"&gt;assertEquals&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;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;plainJohn&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nf"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;plainJohn&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nf"&gt;assertEquals&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="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="nx"&gt;plainJohn&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nf"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;plainJohn&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nf"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;plainJohn&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a pretty bare-bones overview of how you'd use it. There are a could of extra details when it comes to input types and even circular input types, as well as adding generics. I have a couple examples of &lt;code&gt;FactoryIterable&lt;/code&gt; in the wild as well as the source code. &lt;/p&gt;

&lt;p&gt;Here's a main working test that demos this Person / People class &lt;a href="https://github.com/reggi/tree_lint2/blob/main/factory_iterable/test.ts" rel="noopener noreferrer"&gt;https://github.com/reggi/tree_lint2/blob/main/factory_iterable/test.ts&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's the source &lt;a href="https://github.com/reggi/tree_lint2/blob/main/factory_iterable/mod.ts" rel="noopener noreferrer"&gt;https://github.com/reggi/tree_lint2/blob/main/factory_iterable/mod.ts&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are a couple other examples:&lt;/p&gt;

&lt;p&gt;Node:&lt;br&gt;
&lt;a href="https://github.com/reggi/tree_lint2/blob/main/graph/node/mod.ts" rel="noopener noreferrer"&gt;https://github.com/reggi/tree_lint2/blob/main/graph/node/mod.ts&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;EdgeNode &lt;a href="https://github.com/reggi/tree_lint2/blob/main/graph/edge_node/mod.ts" rel="noopener noreferrer"&gt;https://github.com/reggi/tree_lint2/blob/main/graph/edge_node/mod.ts&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Edge &lt;a href="https://github.com/reggi/tree_lint2/blob/main/graph/edge/mod.ts" rel="noopener noreferrer"&gt;https://github.com/reggi/tree_lint2/blob/main/graph/edge/mod.ts&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>I invented a CLI tool to organize my typescript projects.</title>
      <dc:creator>Tea Reggi</dc:creator>
      <pubDate>Fri, 23 Jun 2023 08:11:16 +0000</pubDate>
      <link>https://forem.com/reggi/i-invented-a-cli-tool-to-organize-my-typescript-projects-1j82</link>
      <guid>https://forem.com/reggi/i-invented-a-cli-tool-to-organize-my-typescript-projects-1j82</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt;: This article is about a command-line tool designed to analyze and enforce a specific hierarchical folder structure within TypeScript projects called &lt;a href="https://github.com/reggi/tree_lint" rel="noopener noreferrer"&gt;tree lint&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I often find myself coding outside the standard guidelines of a framework; no MVC for me. I'm usually deep in the weeds making file-system calls, reading here, writing there, and creating a CLI script using node or deno - something I want to be reusable down the line. I frequently struggle to wrangle all the files and folders I create. I usually start out with the approach to have the whole structure be flat, full of focus and vigilance.&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%2F3vvv0rdc0a1yd5tc859l.gif" 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%2F3vvv0rdc0a1yd5tc859l.gif" alt="Gif from all gas no breaks of Andrew interviewing a flat-earther, in which the interviewee says " width="498" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, soon after adopting this philosophy, things start to unravel. The entire folder becomes swamped with too much happening. Without compartmentalization of the files and folders into their respective silos, there's chaos.&lt;/p&gt;

&lt;p&gt;Flat structures in entire projects can make it exceedingly difficult to segment things if you want to break the project out into multiple repositories. I often find myself coding and creating something within that project that could be utilized elsewhere and in other projects. Unfortunately, flat structures make it impossible to easily isolate a single node in the hierarchy, thereby forcing you to dismantle everything. &lt;/p&gt;

&lt;p&gt;So, I've been trying to create smaller files and more of them, and placing them into different subdirectories where they share some characteristic or logic. I usually attempt to keep all these directories flat at the root of the project to avoid deep nesting, but the same thing repeats itself.&lt;/p&gt;

&lt;p&gt;I often create one of these silos, make it perfect, and then realize I've duplicated logic from outside the silo that the rest of the code needs. What to do? I'm not a big fan of one "project" that's siloed reaching into the "cookie-jar" of another project that's supposedly siloed.&lt;/p&gt;

&lt;p&gt;All of this adds to the complexity of a project. Not having a clear hierarchy of how the files and folders interact really throws me off. Even when I "invent" a structure myself, I come back to it days or even hours later, look at it and ask, "What have I done?"&lt;/p&gt;

&lt;p&gt;If none of the issues I've described resonate with you because you write components in a components folder for a living, please feel free to close this browser tab. This post isn't for you.&lt;/p&gt;

&lt;p&gt;Introducing &lt;a href="https://github.com/reggi/tree_lint" rel="noopener noreferrer"&gt;&lt;code&gt;tree_lint&lt;/code&gt;&lt;/a&gt;, a CLI tool that scans a directory and advises you on how the hierarchy should appear. It adheres to a few simple rules. These guidelines facilitate the understanding of which code utilizes what. Furthermore, they simplify the process of sectioning off chunks and transforming them into their own repositories.&lt;/p&gt;

&lt;p&gt;Now, a repository is worth a thousand words, so it might be best to just go check out the repo and get an understanding of the layout from there:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/reggi" rel="noopener noreferrer"&gt;
        reggi
      &lt;/a&gt; / &lt;a href="https://github.com/reggi/tree_lint" rel="noopener noreferrer"&gt;
        tree_lint
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      🌴 ✅ A command-line tool designed to analyze and enforce a specific hierarchical folder structure within TypeScript projects.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;tree lint&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;This is a command-line tool. Its job is to help you organize your TypeScript projects. The philosophy of the structure is to allow you to keep all relevant files within the same hierarchy node so it's easy to break it out into its own repository. The rules for how the directories/projects work are as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Each dir is a "project".&lt;/li&gt;
&lt;li&gt;Each project can contain whatever files you want. The convention is ideally a single &lt;code&gt;mod.ts&lt;/code&gt;, &lt;code&gt;test.ts&lt;/code&gt; etc file.&lt;/li&gt;
&lt;li&gt;Dependents of the project live as a sibling next to its &lt;code&gt;mod.ts&lt;/code&gt; in a folder and so on, building a hierarchy.&lt;/li&gt;
&lt;li&gt;Projects that are not used by any other project will be at the top of the tree.&lt;/li&gt;
&lt;li&gt;Projects with only one usage should be nested within that project.&lt;/li&gt;
&lt;li&gt;Projects that are used by more than one project are placed as a sibling of the highest (top-most) project in…&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/reggi/tree_lint" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Each dir is a "project".&lt;/li&gt;
&lt;li&gt;Each project can contain whatever files you want. The convention is ideally a single &lt;code&gt;mod.ts&lt;/code&gt;, &lt;code&gt;test.ts&lt;/code&gt; etc file.&lt;/li&gt;
&lt;li&gt;Dependents of the project live as a sibling next to its &lt;code&gt;mod.ts&lt;/code&gt; in a folder and so on, building a hierarchy.&lt;/li&gt;
&lt;li&gt;Projects that are not used by any other project will be at the top of the tree.&lt;/li&gt;
&lt;li&gt;Projects with only one usage should be nested within that project.&lt;/li&gt;
&lt;li&gt;Projects that are used by more than one project are placed as a sibling of the highest (top-most) project in the tree.&lt;/li&gt;
&lt;li&gt;Circular projects will be siblings.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There's a lot of complex logic to get circular dependencies to work correctly and to do this hoisting logic to make things siblings. If you're interested, feel free to check out the repo.&lt;/p&gt;

&lt;p&gt;This might very well just be a "me" problem. But on the off chance that it's not, I'd love to hear about it. Comment below if this is useful or if you, too, struggle with the agonizing task of organizing "files" and "folders". I'm now at peace, knowing there's a lintable solution, and I don't need to think about it anymore 🧘.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>deno</category>
      <category>productivity</category>
    </item>
    <item>
      <title>My Frustration With Web Components.</title>
      <dc:creator>Tea Reggi</dc:creator>
      <pubDate>Mon, 29 May 2023 18:51:40 +0000</pubDate>
      <link>https://forem.com/reggi/my-frustration-with-web-components-4f6i</link>
      <guid>https://forem.com/reggi/my-frustration-with-web-components-4f6i</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt;: I want to tool to build and bundle a HTML file (with &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; into a single JS file&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I have a bone to pick with Web Components at the moment. Mainly tooling around bundling and the overall experience of creating them. All which lead to a hit in performance when using them.&lt;/p&gt;

&lt;p&gt;One use case I come back to over and over is a markdown editor. I've cobbled together a working version of a &lt;code&gt;simple-mde&lt;/code&gt; web component. You can see it for yourself in &lt;a href="https://dev.tocomponent"&gt;this gist&lt;/a&gt;. It's loosely based off this 5-year-old &lt;a href="https://github.com/Shridhad/wc-simplemde" rel="noopener noreferrer"&gt;repo here&lt;/a&gt; — a repo filled with outdated build tools, gulp and bower.&lt;/p&gt;

&lt;p&gt;The format for this component is fairly straightforward. It includes a &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; with a single nested &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag. And a single &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag that creates the &lt;code&gt;HTMLElement&lt;/code&gt; and adds it to the DOM. The issue with this code is that both the CSS and JS have external dependencies, totaling 4. They load from CDNs, which comes with its host of pros and cons. At this point, we've agreed that it's best practice to pull down and minify all of this.&lt;/p&gt;

&lt;p&gt;To make matters worse, the way I've built this file in HTML is unusable. There's no standard way to include HTML within a document. A couple of years back, browsers teased us with being able to do &lt;code&gt;&amp;lt;link rel="include" href="/component.html"&amp;gt;&lt;/code&gt;, which doesn't work anymore, is impossible to even find ever existed on the internet. &lt;a href="https://www.jotform.com/blog/html5-imports-import-html-files-into-html-files-83467/" rel="noopener noreferrer"&gt;Here's a post&lt;/a&gt; about it. Of course, this functionality can still be added via custom code.&lt;/p&gt;

&lt;p&gt;But what I want is a way to take the WORKING file I created above for my &lt;code&gt;simple-mde&lt;/code&gt; web component and convert that HTML file to a bundled and minified JS file. I want all of the dependencies to be bundled into the file, the &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; code to be converted to a string, and automatically add it to the DOM when the file loads. Finally, the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag would be removed. All of this can be bundled as a single JS file, so the entire component is contained.&lt;/p&gt;

&lt;p&gt;Why don't we have a way to bundle web components this way? 😤&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>webcomponents</category>
      <category>html</category>
    </item>
  </channel>
</rss>
